/* ==========================================================================
   The Spirographs - thespirographs.com
   Palette taken from the official logo artwork: cream lockup on dark green,
   with the rust colourway as the accent.
   ========================================================================== */

/* ---------- Fonts (self-hosted, no third-party requests) ---------------- */
@font-face {
  font-family: 'Anton';
  src: url('../fonts/anton-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  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: 'Inter';
  src: url('../fonts/inter-latin-var.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  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;
}

/* ---------- Tokens ------------------------------------------------------ */
:root {
  /* Brand */
  --green-900: #131A16;   /* page ground  */
  --green-800: #1C2620;   /* raised panel */
  --green-700: #2C3A33;   /* logo green   */
  --green-600: #3B4C43;   /* borders      */
  --cream:     #F2D8A8;   /* logo cream   */
  --cream-100: #FBF3E4;   /* headings     */
  --rust:      #B4511E;   /* logo rust, used as a solid fill      */
  --rust-lit:  #D2691F;   /* hover / larger accents               */
  /* Small rust text needs to be lighter than the logo rust to clear 4.5:1
     against the dark grounds and the hero artwork. Measured worst case 4.71. */
  --rust-text: #F0954B;

  --text:      #E7DCC6;
  --muted:     #A99F8C;
  --line:      rgba(242, 216, 168, 0.16);
  --line-soft: rgba(242, 216, 168, 0.08);

  /* Type */
  --display: 'Anton', 'Haettenschweiler', 'Arial Narrow Bold', sans-serif;
  --body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
    Helvetica, Arial, sans-serif;

  /* Rhythm */
  --header-h: clamp(80px, 9vw, 118px);
  --wrap: 1180px;
  --gutter: clamp(1.25rem, 4vw, 3rem);
  --section: clamp(4rem, 9vw, 7.5rem);
  --radius: 2px;
}

/* ---------- Reset ------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

/* The hidden attribute has to win. The browser's own rule is display:none at
   the lowest priority, so any component that sets its own display, which here
   means every flex or grid row, silently ignores it: the element reports
   hidden while staying on screen. This bit us twice, first on the countdown
   and then on the pre-release button. */
[hidden] { display: none !important; }

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

/* The masthead is fixed, so an anchor jump would put the target underneath it.
   Every id is a potential jump target (/signup goes to #list-title), so keep
   them all clear of the header plus a little breathing room. */
[id] {
  scroll-margin-top: calc(var(--header-h) + 1.5rem);
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

body {
  background: var(--green-900);
  color: var(--text);
  font-family: var(--body);
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.0625rem);
  line-height: 1.65;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  /* `clip`, not `hidden`: `hidden` would make <body> its own scroll container,
     which breaks the sticky masthead. */
  overflow-x: clip;
}

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

a { color: var(--cream); text-decoration-color: var(--line); text-underline-offset: 3px; }
a:hover { color: var(--rust-lit); }

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

::selection { background: var(--rust); color: #fff; }

/* The "21st century static" - a fine grain over the whole page. */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  opacity: 0.045;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ---------- Layout helpers --------------------------------------------- */
.wrap {
  width: min(var(--wrap), 100%);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap--narrow { --wrap: 760px; }

.section { padding-block: var(--section); }
.section--tight { padding-block: clamp(2.5rem, 5vw, 4rem); }
/* Two releases stacked. Each .section carries --section top and bottom, so
   back-to-back they put up to 15rem between one sleeve and the next. These
   belong to the same run - what is coming, then what is out - so they are
   spaced as one list rather than as two unrelated sections. */
.section--release { padding-block: clamp(2.5rem, 5vw, 4rem); }
.section--release + .section--release { padding-top: 0; }

.section--panel { background: var(--green-800); }

.rule { border: 0; border-top: 1px solid var(--line); }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--rust);
  color: #fff;
  padding: 0.75rem 1.25rem;
  z-index: 10000;
  font-weight: 700;
}
.skip-link:focus { left: 0; }

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------- Type -------------------------------------------------------- */
h1, h2, h3, .display {
  font-family: var(--display);
  font-weight: 400;
  line-height: 0.92;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--cream-100);
}

h1 { font-size: clamp(2.75rem, 9vw, 6rem); }
h2 { font-size: clamp(2rem, 5.5vw, 3.5rem); }
h3 { font-size: clamp(1.15rem, 2.2vw, 1.5rem); line-height: 1.05; }

p + p { margin-top: 1em; }

.lede {
  font-size: clamp(1.125rem, 1rem + 0.7vw, 1.4rem);
  line-height: 1.5;
  color: var(--cream);
}

.eyebrow {
  font-family: var(--body);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--rust-text);
  margin-bottom: 1rem;
}
.eyebrow--cream { color: var(--cream); }

.section-head { margin-bottom: clamp(2rem, 4vw, 3rem); }
.section-head p { color: var(--muted); max-width: 54ch; margin-top: 1rem; }

/* ---------- Buttons ----------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.95rem 1.6rem;
  font-family: var(--body);
  font-size: 0.8125rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  border: 1px solid var(--rust);
  background: var(--rust);
  color: #fff;
  border-radius: var(--radius);
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease,
    border-color 0.18s ease, transform 0.18s ease;
}
.btn:hover {
  background: var(--rust-lit);
  border-color: var(--rust-lit);
  color: #fff;
  transform: translateY(-2px);
}
.btn:active { transform: translateY(0); }

.btn--ghost {
  background: transparent;
  border-color: var(--line);
  color: var(--cream);
}
.btn--ghost:hover {
  background: transparent;
  border-color: var(--cream);
  color: var(--cream);
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 2rem;
}

/* ---------- Masthead / nav ---------------------------------------------- */
/* Floats over the artwork; picks up a backdrop only once you scroll. */
.masthead {
  position: fixed;
  inset: 0 0 auto;
  z-index: 100;
  border-bottom: 1px solid transparent;
  transition: background-color 0.25s ease, border-color 0.25s ease;
}
.masthead.is-stuck {
  background: rgba(19, 26, 22, 0.88);
  backdrop-filter: saturate(140%) blur(12px);
  -webkit-backdrop-filter: saturate(140%) blur(12px);
  border-bottom-color: var(--line-soft);
}
.masthead__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  height: var(--header-h);
}

/* Everything except the takeover starts below the floating header. */
main { padding-top: var(--header-h); }
.home main { padding-top: 0; }

/* The logo is a mask so it can be recoloured / animated from CSS. */
.logo {
  display: block;
  /* Sized by width: the wordmark is a wide block, not the squarish lockup the
     roundel made it, so height-driven sizing would leave it tiny. */
  width: clamp(178px, 20vw, 248px);
  aspect-ratio: 174.78 / 48.05;
  background-color: var(--cream);
  -webkit-mask: url('../img/logo-wordmark.svg?v=489b5205') center / contain no-repeat;
  mask: url('../img/logo-wordmark.svg?v=489b5205') center / contain no-repeat;
  filter: drop-shadow(0 2px 10px rgba(0, 0, 0, 0.65));
  transition: background-color 0.2s ease;
  flex: none;
}
.logo:hover { background-color: var(--rust-lit); }

.nav {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 2.4vw, 2.1rem);
}
.nav a {
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--text);
  padding-block: 0.4rem;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
}
.nav a:hover { color: var(--cream); }
/* Legible over artwork before the header gains its backdrop. */
.masthead:not(.is-stuck) .nav a,
.masthead:not(.is-stuck) .nav__toggle {
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.8);
}
.nav a[aria-current='page'] {
  color: var(--cream);
  border-bottom-color: var(--rust);
}

.nav__toggle {
  display: none;
  background: none;
  border: 1px solid var(--line);
  color: var(--cream);
  padding: 0.55rem 0.8rem;
  font: inherit;
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border-radius: var(--radius);
  cursor: pointer;
}

@media (max-width: 800px) {
  .nav__toggle { display: block; }
  .nav {
    position: absolute;
    inset: 100% 0 auto;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: var(--green-800);
    border-bottom: 1px solid var(--line);
    padding: 0.5rem var(--gutter) 1.25rem;
  }
  .nav[hidden] { display: none; }
  .nav a {
    padding-block: 0.9rem;
    border-bottom: 1px solid var(--line-soft);
    font-size: 0.9rem;
  }
  .nav a[aria-current='page'] { border-bottom-color: var(--rust); }
}

/* ---------- Hero / takeover --------------------------------------------- */
.takeover {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: #0D120F;
}
.takeover__art {
  position: absolute;
  inset: 0;
  z-index: -2;
}
/* The artwork is square, so it is blown up and blurred to fill the field
   while a crisp copy of it sits in the layout, uncropped. */
.takeover__art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
  /* Brightness is here because the scrim below was tuned against the 02.10.26
     tease, which was a light graphic that glowed through it. The real cover is
     a dark illustration and the same scrim swallowed it, so the hero read as
     flat green with no artwork behind it at all. */
  filter: blur(13px) saturate(1.2) brightness(1.35);
  /* Scaled up so the blur does not bleed a soft edge in from the sides. */
  transform: scale(1.12);
}
.takeover__art::after {
  content: '';
  position: absolute;
  inset: 0;
  /* Three passes. The wash comes first, so it sits on top: a soft ellipse
     behind the text column, because easing the scrim enough for the artwork to
     read dropped the small rust eyebrow to 2.5:1 where it needs 4.5. It has no
     hard edge, so it darkens the type without drawing a panel. Then the
     vertical pass, solid at the bottom so the section joins the one below and
     lighter at the top where there is only the heading, and the horizontal
     pass, which keeps the left dark so the crisp sleeve has an edge to sit
     against. */
  background:
    radial-gradient(ellipse 46% 62% at 72% 48%, rgba(19, 26, 22, 0.62),
      rgba(19, 26, 22, 0) 78%),
    linear-gradient(to top, var(--green-900) 1%, rgba(19, 26, 22, 0.72) 45%,
      rgba(19, 26, 22, 0.46) 100%),
    linear-gradient(to right, rgba(19, 26, 22, 0.58), rgba(19, 26, 22, 0.2) 65%);
}
/* Rust on the hero is the one place the accent colour cannot clear AA. Over
   the blurred artwork the eyebrow measured 2.86:1 against 4.5 required, and
   darkening the field enough to fix it would undo the artwork being visible at
   all. Cream measures 4.76:1 in the same spot. The rust accent still carries
   the button, the countdown and the streaming row just below. */
.takeover__inner .eyebrow { color: var(--cream); }
.takeover__inner {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  align-items: center;
  /* Tighter gap buys width for the artwork without pushing the streaming
     pills onto a second row. */
  gap: clamp(1.75rem, 4vw, 3.5rem);
  min-height: min(92svh, 900px);
  /* Top padding clears the floating header. */
  padding-block: calc(var(--header-h) + clamp(1.5rem, 5vh, 3rem))
                 clamp(3.5rem, 9vh, 6rem);
}
/* The same two-column layout reused outside the hero, for a release that is
   no longer the headline. The hero version is sized to fill the screen and
   to clear the floating header; inside a normal section both of those are
   wrong and leave most of a viewport of empty space above the artwork. */
.takeover__inner--inline {
  min-height: 0;
  padding-block: 0;
  grid-template-columns: minmax(0, 0.55fr) minmax(0, 1.45fr);
  align-items: center;
}

@media (max-width: 860px) {
  .takeover__inner { grid-template-columns: 1fr; gap: 2rem; min-height: 0; }
}

/* The hero card holds the Lucy tease. It was the square sleeve; the sleeve is
   still the blurred field behind the whole hero, and still the crisp artwork
   on /music/, so nothing has been lost by putting the moving thing here. */
.takeover__card {
  border: 1px solid var(--line);
  background: var(--green-800);
  box-shadow: 0 30px 80px -20px rgba(0, 0, 0, 0.75);
  aspect-ratio: 1;
  overflow: hidden;
}
.takeover__card img { width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 860px) {
  .takeover__card { max-width: 380px; margin-inline: auto; }
}

.takeover__title {
  font-size: clamp(3.5rem, 12vw, 8.5rem);
  line-height: 0.82;
  letter-spacing: -0.005em;
  color: var(--cream-100);
  text-shadow: 0 6px 40px rgba(0, 0, 0, 0.55);
}
.takeover__meta {
  display: flex;
  flex-wrap: wrap;
  /* Centre, not baseline: the second span is an inline-flex whose first item
     is the separator dot, so baseline alignment resolves against the dot and
     drops the text ~2px below its neighbour. */
  align-items: center;
  gap: 0.6rem 1.4rem;
  margin-top: 1.5rem;
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--cream);
}
.takeover__meta span { display: inline-flex; align-items: center; gap: 0.6rem; }
.takeover__meta span + span::before {
  content: '';
  width: 4px; height: 4px;
  background: var(--rust-lit);
  border-radius: 50%;
}
/* Once the meta stacks, the separator dot reads as a stray bullet. */
@media (max-width: 560px) {
  .takeover__meta { display: grid; gap: 0.35rem; justify-items: start; }
  .takeover__meta span + span::before { display: none; }
}

/* ---------- Countdown --------------------------------------------------- */
.countdown {
  display: flex;
  gap: clamp(0.75rem, 2.5vw, 1.75rem);
  margin-top: 2rem;
  padding: 0;
  list-style: none;
}
.countdown li {
  min-width: clamp(60px, 13vw, 88px);
  padding: 0.7rem 0.5rem 0.6rem;
  text-align: center;
  background: rgba(19, 26, 22, 0.55);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.countdown b {
  display: block;
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(1.75rem, 4.4vw, 2.6rem);
  line-height: 1;
  color: var(--cream-100);
  font-variant-numeric: tabular-nums;
}
.countdown small {
  display: block;
  margin-top: 0.35rem;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Once the clock runs out the script replaces the four boxes with a single
   one reading "Out now". Uppercase it here rather than in the markup, so the
   data-released-text attribute stays readable and the casing is presentation,
   as it is everywhere else on the site. */
.countdown--done b { text-transform: uppercase; letter-spacing: 0.01em; }

/* ---------- Release strip (album feature) ------------------------------- */
.release {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}
@media (max-width: 780px) {
  .release { grid-template-columns: 1fr; }
}
.release__art {
  position: relative;
  border: 1px solid var(--line);
  background: var(--green-800);
}
.release__art img { width: 100%; }

/* ---------- Track list -------------------------------------------------- */
.sides {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
  gap: clamp(2rem, 5vw, 4rem);
}
.side__label {
  font-family: var(--body);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--rust-text);
  padding-bottom: 0.9rem;
  border-bottom: 1px solid var(--line);
}
.tracks { list-style: none; padding: 0; margin: 0; }
.tracks li { border-bottom: 1px solid var(--line-soft); }
.track {
  display: grid;
  grid-template-columns: 2.25rem 1fr auto;
  align-items: center;
  gap: 1rem;
  padding: 0.95rem 0;
  text-decoration: none;
  color: inherit;
}
.track__no {
  font-family: var(--display);
  font-size: 0.95rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.track__title {
  font-family: var(--display);
  font-size: clamp(1.15rem, 2.6vw, 1.6rem);
  line-height: 1;
  color: var(--cream-100);
  text-transform: uppercase;
  transition: color 0.15s ease;
}
.track:hover .track__title { color: var(--rust-lit); }
.track__tag {
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--rust-text);
  border: 1px solid var(--rust);
  padding: 0.25rem 0.5rem;
  border-radius: var(--radius);
  white-space: nowrap;
}

/* ---------- Artwork grid ------------------------------------------------ */
.artgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 210px), 1fr));
  gap: clamp(0.75rem, 1.6vw, 1.25rem);
  list-style: none;
  padding: 0;
}
.art {
  position: relative;
  display: block;
  overflow: hidden;
  background: var(--green-800);
  border: 1px solid var(--line-soft);
  aspect-ratio: 1;
}
.art img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.3, 1), filter 0.4s ease;
  filter: saturate(0.82) contrast(1.03);
}
.art:hover img { transform: scale(1.05); filter: saturate(1) contrast(1.05); }
.art figcaption {
  position: absolute;
  inset: auto 0 0;
  padding: 2.5rem 0.9rem 0.8rem;
  font-family: var(--display);
  font-size: 1rem;
  text-transform: uppercase;
  color: var(--cream-100);
  background: linear-gradient(to top, rgba(13, 18, 15, 0.92), transparent);
  line-height: 1.05;
}

/* Placeholder tile for a track with no artwork yet. */
.art--pending {
  display: grid;
  place-items: center;
  background:
    radial-gradient(circle at 50% 45%, var(--green-700), var(--green-800) 70%);
}
.art--pending::before {
  content: '';
  width: 66%;
  aspect-ratio: 174.78 / 48.05;
  background-color: var(--line);
  -webkit-mask: url('../img/logo-wordmark.svg?v=489b5205') center / contain no-repeat;
  mask: url('../img/logo-wordmark.svg?v=489b5205') center / contain no-repeat;
}

/* ---------- Band ---------------------------------------------------------*/
/* Explicit columns, not auto-fit: four members must never leave an orphan
   cell (auto-fit lands on 3 + 1 in the narrower container). */
.lineup {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  background: var(--line-soft);
  border: 1px solid var(--line-soft);
  list-style: none;
  padding: 0;
}
@media (max-width: 900px) {
  .lineup { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 380px) {
  .lineup { grid-template-columns: minmax(0, 1fr); }
}
.lineup li {
  background: var(--green-900);
  display: flex;
  flex-direction: column;
}
/* The photograph runs edge to edge, so the padding belongs to the text block
   rather than the cell. The fixed ratio means the four cards stay level even
   while the images are still loading. */
.lineup__photo {
  display: block;
  position: relative;
}
.lineup__photo img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  /* The rehearsal shots are lit much warmer than the rest of the site. Taking
     a little saturation and lifting the black point settles them into the
     green without turning them into a filter effect. */
  filter: saturate(0.82) contrast(1.06);
}
.lineup__body {
  padding: clamp(1.5rem, 3vw, 2.25rem) clamp(1.25rem, 2.5vw, 1.75rem);
}
/* Safety net. The padding used to live on the cell; moving it onto the wrapper
   meant any card still written the old way lost its padding silently, which
   shipped twice before anyone spotted it. A card with no wrapper keeps the old
   behaviour instead of collapsing onto its own edges. */
.lineup li:not(:has(.lineup__body)) {
  padding: clamp(1.5rem, 3vw, 2.25rem) clamp(1.25rem, 2.5vw, 1.75rem);
}
.lineup h3 { color: var(--cream-100); }
.lineup p {
  margin-top: 0.5rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--rust-text);
}

/* The home page runs the same cards in one half of a two-column block. Four
   across is not enough room there: the role lines run into each other and the
   names lose their gutters. Two by two fixes that, but full-height portraits
   would make the block twice as tall as the text beside it, so here the
   photograph is a thumbnail alongside the name instead of a card above it. */
.lineup--compact { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 480px) {
  .lineup--compact { grid-template-columns: minmax(0, 1fr); }
}
.lineup--compact li {
  flex-direction: row;
  align-items: center;
  gap: 0.9rem;
  padding: 0.9rem 1rem;
}
.lineup--compact .lineup__photo {
  flex: 0 0 auto;
  width: 64px;
}
.lineup--compact .lineup__photo img {
  width: 64px;
  aspect-ratio: 1;
  /* Square crop of a 4:5 portrait, biased up so it lands on the face rather
     than the middle of the instrument. */
  object-position: 50% 26%;
}
.lineup--compact .lineup__body { padding: 0; }
.lineup--compact h3 {
  font-size: 1.05rem;
  line-height: 1.08;
}
.lineup--compact p {
  margin-top: 0.35rem;
  font-size: 0.6rem;
  letter-spacing: 0.12em;
}

/* ---------- Live dates -------------------------------------------------- */
.dates { list-style: none; padding: 0; }
.dates li {
  display: grid;
  grid-template-columns: 10rem 1fr auto;
  gap: 1.5rem;
  align-items: center;
  padding: 1.35rem 0;
  border-bottom: 1px solid var(--line-soft);
}
@media (max-width: 680px) {
  .dates li { grid-template-columns: 1fr; gap: 0.5rem; }
}
.dates time {
  font-family: var(--display);
  font-size: 1.1rem;
  color: var(--rust-text);
  text-transform: uppercase;
}

.empty-state {
  border: 1px dashed var(--line);
  padding: clamp(2.5rem, 6vw, 4rem);
  text-align: center;
  background: var(--green-800);
}
.empty-state h3 { color: var(--cream); }
.empty-state p { color: var(--muted); margin-top: 0.75rem; }

/* ---------- Form -------------------------------------------------------- */
.form { display: grid; gap: 1.25rem; max-width: 620px; }
.field { display: grid; gap: 0.5rem; }
.field label {
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--cream);
}
.field input,
.field textarea,
.field select {
  font: inherit;
  font-size: 1rem;
  color: var(--text);
  background: var(--green-800);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.85rem 1rem;
  width: 100%;
}

/* Native selects ignore most styling and render an OS arrow, which reads as
   unfinished against the rest of the form. Draw our own. */
.field select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 2.75rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='%23F2D8A8' stroke-width='1.6'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1rem center;
  background-size: 12px 8px;
  cursor: pointer;
}
.field select option { background: var(--green-800); color: var(--text); }
.field textarea { min-height: 170px; resize: vertical; }
.field input:focus,
.field textarea:focus,
.field select:focus {
  outline: none;
  border-color: var(--rust-lit);
  box-shadow: 0 0 0 3px rgba(210, 105, 31, 0.18);
}
.field input::placeholder, .field textarea::placeholder { color: #6F6858; }
.field small { color: var(--muted); font-size: 0.8rem; }

/* Honeypot - hidden from people, visible to naive bots. */
.hp {
  position: absolute !important;
  left: -9999px;
  opacity: 0;
  pointer-events: none;
}

.form__status {
  padding: 1rem 1.25rem;
  border: 1px solid var(--line);
  border-left-width: 3px;
  border-radius: var(--radius);
  font-size: 0.95rem;
}
.form__status[hidden] { display: none; }
.form__status--ok { border-left-color: #6FA96B; color: var(--cream); }
.form__status--err { border-left-color: var(--rust-lit); color: var(--cream); }
.form__status--busy { border-left-color: var(--muted); color: var(--muted); }

/* ---------- Band section with the photograph behind --------------------- */
.who { position: relative; overflow: hidden; background: var(--green-900); }
.who__art { position: absolute; inset: 0; }
.who__art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
}
/* The scrim. The photograph is a brightly lit brick wall, so it needs real
   weight behind the type: the text column is on the left, which is where this
   is heaviest.

   Re-measured on 2026-09-30 when the photograph was swapped: the replacement
   is brighter behind the text, and the previous values dropped the eyebrow and
   the muted paragraph to 4.23 against the 4.5 they need. These give 4.72. One
   step lighter is 4.51, which passes by a hundredth and would tip on a
   different crop, so it was not taken.

   **Re-measure whenever the photograph changes.** The scrim is tuned to the
   image behind it, not to the layout. */
.who__art::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to right, rgba(13, 18, 15, 0.87) 0%, rgba(13, 18, 15, 0.77) 42%,
      rgba(13, 18, 15, 0.47) 100%),
    linear-gradient(to bottom, rgba(19, 26, 22, 0.43), rgba(19, 26, 22, 0.62));
}
.who > .wrap { position: relative; }

/* ---------- Full-bleed banner ------------------------------------------- */
/* Runs the width of the viewport, not the wrap. Capped by height so it does
   not eat the whole screen on a wide monitor, and object-fit keeps the crop
   centred rather than squashing the photograph when the cap bites. */
.banner {
  margin: 0;
  width: 100%;
  background: var(--green-800);
  overflow: hidden;
}
.banner img {
  display: block;
  width: 100%;
  aspect-ratio: 2000 / 857;
  max-height: 60svh;
  object-fit: cover;
  object-position: center 40%;
}
/* The section below opens straight into text, so the banner needs a little
   air under it rather than butting into the eyebrow. */
/* Match the ratio of the 4:3 comp the <picture> swaps in below 700px, or the
   box would stay wide and crop the taller photograph straight back down. */
@media (max-width: 700px) {
  .banner img { aspect-ratio: 4 / 3; max-height: 52svh; }
}
.banner + .section { padding-top: clamp(2.5rem, 5vw, 4rem); }

/* ---------- Video section ----------------------------------------------- */
/* Its own tone, a step up from the page ground but short of the panel grey
   used elsewhere. On /music/ it sits between the hero and the About panel, so
   reusing the panel colour would have merged it into the block below. */
.section--video {
  background: #171F1A;
  padding-block: clamp(2.25rem, 4.5vw, 3.5rem);
}
/* The section above it gives up most of its bottom padding. Two full sections
   of padding met in the middle and left roughly 240px of empty ground on a
   wide screen. */
.section:has(+ .section--video) {
  padding-bottom: clamp(1.75rem, 3.5vw, 2.75rem);
}

/* ---------- Video (click-to-load) --------------------------------------- */
/* The player is only fetched when someone asks for it. Until then this is a
   poster image and a button, so the page makes no request to Google beyond
   the analytics a visitor has opted into. */
.video {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--green-800);
  border: 1px solid var(--line);
  overflow: hidden;
}
/* The tease is a vertical Short and the hero card shows it that way from the
   start, poster included. It is capped by height so it cannot run away on a
   big screen, and the cap is expressed as a max-width derived from it: with
   max-height alone the box would be clipped rather than shrunk, because the
   width is what drives the size here.

   The width must stay explicit. Everything inside is absolutely positioned,
   so the box has no intrinsic width; with auto margins and no width the
   margins ate the whole grid column and it collapsed to the 2px of its own
   borders, invisible on mobile. */
.video.takeover__card {
  aspect-ratio: 9 / 16;
  width: 100%;
  max-width: calc(min(72svh, 620px) * 9 / 16);
  margin-inline: auto;
}
.video__play {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  width: 100%;
  height: 100%;
}
/* The <picture> wrapper needs taking out of flow too, not just the <img>.
   Left in flow it becomes a grid row of its own and pushes the play disc
   below the centre. Measured at 129px out before this was added. */
.video__play picture {
  position: absolute;
  inset: 0;
}
.video__play img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: saturate(0.9) brightness(0.72);
  transition: filter 0.4s ease, transform 0.6s cubic-bezier(0.2, 0.7, 0.3, 1);
}
.video__play:hover img,
.video__play:focus-visible img {
  filter: saturate(1) brightness(0.85);
  transform: scale(1.03);
}
/* The play target: rust disc, cream triangle, same language as the buttons. */
.video__disc {
  position: relative;
  display: grid;
  place-items: center;
  width: clamp(64px, 9vw, 92px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--rust);
  box-shadow: 0 12px 40px -8px rgba(0, 0, 0, 0.7);
  transition: background 0.18s ease, transform 0.18s ease;
}
.video__play:hover .video__disc,
.video__play:focus-visible .video__disc {
  background: var(--rust-lit);
  transform: scale(1.06);
}
.video__disc svg { width: 38%; height: 38%; fill: var(--cream-100); }

.video iframe { width: 100%; height: 100%; border: 0; display: block; }

/* ---------- SoundCloud player -------------------------------------------
   Click-to-load like the video, for the same reason: no request reaches
   SoundCloud, and no SoundCloud cookie is set, until someone asks to play.

   Not built on .video, despite the shared handler. That one is a 16:9 box
   with everything absolutely positioned inside it; SoundCloud's visual
   player is a fixed-height widget and the poster here is a bar rather than
   a full-bleed image, so the two share a look but not a layout. */
.player {
  position: relative;
  background: var(--green-800);
  border: 1px solid var(--line);
  overflow: hidden;
}
.player__play {
  display: flex;
  align-items: center;
  gap: clamp(0.9rem, 2.5vw, 1.4rem);
  width: 100%;
  padding: clamp(0.85rem, 2vw, 1.1rem);
  border: 0;
  background: none;
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
}
.player__art {
  flex: none;
  width: clamp(64px, 12vw, 88px);
  aspect-ratio: 1;
  object-fit: cover;
  display: block;
  filter: saturate(0.95) brightness(0.9);
  transition: filter 0.3s ease;
}
.player__play:hover .player__art,
.player__play:focus-visible .player__art { filter: saturate(1) brightness(1); }

.player__body { flex: 1 1 auto; min-width: 0; }
.player__title {
  font-family: var(--display);
  font-size: clamp(1.1rem, 2.4vw, 1.45rem);
  line-height: 1.1;
  text-transform: uppercase;
  color: var(--cream-100);
}
.player__meta {
  margin-top: 0.3rem;
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}
.player__disc {
  flex: none;
  display: grid;
  place-items: center;
  width: clamp(44px, 7vw, 56px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--rust);
  transition: background 0.18s ease, transform 0.18s ease;
}
.player__play:hover .player__disc,
.player__play:focus-visible .player__disc {
  background: var(--rust-lit);
  transform: scale(1.06);
}
.player__disc svg { width: 38%; height: 38%; fill: var(--cream-100); }

/* 166px is SoundCloud's compact player: artwork thumb, waveform, controls.
   The visual variant is 300px of full-bleed artwork, which was too much
   here - the sleeve is already on this page and the lyric video sits
   directly below, so three images competed and none of them won. */
.player iframe { width: 100%; height: 166px; border: 0; display: block; }

/* The stream block sits between the single and the lyric video, so it takes
   its spacing from both: tight enough to read as part of that run, not so
   tight that the three stack into one mass. */
.section--stream {
  padding-block: clamp(1.5rem, 3vw, 2.25rem);
}
.section:has(+ .section--stream) {
  padding-bottom: clamp(1.75rem, 3.5vw, 2.75rem);
}

/* ---------- Streaming services ------------------------------------------ */
.listen {
  margin-top: 2rem;
}
.listen__label {
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.9rem;
}
.listen__list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  list-style: none;
  padding: 0;
}
.listen__list a {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.68rem 0.72rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: rgba(19, 26, 22, 0.5);
  text-decoration: none;
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--cream);
  white-space: nowrap;
  transition: border-color 0.18s ease, color 0.18s ease, transform 0.18s ease;
}
.listen__list a:hover {
  border-color: var(--cream);
  color: var(--cream);
  transform: translateY(-2px);
}
.listen__list svg { width: 16px; height: 16px; fill: currentColor; flex: none; }

/* Placeholder state, used until the distributor delivers the platform URLs.
   Same pill, dimmed and inert: a <span>, so there is no dead link to click.
   To switch one on, swap the <span class="listen__soon"> for
   <a href="..." rel="noopener"> and drop the trailing <span> note. */
.listen__list .listen__soon {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.68rem 0.72rem;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
  cursor: default;
}
.listen__note {
  margin-top: 0.9rem;
  font-size: 0.78rem;
  color: var(--muted);
}

/* ---------- Album statement --------------------------------------------- */
.statement {
  font-size: clamp(1.05rem, 0.95rem + 0.6vw, 1.35rem);
  line-height: 1.45;
  color: var(--muted);
  max-width: 34ch;
}
.statement b {
  display: block;
  margin-top: 0.35rem;
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(2rem, 5vw, 3.25rem);
  line-height: 0.95;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--cream-100);
}
.statement cite { font-style: normal; color: var(--cream); }

/* ---------- Mailing list signup ----------------------------------------- */
.signup__form { display: grid; gap: 0.9rem; max-width: 460px; }

.signup__row { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.signup__row input[type="email"] {
  flex: 1 1 12rem;
  min-width: 0;
  font: inherit;
  font-size: 0.95rem;
  color: var(--text);
  background: var(--green-800);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.85rem 1rem;
}
.signup__row input[type="email"]::placeholder { color: #6F6858; }
.signup__row input[type="email"]:focus {
  outline: none;
  border-color: var(--rust-lit);
  box-shadow: 0 0 0 3px rgba(210, 105, 31, 0.18);
}
.signup__row .btn { flex: 0 0 auto; }

/* Consent. Unticked by default, and the label says plainly what it signs
   you up for, because UK PECR needs a positive, informed action. */
.signup__consent {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.6rem;
  align-items: start;
  font-size: 0.8rem;
  line-height: 1.5;
  color: var(--muted);
  cursor: pointer;
}
/* The default checkbox renders as a pale OS box and is the one thing on the
   page that looks unstyled against the dark ground. Drawn from scratch. */
.signup__consent input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 1.15rem;
  height: 1.15rem;
  margin: 0.12rem 0 0;
  flex: none;
  display: grid;
  place-content: center;
  background: var(--green-900);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.signup__consent input[type="checkbox"]::before {
  content: '';
  width: 0.62rem;
  height: 0.62rem;
  transform: scale(0);
  transition: transform 0.12s ease-in-out;
  background: #fff;
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
}
.signup__consent input[type="checkbox"]:hover { border-color: var(--cream); }
.signup__consent input[type="checkbox"]:checked {
  background: var(--rust);
  border-color: var(--rust);
}
.signup__consent input[type="checkbox"]:checked::before { transform: scale(1); }
.signup__consent input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--rust-lit);
  outline-offset: 2px;
}
.signup__small { font-size: 0.75rem; color: var(--muted); }

/* Compact variant for the footer column. */
.signup--compact .signup__form { max-width: none; gap: 0.7rem; }
.signup--compact .signup__row input[type="email"] {
  flex-basis: 100%;
  padding: 0.7rem 0.85rem;
  font-size: 0.9rem;
}
.signup--compact .signup__row .btn {
  padding: 0.7rem 1.1rem;
  font-size: 0.72rem;
}
.signup--compact .signup__consent { font-size: 0.72rem; }

/* ---------- Social ------------------------------------------------------ */
.social {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  list-style: none;
  padding: 0;
}
/* Footer columns stack rather than wrap. This was an inline
   style="flex-direction:column" on the markup, repeated on every page,
   which meant the layout could not be changed in one place. */
.social--stack {
  flex-direction: column;
  align-items: flex-start;
  gap: 0.5rem;
}
.social a {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.7rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  text-decoration: none;
  font-size: 0.75rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cream);
  transition: border-color 0.18s ease, color 0.18s ease;
}
.social a:hover { border-color: var(--cream); color: var(--cream); }
.social svg { width: 16px; height: 16px; fill: currentColor; }

/* ---------- Footer ------------------------------------------------------ */
.footer {
  border-top: 1px solid var(--line-soft);
  padding-block: clamp(3rem, 6vw, 4.5rem) 2.5rem;
  background: var(--green-800);
}
/* Signup band: full width, sitting above the columns and separated from them
   by a rule, so it reads as an invitation rather than a fourth link column. */
.footer__signup {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: start;
  padding-bottom: clamp(2.25rem, 4vw, 3rem);
  margin-bottom: clamp(2.25rem, 4vw, 3rem);
  border-bottom: 1px solid var(--line-soft);
}
@media (max-width: 720px) {
  .footer__signup { grid-template-columns: 1fr; gap: 1.25rem; }
}
.footer__signup-text h4 { margin-bottom: 0.6rem; }
.footer__signup-text p {
  color: var(--muted);
  font-size: 0.9rem;
  max-width: 34ch;
}
/* With the whole width available the field can breathe, and the consent line
   sits on one row rather than wrapping to four. */
.footer__signup .signup__form { max-width: none; }
.footer__signup .signup__row input[type="email"] { flex: 1 1 16rem; }
.footer__signup .signup__consent { max-width: 62ch; }

.footer__top {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  gap: clamp(2rem, 4vw, 3rem);
  align-items: start;
}
/* Band mark and label boxed together. Supplied as finished artwork, so it is
   used at its own cream rather than the dimmed treatment the wordmark had. */
.footer__mark {
  width: min(228px, 100%);
  aspect-ratio: 584.11 / 279.17;
  background-color: #EACE9E;
  -webkit-mask: url('../img/logo-footer-lockup.svg?v=1d6ff51f') center / contain no-repeat;
  mask: url('../img/logo-footer-lockup.svg?v=1d6ff51f') center / contain no-repeat;
}
.footer h4 {
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--cream);
  margin-bottom: 1rem;
}
.footer ul { list-style: none; padding: 0; display: grid; gap: 0.7rem; }
.footer ul a {
  text-decoration: none;
  color: var(--muted);
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
}
.footer ul a:hover { color: var(--cream); }

.footer__tagline {
  margin-top: 1.25rem;
  max-width: 22ch;
  color: var(--muted);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  line-height: 1.6;
  text-transform: uppercase;
}

/* Policies row: between the footer columns and the copyright line, with
   room for more (terms, accessibility) as they arrive. */
/* `.footer ul` sets grid; this needs the extra specificity to win. */
.footer .footer__policies {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  list-style: none;
  padding: 1.25rem 0;
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  border-top: 1px solid var(--line-soft);
}
.footer .footer__policies li:has(> [hidden]) { display: none; }
.footer .footer__policies a {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--muted);
}
.footer .footer__policies a:hover { color: var(--cream); }

.footer__legal {
  margin-top: 0;
  padding-top: 1.25rem;
  border-top: 1px solid var(--line-soft);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  line-height: 1.9;
  text-transform: uppercase;
  color: var(--muted);
}
.footer__legal strong { color: var(--cream); font-weight: 700; }

/* ---------- Reveal on scroll (progressive enhancement) ------------------
   Only ever hidden when JS is running - the `js` class is set by an inline
   script in <head>, so without JS the content is simply visible. */
.js .reveal { opacity: 0; transform: translateY(18px); }
.reveal.is-in {
  opacity: 1;
  transform: none;
  transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.2, 0.7, 0.3, 1);
}
@media (prefers-reduced-motion: reduce) {
  .js .reveal { opacity: 1; transform: none; }
}

/* ---------- Analytics consent ------------------------------------------ */

/* A bar, not a modal. Nothing is set until the visitor chooses, so there is no
   reason to trap them behind an overlay to get it. */
.consent {
  position: fixed;
  z-index: 60;
  left: 1rem;
  right: 1rem;
  bottom: 1rem;
  max-width: 34rem;
  margin-inline: auto;
  padding: 1.25rem 1.35rem;
  background: var(--green-800);
  border: 1px solid var(--line-soft);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45);
}
.consent__text {
  color: var(--muted);
  font-size: 0.86rem;
  line-height: 1.55;
}
.consent__row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 1rem;
}
/* Refusing has to be as easy as agreeing, so both buttons are the same size
   and sit side by side. Only the fill differs. */
.consent__row .btn { flex: 1 1 auto; text-align: center; }

@media (prefers-reduced-motion: no-preference) {
  .consent { animation: consent-in 0.25s ease-out both; }
  @keyframes consent-in {
    from { opacity: 0; transform: translateY(0.75rem); }
    to   { opacity: 1; transform: none; }
  }
}

/* ---------- Long-form prose --------------------------------------------- */

/* For pages that are mostly words: privacy, and anything like it. Headings are
   smaller than the display sizes elsewhere, because a run of h2s at 3.5rem
   reads as a series of announcements rather than a document. */
.prose { max-width: 62ch; }
.prose h2 {
  font-size: clamp(1.25rem, 2.4vw, 1.6rem);
  margin-top: 2.75rem;
  margin-bottom: 0.85rem;
}
.prose h2:first-child { margin-top: 0; }
.prose p { color: var(--muted); }
.prose a { color: var(--cream); }
