/* =========================================================================
   8Ball Audio
   Dark, type-led, teal accent. Condensed display face over a neutral body.
   ========================================================================= */

:root {
  --ink:        #050605;
  --ink-2:      #0b0d0c;
  --ink-3:      #121514;
  --line:       #1e2321;
  --line-soft:  #171b1a;
  --paper:      #f2f0e6;
  --mute:       #8a908c;
  --mute-2:     #5f6663;
  --teal:       #2fe0c0;
  --teal-dim:   #1c8a76;

  --shell-max:  1560px;               /* the cap itself, as a plain length */
  --shell:      min(var(--shell-max), 100%);
  --gut:        clamp(18px, 3.4vw, 54px);
  --edge:       clamp(28px, 3vw, 46px);   /* space for the vertical side labels */

  --display: 'Bebas Neue', 'Oswald', Impact, sans-serif;
  --ui:      'Oswald', 'Inter', system-ui, sans-serif;
  --body:    'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;

  --dur: .45s;
  --ease: cubic-bezier(.16, .84, .3, 1);
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; scroll-padding-top: 92px; }

/* The page still scrolls; the bar itself is never painted. Every other
   scrolling surface here is already silent, and one chrome coloured rail down
   the right hand side was the only thing breaking that. */
html { scrollbar-width: none; -ms-overflow-style: none; }
html::-webkit-scrollbar { width: 0; height: 0; display: none; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--body);
  font-size: 15px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { display: block; max-width: 100%; }
ul  { list-style: none; margin: 0; padding: 0; }
a   { color: inherit; text-decoration: none; }

:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 3px;
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--teal); color: #04120f; padding: 10px 18px;
  font-family: var(--ui); letter-spacing: .1em; text-transform: uppercase;
}
.skip:focus { left: 12px; top: 12px; }

/* Bebas Neue ships a single weight. Never bold it: faux-bold breaks the
   match with every other title on the page. */
.display, .sect, .hero__title, .hero__sub, .rel__artist, .pcard__name {
  font-family: var(--display);
  font-weight: 400;
  letter-spacing: .012em;
}

/* ── NAV ──────────────────────────────────────────────────────────────── */

.nav {
  position: sticky; top: 0; z-index: 90;
  background: rgba(5, 6, 5, .82);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line-soft);
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.nav.is-stuck { background: rgba(5, 6, 5, .96); border-bottom-color: var(--line); }

.nav__inner {
  width: var(--shell); margin: 0 auto;
  padding: 12px var(--gut);
  display: flex; align-items: center; gap: 24px;
}

.brand { display: flex; align-items: center; gap: 12px; flex: none; }

/* The supplied logo is a JPG: the ball is inscribed in the square, so the
   corners are opaque white. Clipping to a circle drops them cleanly. Keep
   this if the file is ever swapped for another raster; a transparent PNG or
   SVG would not need it, but it does no harm. */
.brand__mark { border-radius: 50%; }
.brand__mark { width: 44px; height: 44px; object-fit: cover; }
.brand__word {
  font-family: var(--display); font-size: 25px; line-height: .84;
  letter-spacing: .04em; text-transform: uppercase;
}

.nav__links {
  margin-left: auto;
  display: flex; gap: clamp(16px, 2.2vw, 40px);
}
.nav__links a {
  font-family: var(--ui); font-size: 13px; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--paper); padding: 6px 0;
  position: relative;
}
.nav__links a::after {
  content: ''; position: absolute; left: 0; right: 100%; bottom: 0;
  height: 1px; background: var(--teal);
  transition: right var(--dur) var(--ease);
}
.nav__links a:hover::after, .nav__links a.is-active::after { right: 0; }
.nav__links a.is-active { color: var(--teal); }

.nav__right { margin-left: auto; display: flex; align-items: center; gap: 14px; }
.nav__links + .nav__right { margin-left: 0; }

.nav__socials { display: flex; gap: 12px; }
/* 40px box, 19px glyph: the target grows, the mark does not. */
.nav__socials a, .drawer__socials a {
  width: 40px; height: 40px; display: grid; place-items: center;
  color: var(--mute); transition: color .2s, transform .2s;
}
.nav__socials a:hover, .drawer__socials a:hover { color: var(--teal); transform: translateY(-2px); }
.nav__socials svg, .drawer__socials svg { width: 19px; height: 19px; }

.burger {
  display: none; width: 40px; height: 40px; padding: 0;
  background: none; border: 0; cursor: pointer;
  flex-direction: column; justify-content: center; gap: 5px; align-items: center;
}
.burger span {
  display: block; width: 22px; height: 1.5px; background: var(--paper);
  transition: transform .3s var(--ease), opacity .2s;
}
.burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }
.burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); }

.drawer {
  position: sticky; top: 68px; z-index: 89;
  background: var(--ink-2); border-bottom: 1px solid var(--line);
  padding: 18px var(--gut) 26px;
}
.drawer[hidden] { display: none; }
.drawer__links { display: grid; gap: 2px; }
.drawer__links a {
  font-family: var(--display); font-size: 30px; letter-spacing: .04em;
  text-transform: uppercase; padding: 7px 0;
  border-bottom: 1px solid var(--line-soft);
}
.drawer__links a:hover { color: var(--teal); }
.drawer__socials { display: flex; gap: 14px; margin-top: 20px; }

/* ── HERO ─────────────────────────────────────────────────────────────── */

.hero {
  position: relative;
  border-bottom: 1px solid var(--line);
  background: #000;
}

.hero__rail { position: relative; }

.slide {
  position: absolute; inset: 0;
  opacity: 0; visibility: hidden;
  transition: opacity .7s var(--ease);
  display: grid; align-items: center;
}
.slide.is-on { position: relative; opacity: 1; visibility: visible; }

/* Full bleed artwork, type laid over it. The scrim is heaviest on the left
   so the headline always has something solid to sit on, and lifts towards
   the right where the artwork should be visible. */
.slide__art {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  object-position: 65% center;
}
.slide__scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(96deg, rgba(3,5,4,.95) 0%, rgba(3,5,4,.86) 26%, rgba(3,5,4,.34) 58%, rgba(3,5,4,.62) 100%),
    linear-gradient(0deg, rgba(0,0,0,.78) 0%, transparent 46%);
}

.slide__body {
  position: relative;
  width: var(--shell); margin: 0 auto; max-width: 100%;
  padding: clamp(56px, 9vh, 116px) calc(var(--gut) + var(--edge)) clamp(92px, 13vh, 142px);
  min-height: clamp(440px, 66vh, 700px);
  display: flex; flex-direction: column; justify-content: center;
}

.kicker {
  font-family: var(--ui); font-size: 12px; font-weight: 500;
  letter-spacing: .26em; text-transform: uppercase;
  color: var(--teal); margin: 0 0 14px;
}

.hero__title {
  margin: 0;
  font-size: clamp(52px, 8.4vw, 118px);
  line-height: .88; text-transform: uppercase;
}
.hero__sub {
  margin: 4px 0 0;
  font-size: clamp(30px, 4.6vw, 62px);
  line-height: .96; text-transform: uppercase;
  color: #e6e3d6;
}

.hero__meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
  margin: 22px 0 0;
  font-family: var(--ui); font-size: 14px; letter-spacing: .1em;
  color: #cfd3cf;
}
.hero__meta i { width: 5px; height: 5px; border-radius: 50%; background: var(--teal); display: block; }

.hero__cta { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 30px; }

/* Below the wide layout the pair is stacked outright rather than left to
   wrap. Wrapping is decided by label length, so "Listen / Buy · All releases"
   stayed on one line while "Submit a demo · About the label" broke onto two,
   and the second button moved between slides of the same carousel. Stacking
   both puts it in one place. Each keeps its own width: sizing them to match
   would change the buttons, and only their position was wrong. */
@media (max-width: 860px) {
  .hero__cta { flex-direction: column; align-items: flex-start; gap: 12px; }
  /* Stacked, each button is alone on its line, so the generous side padding
     that balanced them in a row only makes them sprawl. */
  .hero__cta .btn { padding: 12px 18px; font-size: 12px; letter-spacing: .14em; }
}

.btn {
  font-family: var(--ui); font-size: 13px; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase;
  padding: 14px 30px; border: 1px solid var(--paper); color: var(--paper);
  background: transparent; cursor: pointer;
  transition: background .25s var(--ease), color .25s var(--ease), border-color .25s var(--ease);
}
.btn:hover { background: var(--paper); color: var(--ink); }
.btn--teal { border-color: var(--teal); color: var(--teal); }
.btn--teal:hover { background: var(--teal); color: #04120f; border-color: var(--teal); }
.btn--solid { background: var(--teal); border-color: var(--teal); color: #04120f; }
.btn--solid:hover { background: #6ff0d8; border-color: #6ff0d8; color: #04120f; }

.edge {
  position: absolute; top: 50%; margin: 0;
  font-family: var(--ui); font-size: 11px; letter-spacing: .3em;
  text-transform: uppercase; color: var(--teal-dim);
  white-space: nowrap; z-index: 3;
}
.edge--left  { left: calc(var(--gut) - 4px);  transform: translateY(-50%) rotate(180deg); writing-mode: vertical-rl; }
.edge--right { right: calc(var(--gut) - 4px); transform: translateY(-50%); writing-mode: vertical-rl; }

.hero__controls {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 4;
  width: var(--shell); margin: 0 auto;
  padding: 0 calc(var(--gut) + var(--edge)) clamp(22px, 4vh, 40px);
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  max-width: 100%;
}

.dots { display: flex; align-items: center; gap: 16px; }
.dot {
  background: none; border: 0; padding: 6px 0; cursor: pointer;
  font-family: var(--ui); font-size: 13px; letter-spacing: .1em;
  color: var(--mute-2); display: flex; align-items: center; gap: 16px;
}
.dot::after {
  content: ''; width: 0; height: 1px; background: var(--teal);
  transition: width var(--dur) var(--ease);
}
.dot.is-on { color: var(--teal); }
.dot.is-on::after { width: 46px; }

.arrows { display: flex; gap: 10px; }
.arrow {
  width: 44px; height: 40px; display: grid; place-items: center;
  background: rgba(0,0,0,.4); border: 1px solid var(--teal-dim);
  color: var(--teal); font-size: 20px; line-height: 1; cursor: pointer;
  transition: background .25s, color .25s;
}
.arrow:hover { background: var(--teal); color: #04120f; }

/* ── SECTION FURNITURE ────────────────────────────────────────────────── */

.band {
  width: var(--shell); margin: 0 auto;
  padding: clamp(34px, 5vw, 58px) var(--gut);
  border-bottom: 1px solid var(--line);
  max-width: 100%;
}

.band__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 20px; margin-bottom: 26px;
}
.band__head--tight { margin-bottom: 18px; }

.sect {
  margin: 0;
  font-size: clamp(19px, 1.7vw, 25px);
  letter-spacing: .12em; text-transform: uppercase; color: var(--teal);
}

.more {
  font-family: var(--ui); font-size: 12px; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase; color: var(--teal);
  white-space: nowrap;
}
.more span { display: inline-block; transition: transform .25s var(--ease); }
.more:hover span { transform: translateX(5px); }

/* ── ARTIST TICKER ────────────────────────────────────────────────────────
   Free floating: no panel, no rule, no background. Just the roster crossing
   the page between the hero and the releases.
------------------------------------------------------------------------- */
.ticker {
  padding: clamp(16px, 2.2vw, 28px) 0;
  overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none;
  overscroll-behavior-x: contain;   /* do not chain to the page mid-strip */
  cursor: grab;
  /* The names run edge to edge, so they must not be clipped by the section
     gutters. The fade at each end does the containing instead. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.ticker::-webkit-scrollbar { display: none; }
.ticker.is-dragging { cursor: grabbing; }

.ticker__track { display: flex; width: max-content; }
.ticker__row { display: flex; align-items: center; }
.ticker__item {
  font-family: var(--display); font-weight: 400;
  font-size: clamp(15px, 1.5vw, 23px);
  line-height: 1; letter-spacing: .1em; text-transform: uppercase;
  color: var(--mute); white-space: nowrap;
}
.ticker__dot {
  width: 4px; height: 4px; flex: none; border-radius: 50%;
  background: var(--teal);
  margin: 0 clamp(12px, 1.4vw, 20px);
}

/* ── RELEASES ─────────────────────────────────────────────────────────── */

/* One variable drives both states. A rail has to show exactly the same number
   of cards the grid puts in a row, so the count is declared once and the
   breakpoints below move it. Two hardcoded lists would drift apart the first
   time one of them was edited.

   Releases and merch are the same shelf: a single scrolling line by default,
   which Releases can open out into the full grid. Anything sitting in
   `--shelf-cols` gets both behaviours for free. */
.releases, .merch {
  --shelf-gap: clamp(14px, 1.4vw, 22px);
  display: grid; gap: var(--shelf-gap);
  grid-template-columns: repeat(var(--shelf-cols), 1fr);
}
.releases { --shelf-cols: 6; --shelf-rows: 1; }
.merch    { --shelf-cols: 8; --shelf-rows: 2; }

/* Lines that scroll sideways through the whole list. The cards running off
   the edge are the affordance, so there is no scrollbar.

   Still a grid, not a flex row: `--shelf-rows` sets how many lines deep the
   rail runs, and column flow fills each line before starting the next.
   Releases run one line, merch two. A flex rail could only ever be one. */
.releases.is-rail, .merch.is-rail {
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: repeat(var(--shelf-rows, 1), auto);
  grid-auto-columns: calc((100% - (var(--shelf-cols) - 1) * var(--shelf-gap)) / var(--shelf-cols));
  grid-template-columns: none;       /* the row template drives the rail */
  align-content: start;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;    /* do not chain to the page mid-rail */
  scrollbar-width: none;
  padding-bottom: 2px;
}
.releases.is-rail::-webkit-scrollbar,
.merch.is-rail::-webkit-scrollbar { display: none; }

.releases.is-rail > .rel,
.merch.is-rail > .merch__item { scroll-snap-align: start; }

.releases.is-rail.is-dragging, .merch.is-rail.is-dragging {
  scroll-snap-type: none; cursor: grabbing; user-select: none;
}
.releases.is-rail.is-dragging a, .merch.is-rail.is-dragging a { pointer-events: none; }

/* The expander is the chevron alone, so the glyph is all there is to aim at.
   A full 44x44 target, with the box pulled outward by negative margin so the
   extra area is invisible: the chevron stays optically flush with the
   heading's right edge and the head keeps its height. */
.more--btn {
  width: 44px; height: 44px; padding: 0;
  margin: -11px -10px -11px 0;
  display: grid; place-items: center;
  /* The head aligns its children on the baseline, which a 44px box has none
     of in the right place: it hung the glyph 6px high. Centre this one
     against the heading instead. */
  align-self: center;
  background: none; border: 0; cursor: pointer;
  color: var(--teal);
  font-size: 26px; line-height: 1;
}
.more--btn[hidden] { display: none; }
.more--btn span { display: inline-block; transition: transform .25s var(--ease); }
.more--btn:hover span { transform: translateX(4px); }
.more--btn[aria-expanded="true"]:hover span { transform: translateX(-4px); }

/* The card internals are spans so they can nest inside a single <a>.
   They must be told to be blocks: an inline box ignores aspect-ratio and
   gives absolutely positioned children a fragmented containing block. */
.rel__frame, .rel__play, .rel__flag,
.ev__frame, .ev__over, .ev__title, .ev__meta, .ev__venue, .ev__badge { display: block; }

.rel a { display: block; }
.rel__frame {
  position: relative; aspect-ratio: 1; overflow: hidden;
  border: 1px solid var(--line);
  background: var(--ink-3);
}
.rel__frame img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform .6s var(--ease), filter .4s var(--ease);
}
.rel a:hover .rel__frame img { transform: scale(1.06); filter: brightness(1.12); }

.rel__flag {
  position: absolute; top: 0; left: 0;
  background: var(--teal); color: #04120f;
  font-family: var(--ui); font-size: 10px; font-weight: 600;
  letter-spacing: .18em; text-transform: uppercase;
  padding: 5px 11px;
}

.rel__play {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: rgba(4, 12, 10, .55);
  opacity: 0; transition: opacity .3s var(--ease);
}
.rel a:hover .rel__play { opacity: 1; }
.rel__play span {
  display: inline-block;
  font-family: var(--ui); font-size: 11px; letter-spacing: .2em;
  text-transform: uppercase; color: var(--teal);
  border: 1px solid var(--teal); padding: 9px 16px;
}

.rel__artist {
  margin: 12px 0 0; font-size: 19px;
  letter-spacing: .05em; text-transform: uppercase;
}
/* The catalogue number is a reference, not a heading: it sits directly under
   the artist in the accent, spaced wide so it never competes with the title
   line below it. */
.rel__cat   { margin: 2px 0 0; font-family: var(--ui); font-size: 11px; font-weight: 500;
              color: var(--teal-dim); letter-spacing: .18em; text-transform: uppercase; }
.rel__title { margin: 2px 0 0; font-family: var(--ui); font-size: 13px; color: #c8ccc8; letter-spacing: .04em; }
.rel__date  { margin: 3px 0 0; font-family: var(--ui); font-size: 12px; color: var(--mute-2); letter-spacing: .12em; }

/* ── EVENTS ───────────────────────────────────────────────────────────── */

.events {
  display: grid; gap: clamp(14px, 1.4vw, 22px);
  grid-template-columns: repeat(5, 1fr);
}

.ev a { display: block; position: relative; }
.ev__frame {
  position: relative; aspect-ratio: 4 / 5; overflow: hidden;
  border: 1px solid var(--line); background: var(--ink-3);
}
.ev__frame img { width: 100%; height: 100%; object-fit: cover; transition: transform .6s var(--ease); }
.ev a:hover .ev__frame img { transform: scale(1.05); }

.ev__over {
  position: absolute; inset: auto 0 0 0;
  padding: 46px 15px 13px;
  background: linear-gradient(0deg, rgba(0,0,0,.95) 20%, transparent);
}
.ev__venue {
  font-family: var(--ui); font-size: 12px; letter-spacing: .1em;
  text-transform: uppercase; color: #c6cac6;
}

/* Available to assistive tech, never painted. */
.sr {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.ev__badge {
  position: absolute; top: 10px; right: 10px;
  font-family: var(--ui); font-size: 10px; letter-spacing: .16em; text-transform: uppercase;
  padding: 4px 9px; border: 1px solid var(--teal); color: var(--teal);
  background: rgba(0,0,0,.6);
}
.ev__badge--past { border-color: var(--mute-2); color: var(--mute); }

/* ── PORTRAIT CARDS + RAIL ────────────────────────────────────────────── */

.rail {
  display: flex; gap: clamp(10px, 1vw, 16px);
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;              /* the arrows are the affordance */
  padding-bottom: 2px;
}
.rail::-webkit-scrollbar { display: none; }
.rail.is-dragging { scroll-snap-type: none; cursor: grabbing; user-select: none; }
.rail.is-dragging a { pointer-events: none; }

/* 3:4 portrait, same proportions Scene's Discover rails use. */
.pcard {
  flex: 0 0 auto;
  width: clamp(132px, 12vw, 168px);
  scroll-snap-align: start;
}
.pcard a {
  display: block; position: relative;
  aspect-ratio: 3 / 4;
  border-radius: 14px; overflow: hidden;
  /* A dark edge, not a bright one: it separates a photograph from the page
     behind it rather than decorating the card. `border` cannot carry a
     gradient, so the fill is clipped to the padding box and the edge to the
     border box, showing through a transparent border. Give the border a
     colour and the gradient disappears behind it. */
  border: 1px solid transparent;
  background:
    linear-gradient(135deg, rgba(47,224,192,.28), rgba(10,95,82,.22)) padding-box,
    linear-gradient(155deg, #1b2422 0%, #080b0a 100%) border-box;
  transition: transform .18s var(--ease);
}
.pcard a:hover { transform: translateY(-2px); }

.pcard__img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  transition: transform .6s var(--ease);
}
.pcard a:hover .pcard__img { transform: scale(1.04); }

/* Exists to keep the name legible over a photograph, nothing else. */
.pcard__veil {
  position: absolute; inset: 0;
  background: linear-gradient(to bottom,
    rgba(0,0,0,.10) 0%, rgba(0,0,0,0) 35%, rgba(0,0,0,.62) 75%, rgba(0,0,0,.9) 100%);
}

.pcard__pill {
  position: absolute; top: 9px; right: 9px;
  font-family: var(--ui); font-size: 9px; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase;
  padding: 3px 8px; border-radius: 6px;
  background: var(--teal); color: #04120f;
  filter: brightness(.86);
}

.pcard__info {
  position: absolute; inset: auto 0 0 0;
  padding: 11px 12px;
  display: flex; flex-direction: column;
}
.pcard__name {
  font-family: var(--display); font-weight: 400;
  font-size: 17px; line-height: 1.05; letter-spacing: .05em;
  text-transform: uppercase; color: #fff;
}
/* Empty until the YesPleez link-up fills it. `:empty` collapses the margin
   so a card with no location is not a card with a gap in it. */
.pcard__loc {
  margin-top: 3px;
  font-family: var(--ui); font-size: 10px; letter-spacing: .06em;
  color: rgba(255,255,255,.55);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pcard__loc:empty { display: none; }

/* A quiet marker, not a badge: this card is following a live profile. */
.pcard.is-yp .pcard__name::after {
  content: ''; display: inline-block;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--teal); vertical-align: middle;
  margin-left: 6px; margin-bottom: 2px;
}

.pcard__sub {
  margin-top: 4px;
  font-family: var(--ui); font-size: 10px; line-height: 1.4;
  letter-spacing: .04em; color: var(--teal);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

.rail__note {
  margin: clamp(16px, 1.8vw, 24px) 0 0;
  font-family: var(--ui); font-size: 12px; letter-spacing: .08em;
  color: var(--mute-2);
}
.rail__note:empty { display: none; }
.rail__note a { color: var(--teal); border-bottom: 1px solid rgba(47,224,192,.35); }
.rail__note a:hover { border-bottom-color: var(--teal); }

.rail__nav { display: flex; gap: 8px; }
.arrow--sm { width: 36px; height: 32px; font-size: 17px; }
.arrow:disabled { opacity: .3; cursor: default; }
.arrow:disabled:hover { background: rgba(0,0,0,.4); color: var(--teal); }

/* ── MERCH ────────────────────────────────────────────────────────────── */


.merch__item a { display: block; }
.merch__frame {
  display: block; position: relative;
  aspect-ratio: 1; overflow: hidden;
  border: 1px solid var(--line); background: var(--ink-3);
}
.merch__frame img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform .6s var(--ease), filter .4s var(--ease);
}
.merch__item a:hover .merch__frame img { transform: scale(1.06); filter: brightness(1.1); }

/* Sold out is dimmed rather than hidden: the product page is still worth
   reaching, and an empty gap would read as a loading failure. */
.merch__item:has(.merch__sold) .merch__frame img { filter: grayscale(.75) brightness(.62); }
.merch__item:has(.merch__sold) a:hover .merch__frame img { filter: grayscale(.3) brightness(.8); }

.merch__sold {
  position: absolute; top: 0; left: 0;
  font-family: var(--ui); font-size: 10px; font-weight: 600;
  letter-spacing: .18em; text-transform: uppercase;
  padding: 5px 11px; background: var(--ink); color: var(--mute);
  border-right: 1px solid var(--line); border-bottom: 1px solid var(--line);
}

.merch__title {
  margin: 11px 0 0;
  font-family: var(--ui); font-size: 13px; font-weight: 500; line-height: 1.35;
  letter-spacing: .05em; text-transform: uppercase; color: var(--paper);
}
.merch__meta { margin: 5px 0 0; display: flex; align-items: baseline; flex-wrap: wrap; gap: 5px; }
.merch__price {
  font-family: var(--display); font-weight: 400; font-size: 19px;
  letter-spacing: .04em; color: var(--teal);
}
.merch__price--out { color: var(--mute-2); font-size: 15px; }
.merch__cur { font-family: var(--ui); font-size: 10px; letter-spacing: .16em; color: var(--mute-2); }
.merch__type {
  font-family: var(--ui); font-size: 10.5px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--mute-2);
  margin-left: auto;
}
/* On a narrow card the `margin-left:auto` pushes this onto its own line and
   the rows go ragged. The category is the least useful thing on the card, so
   it is the thing that goes. Title and price stay. */
@media (max-width: 900px) { .merch__type { display: none; } }

.merch__note {
  margin: clamp(18px, 2vw, 26px) 0 0;
  font-family: var(--ui); font-size: 12px; letter-spacing: .08em;
  color: var(--mute-2);
}

/* ── FEATURED EVENT ───────────────────────────────────────────────────── */

.feat {
  display: grid;
  /* The details column carries long act names, so it gets real width rather
     than whatever is left over. At 2.05fr the flyer took two thirds and the
     lineup wrapped mid name. The flyer can afford it: it is a wide banner,
     and the past posters share its column below. */
  grid-template-columns: minmax(0, 1.35fr) minmax(320px, 1fr);
  gap: clamp(18px, 2.4vw, 34px);
  align-items: start;
}
.feat + .events:empty { display: none; }

.feat__art {
  display: block; overflow: hidden;
  border: 1px solid var(--line);
  background: var(--ink-3);
}
.feat__art img {
  width: 100%; height: auto;
  transition: transform .7s var(--ease);
}
.feat__art:hover img { transform: scale(1.02); }

.feat__badge {
  display: inline-block;
  font-family: var(--ui); font-size: 10px; font-weight: 500;
  letter-spacing: .2em; text-transform: uppercase;
  padding: 4px 10px; border: 1px solid var(--teal); color: var(--teal);
}
.feat__badge--past { border-color: var(--mute-2); color: var(--mute); }

.feat__presents {
  margin: 14px 0 0;
  font-family: var(--ui); font-size: 11px; letter-spacing: .24em;
  text-transform: uppercase; color: var(--mute);
}
.feat__title {
  margin: 6px 0 0;
  font-family: var(--display); font-weight: 400;
  font-size: clamp(34px, 3.4vw, 52px); line-height: .92;
  letter-spacing: .03em; text-transform: uppercase; color: var(--teal);
}
.feat__when {
  margin: 6px 0 0;
  font-family: var(--ui); font-size: 15px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--paper);
}

.feat__label {
  margin: 0 0 9px;
  font-family: var(--ui); font-size: 11px; letter-spacing: .24em;
  text-transform: uppercase; color: var(--mute-2);
}
/* Tightened so the details column ends level with the flyer beside it: at the
   old spacing the genres line hung 24px below the artwork. */
.feat__lineup { margin-top: clamp(12px, 1.2vw, 16px); }
.feat__lineup ul { display: flex; flex-wrap: wrap; gap: 5px 0; }
.feat__lineup li {
  font-family: var(--ui); font-size: clamp(14px, 1.15vw, 17px); font-weight: 500;
  letter-spacing: .06em; text-transform: uppercase; color: #e8e5da;
}
/* Separator trails the item it follows, so a wrap never starts a line
   with a stray dot. */
.feat__lineup li:not(:last-child)::after {
  content: '·'; color: var(--teal); margin: 0 9px; font-weight: 400;
}

.feat__genres {
  margin: clamp(10px, 1vw, 14px) 0 0; padding-top: 12px;
  border-top: 1px solid var(--line);
  font-family: var(--ui); font-size: 12px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--teal);
}
.feat__genres span { color: var(--mute-2); }

/* ── ABOUT ────────────────────────────────────────────────────────────── */

.about__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(24px, 4vw, 60px);
  align-items: center;
}
/* Same .84 as the header wordmark. Left to inherit the body's 1.62, the two
   words drifted apart and stopped reading as one mark. */
.display {
  margin: 0; font-size: clamp(64px, 6.8vw, 96px);
  line-height: .84;
  /* No tracking: at 96px the .05em it used to carry pushed "8BALL" 2px wider
     than the column it sits in. */
  letter-spacing: 0; text-transform: uppercase;
}
.stamp {
  margin: 12px 0 20px;
  font-family: var(--ui); font-size: 12px; letter-spacing: .2em;
  text-transform: uppercase; color: var(--teal);
}
.prose p { margin: 0 0 15px; color: #b9bdb9; font-size: 14.5px; max-width: 60ch; }
.prose p:last-child { margin-bottom: 0; }
/* The mark is not a figure beside the text, it is the panel's ground:
   oversized, anchored into the bottom right corner and cropped by the
   section, so only a portion of the ball is ever in frame. Out of flow, so
   the text keeps the full column and the words still set the height.

   This is the treatment at EVERY width. It used to live in the desktop
   media query, which left phones falling back to a small centred circle. */
.about { position: relative; overflow: hidden; }
.about .about__grid { grid-template-columns: minmax(0, 1fr); align-content: start; }
.about .about__text { position: relative; z-index: 1; }

.about__mark {
  position: absolute; z-index: 0;
  right: 0; bottom: 0;
  width: 155%; height: auto; max-width: none;
  border-radius: 50%;
  /* Pinned to the corner, then pushed out by 50% of ITS OWN size in each
     axis, which puts the ball's centre exactly on the corner so precisely one
     quarter is in frame. Percentages of the SECTION instead left the whole
     ball on screen with a slice off one side, reading as a mistake rather
     than a crop; and percentages on `transform` resolve against the element,
     so this holds at any panel width. */
  transform: translate(50%, 50%);
  opacity: .85; pointer-events: none;
}

/* On a phone the panel is only as tall as a short paragraph, so the arc lands
   straight across the words. The section gets extra room underneath for the
   ball to live in, and the ball itself eases back: same treatment, still
   legible. */
@media (max-width: 799px) {
  .about { padding-bottom: clamp(96px, 30vw, 170px); }
  .about__mark { width: 132%; opacity: .5; }
}

/* ── PAST EVENTS ──────────────────────────────────────────────────────────
   Posters of what has already happened, run down the side of the feature.
   They are a record, not an invitation, so they are small, quiet and carry
   the artwork alone: no badge repeating the label above them, no overlay
   restating a date nobody can act on.
------------------------------------------------------------------------- */
.past {
  margin: clamp(20px, 2.6vw, 34px) 0 0;
  padding-top: clamp(16px, 2vw, 24px);
}
.past[hidden] { display: none; }

/* Inside the feature the posters take the flyer's column, second row, and the
   details column is told to span BOTH rows. Without that span the details set
   the height of row one and the posters start below them, which leaves the
   gap beside the text exactly as empty as before. */
/* No spacing of its own inside the feature: the feature's own row gap is
   already the separation, and stacking a margin and a padding on top of it
   pushed the strip 74px clear of the lineup. */
.feat > .past { margin-top: 0; padding-top: 0; }

/* Only while the feature actually HAS two columns. Below that breakpoint it
   is a single column and an explicit row 2 / column 2 placement would drop
   the details into a phantom second column. */
@media (min-width: 1181px) {
  /* The strip runs the FULL width of the feature, under the flyer and the
     lineup both, so the details column no longer spans two rows. */
  .feat > .past { grid-column: 1 / -1; grid-row: 2; align-self: start; }
  .feat:has(> .past) .feat__body { grid-column: 2; grid-row: 1; }

  /* One line, however many posters there are. Column flow rather than a fixed
     count: six is today's number, not a rule, and a `repeat(6, 1fr)` would
     silently wrap the seventh onto a second row. */
  .feat > .past .past__rail {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
  }
}

/* A chip, not a heading: it labels the strip the way the feature's own badge
   labels the show above it, rather than announcing a new section. */
/* Chip on the left, its expander hard against the right, on one line. */
.past__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 14px;
}

.past__chip {
  display: inline-block; margin: 0; padding: 5px 11px;
  border: 1px solid var(--line); border-radius: 2px;
  font-family: var(--ui); font-size: 11px; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; color: var(--mute-2);
}

/* Three posters across at every size. They keep their own proportions and
   the row simply scales, so a phone gets the same layout as a desktop
   rather than a different one. */
.past__rail {
  display: grid; gap: clamp(8px, 1.2vw, 16px);
  grid-template-columns: repeat(3, 1fr);
}

/* Below the one line layout the strip is three across, so seven posters run
   to three rows and the section grows a whole row taller than it needs to be
   on a phone. Two rows show, the chevron beside the chip reveals the rest.
   Hiding the seventh item onward is what caps it: a max-height would have to
   be measured, and the poster heights depend on how wide the column lands. */
@media (max-width: 1180px) {
  .past__rail:not(.is-open) > li:nth-child(n+7) { display: none; }
}

/* Artwork only: no badge repeating the chip above, no overlay restating a
   date nobody can act on. */
.past__rail .ev__badge, .past__rail .ev__over { display: none; }
.past__rail .ev__frame { aspect-ratio: 4 / 5; }
.past__rail .ev { opacity: .8; transition: opacity .25s var(--ease); }
.past__rail .ev:hover { opacity: 1; }

/* Shown only when there is genuinely nothing booked. */
.events__none {
  margin: 0; padding: 22px 0 4px;
  font-family: var(--ui); font-size: 15px; letter-spacing: .04em;
  color: var(--mute);
}

/* ── CONTACT ──────────────────────────────────────────────────────────── */

.contact__grid {
  display: grid; grid-template-columns: minmax(240px, 1fr) 2fr;
  gap: clamp(24px, 4vw, 60px); align-items: start;
}
.contact__lead { margin: 12px 0 0; color: var(--mute); max-width: 34ch; }

.contact__list {
  margin: 0; display: grid; gap: 0;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.contact__list div { padding: 14px 0; border-top: 1px solid var(--line); }
.contact__list dt {
  font-family: var(--ui); font-size: 11px; letter-spacing: .2em;
  text-transform: uppercase; color: var(--mute-2); margin-bottom: 5px;
}
.contact__list dd { margin: 0; font-family: var(--ui); font-size: 15px; letter-spacing: .04em; }
.contact__list dd a:hover { color: var(--teal); }

/* ── FOOTER ───────────────────────────────────────────────────────────── */

.foot { background: var(--ink-2); }
.foot__inner {
  width: var(--shell); margin: 0 auto; max-width: 100%;
  padding: clamp(26px, 3vw, 40px) var(--gut);
  display: flex; flex-wrap: wrap; align-items: center; gap: clamp(20px, 3vw, 48px);
}
.brand--foot .brand__mark { width: 40px; height: 40px; }
.brand--foot .brand__word { font-size: 22px; }

.foot__meta p { margin: 0; font-family: var(--ui); font-size: 12px; letter-spacing: .1em; color: var(--mute-2); }

.foot__links { display: flex; flex-wrap: wrap; gap: clamp(14px, 2vw, 32px); }
.foot__links a {
  font-family: var(--ui); font-size: 12px; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase; color: var(--mute);
}
.foot__links a:hover { color: var(--teal); }

.sub { margin-left: auto; }
.sub__label {
  display: block; font-family: var(--ui); font-size: 11px;
  letter-spacing: .2em; text-transform: uppercase; color: var(--mute-2); margin-bottom: 8px;
}
.sub__row { display: flex; }
.sub__input {
  background: var(--ink); border: 1px solid var(--line); border-right: 0;
  color: var(--paper); font-family: var(--ui); font-size: 14px;
  padding: 13px 15px; min-width: 210px;
}
.sub__input::placeholder { color: var(--mute-2); }
.sub__input:focus { outline: none; border-color: var(--teal-dim); }
.sub__note { margin: 8px 0 0; font-family: var(--ui); font-size: 12px; letter-spacing: .06em; color: var(--teal); min-height: 1em; }
.sub__note.is-bad { color: #ff8b8b; }

/* ── REVEAL ───────────────────────────────────────────────────────────── */

.reveal { opacity: 0; transform: translateY(18px); }
.reveal.is-in {
  opacity: 1; transform: none;
  transition: opacity .7s var(--ease), transform .7s var(--ease);
}
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; }
  .slide { transition: none; }
}

/* ── DESKTOP PAIRING ──────────────────────────────────────────────────────
   About and Events share one row on a wide screen. `main` becomes a two
   column grid where every section still spans the full width, and only these
   two take a single column each, so the change is contained to the pair.

   Grid items stretch, so the two sections end level and their bottom rules
   line up as one. Left to their own heights they would not.
------------------------------------------------------------------------- */
@media (min-width: 800px) {
  /* Two empty gutter tracks hold the pair on the same centre line as every
     other section. Sizing the sections themselves inside plain 1fr 2fr
     columns does NOT do that: those columns divide the whole VIEWPORT, so
     past 1560px their boundary drifts left of the shell's own third and the
     pair sat 167px off centre from Releases above it.

     The 1fr gutters absorb whatever is beyond the shell, and full width
     children still span 1 / -1, so the hero keeps running edge to edge. */
  main {
    display: grid;
    grid-template-columns:
      1fr
      min(calc(var(--shell-max) * .19), 19%)
      min(calc(var(--shell-max) * .81), 81%)
      1fr;
    align-items: stretch;
  }
  main > * { grid-column: 1 / -1; }

  /* `container-type: size` so the text can be pushed down by a share of the
     panel's HEIGHT. A percentage margin would not do it: percentage margins
     and padding resolve against the container's WIDTH, whatever axis they
     sit on. Size containment is safe here because the row's height comes
     from the Events column beside it, not from this panel's own content. */
  /* The rule under Events sits just below the posters rather than a full
     gutter beneath them. */
  #events { padding-bottom: 10px; }

  #about  { grid-column: 2; margin: 0; container-type: size; }
  #about .about__text { margin-top: 5cqh; }

  /* The blurb runs wider than the heading by reaching into the panel's right
     padding, stopping 12px short of the edge so it never outruns the ball
     behind it. Expressed against the gutter rather than as a fixed width, so
     it stays inside the panel as the column narrows. */
  #about .prose p { width: calc(100% + var(--gut) - 12px); }
  #events { grid-column: 3; margin: 0; }

  /* Narrower than a full bleed row, so the cards get their own count. */
  #events .events { grid-template-columns: repeat(2, 1fr); }

}

/* ── RESPONSIVE ───────────────────────────────────────────────────────── */

@media (max-width: 1400px) {
  .releases { --shelf-cols: 4; }
  .merch    { --shelf-cols: 6; }
}

@media (max-width: 1180px) {
  .nav__links { display: none; }
  .burger { display: flex; }
  /* The links are hidden, but `.nav__links + .nav__right` still matches them
     and zeroes the auto margin, which stranded the burger in mid bar. Put it
     back so the burger sits hard against the right edge. */
  .nav__links + .nav__right { margin-left: auto; }
  .events { grid-template-columns: repeat(3, 1fr); }
  .merch  { --shelf-cols: 5; }
  .feat { grid-template-columns: 1fr; }
}

@media (max-width: 860px) {
  .about__grid { gap: 26px; }
  html { scroll-padding-top: 76px; }
  .nav__socials { display: none; }
  .edge { display: none; }
  .slide__body { padding-left: var(--gut); padding-right: var(--gut); }
  .hero__controls { padding-left: var(--gut); padding-right: var(--gut); }
  .releases { --shelf-cols: 3; }
  .merch    { --shelf-cols: 4; }
  .events   { grid-template-columns: repeat(2, 1fr); }
  .contact__grid { grid-template-columns: 1fr; }
  .sub { margin-left: 0; width: 100%; }
  .sub__row { flex-wrap: wrap; gap: 10px; }
  .sub__input { border-right: 1px solid var(--line); flex: 1 1 200px; }
}

@media (max-width: 560px) {
  /* Three across on a phone, with the 2.4 row cap above doing the vertical
     trimming. More of the catalogue is in view at once and the covers are
     still large enough to read. */
  .releases { --shelf-cols: 3; }
  .merch    { --shelf-cols: 3; }
  .hero__meta { font-size: 12.5px; gap: 9px; }
  .dot { font-size: 12px; }
  .dot.is-on::after { width: 28px; }
  .foot__inner { gap: 22px; }
}
