/* ---------- 0. Font (self-hosted) ------------------------- */

/* Afacad Flux, SIL Open Font License, served from our own origin.
   Loading it from fonts.googleapis.com sent every visitor's IP to Google,
   which is the exact practice a Munich court ruled on in 2022 and which
   drove a wave of German warning letters. Self-hosting removes the
   third-party request entirely, and drops two DNS lookups and a
   render-blocking stylesheet while it is at it. Do not put the Google
   <link> back. */

@font-face {
  font-family: 'Afacad Flux';
  font-style: normal;
  font-weight: 100 1000;
  font-display: swap;
  src: url('assets/fonts/afacad-flux-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Afacad Flux';
  font-style: normal;
  font-weight: 100 1000;
  font-display: swap;
  src: url('assets/fonts/afacad-flux-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ============================================================
   frgmchine, single page site
   Built from Claude Design "Frgmchine Site.dc.html", direction 4a
   ("Horizon, combined"). Values are transcribed from that frame;
   see HANDOVER.md for the token table and for the list of things
   that were interpreted or changed rather than copied.
   ============================================================ */

/* ---------- 1. Tokens ------------------------------------- */

:root {
  /* Base + layered surfaces */
  --base:          #0A1016;   /* page / hero / about / played-at / contact */
  --surface-music: #0F1922;   /* the one lifted band, behind the gradient */
  --scrim:          10, 16, 22;  /* --base as an rgb triplet, for overlays */

  /* Sunset accents */
  --teal:  #17B2A6;
  --coral: #F0806A;
  --amber: #F5A15A;
  --teal-rgb:  23, 178, 166;
  --coral-rgb: 240, 128, 106;
  --amber-rgb: 245, 161,  90;

  /* Ink, one warm off-white, stepped down by alpha. Never a grey hex. */
  --ink-rgb:  241, 236, 228;
  --ink:      rgb(var(--ink-rgb));            /* #F1ECE4  headings, hero, venues */
  --ink-75:   rgba(var(--ink-rgb), .75);      /* hero tagline */
  --ink-72:   rgba(var(--ink-rgb), .72);      /* social links */
  --ink-62:   rgba(var(--ink-rgb), .62);      /* about body copy */
  --ink-60:   rgba(var(--ink-rgb), .60);      /* release subtitle */
  --ink-55:   rgba(var(--ink-rgb), .55);      /* venue meta */
  --ink-50:   rgba(var(--ink-rgb), .50);      /* nav links */
  --ink-45:   rgba(var(--ink-rgb), .45);      /* hero corner labels, booking eyebrow */
  --ink-40:   rgba(var(--ink-rgb), .40);      /* played-at note */
  --ink-35:   rgba(var(--ink-rgb), .35);      /* button border */
  --ink-32:   rgba(var(--ink-rgb), .32);      /* footer */
  --ink-30:   rgba(var(--ink-rgb), .30);      /* scroll rule */
  --ink-12:   rgba(var(--ink-rgb), .12);      /* hairline between venues */

  /* Type */
  --font: 'Afacad Flux', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --w:    400;              /* 4a uses exactly one weight */

  --fs-hero:     88px;
  --fs-email:    clamp(30px, 5.2vw, 64px);
  --fs-release:  32px;
  --fs-venue:    22px;   /* venue name, card caption */
  --fs-lead:     27px;
  --fs-body:     17px;      /* also the nav wordmark */
  --fs-tagline:  16px;
  --fs-meta:     15px;
  --fs-social:   14px;
  --fs-micro:    13px;      /* eyebrows, nav links, buttons, footer */

  --ls-eyebrow: .28em;
  --ls-tagline: .24em;
  --ls-nav:     .18em;      /* also social links */
  --ls-button:  .16em;      /* also hero corner labels */
  --ls-meta:    .12em;
  --ls-footer:  .10em;
  --ls-word:    .06em;      /* both wordmarks */

  --lh-body:    1.85;
  --lh-lead:    1.5;

  /* Spacing, a 4px grid, used at these steps only */
  --s-1:   8px;
  --s-2:  10px;
  --s-3:  18px;
  --s-4:  20px;
  --s-5:  22px;
  --s-6:  26px;
  --s-7:  28px;
  --s-8:  32px;
  --s-9:  34px;
  --s-10: 40px;
  --s-11: 48px;
  --s-13: 64px;
  --s-14: 80px;

  /* Section vertical padding. Sections are full-height and centre their
     own content, so this only has to stop copy touching the edges on a
     short viewport, hence it shrinks with vh instead of holding 130px. */
  --pad-y-a: clamp(48px, 6vh, 88px);     /* about, played-at */
  --pad-y-b: clamp(64px,  9vh, 110px);   /* music, contact */

  /* Height of the sticky bar. main.js overwrites this with the measured
     value on load and on resize; these are the fallbacks. */
  --nav-h: 82px;

  --gutter:    48px;
  --maxw:    1180px;    /* full content frame */
  --maxw-prose: 660px;  /* about column */

  /* Everything is square. No radius anywhere in 4a. */
  --radius: 0;

  /* Motion */
  --t-breathe-hero-a: 16s;
  --t-breathe-hero-b: 20s;
  --t-hover:         .25s;
}

/* ---------- 2. Reset -------------------------------------- */

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

html {
  scroll-behavior: smooth;
  scroll-padding-top: var(--nav-h);
}

body {
  margin: 0;
  background: var(--base);
  color: var(--ink);
  font-family: var(--font);
  font-weight: var(--w);
  font-size: var(--fs-body);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  font-synthesis: none;
  overflow-x: hidden;
}

h1, h2, p, ul { margin: 0; }

/* 4a uses exactly one weight. Headings default to bold in every browser,
   and because this is a variable font that resolves to a genuine 700
   instance, so <h2 class="eyebrow"> rendered heavier than the identical
   <p class="eyebrow"> beside it. Pin every heading to the one weight. */
h1, h2, h3, h4, h5, h6 { font-weight: var(--w); }
ul { padding: 0; list-style: none; }
img { display: block; max-width: 100%; }

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

:focus-visible {
  outline: 1px solid var(--teal);
  outline-offset: 4px;
}

.skip-link { position: absolute; left: -9999px; }
.skip-link:focus {
  left: var(--gutter);
  top: 12px;
  z-index: 10;
  padding: 8px 14px;
  background: var(--base);
  border: 1px solid var(--teal);
}

/* ---------- 3. Shared primitives -------------------------- */

/* Every section fills one screen minus the sticky bar, and centres its
   own content vertically. */
.section {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: calc(100vh - var(--nav-h));
  min-height: calc(100svh - var(--nav-h));
  padding-inline: var(--gutter);
  overflow: hidden;
}

.eyebrow {
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--teal);
}
.eyebrow--muted { color: var(--ink-45); }

/* Edge-anchored light. Never a colour block, always a bloom
   bleeding in from an edge, sized in % so it scales with the viewport. */
.bloom {
  position: absolute;
  pointer-events: none;
}

/* A 1px sunset hairline across the top of a band. This is what
   divides played-at from contact. */
.edge-rule {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 1px;
  pointer-events: none;
}

/* ---------- 4. Sticky nav -------------------------------- */

/* 1px probe above the bar. Once it leaves the viewport the bar is
   "stuck", which is when the bottom hairline fades in. Cheaper than a
   scroll listener and it never fights with smooth scrolling. */
.nav-sentinel {
  position: absolute;
  top: 0;
  height: 1px;
  width: 1px;
  pointer-events: none;
}

.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(var(--scrim), .78);
  -webkit-backdrop-filter: blur(14px) saturate(120%);
  backdrop-filter: blur(14px) saturate(120%);
}

/* Hairline that appears only once the bar is over content. */
.nav::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: var(--ink-12);
  opacity: 0;
  transition: opacity .3s ease;
}
.nav.is-stuck::after { opacity: 1; }

/* Browsers without backdrop-filter get a solid bar rather than a
   half-transparent one with text scrolling through it. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .nav { background: var(--base); }
}

.nav__inner {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s-6);
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: var(--s-6) var(--gutter);
}

.nav__brand {
  display: flex;
  align-items: center;
  gap: 14px;
}

.nav__cube { height: 30px; width: auto; }

.nav__wordmark {
  font-size: var(--fs-body);
  letter-spacing: var(--ls-word);
}

.nav__links {
  display: flex;
  gap: var(--s-9);
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-nav);
  text-transform: uppercase;
  color: var(--ink-50);
}

.nav__links a { transition: color var(--t-hover) ease; }
.nav__links a:hover,
.nav__links a[aria-current="true"] { color: var(--ink); }

/* ---------- 5. Hero --------------------------------------- */

/* First screen = sticky bar + hero. */
.hero {
  position: relative;
  min-height: calc(100vh - var(--nav-h));
  min-height: calc(100svh - var(--nav-h));
  overflow: hidden;
}

.hero__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 64%;
  opacity: .72;
}

/* Vertical scrim: dark at the top so the nav clears, open through the
   middle so the cover art reads, dark again at the foot so type sits. */
.hero__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgba(var(--scrim), .70)   0%,
    rgba(var(--scrim), .10)  40%,
    rgba(var(--scrim), .95) 100%
  );
}

.bloom--hero-teal {
  left: -10%; right: -10%; bottom: -24%; height: 64%;
  background: radial-gradient(52% 100% at 32% 100%, rgba(var(--teal-rgb), .26), transparent 70%);
  filter: blur(10px);
  animation: breathe var(--t-breathe-hero-a) ease-in-out infinite;
}
.bloom--hero-coral {
  left: -10%; right: -10%; bottom: -26%; height: 68%;
  background: radial-gradient(60% 100% at 70% 100%, rgba(var(--coral-rgb), .30), transparent 72%);
  filter: blur(10px);
  animation: breathe var(--t-breathe-hero-b) ease-in-out infinite;
}

.hero__center {
  position: absolute;
  left: 50%;
  top: 52%;
  transform: translate(-50%, -50%);
  width: 100%;
  padding: 0 var(--gutter);
  text-align: center;
}

.hero__wordmark {
  font-size: var(--fs-hero);
  font-weight: var(--w);
  letter-spacing: var(--ls-word);
  line-height: 1;
  text-shadow: 0 2px 40px rgba(var(--scrim), .5);
}

.hero__tagline {
  margin-top: var(--s-8);
  font-size: var(--fs-tagline);
  letter-spacing: var(--ls-tagline);
  text-transform: uppercase;
  color: var(--ink-75);
}

.hero__corner {
  position: absolute;
  bottom: var(--s-9);
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-button);
  text-transform: uppercase;
  color: var(--ink-45);
}
.hero__corner--left  { left: var(--gutter); }
.hero__corner--right {
  right: var(--gutter);
  display: flex;
  align-items: center;
  gap: var(--s-2);
}
.hero__rule {
  width: 44px;
  height: 1px;
  background: var(--ink-30);
}

/* ---------- 6. About -------------------------------------- */

.about {
  align-items: center;
  padding-block: var(--pad-y-a);
}

.bloom--about {
  left: -14%; top: -40%; width: 58%; height: 180%;
  background: radial-gradient(50% 50% at 50% 50%, rgba(var(--teal-rgb), .10), transparent 70%);
}

.about__col {
  position: relative;
  width: 100%;
  max-width: var(--maxw-prose);
}

.about .eyebrow { margin-bottom: var(--s-9); }

.about__lead {
  margin-bottom: var(--s-6);
  font-size: var(--fs-lead);
  line-height: var(--lh-lead);
  text-wrap: pretty;
}

.about__body {
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--ink-62);
  text-wrap: pretty;
}
.about__body + .about__body { margin-top: var(--s-5); }

/* ---------- 7. Music -------------------------------------- */

.music {
  background: var(--surface-music);
  padding-block: var(--pad-y-b);
}

.bloom--music-teal,
.bloom--music-coral,
.bloom--music-amber { inset: 0; }

.bloom--music-teal  { background: radial-gradient(80% 120% at 12%   0%, rgba(var(--teal-rgb), .26), transparent 62%); }
.bloom--music-coral { background: radial-gradient(90% 130% at 88% 100%, rgba(var(--coral-rgb), .32), transparent 64%); }
.bloom--music-amber { background: radial-gradient(60%  90% at 58% 110%, rgba(var(--amber-rgb), .24), transparent 68%); }

.edge-rule--music {
  background: linear-gradient(
    90deg,
    transparent,
    rgba(var(--teal-rgb), .6),
    rgba(var(--coral-rgb), .7),
    rgba(var(--amber-rgb), .6),
    transparent
  );
}

.music .eyebrow { margin-bottom: var(--s-7); }

/* Cover + the offset sunset card behind it. */
.cover {
  position: relative;
  width: 320px;
  max-width: 100%;
  aspect-ratio: 1;
}
.cover__card {
  position: absolute;
  inset: 0;
  transform: translate(14px, 14px);
  background: linear-gradient(135deg, var(--teal), var(--coral) 55%, var(--amber));
  opacity: .5;
}
.cover__art {
  position: relative;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.music__title {
  margin-top: var(--s-6);
  font-size: var(--fs-release);
  font-weight: var(--w);
}

.music__sub {
  margin-top: var(--s-1);
  font-size: var(--fs-meta);
  color: var(--ink-60);
}

.btn-row {
  margin-top: var(--s-5);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
}

.btn {
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-button);
  text-transform: uppercase;
  border: 1px solid var(--ink-35);
  border-radius: var(--radius);
  padding: 9px 16px;
  transition: border-color var(--t-hover) ease, color var(--t-hover) ease;
}
.btn:hover {
  border-color: var(--coral);
  color: var(--coral);
}

.player {
  display: block;
  width: 100%;
  border: 0;
}

/* Subsection label inside the music band. Deliberately quieter than the
   teal section eyebrow, same size and tracking, muted ink, so "music"
   still reads as the section and these read as parts of it. */
.subhead {
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--ink-45);
  margin-bottom: 16px;
}

/* Heights mirror the height attributes on the iframes so they can be
   tuned per breakpoint from here. */
.player--spotify    { height: 152px; }

/* ---------- 7b. Custom SoundCloud player ------------------ */

/* SoundCloud's widget has no dark theme, forces its height, and paints
   the profile avatar as an uncontrollable centre-crop. So the widget is
   kept for playback only and hidden, and this is the UI on top of it.
   Panel treatment is the design's own embed box: hairline border over
   rgba(base, .35). */

.scp {
  position: relative;
  height: 300px;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--ink-12);
  background: rgba(var(--scrim), .35);
  overflow: hidden;
}

/* The real widget: audio only, never seen. Kept inside the box at 1px
   rather than display:none, hidden iframes are less reliably allowed
   to play audio. */
.scp__frame {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 1px;
  height: 1px;
  border: 0;
  opacity: 0;
  pointer-events: none;
}

/* Shown before the API answers, and left in place permanently if it
   never does. */
.scp__fallback {
  margin: auto;
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-button);
  text-transform: uppercase;
  color: var(--ink-45);
  transition: color var(--t-hover) ease;
}
.scp__fallback:hover { color: var(--ink); }

.scp__now {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px;
  border-bottom: 1px solid var(--ink-12);
}

.scp__art {
  flex: 0 0 auto;
  width: 84px;
  height: 84px;
  object-fit: cover;
  background: rgba(var(--scrim), .6);
}

.scp__meta { flex: 1 1 auto; min-width: 0; }

.scp__title {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: var(--fs-body);
  line-height: 1.35;
  margin-bottom: 12px;
  transition: color var(--t-hover) ease;
}
.scp__title:hover { color: var(--amber); }

.scp__scrub {
  position: relative;
  height: 2px;
  background: var(--ink-12);
  cursor: pointer;
}
/* A 2px line is not a hit target. */
.scp__scrub::before {
  content: "";
  position: absolute;
  inset: -9px 0;
}
.scp__fill {
  height: 100%;
  background: linear-gradient(90deg, var(--teal), var(--coral));
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .15s linear;
}

.scp__times {
  display: flex;
  justify-content: space-between;
  margin-top: 8px;
  font-size: 12px;
  letter-spacing: var(--ls-footer);
  color: var(--ink-45);
  font-variant-numeric: tabular-nums;
}

.scp__play {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  background: none;
  border: 1px solid var(--ink-35);
  border-radius: var(--radius);
  color: var(--ink);
  cursor: pointer;
  transition: border-color var(--t-hover) ease, color var(--t-hover) ease;
}
.scp__play:hover { border-color: var(--teal); color: var(--teal); }
.scp__play svg { width: 20px; height: 20px; fill: currentColor; }

.scp__list {
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgba(var(--ink-rgb), .2) transparent;
}
.scp__list::-webkit-scrollbar { width: 6px; }
.scp__list::-webkit-scrollbar-thumb { background: rgba(var(--ink-rgb), .2); }
.scp__list::-webkit-scrollbar-track { background: transparent; }

/* Rows echo the played-at list: name left, duration right, hairline between. */
.scp__row {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  padding: 13px 16px;
  background: none;
  border: 0;
  border-bottom: 1px solid rgba(var(--ink-rgb), .06);
  font: inherit;
  color: var(--ink-55);
  text-align: left;
  cursor: pointer;
  transition: color var(--t-hover) ease, background var(--t-hover) ease;
}
.scp__row:hover { color: var(--ink); background: rgba(var(--ink-rgb), .04); }
.scp__list li:last-child .scp__row { border-bottom: 0; }

.scp__row[aria-current="true"] {
  color: var(--ink);
  box-shadow: inset 2px 0 0 var(--teal);
}

.scp__row-title {
  font-size: var(--fs-meta);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.scp__row-time {
  flex: 0 0 auto;
  font-size: 12px;
  letter-spacing: var(--ls-footer);
  color: var(--ink-40);
  font-variant-numeric: tabular-nums;
}

/* Inner wrapper: holds content to the 1180px frame while the section's
   background and blooms stay full-bleed. */
.music__inner {
  position: relative;
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
}

/* Release: cover left, everything about the record right. Moving the meta
   across balances it, left alone, a lone 152px player looked stranded
   against a ~510px cover column. */
.release {
  display: grid;
  grid-template-columns: 320px 1fr;
  gap: var(--s-13);
  align-items: start;
}

.release__meta .music__title { margin-top: 0; }
.release__meta .player       { margin-top: var(--s-6); }

/* Sets gets its own block rather than its own section, so the player is
   free of the cover's height and can show more of the list. */
.scp--tall { height: 420px; }

.sets__link {
  margin-top: var(--s-6);
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-button);
  text-transform: uppercase;
  color: var(--ink-45);
}
.sets__link a { transition: color var(--t-hover) ease; }
.sets__link a:hover { color: var(--ink); }

/* Hairline between the two blocks, the only thing dividing them, since
   they deliberately share one background. */
.subdivider {
  height: 1px;
  background: var(--ink-12);
  margin: var(--s-13) 0;
}

/* ---------- 7d. Play gates -------------------------------- */

/* Neither player loads until asked, because both contact their own servers
   the moment their iframe exists, a transfer before consent. Gating them
   means the site makes no unconsented third-party request at all, so no
   cookie banner is needed. Art. 6(1)(a): the click is the consent.
   
   The first version of this looked like a legal notice, which is the wrong
   thing to put in front of the primary action on a music page. So the gate
   is shaped like a player instead: artwork, title, play triangle. Clicking
   it loads the real player but does not start audio, so nothing begins
   playing that the visitor did not ask for twice. */

/* The wrapper, not the button, is what gets swapped for the real iframe, so the disclosure line disappears along with the button instead of
   lingering underneath a loaded player. */
.playgate-wrap { display: block; }

.playgate {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  width: 100%;
  min-height: 118px;
  padding: var(--s-3);
  background: rgba(var(--scrim), .35);
  border: 1px solid var(--ink-12);
  border-radius: var(--radius);
  font: inherit;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
  transition: border-color var(--t-hover) ease, background var(--t-hover) ease;
}
.playgate:hover {
  border-color: rgba(var(--teal-rgb), .55);
  background: rgba(var(--scrim), .55);
}

.playgate__art {
  flex: 0 0 auto;
  width: 84px;
  height: 84px;
  object-fit: cover;
}

.playgate__body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.playgate__eyebrow {
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--ink-45);
}

.playgate__title {
  font-size: var(--fs-body);
  line-height: 1.3;
}

.playgate__icon {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 1px solid var(--ink-35);
  color: var(--ink);
  transition: border-color var(--t-hover) ease, color var(--t-hover) ease;
}
.playgate:hover .playgate__icon { border-color: var(--teal); color: var(--teal); }
.playgate__icon svg { width: 20px; height: 20px; fill: currentColor; }

/* The disclosure. Deliberately quiet, footer ink, one line. */
/* Set at body-copy ink and size on purpose. An earlier version used
   --ink-32, the faintest value on the site, which made a loud button sit
   over an unreadable disclosure. If the consent is worth asking for, the
   sentence explaining it has to be as readable as the rest of the page. */
/* Runs the full width of the gate above it. A 62ch cap was making it four
   narrow lines under a wide box, which read worse and cost more height than
   simply letting it match. */
.playgate-note {
  margin-top: var(--s-3);
  font-size: var(--fs-meta);
  line-height: 1.7;
  color: var(--ink-62);
}
.playgate-note a {
  color: var(--amber);
  transition: color var(--t-hover) ease;
}
.playgate-note a:hover { color: var(--coral); }

/* While gated the SoundCloud box collapses to the gate's own height, so a
   420px panel is not sitting empty waiting to be filled. It expands when
   the real player replaces it. */
.scp.is-gated {
  height: auto;
  border: 0;
  background: none;
  overflow: visible;
}

/* ---------- 8. Played at ---------------------------------- */

.played { padding-block: var(--pad-y-a); }

.played__inner {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
}

.played .eyebrow { margin-bottom: var(--s-10); }

/* Played-at is three photo cards: each venue shown with a photo of that
   venue. Full 1180 frame, three across, 3:4, the ratio the source photos
   were normalised to, so nothing is cropped twice. */
.vcards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s-8);
  margin-inline: auto;

  /* The photos are the only flexible dimension in this section, so the row
     width is capped by the height that is left after the sticky bar, the
     padding, the eyebrow and the captions (~250px). At 3:4, photo height is
     card width x 4/3, so card width = available height x 0.75. On a tall
     screen this resolves above 1180px and the 100% cap wins. Without it the
     section overflows on a 13" laptop. */
  max-width: min(100%, calc((100svh - var(--nav-h) - 250px) * 0.75 * 3 + 2 * var(--s-8)));
}

.vcard { margin: 0; }

.vcard__img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 4;
  object-fit: cover;
}

/* The hairline the design used between list rows now sits under each
   caption instead. */
.vcard figcaption {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: var(--s-3);
  padding-top: var(--s-3);
  border-top: 1px solid var(--ink-12);
}

.vcard__name { font-size: var(--fs-venue); }

.vcard__meta {
  font-size: var(--fs-meta);
  letter-spacing: var(--ls-meta);
  text-transform: uppercase;
  color: var(--ink-55);
}

/* ---------- 9. Contact ------------------------------------ */

.contact { padding-block: var(--pad-y-b); }

.bloom--contact-teal {
  left: -6%; right: -6%; bottom: -60%; height: 150%;
  background: radial-gradient(50% 100% at 24% 100%, rgba(var(--teal-rgb), .20), transparent 70%);
}
.bloom--contact-coral {
  left: -6%; right: -6%; bottom: -70%; height: 160%;
  background: radial-gradient(60% 100% at 72% 100%, rgba(var(--coral-rgb), .24), transparent 72%);
}

/* Every section that sits on the base is divided the same way, so the
   scroll reads with one rhythm: about, played-at and contact share this
   rule, and music's lifted band opens with its own stronger one. A plain
   --ink-12 hairline was tried first and is invisible at 1px on #0A1016. */
.edge-rule--about,
.edge-rule--played,
.edge-rule--contact {
  background: linear-gradient(
    90deg,
    transparent,
    rgba(var(--teal-rgb), .5) 20%,
    rgba(var(--coral-rgb), .6) 55%,
    rgba(var(--amber-rgb), .5) 80%,
    transparent
  );
}

/* margin-block:auto centres this in whatever space the footer leaves, so
   the emptiness reads as framing rather than as a gap. */
.contact__inner {
  position: relative;
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  margin-block: auto;
}

.contact .eyebrow { margin-bottom: var(--s-5); }

.contact__email {
  margin-top: var(--s-3);
  font-size: var(--fs-email);
  line-height: 1.1;
  letter-spacing: -.005em;
  overflow-wrap: anywhere;
}
.contact__email a { transition: color var(--t-hover) ease; }
.contact__email a:hover { color: var(--amber); }

/* Sits between the address and the follow links, so the three read as one
   block rather than as an address floating in a full screen. */
/* No width cap. A 46ch measure forced this onto two lines for no reason;
   it is one short sentence and it should sit on one. */
.contact__note {
  margin-top: var(--s-5);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--ink-62);
}

.contact__social {
  margin-top: var(--s-8);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-7);
  font-size: var(--fs-social);
  letter-spacing: var(--ls-nav);
  text-transform: uppercase;
  color: var(--ink-72);
}
.contact__social a { transition: color var(--t-hover) ease; }
.contact__social a:hover { color: var(--ink); }

.contact__legal { display: flex; gap: var(--s-5); }
.contact__legal a { transition: color var(--t-hover) ease; }
.contact__legal a:hover { color: var(--ink); }

.contact__footer {
  position: relative;
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  margin-top: var(--s-14);
  padding-top: var(--s-5);
  border-top: 1px solid var(--ink-12);
  display: flex;
  justify-content: space-between;
  gap: var(--s-4);
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-footer);
  color: var(--ink-32);
}

/* ---------- 9b. 404 --------------------------------------- */

/* Reuses the hero shell, minus the cover art, just the base with the
   two blooms breathing over it. */

.notfound__code { margin-bottom: var(--s-5); }

.notfound__title {
  font-size: clamp(30px, 5vw, 52px);
  font-weight: var(--w);
  letter-spacing: var(--ls-word);
  line-height: 1.15;
  text-wrap: pretty;
}

.notfound__back {
  margin-top: var(--s-8);
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-button);
  text-transform: uppercase;
  color: var(--ink-45);
}
.notfound__back a { transition: color var(--t-hover) ease; }
.notfound__back a:hover { color: var(--ink); }

/* ---------- 10. Motion ------------------------------------ */

@keyframes breathe {
  0%, 100% { opacity: .35; }
  50%      { opacity: .70; }
}

/* ---------- 11. Responsive -------------------------------- */

/* Tablet: the music band loses its two-column split. */
@media (max-width: 900px) {
  :root {
    --gutter: 32px;
    --fs-hero:  clamp(46px, 9vw, 72px);
    --fs-lead:  24px;
  }

  .release {
    grid-template-columns: 1fr;
    gap: var(--s-10);
  }
  .scp--tall  { height: 380px; }
  .subdivider { margin: var(--s-11) 0; }
}

/* Phone: nav in two centred rows, everything stacks. */
@media (max-width: 640px) {
  :root {
    --gutter: 22px;
    --fs-body:    16px;
    --fs-release: 28px;
    --fs-tagline: 12px;
    --ls-tagline: .18em;
  }

  /* One compact row. The wordmark goes, the cube still reads as the
     brand and still links home, and the hero repeats the wordmark at
     full size directly below. Buys ~85px of width for the links. */
  :root { --nav-h: 58px; }

  .nav__inner {
    gap: var(--s-3);
    padding: 14px var(--gutter);
  }
  .nav__wordmark { display: none; }
  .nav__cube { height: 26px; }
  .nav__links {
    gap: 14px;
    font-size: 12px;
    letter-spacing: .14em;
    flex-wrap: wrap;
    justify-content: flex-end;
  }
  .hero__center { top: 48%; }
  .hero__wordmark { text-shadow: 0 2px 24px rgba(var(--scrim), .6); }

  /* One corner label only, "scroll" is redundant on touch. */
  .hero__corner--right { display: none; }
  .hero__corner--left  { right: var(--gutter); text-align: center; }

  .cover       { width: 100%; }
  .cover__card { transform: translate(10px, 10px); }

  .scp      { height: 280px; }
  .scp__art { width: 68px; height: 68px; }

  /* Two up rather than three, at 106px a card is a thumbnail, not a
     photo. Hennwack takes the full width and the other two pair up below
     it, which also avoids ending the grid on an orphan. */
  .vcards {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--s-2);
    max-width: 100%;          /* the height cap is desktop-only */
  }
  .vcard:first-child { grid-column: 1 / -1; }
  .vcard__name { font-size: 18px; }   /* overrides --fs-venue at card scale */
  .vcard__meta { font-size: 12px; }

  .contact__social { margin-top: var(--s-8); gap: var(--s-4) var(--s-5); }
  .contact__footer {
    margin-top: var(--s-11);
    flex-direction: column;
    gap: 6px;
  }
}

/* Short viewports (landscape phones, small laptops): stop forcing a
   full screen per section, or the copy gets squeezed against the edges. */
@media (max-height: 620px) {
  .section { min-height: 0; }
  .hero { min-height: 380px; }
}

/* ---------- 12. Legal pages ------------------------------- */

/* Impressum and Datenschutzerklärung. Prose pages, so they drop the
   one-section-per-screen rule entirely and just flow. */

.legal {
  padding: calc(var(--nav-h) / 2 + var(--s-11)) var(--gutter) var(--pad-y-a);
}

.legal__inner {
  width: 100%;
  max-width: 760px;
  margin-inline: auto;
}

.legal__title {
  margin: var(--s-3) 0 var(--s-10);
  font-size: clamp(32px, 5vw, 52px);
  font-weight: var(--w);
  letter-spacing: var(--ls-word);
  line-height: 1.1;
}

.legal h2 {
  margin: var(--s-13) 0 var(--s-4);
  font-size: 20px;
  font-weight: var(--w);
  color: var(--ink);
}

.legal p,
.legal li {
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--ink-62);
  text-wrap: pretty;
}

.legal p + p { margin-top: var(--s-4); }

.legal ul {
  margin: var(--s-4) 0;
  padding-left: 1.1em;
  list-style: disc;
}
.legal li { margin-bottom: 6px; }

.legal a { color: var(--amber); }
.legal a:hover { color: var(--coral); }

.legal strong { color: var(--ink); font-weight: var(--w); }

.legal code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .9em;
  color: var(--teal);
}


.legal__meta {
  margin-top: var(--s-13);
  font-size: var(--fs-meta);
  color: var(--ink-40);
}

.legal__back {
  margin-top: var(--s-10);
  padding-top: var(--s-5);
  border-top: 1px solid var(--ink-12);
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-button);
  text-transform: uppercase;
}
.legal__back a { color: var(--ink-45); }
.legal__back a:hover { color: var(--ink); }

.legal__footer {
  padding: var(--s-6) var(--gutter) var(--s-11);
  border-top: 1px solid var(--ink-12);
}
.legal__footer-inner {
  width: 100%;
  max-width: 760px;
  margin-inline: auto;
  display: flex;
  justify-content: space-between;
  gap: var(--s-4);
  flex-wrap: wrap;
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-footer);
  color: var(--ink-32);
}
.legal__footer-inner a { transition: color var(--t-hover) ease; }
.legal__footer-inner a:hover { color: var(--ink); }

/* ---------- 13. Reduced motion ---------------------------- */

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

  /* Hold the two breathing blooms at their mid-point rather than their
     trough, so the hero keeps its light. The static blooms elsewhere
     are untouched and keep their own opacity. */
  .bloom--hero-teal,
.bloom--hero-coral {
    animation: none !important;
    opacity: .7;
  }

  *, *::before, *::after {
    transition-duration: .01ms !important;
  }
}
