* {
  box-sizing: border-box;
}

html {
  font-family: var(--font-body);
  font-size: 16px;
  background: var(--bg);
  color: var(--text);
  scroll-behavior: smooth;
}

body {
  position: relative;
  min-height: 100vh;
  margin: 0;
  display: flex;
  flex-direction: column;
  overflow-x: hidden;
  background: var(--bg);
}

body.menu-open {
  overflow: hidden;
}

::selection {
  color: var(--on-fill);
  background: var(--red);
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
}

:where(svg) {
  display: block;
  fill: currentColor;
}

/* Flare square focus ring */
:focus-visible {
  outline: 2px solid var(--red);
  outline-offset: 4px;
}

main {
  flex: 1;
  display: flex;
  flex-direction: column;
}

/* ── Icon helper ── */
.icon {
  display: block;
  width: 1em;
  height: 1em;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ── Buttons (two variants, radius 0, no shadow) ── */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  min-height: 44px;
  border: 1.5px solid var(--line-field);
  border-radius: 0;
  padding: 0.8125rem 1.625rem;
  color: var(--text);
  font-family: var(--font-body);
  font-size: 0.875rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: transparent;
  cursor: pointer;
  transition:
    transform 0.14s ease,
    border-color 0.14s ease,
    color 0.14s ease,
    background 0.14s ease;
}

.button svg {
  flex: none;
  width: 0.9rem;
  height: 0.9rem;
}

.button--primary {
  border-color: var(--red);
  color: var(--on-fill);
  background: var(--red);
}

/* ── Film grain overlay ── */
.grain {
  position: relative;
}

.grain::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Above photo layers so the grain textures them (the split hero photo's
     flex `order` also reorders painting, which would otherwise paint the
     photo over the grain). Content sits at z-index 2+. */
  z-index: 1;
  pointer-events: none;
  opacity: 0.055;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140' viewBox='0 0 140 140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.75' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.8'/%3E%3C/svg%3E");
}

/* ── Country flag ── */
.flag {
  flex-shrink: 0;
  width: 1.1em;
  height: 0.825em;
  border-radius: 1px;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25);
}

/* ── Section eyebrow / kicker ── */
.section-kicker,
.eyebrow {
  display: inline-block;
  color: var(--red);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
}

/* Filled red tag variant — the "SHOWS" / "PRESS KIT" / "UPCOMING" tab */
.eyebrow--tag {
  padding: 0.3rem 0.75rem;
  color: var(--on-fill);
  letter-spacing: 0.18em;
  background: var(--red);
}

/* ── Page title block: red tag + huge headline over a faint red stage glow ── */
.page-title {
  position: relative;
  isolation: isolate;
  padding: clamp(4rem, 10vw, 7rem) var(--gutter) clamp(2.5rem, 6vw, 4rem);
  background:
    radial-gradient(75% 55% at 50% 0%, rgb(var(--red-rgb) / 0.12), transparent 70%), var(--bg);
}

.page-title__inner {
  width: min(100%, var(--container));
  margin: 0 auto;
}

.page-title h1 {
  margin: 0.5rem 0 0;
  color: var(--text);
  font-family: var(--font-display);
  font-size: clamp(2.75rem, 8.5vw, 6.5rem);
  font-weight: 800;
  line-height: 0.9;
  letter-spacing: -0.03em;
  text-transform: uppercase;
}

.page-title__note {
  margin: 0.875rem 0 0;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* ── Reveal animation ── */
/* Gated on the `js` class base.html sets inline: without JS nothing would ever
   add .is-visible, so an ungated opacity: 0 would hide the content for good. */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(26px);
  transition:
    opacity 0.7s ease,
    transform 0.7s ease;
}

.js [data-reveal].is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* ── Site header ── */
.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  min-height: var(--header-h);
  border-bottom: 2px solid var(--line-head);
  background: rgb(var(--bg-rgb) / 0.94);
}

/* 3px pulsing red filament at the very top of the header */
.site-header::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 3px;
  background: var(--red);
  animation: filamentPulse 6s ease-in-out infinite;
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  width: min(100%, var(--container));
  min-height: var(--header-h);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.brand {
  display: inline-flex;
  align-items: center;
  min-width: 0;
}

.brand-logo {
  display: block;
  width: auto;
  height: 26px;
  filter: drop-shadow(0 0 14px rgb(var(--amber-rgb) / 0.45));
}

/* Nav: all-caps, 700, Ash color, hover → Bone, current = Bone + 2px Flare underline */
.site-nav {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 2.4vw, 2rem);
  color: var(--text-muted);
  font-family: var(--font-body);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.site-nav a {
  position: relative;
  padding: 0.35rem 0;
  transition: color 0.14s ease;
}

.site-nav a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: var(--red);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.14s ease;
}

.site-nav [aria-current="page"] {
  color: var(--text);
}

.site-nav [aria-current="page"]::after {
  transform: scaleX(1);
}

/* Burger — appears < 880px */
.menu-toggle {
  display: none;
  position: relative;
  z-index: 22;
  width: 30px;
  height: 26px;
  border: 0;
  border-radius: 0;
  color: var(--text-muted);
  background: transparent;
  cursor: pointer;
}

.menu-toggle span {
  position: absolute;
  right: 0;
  height: 2px;
  background: currentColor;
  transition: transform 0.2s ease;
}

.menu-toggle span:first-child {
  top: 7px;
  width: 28px;
}

.menu-toggle span:last-child {
  top: 17px;
  width: 20px;
}

.menu-open .menu-toggle span:first-child {
  color: var(--text);
  transform: translateY(5px) rotate(45deg);
}

.menu-open .menu-toggle span:last-child {
  width: 28px;
  color: var(--text);
  transform: translateY(-5px) rotate(-45deg);
}

/* Full-screen mobile menu */
.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 21;
  display: grid;
  /* minmax(0, 1fr), not the default auto column: an auto column floors at its
     widest item's min-content, and the booking bar's is 297px — wider than a
     320px phone's menu, which pushed the whole grid past the viewport. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: 1fr auto;
  gap: 2rem;
  padding: 6.4rem 1.875rem 2.875rem;
  /* radial red glow at top */
  background:
    radial-gradient(120% 48% at 50% -8%, rgb(var(--red-rgb) / 0.28), transparent 56%), var(--bg);
}

.mobile-menu[hidden] {
  display: none;
}

.mobile-menu__nav {
  display: grid;
  align-content: center;
  gap: 0;
}

/* Wraps rather than stacking at a fixed breakpoint: the meta cell drops under
   the label only on the narrowest phones, where label + meta would otherwise
   force the row (and the whole grid column) wider than the menu. */
.mobile-menu__nav a {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.9rem;
  align-items: baseline;
  justify-content: space-between;
  color: var(--text);
  font-family: var(--font-display);
  font-size: clamp(1.9rem, 10vw, 3.2rem);
  font-weight: 800;
  line-height: 0.95;
  letter-spacing: -0.02em;
  padding: 1rem 0.75rem;
  border-bottom: 1px solid var(--line);
  text-transform: none;
  transition:
    background 0.14s ease,
    color 0.14s ease;
}

.mobile-menu__label {
  min-width: 0;
}

/* Meta cell: what's behind the link. Never shrinks below its own text — the
   display label is the flexible half of the row. */
.mobile-menu__meta {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  flex: none;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
}

.mobile-menu__ext {
  width: 0.85em;
  height: 0.85em;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Current page: the mobile menu's answer to the desktop nav's red underline. */
.mobile-menu__nav [aria-current="page"] {
  border-left: 3px solid var(--red);
  padding-left: calc(0.75rem - 3px);
}

.mobile-menu__nav [aria-current="page"] .mobile-menu__meta {
  color: var(--red);
}

.mobile-menu__bottom {
  display: grid;
  /* Same reason as .mobile-menu: keep the column shrinkable so the booking bar
     ellipsizes instead of overflowing on the narrowest phones. */
  grid-template-columns: minmax(0, 1fr);
  gap: 1.2rem;
}

/* The menu stretches the booking bar full-width; let the email cell grow so
   the segments fill the border instead of leaving an empty tail segment. */
.mobile-menu .booking-container {
  display: flex;
}

.mobile-menu .booking-link {
  flex: 1;
  /* min-width: 0 so the cell may ellipsize: at its natural size the bar is
     297px, which is wider than the menu's column on a 320px phone and dragged
     the whole grid (nav rows included) past the viewport. */
  min-width: 0;
  max-width: none;
}

.mobile-menu__wordmark {
  display: block;
  width: clamp(180px, 60vw, 260px);
  height: auto;
  opacity: 0.12;
  margin: 0.5rem auto 0;
  pointer-events: none;
  user-select: none;
}

.mobile-menu .social-links {
  justify-content: center;
}

/* ── Footer ── */
footer {
  width: min(100%, var(--container));
  margin: 0 auto;
  padding: var(--section) var(--gutter) 3rem;
  border-top: 2px solid var(--line-head);
}

.footer-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1.25rem 2rem;
}

/* Booking suppressed (Live's empty state already carries the bar): the socials
   are the only child left, so center them instead of stranding them at the edge. */
.footer-top--solo {
  justify-content: center;
}

/* Big faded wordmark */
.footer-wordmark {
  display: block;
  width: auto;
  height: 48px;
  opacity: 0.07;
  margin: 2rem 0;
}

.footer-bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem 2rem;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* Small screens: footer stacks and centers */
@media (max-width: 620px) {
  .footer-top {
    flex-direction: column;
    align-items: center;
  }

  .footer-wordmark {
    margin-inline: auto;
  }

  .footer-bottom {
    flex-direction: column;
    align-items: center;
    gap: 0.75rem;
    text-align: center;
  }
}

/* Social icons: 40px bordered squares, hover = solid Flare fill */
.social-links {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--text-muted);
}

.social-links a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: 1px solid var(--line);
  color: var(--brand, var(--text-muted));
  transition:
    border-color 0.14s ease,
    background 0.14s ease,
    color 0.14s ease;
}

.social-links a[data-brand="instagram"] {
  --brand: var(--brand-instagram);
}

.social-links a[data-brand="facebook"] {
  --brand: var(--brand-facebook);
}

.social-links a[data-brand="spotify"] {
  --brand: var(--brand-spotify);
}

.social-links a[data-brand="youtube"] {
  --brand: var(--brand-youtube);
}

.social-links svg {
  width: 20px;
  height: 20px;
}

/* ── Booking bar: connected-segment field ── */
.booking-container {
  display: inline-flex;
  align-items: stretch;
  max-width: 100%;
  gap: 0;
  border: 1.5px solid var(--line-field);
  color: var(--text);
}

.booking-label {
  display: flex;
  align-items: center;
  padding: 0.6rem 0.875rem;
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  border-right: 1.5px solid var(--line-field);
  white-space: nowrap;
}

/* Block, not flex: `text-overflow: ellipsis` only applies to a block container,
   and this cell must be able to truncate. Centering is set here rather than
   inherited — the bar sits inside centered blocks (hero, empty state) and
   start-aligned ones (footer, menu), and the email should read the same in all
   of them, including when the menu stretches the cell with `flex: 1`. */
.booking-link {
  display: block;
  padding: 0.6rem 0.875rem;
  max-width: min(52vw, 18rem);
  overflow: hidden;
  color: var(--text);
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.45;
  text-align: center;
  text-overflow: ellipsis;
  white-space: nowrap;
  border-right: 1.5px solid var(--line-field);
}

.btn-copy {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: auto;
  padding: 0;
  border: 0;
  color: var(--text-muted);
  background: rgb(var(--red-rgb) / 0.1);
  cursor: pointer;
  transition:
    background 0.14s ease,
    color 0.14s ease;
}

.btn-copy svg {
  width: 16px;
  height: 16px;
}

/* Signal-green check state is set by JS via aria-label swap + icon swap */
.btn-copy[data-copied] {
  color: var(--signal);
  background: rgb(var(--signal-rgb) / 0.12);
}

/* Ghost weight — same sanctioned segmented control, meta-level presence.
   1px --line-strong instead of the 1.5px bone edge, tighter cells, muted email,
   untinted copy cell: for places where the bar sits beside real buttons and must
   not compete with them (the home hero's third CTA line). */
.booking-container--ghost {
  border-width: 1px;
  border-color: var(--line-strong);
}

.booking-container--ghost .booking-label,
.booking-container--ghost .booking-link {
  padding: 0.4rem 0.72rem;
  border-right-width: 1px;
  border-right-color: var(--line-strong);
}

.booking-container--ghost .booking-label {
  font-size: 0.6rem;
  letter-spacing: 0.16em;
}

.booking-container--ghost .booking-link {
  font-size: 0.78rem;
  color: var(--text-muted);
}

.booking-container--ghost .btn-copy {
  width: 32px;
  background: transparent;
}

/* ── Booking bar: narrow slots ──
   Which layout the bar can take depends on the slot it sits in, not on the
   window. At a 390px viewport the footer hands it 354px, the mobile menu 330px
   and Live's padded empty-state panel only 280px — one viewport breakpoint
   can't be right for all three, and the old 480px one stacked every placement
   long before any of them ran out of room. So each host registers as a query
   container and the bar answers for its own space. (The hero's ghost bar has no
   container ancestor, so nothing here reaches it — it already fits at 320.) */
.footer-top,
.mobile-menu__bottom,
.event-empty {
  container-type: inline-size;
  container-name: booking-slot;
}

/* Tight horizontal: the same three segments with the air squeezed out. At full
   size the bar needs 297px; trimmed it needs 272px, which keeps it horizontal
   in the 320px footer, the 360px menu and Live's panel from 390 up — all of
   which used to stack. `max-width: none` because the container query now owns
   the fit decision; the base 52vw cap is a viewport guess that in this range
   can only truncate an address that fits. */
@container booking-slot (max-width: 300px) {
  .booking-container:not(.booking-container--ghost) .booking-label,
  .booking-container:not(.booking-container--ghost) .booking-link {
    padding-inline: 0.55rem;
  }

  .booking-container:not(.booking-container--ghost) .booking-link {
    max-width: none;
  }

  .booking-container:not(.booking-container--ghost) .btn-copy {
    width: 34px;
  }
}

/* Below that even the trimmed segments can't hold the address on one line, so
   the label moves to its own row and the email takes the full width beside the
   copy cell. Centered once stacked: the label now spans the whole bar rather
   than leading it, so leading-aligned text reads as a stray offset against the
   centered address below it. */
@container booking-slot (max-width: 278px) {
  .booking-container:not(.booking-container--ghost) {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    width: 100%;
  }

  .booking-container:not(.booking-container--ghost) .booking-label {
    grid-column: 1 / -1;
    justify-content: center;
    border-right: 0;
    border-bottom: 1.5px solid var(--line-field);
  }
}

/* ── Animations ── */
@keyframes filamentPulse {
  0%,
  100% {
    opacity: 0.6;
  }
  50% {
    opacity: 1;
  }
}

/* ── Hover states ── */
@media (hover: hover) and (pointer: fine) {
  a:hover {
    color: var(--text);
  }

  .site-nav a:hover {
    color: var(--text);
  }

  .site-nav a:hover::after {
    transform: scaleX(1);
  }

  .button:hover {
    background: rgb(var(--bone-rgb) / 0.9);
    color: var(--bg);
    border-color: rgb(var(--bone-rgb) / 0.9);
    transform: translateY(-2px);
  }

  .button--primary:hover {
    background: var(--red-hover);
    border-color: var(--red-hover);
    color: var(--on-fill);
    transform: translateY(-2px);
  }

  .button--primary:active {
    background: var(--blood);
    border-color: var(--blood);
    transform: translateY(0);
  }

  .btn-copy:hover {
    background: rgb(var(--red-rgb) / 0.2);
    color: var(--text);
  }

  /* The ghost bar's copy cell has no resting tint; hover supplies the Flare wash. */
  .booking-container--ghost .btn-copy:hover {
    background: var(--wash-flare);
  }

  .booking-container--ghost .booking-link:hover {
    color: var(--text);
  }

  /* Hover fills with the link's own brand tint rather than Flare — which also
     takes red off an element that was never an action in the first place. */
  .social-links a:hover {
    border-color: var(--brand, var(--red));
    background: var(--brand, var(--red));
    color: var(--on-fill);
  }

  .mobile-menu__nav a:hover {
    background: var(--wash-flare);
    color: var(--text);
  }
}

/* ── Responsive ── */
@media (max-width: 880px) {
  .site-nav {
    display: none;
  }

  .menu-toggle {
    display: inline-flex;
  }
}

@media (max-width: 480px) {
  .brand-logo {
    height: 22px;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }

  /* Same specificity as the gated rule above, or this reset would lose to it */
  .js [data-reveal] {
    opacity: 1;
    transform: none;
  }
}

/* ── Shared event list + event-row component ── */
/* Used by both Home (upcoming) and Live; single source of truth. */
.event-list {
  width: min(100%, var(--container));
  margin: 0 auto 4rem;
  padding: 0 var(--gutter);
  border-top: 2px solid var(--line-top);
}

.event-row {
  display: grid;
  grid-template-columns: 80px minmax(0, 1fr) auto;
  align-items: center;
  gap: 1.625rem;
  border-bottom: 1px solid var(--line);
  padding: 1.5rem 0;
  transition: background 0.18s ease;
}

.event-row__date {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-right: 1.5rem;
  border-right: 1px solid var(--line);
  user-select: none;
  text-align: center;
}

.event-row__month {
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  line-height: 1;
  text-transform: uppercase;
}

.event-row__day {
  color: var(--text);
  font-family: var(--font-display);
  font-size: 2.25rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.02em;
}

.event-row__year {
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 0.6rem;
  letter-spacing: 0.14em;
  line-height: 1;
  margin-top: 0.15rem;
  text-transform: uppercase;
}

.event-row__info {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 0;
  color: inherit;
}

.event-row__name {
  overflow: hidden;
  color: var(--text);
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: -0.015em;
  text-transform: uppercase;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.event-row__venue {
  display: flex;
  align-items: center;
  gap: 0.42rem;
  min-width: 0;
  color: var(--text-body);
  font-size: 0.875rem;
  flex-wrap: wrap;
}

.event-row__location,
.event-row__address {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.event-row__sep {
  color: var(--text-dim);
}

.event-row__time {
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
}

.event-row__address {
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 0.72rem;
}

/* Free-entry tag: Signal green square — status indicator, not an action button */
.event-row__free {
  display: inline-flex;
  align-items: center;
  border: 1px solid rgb(var(--signal-rgb) / 0.3);
  border-radius: 0;
  padding: 0.45rem 0.875rem;
  color: var(--signal);
  font-family: var(--font-mono);
  font-size: 0.65rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
  background: rgb(var(--signal-rgb) / 0.06);
}

@media (hover: hover) and (pointer: fine) {
  .event-row:hover {
    background: var(--wash-flare);
  }
}

/* 620px: date stub narrows, action wraps to own full-width row */
@media (max-width: 620px) {
  .event-row {
    grid-template-columns: 64px 1fr;
    grid-template-rows: auto auto;
    gap: 0.875rem;
  }

  /* Narrow screens: wrap the name instead of cutting it off */
  .event-row__name {
    white-space: normal;
    line-height: 1.15;
  }

  .event-row__action {
    grid-column: 1 / -1;
    display: flex;
  }

  .event-row__action .button,
  .event-row__free {
    width: 100%;
    justify-content: center;
  }
}

@media (max-width: 420px) {
  .event-row {
    padding: 1rem 0;
  }
}
