/* ============================================================
   BASE — resets, layout, typography scale, transitions
   ============================================================ */

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

:root {
  /* Spacing scale */
  --space-xs:  0.25rem;
  --space-sm:  0.5rem;
  --space-md:  1rem;
  --space-lg:  2rem;
  --space-xl:  4rem;
  --space-2xl: 8rem;

  /* Typography scale */
  --text-sm:   0.875rem;
  --text-base: 1rem;
  --text-lg:   1.25rem;
  --text-xl:   1.5rem;
  --text-2xl:  2rem;
  --text-3xl:  3rem;
  --text-4xl:  4.5rem;

  /* Transition timing */
  --transition-fast:   150ms ease;
  --transition-normal: 300ms ease;
  --transition-slow:   600ms ease;

  /* Era transition zone height (20vh overlap) */
  --transition-zone: 20vh;

  /* Timeline */
  --timeline-spine-width:    3px;
  --timeline-marker-size:    64px;
  --timeline-spine-offset:   28px;  /* mobile: spine x-position from left */
  --timeline-card-radius:    10px;
  --timeline-draw:           1;     /* spine "draw" progress, 0–1 (set by JS) */
  /* Vertical gap between stacked timeline nodes. Authored in px, not rem,
     because js/timeline-layout.js reads it back and needs a real length. */
  --timeline-row-gap:        32px;
  /* Clear space between one marker circle and the next down the spine.
     Also read by js/timeline-layout.js, so likewise in px. Big enough to
     clear the 4px ring the marker's box-shadow draws around itself. */
  --timeline-marker-gap:     16px;
}

html {
  scroll-behavior: smooth;
  font-size: 16px;
}

body {
  background-color: var(--color-bg, #f5e6c8);
  color: var(--color-text, #2c1810);
  font-family: var(--font-body, 'Courier Prime', serif);
  line-height: 1.6;
}

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

/* ── Site header ─────────────────────────────────────────── */

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-sm) var(--space-lg);
  background-color: var(--color-bg, #f5e6c8);
  border-bottom: 1px solid var(--color-accent, #c0392b);
  transition: background-color var(--transition-normal),
              border-color var(--transition-normal);
}

.site-header__logo-wrap {
  position: relative;
  height: 48px;
  width: 200px;
  flex: 0 0 auto;
}

.site-header__logo {
  position: absolute;
  top: 0;
  left: 0;
  height: 48px;
  width: auto;
  max-width: 140px;
  object-fit: contain;
  will-change: opacity;
}

.site-header__logo--outgoing { opacity: 1; }
.site-header__logo--incoming { opacity: 0; }

/* When the JS hasn't supplied a src yet, the empty <img> would draw a
   broken-image glyph — hide until the loader has wired it up. */
.site-header__logo:not([src]),
.site-header__logo[src=""] { visibility: hidden; }

/* Text fallback shown when an era has no logo image.
   Font is hardcoded — not var(--font-heading) — so it never changes
   regardless of which era theme is active. */
.site-header__logo-text {
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  align-items: center;
  height: 100%;
  font-family: 'Bebas Neue', sans-serif;
  font-size: 1.75rem;
  line-height: 1;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-text, #2c1810);
  white-space: nowrap;
  opacity: 0;
  transition: opacity var(--transition-normal), color var(--transition-normal);
  pointer-events: none;
}

/* On non-timeline pages the text logo is always shown */
.site-header__logo-wrap--text .site-header__logo-text {
  opacity: 1;
}



/* ── Site search ─────────────────────────────────────────── */

.site-search {
  display: flex;
  align-items: center;
  gap: 0;
  flex: 0 0 auto;
}

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

.site-search__input {
  width: 0;
  min-width: 0;
  padding: 0;
  border: none;
  border-bottom: 1px solid transparent;
  background: transparent;
  color: var(--color-text, #2c1810);
  font-family: var(--font-body, 'Courier Prime', serif);
  font-size: var(--text-sm);
  outline: none;
  transition: width var(--transition-normal),
              padding var(--transition-normal),
              border-color var(--transition-normal);
  appearance: none;
  -webkit-appearance: none;
}

/* Remove the browser's native clear (×) button */
.site-search__input::-webkit-search-cancel-button { display: none; }

.site-search__input:focus,
.site-search__input:not(:placeholder-shown) {
  width: 160px;
  padding: 2px 6px;
  border-bottom-color: var(--color-accent, #c0392b);
}

.site-search__btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-text, #2c1810);
  transition: color var(--transition-fast);
  flex: 0 0 auto;
}

.site-search__btn:hover {
  color: var(--color-accent, #c0392b);
}

.site-search__icon {
  width: 18px;
  height: 18px;
}

/* ── Search dropdown (inline, desktop only) ──────────────── */

.search-dropdown {
  position: fixed;
  width: 380px;
  max-height: 70vh;
  overflow-y: auto;
  background: var(--color-bg, #f5e6c8);
  border: 1px solid var(--color-accent, #c0392b);
  border-top: 3px solid var(--color-accent, #c0392b);
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
  z-index: 200;
  overscroll-behavior: contain;
}

.search-dropdown__section {
  padding: var(--space-sm) 0;
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}

.search-dropdown__label {
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-accent, #c0392b);
  padding: var(--space-xs) var(--space-md);
  margin: 0;
}

.search-dropdown__item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  color: var(--color-text, #2c1810);
  text-decoration: none;
  transition: background var(--transition-fast);
  outline-offset: -2px;
}

.search-dropdown__item:hover,
.search-dropdown__item:focus {
  background: var(--color-bg-alt, #e8d5a3);
  outline: none;
}

.search-dropdown__glyph {
  flex: 0 0 auto;
  width: 1.2rem;
  font-size: var(--text-sm);
  color: var(--color-accent, #c0392b);
  opacity: 0.7;
  margin-top: 1px;
}

.search-dropdown__info {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.search-dropdown__title {
  font-family: var(--font-body, 'Courier Prime', serif);
  font-size: var(--text-base);
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.search-dropdown__meta {
  font-size: var(--text-sm);
  opacity: 0.6;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.search-dropdown__empty {
  padding: var(--space-md) var(--space-lg);
  font-family: var(--font-body, 'Courier Prime', serif);
  font-size: var(--text-sm);
  color: var(--color-text, #2c1810);
  opacity: 0.6;
  margin: 0;
}

.search-dropdown__topbar {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  background: var(--color-bg, #f5e6c8);
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  padding: var(--space-xs) var(--space-md);
}

.search-dropdown__all {
  display: inline-block;
  padding: var(--space-xs) 0;
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: var(--text-sm);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent, #c0392b);
  text-decoration: none;
  transition: opacity var(--transition-fast);
}

.search-dropdown__all:hover,
.search-dropdown__all:focus {
  opacity: 0.7;
  outline: none;
}

mark {
  background: rgba(192, 57, 43, 0.15);
  color: inherit;
  border-radius: 2px;
  padding: 0 1px;
}

/* ── Site nav (desktop) ──────────────────────────────────── */

.site-nav {
  flex: 1 1 auto;
  display: none;
  justify-content: center;
}

.site-nav__list {
  display: flex;
  gap: var(--space-lg);
  list-style: none;
  margin: 0;
  padding: 0;
  align-items: center;
}

.site-nav__link {
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: var(--text-sm);
  color: var(--color-text, #2c1810);
  text-decoration: none;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  /* "Still to Come" and "Gig Map" would otherwise wrap to stacked words. */
  white-space: nowrap;
  padding-bottom: 3px;
  border-bottom: 2px solid transparent;
  transition: color var(--transition-fast), border-color var(--transition-fast);
}

.site-nav__link:hover,
.site-nav__link[aria-current="page"],
.site-nav__link.is-active {
  color: var(--color-accent, #c0392b);
  border-bottom-color: var(--color-accent, #c0392b);
}

/* ── Header actions (search + burger) ───────────────────── */

.site-header__actions {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  flex: 0 0 auto;
}

/* ── Language switcher ───────────────────────────────────── */
/* Built by js/i18n.js: a compact EN NL DE row in the header on wider
   screens, and the full language names in the mobile drawer. */

.lang-switch {
  display: flex;
  align-items: center;
}

.lang-switch__link {
  color: var(--color-text, #2c1810);
  font-family: var(--font-body, 'Courier Prime', serif);
  text-decoration: none;
  opacity: 0.65;
  transition: opacity var(--transition-fast), color var(--transition-fast);
}

.lang-switch__link:hover,
.lang-switch__link:focus-visible {
  opacity: 1;
  color: var(--color-accent, #c0392b);
}

.lang-switch__link.is-active {
  opacity: 1;
  color: var(--color-accent, #c0392b);
}

.lang-switch--header {
  display: none;
  gap: 2px;
}

.lang-switch--header .lang-switch__link {
  padding: 4px 5px;
  font-size: var(--text-xs, 0.75rem);
  letter-spacing: 0.08em;
}

.lang-switch--header .lang-switch__link.is-active {
  text-decoration: underline;
  text-underline-offset: 4px;
}

.lang-switch--drawer {
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-md);
  margin-top: var(--space-lg);
}

.lang-switch--drawer .lang-switch__link {
  padding: var(--space-xs) 0;
  font-size: var(--text-sm);
}

.lang-switch--drawer .lang-switch__link.is-active {
  border-bottom: 1px solid var(--color-accent, #c0392b);
}

@media (min-width: 768px) {
  .lang-switch--header { display: flex; }
}

/* ── Logo link ───────────────────────────────────────────── */

.site-header__logo-link {
  display: block;
  height: 100%;
  width: 100%;
}

/* ── Burger button ───────────────────────────────────────── */

.site-nav__burger {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 36px;
  height: 36px;
  padding: 7px 6px;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-text, #2c1810);
  border-radius: 4px;
  transition: color var(--transition-fast);
  flex: 0 0 auto;
}

.site-nav__burger:hover { color: var(--color-accent, #c0392b); }

.site-nav__burger-bar {
  display: block;
  width: 100%;
  height: 2px;
  background: currentColor;
  border-radius: 1px;
  transition: transform var(--transition-normal), opacity var(--transition-fast);
}

.site-nav__burger[aria-expanded="true"] .site-nav__burger-bar:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}
.site-nav__burger[aria-expanded="true"] .site-nav__burger-bar:nth-child(2) {
  opacity: 0;
  transform: scaleX(0);
}
.site-nav__burger[aria-expanded="true"] .site-nav__burger-bar:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

/* ── Mobile nav drawer ───────────────────────────────────── */

.site-nav__drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(300px, 88vw);
  background: var(--color-bg, #f5e6c8);
  border-left: 2px solid var(--color-accent, #c0392b);
  z-index: 1200; /* above Leaflet's max of 1000 */
  transform: translateX(100%);
  transition: transform var(--transition-normal);
  padding: var(--space-lg) var(--space-lg) var(--space-xl);
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.site-nav__drawer.is-open {
  transform: translateX(0);
}

/* Scrim behind the open drawer */
.nav-drawer-open::after {
  content: '';
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 1100; /* above Leaflet, below drawer */
  pointer-events: none;
}

.site-nav__drawer-close {
  align-self: flex-end;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  background: none;
  border: 1px solid var(--color-accent, #c0392b);
  border-radius: 50%;
  color: var(--color-text, #2c1810);
  cursor: pointer;
  flex: 0 0 auto;
  transition: background-color var(--transition-fast), color var(--transition-fast);
}

.site-nav__drawer-close:hover {
  background: var(--color-accent, #c0392b);
  color: var(--color-bg, #f5e6c8);
}

.site-nav__drawer-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.site-nav__drawer-link {
  display: block;
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: var(--text-2xl);
  color: var(--color-text, #2c1810);
  text-decoration: none;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: var(--space-sm) 0;
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
  transition: color var(--transition-fast);
}

.site-nav__drawer-link:hover,
.site-nav__drawer-link[aria-current="page"],
.site-nav__drawer-link.is-active {
  color: var(--color-accent, #c0392b);
}

.site-nav__drawer-search {
  margin-top: auto;
  padding-top: var(--space-lg);
  border-top: 1px solid var(--color-accent, #c0392b);
}

.site-nav__drawer-search form {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.site-nav__drawer-search input {
  flex: 1;
  background: none;
  border: none;
  border-bottom: 1px solid var(--color-accent, #c0392b);
  padding: var(--space-xs) 0;
  color: var(--color-text, #2c1810);
  font-family: var(--font-body, 'Courier Prime', serif);
  font-size: var(--text-base);
  outline: none;
}

.site-nav__drawer-search input::placeholder {
  opacity: 0.5;
}

.site-nav__drawer-search button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  background: none;
  border: none;
  color: var(--color-text, #2c1810);
  cursor: pointer;
  transition: color var(--transition-fast);
  flex: 0 0 auto;
}

.site-nav__drawer-search button:hover {
  color: var(--color-accent, #c0392b);
}

.site-nav__drawer-search button svg {
  width: 18px;
  height: 18px;
}

/* Wide screens: show nav, hide burger.
   Later than the 1200px desktop breakpoint on purpose. The header carries
   seven links, the language switcher and search, and the album themes set the
   links in Playfair Display. In Dutch ("Optredenskaart", "Nog te
   verwachten", "Playlist (12)") the row needs about 1330px; below that it
   scrolled the page sideways, so tablets and small laptops get the burger
   menu instead. 1360px leaves some room for a longer label. */
@media (min-width: 1360px) {
  .site-nav { display: flex; }
  .site-nav__burger { display: none; }
  .site-nav__drawer { display: none; }
}

/* ── Era jumper (floating, timeline page only) ───────────── */

.era-jumper {
  position: fixed;
  right: var(--space-md);
  bottom: var(--space-xl);
  z-index: 90;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-xs);
}

.era-jumper__btn {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 0.5em 1em;
  background: var(--color-bg, #f5e6c8);
  border: 1px solid var(--color-accent, #c0392b);
  border-radius: 999px;
  color: var(--color-text, #2c1810);
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: 0.72rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  cursor: pointer;
  box-shadow: 0 2px 14px rgba(0, 0, 0, 0.14);
  transition: background-color var(--transition-fast),
              color var(--transition-fast),
              border-color var(--transition-fast);
}

.era-jumper__btn:hover {
  background: var(--color-accent, #c0392b);
  color: var(--color-bg, #f5e6c8);
}

.era-jumper__icon { font-size: 0.85rem; }

.era-jumper__panel {
  background: var(--color-bg, #f5e6c8);
  border: 1px solid var(--color-accent, #c0392b);
  border-radius: 8px;
  box-shadow: 0 4px 22px rgba(0, 0, 0, 0.16);
  min-width: 200px;
  max-height: 60vh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.era-jumper__list {
  list-style: none;
  margin: 0;
  padding: var(--space-xs) 0;
}

.era-jumper__link {
  display: block;
  position: relative;
  padding: 0.45em var(--space-md);
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: var(--text-sm);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text, #2c1810);
  text-decoration: none;
  border-bottom: 1px solid rgba(0, 0, 0, 0.06);
  transition: background-color var(--transition-fast), color var(--transition-fast);
  line-height: 1.35;
}

.era-jumper__list li:last-child .era-jumper__link { border-bottom: none; }

.era-jumper__link:hover,
.era-jumper__link:focus {
  background: var(--color-bg-alt, #e8d5a3);
  color: var(--color-accent, #c0392b);
  outline: none;
}

/* Album entries hang under their era: indented, slightly quieter, and
   marked with a rule down the left so the grouping reads at a glance. */
.era-jumper__link--album {
  padding-left: calc(var(--space-md) + var(--space-sm));
  font-size: calc(var(--text-sm) * 0.92);
  opacity: 0.85;
}

.era-jumper__link--album::before {
  content: '';
  position: absolute;
  left: var(--space-md);
  top: 0.5em;
  bottom: 0.5em;
  width: 1px;
  background: currentColor;
  opacity: 0.35;
}

.era-jumper__link-year {
  display: block;
  font-size: 0.62rem;
  opacity: 0.55;
  letter-spacing: 0.18em;
}

@media (prefers-reduced-motion: reduce) {
  .site-nav__burger-bar,
  .site-nav__drawer,
  .era-jumper__btn { transition: none; }
}

/* ── Timeline filter (floating, timeline page only) ─────── */

.timeline-filter {
  position: fixed;
  left: var(--space-md);
  bottom: var(--space-xl);
  z-index: 90;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-xs);
}

.timeline-filter__btn {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 0.5em 1em;
  background: var(--color-bg, #f5e6c8);
  border: 1px solid var(--color-accent, #c0392b);
  border-radius: 999px;
  color: var(--color-text, #2c1810);
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: 0.72rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  cursor: pointer;
  box-shadow: 0 2px 14px rgba(0, 0, 0, 0.14);
  transition: background-color var(--transition-fast),
              color var(--transition-fast),
              border-color var(--transition-fast);
}

.timeline-filter__btn:hover {
  background: var(--color-accent, #c0392b);
  color: var(--color-bg, #f5e6c8);
}

.timeline-filter__icon { font-size: 0.85rem; }

.timeline-filter__panel {
  background: var(--color-bg, #f5e6c8);
  border: 1px solid var(--color-accent, #c0392b);
  border-radius: 8px;
  box-shadow: 0 4px 22px rgba(0, 0, 0, 0.16);
  padding: var(--space-sm);
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  min-width: 160px;
  max-height: 70vh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* display:flex overrides the UA [hidden] rule — restore it explicitly */
.timeline-filter__panel[hidden],
.timeline-filter__btn[hidden] { display: none; }

.timeline-filter__chip {
  display: flex;
  align-items: center;
  gap: 0.5em;
  width: 100%;
  padding: 0.4em 0.75em;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 6px;
  color: var(--color-text, #2c1810);
  font-family: var(--font-body, 'Courier Prime', serif);
  font-size: var(--text-sm);
  text-align: left;
  cursor: pointer;
  opacity: 0.55;
  transition: background-color var(--transition-fast),
              opacity var(--transition-fast),
              border-color var(--transition-fast);
}

.timeline-filter__chip[aria-pressed="true"] {
  opacity: 1;
  border-color: var(--color-accent, #c0392b);
  background: var(--color-bg-alt, #e8d5a3);
}

.timeline-filter__chip:hover {
  opacity: 1;
  background: var(--color-bg-alt, #e8d5a3);
}

.timeline-filter__chip-glyph {
  font-size: 0.9em;
  flex-shrink: 0;
}

.timeline-filter__chip-label {
  font-size: 0.78rem;
  letter-spacing: 0.05em;
}

.timeline-filter__status {
  display: block;
  padding: 0.35em 0.75em 0;
  font-size: 0.7rem;
  opacity: 0.6;
  font-family: var(--font-body, 'Courier Prime', serif);
  border-top: 1px solid rgba(0, 0, 0, 0.08);
  margin-top: var(--space-xs);
}

.timeline-filter__status:empty { display: none; }

.timeline-filter__panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.3em 0.75em 0.4em;
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
  margin-bottom: var(--space-xs);
}

.timeline-filter__panel-title {
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: 0.78rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
}

.timeline-filter__close {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-text, #2c1810);
  font-size: 1.1rem;
  line-height: 1;
  padding: 0 0.1em;
  opacity: 0.55;
  transition: opacity var(--transition-fast);
}

.timeline-filter__close:hover { opacity: 1; }

.timeline-filter__section-heading {
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: 0.62rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  opacity: 0.5;
  padding: 0.2em 0.75em 0.1em;
}

/* ── Year range slider ──────────────────────────────────── */

.timeline-filter__range {
  border-top: 1px solid rgba(0, 0, 0, 0.08);
  padding-top: var(--space-xs);
  margin-top: var(--space-xs);
}

.timeline-filter__range-labels {
  display: flex;
  justify-content: space-between;
  padding: 0 0.75em;
  font-size: 0.75rem;
  font-family: var(--font-body, 'Courier Prime', serif);
  opacity: 0.75;
  margin-bottom: 0.3em;
}

.timeline-filter__range-track {
  position: relative;
  height: 28px;
  margin: 0 0.75em;
}

/* The coloured fill between the two thumbs */
.timeline-filter__range-fill {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  height: 4px;
  background: var(--color-accent, #c0392b);
  border-radius: 2px;
  pointer-events: none;
  z-index: 1;
}

/* Base track (unfilled portions) */
.timeline-filter__range-track::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 4px;
  transform: translateY(-50%);
  background: var(--color-bg-alt, #e8d5a3);
  border-radius: 2px;
}

.timeline-filter__range-input {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  pointer-events: none;
  z-index: 2;
}

.timeline-filter__range-input::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--color-accent, #c0392b);
  border: 2px solid var(--color-bg, #f5e6c8);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
  cursor: pointer;
  pointer-events: all;
  transition: transform var(--transition-fast);
}

.timeline-filter__range-input::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--color-accent, #c0392b);
  border: 2px solid var(--color-bg, #f5e6c8);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
  cursor: pointer;
  pointer-events: all;
}

.timeline-filter__range-input:focus-visible::-webkit-slider-thumb {
  outline: 2px solid var(--color-accent, #c0392b);
  outline-offset: 2px;
}

.timeline-filter__range-input::-webkit-slider-thumb:hover,
.timeline-filter__range-input::-webkit-slider-thumb:active {
  transform: scale(1.2);
}

@media (prefers-reduced-motion: reduce) {
  .timeline-filter__range-input::-webkit-slider-thumb { transition: none; }
}

/* Hidden nodes, album-reveal sections, era-sections, and phase wrappers */
.timeline__node.is-hidden-by-filter,
.album-reveal.is-hidden-by-filter,
.era-section.is-hidden-by-filter,
.phase-collapse.is-hidden-by-filter { display: none; }

@media (prefers-reduced-motion: reduce) {
  .timeline-filter__btn,
  .timeline-filter__chip { transition: none; }
}

/* ── Floating buttons over the intro photos (phones) ───────
   On a phone the bottom-anchored Filter and Jump buttons sit on top of the
   intro captions. While the photos are pinned (scroller.js sets
   body.is-intro-pinned) they ride up to just under the header, and slide
   back down once the photos start to scroll away.

   The lift is a transform, so it animates cheaply and needs no measuring:
   a closed wrapper is just its button, so its top edge is
   viewport - bottom gap - own height (100%), and moving that to
   header + gap is the translate below. dvh tracks the visible viewport as
   mobile browser bars come and go; vh is the fallback.

   An open panel grows upwards from the button and would run off the top of
   the screen, so an open wrapper drops back to its usual place. So do both
   while a track plays: the mini-player bar sits just under the header, on
   top of them (z-index 99 to their 90), and they could not be tapped. */
@media (max-width: 767px) {
  .timeline-filter,
  .era-jumper {
    transition: transform 0.35s ease;
  }

  .is-intro-pinned:not(:has(.mini-player--visible)) .timeline-filter:not(:has(.timeline-filter__panel:not([hidden]))),
  .is-intro-pinned:not(:has(.mini-player--visible)) .era-jumper:not(:has(.era-jumper__panel:not([hidden]))) {
    transform: translateY(calc(var(--site-header-h, 64px) + var(--space-sm) + var(--space-xl) + 100% - 100vh));
    transform: translateY(calc(var(--site-header-h, 64px) + var(--space-sm) + var(--space-xl) + 100% - 100dvh));
  }
}

@media (max-width: 767px) and (prefers-reduced-motion: reduce) {
  .timeline-filter,
  .era-jumper { transition: none; }
}

/* ── Intro gallery (scrollytelling photo intro) ────────────

   Full-bleed photo gallery rendered before the timeline. The section
   is N viewports tall (one viewport per slide); a sticky inner element
   keeps the image stage pinned in view while the user scrolls through
   that range. Slides crossfade based on --intro-progress (set by JS),
   captions sit over each image, and a custom progress indicator on
   the right edge tracks position. */

.intro-gallery {
  position: relative;
  height: calc(100vh * var(--intro-slide-count, 4));
  background-color: var(--color-bg);
  color: var(--color-text);
}

.intro-gallery__sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  width: 100%;
  overflow: hidden;
}

.intro-gallery__stage {
  position: absolute;
  inset: 0;
}

.intro-gallery__slide {
  position: absolute;
  inset: 0;
  margin: 0;
}

/* Photo + vignette. This is what crossfades between slides; the caption
   is a sibling so the two can fade on independent curves (scroller.js). */
.intro-gallery__media {
  position: absolute;
  inset: 0;
  /* opacity is set by JS — initial value is in the inline style */
  transition: opacity 80ms linear; /* small smoothing — main fade is via direct opacity per frame */
}

.intro-gallery__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Per-slide focus point. Set via inline custom property in loader.js;
     defaults to centred if no focus is supplied in JSON. */
  object-position: var(--obj-pos, 50% 50%);
  display: block;
  filter: contrast(1.05) saturate(0.85);
}

@media (max-width: 768px) {
  .intro-gallery__img {
    object-position: var(--obj-pos-mobile, var(--obj-pos, 50% 50%));
  }
}

/* Vignette + caption gradient so light captions stay readable on top
   of busy photos. */
.intro-gallery__shade {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to bottom,
      rgba(0, 0, 0, 0.35) 0%,
      rgba(0, 0, 0, 0) 30%,
      rgba(0, 0, 0, 0) 55%,
      rgba(0, 0, 0, 0.65) 100%
    );
  pointer-events: none;
}

.intro-gallery__caption {
  position: absolute;
  left: 50%;
  bottom: 14vh;
  transform: translateX(-50%);
  width: min(720px, 86vw);
  margin: 0;
  text-align: center;
  font-family: var(--font-heading);
  font-size: clamp(1.6rem, 4vw, 3rem);
  line-height: 1.1;
  letter-spacing: 0.04em;
  color: #fff;
  text-shadow: 0 2px 24px rgba(0, 0, 0, 0.7);
  /* opacity is set by JS — initial value is in the inline style */
  transition: opacity 80ms linear;
}

/* ── Progress indicator (right edge) ───────────────────── */

.intro-gallery__progress {
  position: absolute;
  top: 50%;
  right: clamp(18px, 3vw, 48px);
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-md);
  color: #fff;
  z-index: 3;
}

.intro-gallery__progress-count {
  font-family: var(--font-heading);
  font-size: 0.78rem;
  letter-spacing: 0.32em;
  opacity: 0.85;
}

.intro-gallery__progress-track {
  position: relative;
  width: 2px;
  height: clamp(180px, 28vh, 280px);
  background-color: rgba(255, 255, 255, 0.22);
  border-radius: 2px;
}

/* Filled portion grows from the top down to the thumb. */
.intro-gallery__progress-fill {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: calc(var(--intro-progress, 0) * 100%);
  background-color: var(--color-accent);
  border-radius: 2px;
  transition: height 60ms linear;
}

.intro-gallery__progress-tick {
  position: absolute;
  left: 50%;
  top: var(--tick-pos, 0%);
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: rgba(255, 255, 255, 0.55);
  transform: translate(-50%, -50%);
}

.intro-gallery__progress-thumb {
  position: absolute;
  left: 50%;
  top: calc(var(--intro-progress, 0) * 100%);
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background-color: #fff;
  border: 2px solid var(--color-accent);
  transform: translate(-50%, -50%);
  box-shadow: 0 0 18px rgba(255, 255, 255, 0.5);
  transition: top 60ms linear;
}

/* "Scroll to begin the story ↓" hint sitting just under the site header —
   the only cue that the page is a scroll-driven story, so it is
   deliberately large enough to read at a glance. It stays at full
   strength for the whole gallery and scrolls away with the sticky stage
   once the last photo has gone, rather than fading out early. */
.intro-gallery__scroll-hint {
  position: absolute;
  left: 50%;
  top: calc(var(--site-header-h, 64px) + 4vh);
  transform: translateX(-50%);
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-sm);
  color: #fff;
  z-index: 3;
  pointer-events: none;
}

.intro-gallery__scroll-hint-label {
  font-family: var(--font-heading);
  font-size: clamp(0.78rem, 1.6vw, 0.95rem);
  letter-spacing: 0.32em;
  text-transform: uppercase;
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.8);
}

.intro-gallery__scroll-hint-arrow {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border: 1px solid rgba(255, 255, 255, 0.55);
  border-radius: 50%;
  background-color: rgba(0, 0, 0, 0.28);
  animation: intro-arrow 1.8s ease-in-out infinite;
}

.intro-gallery__scroll-hint-arrow svg {
  width: 22px;
  height: 22px;
  display: block;
}

@keyframes intro-arrow {
  0%, 100% { transform: translateY(0); opacity: 0.75; }
  50%      { transform: translateY(7px); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .intro-gallery__scroll-hint-arrow { animation: none; }
}

/* ── Intro about card ────────────────────────────────────

   Full-viewport section with a centred framed card carrying the
   placeholder bio paragraph. Picks up the continuous spine line
   from the existing rule. */

.intro-about {
  position: relative;
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  padding: var(--space-2xl) var(--space-md);
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

.intro-about__inner {
  position: relative;
  max-width: 720px;
  text-align: center;
  background-color: var(--color-bg);
  border: 2px solid var(--color-accent);
  border-radius: 28px;
  padding: var(--space-2xl) var(--space-xl);
}

.intro-about__eyebrow {
  font-family: var(--font-heading);
  font-size: var(--text-sm);
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--color-accent);
  opacity: 0.85;
  margin-bottom: var(--space-md);
}

.intro-about__title {
  font-family: var(--font-heading);
  font-size: clamp(var(--text-3xl), 8vw, var(--text-4xl));
  line-height: 1;
  color: var(--color-text);
  margin: 0 0 var(--space-lg);
  position: relative;
  padding: var(--space-md) 0;
}

.intro-about__title::before,
.intro-about__title::after {
  content: '';
  display: block;
  width: clamp(120px, 32vw, 360px);
  height: 1px;
  background: var(--color-accent);
  margin: 0 auto;
  opacity: 0.55;
}

.intro-about__title::before { margin-bottom: var(--space-md); }
.intro-about__title::after  { margin-top:    var(--space-md); }

.intro-about__body {
  font-size: var(--text-base);
  line-height: 1.75;
  text-align: left;
  max-width: 56ch;
  margin: 0 auto;
}

/* Narrow viewports: the card's 4rem horizontal padding plus the section's
   own gutter left only ~215px of text width at 375px. Tighten both so the
   bio paragraph gets a usable measure. */
@media (max-width: 768px) {
  .intro-about {
    padding-left: var(--space-sm);
    padding-right: var(--space-sm);
  }

  .intro-about__inner {
    padding-left: var(--space-md);
    padding-right: var(--space-md);
  }
}

/* ── Era sections ────────────────────────────────────────── */

.era-section {
  position: relative;
  /* No min-height — sections size to their actual content. The previous
     100vh floor produced a long empty line below short timelines. */
  padding: var(--space-md) var(--space-md);
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  overflow: hidden; /* contain glow effects */
}

/* ── Continuous spine line ────────────────────────────────

   A single 2px vertical line runs down the centre of every main-level
   section so the timeline never visually stops. The line is rendered as
   a pseudo-element behind sibling content; framed title cards (phase
   reveal, era header, album hero) have a solid theme-coloured background
   that obscures the line where they sit, with a rounded-rect border
   that visually "becomes" the line wrapping the title. Each section
   draws its own slice of the line so the colour follows the section's
   accent — colours flip cleanly at section boundaries, in step with the
   rest of the theme transition. */

.era-section::before,
.album-reveal::before,
.phase-collapse::before,
.intro-about::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: var(--timeline-spine-width);
  background-color: var(--color-accent);
  transform: translateX(-50%);
  pointer-events: none;
  /* No z-index — siblings render on top in source order, and framed
     cards' solid backgrounds cover the line where they sit. */
}

/* Mobile: align the continuous line with the timeline markers on the
   left rather than centred. Framed cards (era-header, phase-card,
   album-hero) are centred with max-width, so on a narrow viewport the
   line passes behind them on the left — it enters at the top-left and
   leaves at the bottom-left of each box. */
@media (max-width: 899px) {
  .era-section::before,
  .album-reveal::before,
  .phase-collapse::before,
  .intro-about::before {
    left: var(--timeline-spine-offset);
    transform: none;
  }
}

.era-section--header-only {
  min-height: auto;
  padding-bottom: var(--space-lg);
}

.era-section__header {
  position: relative;
  z-index: 2;
  max-width: 60ch;
  margin: 0 auto var(--space-md);
  text-align: center;
  padding: var(--space-lg) var(--space-xl);
  background-color: var(--color-bg);
  border: 2px solid var(--color-accent, #c0392b);
  border-radius: 28px;
}

.era-section__label {
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: var(--text-sm);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--color-accent, #c0392b);
  margin-bottom: var(--space-xs);
}

.era-section__band-name {
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: var(--text-3xl);
  line-height: 1;
  color: var(--color-text, #2c1810);
}

.era-section__year {
  font-family: var(--font-body, 'Courier Prime', serif);
  font-size: var(--text-base);
  color: var(--color-text, #2c1810);
  opacity: 0.7;
  margin-top: var(--space-sm);
  letter-spacing: 0.1em;
}

.era-section__members {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-sm);
  margin-top: var(--space-md);
}

.era-section__member-tag {
  font-size: var(--text-sm);
  border: 1px solid var(--color-accent, #c0392b);
  border-radius: var(--border-radius, 0px);
  padding: var(--space-xs) var(--space-sm);
  color: var(--color-accent, #c0392b);
}

/* ============================================================
   TIMELINE
   ============================================================ */

.timeline {
  position: relative;
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: 1100px;
}

/* The per-timeline spine is replaced by the continuous line on the
   parent section (see .era-section::before above), so this element is
   hidden. The .timeline ol still emits it because the loader was built
   around it; leaving the element in place keeps existing JS hooks (the
   --timeline-draw progress writer) harmless. */
.timeline__spine {
  display: none;
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--timeline-spine-offset);
  width: var(--timeline-spine-width);
  background:
    linear-gradient(
      to bottom,
      var(--color-accent) 0%,
      var(--color-accent-secondary, var(--color-accent)) 100%
    );
  border-radius: var(--timeline-spine-width);
  transform: scaleY(var(--timeline-draw, 1));
  transform-origin: top;
  opacity: 0.85;
  pointer-events: none;
  box-shadow: 0 0 18px var(--color-accent);
  transition: background var(--transition-slow),
              box-shadow var(--transition-slow);
  will-change: transform;
}

@media (prefers-reduced-motion: reduce) {
  .timeline__spine {
    transform: none;
  }
}

/* ── Node ─────────────────────────────────────────────────── */

.timeline__node {
  position: relative;
  margin: var(--timeline-row-gap) 0;
  padding-left: calc(var(--timeline-spine-offset) + var(--space-lg) + var(--timeline-marker-size) / 2);
  list-style: none;
}

/* Each node fades + slides in slightly when it enters the viewport.
   Without JS adding .is-visible they remain at full opacity. */
.timeline__node[data-animated="true"] {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.7s ease, transform 0.7s ease;
}

.timeline__node[data-animated="true"].is-visible {
  opacity: 1;
  transform: none;
}

/* ── Deep-link landing ────────────────────────────────────

   Arriving from "See in timeline" or a search result opens the target and
   scrolls to it, which on a page of near-identical rows leaves no clue which
   row was the one asked for. A brief wash of the era's accent answers that,
   then fades out so it doesn't read as a permanent selection.

   The rule is deliberately generic: album reveals are landed on too, and they
   are sections rather than nodes. */
.is-link-target {
  animation: link-target-fade 2.4s ease-out 1;
}

@keyframes link-target-fade {
  0%   { background-color: color-mix(in srgb, var(--color-accent) 22%, transparent); }
  35%  { background-color: color-mix(in srgb, var(--color-accent) 22%, transparent); }
  100% { background-color: transparent; }
}

/* Reduced motion keeps the cue but drops the animation: the jump is instant,
   so a static tint that clears on the next interaction is enough. */
@media (prefers-reduced-motion: reduce) {
  .is-link-target {
    animation: none;
    background-color: color-mix(in srgb, var(--color-accent) 16%, transparent);
  }
}

/* ── Toggle (marker + lead) ──────────────────────────────── */

.timeline__toggle {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  background: transparent;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  width: 100%;
  text-align: left;
}

.timeline__toggle:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 6px;
  border-radius: 6px;
}

.timeline__marker {
  position: absolute;
  top: 0;
  left: calc(var(--timeline-spine-offset) - var(--timeline-marker-size) / 2 + var(--timeline-spine-width) / 2);
  width: var(--timeline-marker-size);
  height: var(--timeline-marker-size);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background-color: var(--color-bg);
  border: 2px solid var(--color-accent);
  color: var(--color-accent);
  box-shadow:
    0 0 0 4px var(--color-bg),
    0 4px 18px rgba(0, 0, 0, 0.18);
  transition:
    transform var(--transition-normal),
    background-color var(--transition-normal),
    color var(--transition-normal),
    border-color var(--transition-normal),
    box-shadow var(--transition-normal);
  will-change: transform;
  z-index: 2;
}

.timeline__marker-glyph {
  font-size: 1.05rem;
  line-height: 1;
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
}

.timeline__marker-year {
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  margin-top: 2px;
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  opacity: 0.85;
}

/* Hover / active marker — pop, glow, invert */
.timeline__toggle:hover .timeline__marker,
.timeline__node.is-active .timeline__marker {
  transform: scale(1.08);
  background-color: var(--color-accent);
  color: var(--color-bg);
  box-shadow:
    0 0 0 6px var(--color-bg),
    0 0 28px var(--color-accent),
    0 6px 22px rgba(0, 0, 0, 0.22);
}

/* Soft pulse on the active node (open one) */
.timeline__node[data-collapsed="false"] > .timeline__toggle .timeline__marker {
  animation: marker-pulse 2.6s ease-in-out infinite;
}

@keyframes marker-pulse {
  0%, 100% { box-shadow: 0 0 0 6px var(--color-bg), 0 0 18px var(--color-accent), 0 6px 22px rgba(0,0,0,0.22); }
  50%      { box-shadow: 0 0 0 6px var(--color-bg), 0 0 36px var(--color-accent), 0 6px 26px rgba(0,0,0,0.28); }
}

@media (prefers-reduced-motion: reduce) {
  .timeline__node[data-collapsed="false"] > .timeline__toggle .timeline__marker {
    animation: none;
  }
}

/* Lead (label + title + date) */
.timeline__lead {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1 1 auto;
  min-width: 0;
}

.timeline__type {
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: 0.7rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--color-accent);
  opacity: 0.85;
}

.timeline__lead-title {
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: var(--text-lg);
  line-height: 1.2;
  color: var(--color-text);
}

.timeline__lead-date {
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  opacity: 0.7;
}

/* Caret hint that the node is expandable */
.timeline__toggle::after {
  content: '+';
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: var(--text-xl);
  line-height: 1;
  color: var(--color-accent);
  margin-left: auto;
  flex: 0 0 auto;
  opacity: 0.6;
  transition: transform var(--transition-normal), opacity var(--transition-fast);
}

.timeline__node[data-collapsed="false"] > .timeline__toggle::after {
  content: '−';
  opacity: 0.9;
}

/* ── Card (collapsible content) ──────────────────────────── */

.timeline__card {
  margin-top: var(--space-md);
  padding: var(--space-md) var(--space-lg);
  background-color: var(--color-bg-alt);
  border: 1px solid var(--color-accent);
  border-left-width: 4px;
  border-radius: var(--timeline-card-radius);
  box-shadow: 0 6px 30px rgba(0, 0, 0, 0.08);
  position: relative;
  animation: card-unfurl var(--transition-normal) ease-out;
  overflow: hidden;
}

.timeline__card[hidden] { display: none; }

@keyframes card-unfurl {
  from {
    opacity: 0;
    transform: translateY(-6px) scale(0.985);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .timeline__card { animation: none; }
}

/* ── Card body content ───────────────────────────────────── */

.card-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.card-body__meta {
  font-size: 0.8rem;
  letter-spacing: 0.05em;
  opacity: 0.75;
}

.card-body__text {
  font-size: var(--text-base);
  line-height: 1.7;
  max-width: 65ch;
}

.card-body__byline {
  margin-top: var(--space-sm);
  font-style: italic;
  opacity: 0.7;
}

.card-body__rating {
  font-size: var(--text-lg);
  color: var(--color-accent);
  font-weight: bold;
}

.card-body__copyright {
  margin-top: var(--space-sm);
  font-size: 0.75rem;
  line-height: 1.5;
  letter-spacing: 0.02em;
  opacity: 0.65;
  max-width: 65ch;
}

.card-body__quote-text {
  font-size: var(--text-xl);
  line-height: 1.4;
  font-style: italic;
  border-left: 3px solid var(--color-accent);
  padding-left: var(--space-md);
}

.card-body__quote-attribution {
  display: block;
  font-style: normal;
  font-size: 0.85rem;
  opacity: 0.7;
  margin-top: var(--space-sm);
  padding-left: calc(var(--space-md) + 3px);
}

.card-body__name-change-names {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-md);
  margin-bottom: var(--space-xs);
}

.card-body__name-from {
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: var(--text-xl);
  opacity: 0.5;
  text-decoration: line-through;
}

.card-body__name-arrow {
  font-size: var(--text-xl);
  color: var(--color-accent);
}

.card-body__name-to {
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: var(--text-xl);
  color: var(--color-accent);
}

/* ── Inline media (figures, embeds) ──────────────────────── */

.media-figure {
  margin-top: var(--space-sm);
}

.media-figure img {
  max-width: 100%;
  height: auto;
  display: block;
  border-radius: var(--border-radius, 0px);
}

.media-figure figcaption {
  font-size: 0.78rem;
  opacity: 0.7;
  margin-top: var(--space-xs);
}

.media-embed {
  position: relative;
  aspect-ratio: 16 / 9;
  width: 100%;
  max-width: 640px;
  margin-top: var(--space-sm);
  background: #000;
  border-radius: var(--border-radius, 6px);
  overflow: hidden;
}

.media-embed iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* ── Gallery (inline image strip in expanded card) ───────── */

.gallery {
  position: relative;
  margin-top: var(--space-md);
  margin-left: calc(var(--space-lg) * -1);
  margin-right: calc(var(--space-lg) * -1);
  padding: 0 var(--space-lg);
}

.gallery__track {
  display: flex;
  gap: var(--space-md);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: var(--space-xs);
  scrollbar-width: thin;
}

.gallery__item {
  flex: 0 0 220px;
  scroll-snap-align: start;
  position: relative;
  border-radius: var(--border-radius, 6px);
  overflow: hidden;
  background-color: var(--color-bg);
  border: 1px solid var(--color-accent);
  transition: transform var(--transition-fast);
}

.gallery__item:hover {
  transform: translateY(-2px);
}

.gallery__item img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

/* Expand (fullscreen) button — top-left corner of each image. */
.gallery__expand-btn {
  position: absolute;
  top: 8px;
  left: 8px;
  width: 26px;
  height: 26px;
  padding: 0;
  border-radius: 50%;
  background-color: rgba(0, 0, 0, 0.45);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.55);
  cursor: pointer;
  z-index: 3;
  opacity: 0.85;
  display: flex;
  align-items: center;
  justify-content: center;
  transition:
    background-color var(--transition-fast),
    border-color var(--transition-fast),
    opacity var(--transition-fast),
    transform var(--transition-fast);
}

.gallery__expand-btn:hover,
.gallery__expand-btn:focus-visible {
  background-color: rgba(0, 0, 0, 0.78);
  opacity: 1;
  transform: scale(1.06);
  outline: none;
}

/* Magnify button — desktop only, sits next to the expand button. */
.gallery__zoom-btn {
  position: absolute;
  top: 8px;
  left: 40px; /* 8px offset + 26px expand btn + 6px gap */
  width: 26px;
  height: 26px;
  padding: 0;
  border-radius: 50%;
  background-color: rgba(0, 0, 0, 0.45);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.55);
  cursor: pointer;
  z-index: 3;
  opacity: 0.85;
  display: flex;
  align-items: center;
  justify-content: center;
  transition:
    background-color var(--transition-fast),
    border-color     var(--transition-fast),
    opacity          var(--transition-fast),
    transform        var(--transition-fast);
}

.gallery__zoom-btn:hover,
.gallery__zoom-btn:focus-visible {
  background-color: rgba(0, 0, 0, 0.78);
  opacity: 1;
  transform: scale(1.06);
  outline: none;
}

/* Active state: invert colours so it's clear the lens is on. */
.gallery__zoom-btn[aria-pressed="true"] {
  background-color: rgba(255, 255, 255, 0.92);
  color: #111;
  border-color: rgba(255, 255, 255, 0.92);
  opacity: 1;
}

/* Hide on mobile — pinch-zoom is the native gesture there. */
@media (max-width: 768px) {
  .gallery__zoom-btn { display: none; }
}

/* ── Magnifier loupe ──────────────────────────────────────── */

.loupe {
  position: fixed;
  width: 440px;
  height: 248px;
  border: 2px solid rgba(255, 255, 255, 0.85);
  border-radius: 4px;
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.25);
  pointer-events: none;
  background-color: #111; /* fallback while image loads */
  background-repeat: no-repeat;
  overflow: hidden;
  z-index: 9999;
  display: none;
}

/* Info button — small "i" circle, top-right of the image. Subtle until
   hover/focus, then it firms up. */
.gallery__info-btn {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 26px;
  height: 26px;
  padding: 0;
  border-radius: 50%;
  background-color: rgba(0, 0, 0, 0.45);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.55);
  font-family: 'Playfair Display', Georgia, serif;
  font-style: italic;
  font-weight: 600;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  z-index: 3;
  opacity: 0.85;
  display: flex;
  align-items: center;
  justify-content: center;
  transition:
    background-color var(--transition-fast),
    border-color var(--transition-fast),
    opacity var(--transition-fast),
    transform var(--transition-fast);
}

.gallery__info-btn:hover,
.gallery__info-btn:focus-visible {
  background-color: rgba(0, 0, 0, 0.78);
  opacity: 1;
  transform: scale(1.06);
  outline: none;
}

.gallery__info-btn[aria-expanded="true"] {
  background-color: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-bg);
  opacity: 1;
}

/* Caption is now a slide-up overlay over the image, hidden by default. */
.gallery__caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.78);
  color: #fff;
  font-size: 0.78rem;
  line-height: 1.45;
  padding: var(--space-sm) var(--space-md);
  transform: translateY(101%);
  transition: transform var(--transition-normal);
  z-index: 2;
  pointer-events: none;
}

.gallery__item.is-info-shown .gallery__caption {
  transform: translateY(0);
}

/* Hover-to-reveal on devices that actually have hover (desktop pointers).
   On touch this rule no-ops, so the button is the primary trigger there. */
@media (hover: hover) {
  .gallery__item:hover .gallery__caption {
    transform: translateY(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .gallery__caption { transition: none; }
}

/* Placeholder badge — moved to top-LEFT so it doesn't collide with the
   info button now sitting in the top-right corner. */
.gallery__item--placeholder::before {
  content: 'PLACEHOLDER';
  position: absolute;
  top: var(--space-xs);
  left: var(--space-xs);
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: 0.6rem;
  letter-spacing: 0.18em;
  background: var(--color-accent);
  color: var(--color-bg);
  padding: 2px 6px;
  border-radius: 3px;
  z-index: 2;
  pointer-events: none;
  opacity: 0.92;
}

/* ── Lightbox ─────────────────────────────────────────────── */

.lightbox {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  margin: 0;
  padding: 0;
  background: transparent;
  border: none;
}

/* display:flex must be scoped to [open] — otherwise it overrides the UA's
   display:none for closed dialogs, leaving an invisible overlay that eats
   all clicks across the page. */
.lightbox[open] {
  display: flex;
  align-items: center;
  justify-content: center;
}

.lightbox::backdrop {
  background: rgba(0, 0, 0, 0.92);
}

.lightbox__close {
  position: absolute;
  top: var(--space-md);
  right: var(--space-md);
  width: 40px;
  height: 40px;
  padding: 0;
  border-radius: 50%;
  background-color: rgba(255, 255, 255, 0.12);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.35);
  cursor: pointer;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  transition:
    background-color var(--transition-fast),
    transform var(--transition-fast);
}

.lightbox__close:hover,
.lightbox__close:focus-visible {
  background-color: rgba(255, 255, 255, 0.24);
  transform: scale(1.08);
  outline: none;
}

.lightbox__figure {
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: min(92vw, 1200px);
  max-height: 92dvh;
  margin: 0;
  padding: 0;
}

.lightbox__img {
  display: block;
  max-width: 100%;
  max-height: 80dvh;
  object-fit: contain;
  border-radius: 2px;
}

.lightbox__caption {
  margin-top: var(--space-sm);
  color: rgba(255, 255, 255, 0.82);
  font-size: 0.85rem;
  line-height: 1.5;
  text-align: center;
  max-width: 60ch;
  padding: 0 var(--space-sm);
}

@media (prefers-reduced-motion: no-preference) {
  .lightbox[open] {
    animation: lightbox-in 0.18s ease;
  }
  @keyframes lightbox-in {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
}

/* ── Media figure placeholder ─────────────────────────────── */

.media-figure--placeholder {
  position: relative;
}

.media-figure--placeholder::before {
  content: 'PLACEHOLDER';
  position: absolute;
  top: var(--space-xs);
  right: var(--space-xs);
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: 0.6rem;
  letter-spacing: 0.18em;
  background: var(--color-accent);
  color: var(--color-bg);
  padding: 2px 6px;
  border-radius: 3px;
}

/* ── Map pin icon on gig timeline cards ──────────────────── */

.card-map-link {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 4px;
  margin-left: var(--space-xs);
  color: var(--color-accent);
  text-decoration: none;
  border-radius: var(--border-radius);
  transition: background-color var(--transition-fast),
              color var(--transition-fast);
  vertical-align: middle;
  line-height: 1;
}

.card-map-link:hover,
.card-map-link:focus-visible {
  background-color: var(--color-accent);
  color: var(--color-bg);
  outline: none;
}

.card-map-link svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}

/* ── Per-event-type marker accents ───────────────────────── */

.timeline__node--music .timeline__marker { border-style: dashed; }
.timeline__node--gig   .timeline__marker { border-style: solid; }
.timeline__node--image .timeline__marker { border-style: double; border-width: 4px; }
.timeline__node--story .timeline__marker { border-style: solid; transform: rotate(2deg); }
.timeline__node--review .timeline__marker { background-color: var(--color-accent); color: var(--color-bg); }
.timeline__node--quote .timeline__marker  { background-color: var(--color-bg-alt); }
.timeline__node--name_change .timeline__marker {
  border-style: dotted;
  background-color: var(--color-bg-alt);
}

/* Whole-row name_change becomes a horizontal divider */
.timeline__node--name_change {
  text-align: center;
}

/* ── Link bar at the foot of a timeline ──────────────────── */

.timeline__links {
  margin-top: var(--space-xl);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-sm);
}

.event-link__btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-sm) var(--space-md);
  border: 2px solid var(--color-accent, #c0392b);
  border-radius: var(--border-radius, 0px);
  color: var(--color-accent, #c0392b);
  text-decoration: none;
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: var(--text-base);
  letter-spacing: 0.05em;
  transition:
    background-color var(--transition-fast),
    color var(--transition-fast);
}

.event-link__btn:hover {
  background-color: var(--color-accent, #c0392b);
  color: var(--color-bg, #f5e6c8);
}

/* ── Phase collapse (pre / post chapter card + content) ───

   Pre and post phases render as a small framed card (matching the era
   header) with an Expand button. The phase's era timelines sit inside,
   collapsed by default. Click the Expand button to reveal them. */

.phase-collapse {
  position: relative;
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  padding: var(--space-lg) 0;
  transition: background-color var(--transition-normal),
              color var(--transition-normal);
}

.phase-card {
  position: relative;
  z-index: 2;
  max-width: 60ch;
  margin: 0 auto;
  text-align: center;
  padding: var(--space-lg) var(--space-xl);
  background-color: var(--color-bg);
  border: 2px solid var(--color-accent);
  border-radius: 28px;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.phase-card__eyebrow {
  font-family: var(--font-heading);
  font-size: var(--text-sm);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--color-accent);
  margin-bottom: var(--space-xs);
}

.phase-card__title {
  font-family: var(--font-heading);
  font-size: var(--text-3xl);
  line-height: 1;
  color: var(--color-text);
  margin: 0;
}

.phase-card__range {
  font-family: var(--font-body);
  font-size: var(--text-base);
  color: var(--color-text);
  opacity: 0.7;
  letter-spacing: 0.1em;
  margin: 0;
}

.phase-card__subtitle {
  font-size: var(--text-sm);
  opacity: 0.7;
  line-height: 1.55;
  max-width: 50ch;
  margin: var(--space-xs) auto 0;
}

.phase-card__expand {
  align-self: center;
  margin-top: var(--space-md);
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 0.55em 1.2em;
  background: transparent;
  border: 1px solid var(--color-accent);
  border-radius: 999px;
  color: var(--color-accent);
  font-family: var(--font-heading);
  font-size: 0.78rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color var(--transition-fast),
              color var(--transition-fast);
}

.phase-card__expand:hover,
.phase-card__expand:focus-visible {
  background-color: var(--color-accent);
  color: var(--color-bg);
  outline: none;
}

/* Caret formed from a CSS triangle that flips when expanded. */
.phase-card__expand-icon {
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 5px 4px 0 4px;
  border-color: currentColor transparent transparent transparent;
  transition: transform var(--transition-normal);
}

.phase-collapse[data-collapsed="false"] .phase-card__expand-icon {
  transform: rotate(180deg);
}

.phase-collapse__content {
  display: none;
}

.phase-collapse[data-collapsed="false"] > .phase-collapse__content {
  display: block;
  animation: phase-collapse-unfurl var(--transition-slow) ease-out;
}

@keyframes phase-collapse-unfurl {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .phase-collapse[data-collapsed="false"] > .phase-collapse__content {
    animation: none;
  }
}

/* Pre phase: prologue feel — slightly faded photos in any timeline that
   follows, to read as "before the band exists". */
.era-section--phase-pre .gallery__item img,
.era-section--phase-pre .media-figure img {
  filter: saturate(0.7) contrast(0.95);
}

/* ── Album reveal ────────────────────────────────────────

   Layout:
     ┌──────────────────────────────────────────────┐
     │           hero (eyebrow / title / year)      │
     ├───────────────────────┬──────────────────────┤
     │   covers gallery      │   tracklist          │
     │   (primary + thumbs)  │   (click → player)   │
     └───────────────────────┴──────────────────────┘
   Clicking a track crossfades the cover pane to a YouTube player. */

.album-reveal {
  position: relative;
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  padding: var(--space-2xl) var(--space-md);
}

.album-reveal__hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: var(--space-xl) var(--space-xl);
  gap: var(--space-sm);
  background-color: var(--color-bg);
  border: 2px solid var(--color-accent);
  border-radius: 28px;
  max-width: 700px;
  margin: 0 auto var(--space-xl);
  position: relative;
}

/* Narrow viewports: same problem the intro opener has — the hero's 4rem
   horizontal padding plus the section gutter left the description about
   215px wide at 375px. Match .intro-about's mobile gutters so the two
   framed cards read alike. */
@media (max-width: 768px) {
  .album-reveal {
    padding-left: var(--space-sm);
    padding-right: var(--space-sm);
  }

  .album-reveal__hero {
    padding-left: var(--space-md);
    padding-right: var(--space-md);
  }
}

.album-reveal__eyebrow {
  font-family: var(--font-heading);
  font-size: var(--text-sm);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--color-accent);
}

.album-reveal__title {
  font-family: var(--font-heading);
  font-size: var(--text-4xl);
  line-height: 1;
  color: var(--color-text);
}

.album-reveal__year {
  font-family: var(--font-body);
  font-size: var(--text-lg);
  color: var(--color-accent);
  letter-spacing: 0.1em;
}

.album-reveal__description {
  font-size: var(--text-base);
  opacity: 0.75;
  max-width: 50ch;
  margin-top: var(--space-sm);
}

/* Rich text keeps the markup authored in content.json — the global reset
   zeroes margins, so paragraphs, lists and headings inside need their own
   spacing or multi-paragraph prose runs together as one block. Mirrors the
   rules map.css already applies to gig-card descriptions. */
.album-reveal__description--rich p,
.card-body__text--rich p {
  margin-bottom: var(--space-sm);
}

.album-reveal__description--rich p:last-child,
.card-body__text--rich p:last-child {
  margin-bottom: 0;
}

.album-reveal__description--rich ul,
.album-reveal__description--rich ol,
.card-body__text--rich ul,
.card-body__text--rich ol {
  margin: 0 0 var(--space-sm) var(--space-sm);
  padding-left: var(--space-md);
}

.album-reveal__description--rich li,
.card-body__text--rich li {
  margin-bottom: var(--space-xs);
}

.album-reveal__description--rich h3,
.album-reveal__description--rich h4,
.card-body__text--rich h3,
.card-body__text--rich h4 {
  font-size: var(--text-base);
  margin: var(--space-sm) 0 var(--space-xs);
}

/* Body — single column on mobile, side-by-side on wider screens. */
.album-reveal__body {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-xl);
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 var(--space-lg);
  align-items: start;
}

@media (min-width: 900px) {
  .album-reveal__body {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--space-2xl);
  }
}

/* ── Media (covers + player) ─────────────────────────── */

.album-reveal__media {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

/* Cover and player pane occupy the same square, crossfading via opacity. */
.album-reveal__pane {
  transition: opacity 450ms ease, transform 450ms ease;
}

.album-reveal__pane--cover {
  opacity: 1;
  transform: scale(1);
}

.album-reveal__pane--player {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  opacity: 0;
  transform: scale(0.94);
  pointer-events: none;
}

.album-reveal--playing .album-reveal__pane--cover,
.music-player--playing .album-reveal__pane--cover {
  opacity: 0;
  transform: scale(0.94);
  pointer-events: none;
}

.album-reveal--playing .album-reveal__pane--player,
.music-player--playing .album-reveal__pane--player {
  opacity: 1;
  transform: scale(1);
  pointer-events: auto;
}

/* ── Compact album-style player for music timeline events ─ */

.music-player {
  grid-template-columns: 1fr !important;
  max-width: none;
  padding: 0;
  margin-top: var(--space-md);
  gap: var(--space-md);
}

.music-player .album-reveal__cover-primary {
  aspect-ratio: 16 / 9;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
  overflow: hidden;
}

.music-player .album-reveal__tracks-heading {
  margin-bottom: 0;
}

/* The audio-only hidden iframe sink and waveform overlay are retired
   — playback now uses a visible YouTube iframe via .album-reveal__player.
   Mini-player (below) keeps a small bouncing waveform animation, so the
   keyframes are kept and re-used there. */

@keyframes waveform-bounce {
  from { height: 4px;  }
  to   { height: 18px; }
}

/* ── Sticky mini-player bar ─────────────────────────────── */

.mini-player {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  /* sit below the sticky header — JS sets --mini-player-offset */
  z-index: 99;
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-sm) var(--space-lg);
  background: var(--color-bg, #1a1a1a);
  color: var(--color-text, #fff);
  border-bottom: 1px solid var(--color-accent, #c0392b);
  box-shadow: 0 4px 20px rgba(0,0,0,0.4);
  /* Hidden: translate fully above top:0 so it's off-screen */
  transform: translateY(-110%);
  transition: transform 380ms cubic-bezier(0.22, 1, 0.36, 1);
}

/* display:flex overrides the UA [hidden] rule — restore it explicitly. Parked
   off-screen alone, the idle bar's Pause and Close buttons stayed in the Tab
   order and screen readers read it out on every page. The JS clears hidden and
   forces a reflow before adding --visible, so the slide-in still runs. */
.mini-player[hidden] { display: none; }

/* Visible: slide down to sit just below the header */
.mini-player--visible {
  transform: translateY(var(--mini-player-offset, 0px));
}

/* Stopping: drop animation immediately, then slide out */
.mini-player--stopping .mini-player__bar {
  animation: none;
  height: 4px;
}

.mini-player__waveform {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 18px;
  flex-shrink: 0;
}

.mini-player__bar {
  display: block;
  width: 3px;
  height: 6px;
  background: var(--color-accent, #c0392b);
  border-radius: 1px;
  animation: waveform-bounce 0.7s ease-in-out infinite alternate;
}

.mini-player__bar:nth-child(2) { animation-delay: 0.18s; }
.mini-player__bar:nth-child(3) { animation-delay: 0.36s; }
.mini-player__bar:nth-child(4) { animation-delay: 0.12s; }

.mini-player__label {
  flex: 1;
  font-family: var(--font-body);
  font-size: var(--text-sm, 0.875rem);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mini-player__prev,
.mini-player__next {
  flex-shrink: 0;
  background: transparent;
  border: 1px solid currentColor;
  color: inherit;
  font-size: 1rem;
  line-height: 1;
  padding: 0.2em 0.55em;
  border-radius: 999px;
  cursor: pointer;
  opacity: 0.7;
  transition: opacity var(--transition-fast, 150ms);
}

.mini-player__prev:hover,
.mini-player__next:hover { opacity: 1; }

.mini-player__prev:disabled,
.mini-player__next:disabled { opacity: 0.25; cursor: default; }

.mini-player__pause {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid currentColor;
  color: inherit;
  font-size: 1rem;
  line-height: 1;
  padding: 0.2em 0.55em;
  border-radius: 999px;
  cursor: pointer;
  opacity: 0.7;
  transition: opacity var(--transition-fast, 150ms);
}

.mini-player__pause:hover { opacity: 1; }

/* CSS-drawn pause/play icons. Same reasoning as .album-track__play: the
   U+23F8 and U+25B6 glyphs have no text-presentation form in the iOS
   system fonts, so Safari substitutes full-colour Apple emoji and the
   button falls out of the era theme. Drawing from currentColor keeps it
   monochrome and on-theme everywhere. */
.mini-player__pause-icon {
  position: relative;
  display: block;
  width: 10px;
  height: 12px;
}

/* Playing — two upright bars */
.mini-player__pause-icon::before,
.mini-player__pause-icon::after {
  content: '';
  position: absolute;
  top: 0;
  width: 3px;
  height: 100%;
  background-color: currentColor;
}

.mini-player__pause-icon::before { left: 0; }
.mini-player__pause-icon::after  { right: 0; }

/* Paused — a single triangle, centred on the triangle's centroid
   (1/3 in from the wide edge) so it sits optically central in the pill. */
.mini-player__pause--paused .mini-player__pause-icon::before {
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  background-color: transparent;
  border-style: solid;
  border-width: 6px 0 6px 10px;
  border-color: transparent transparent transparent currentColor;
  transform: translate(-33%, -50%);
}

.mini-player__pause--paused .mini-player__pause-icon::after { content: none; }

.mini-player__close {
  flex-shrink: 0;
  background: transparent;
  border: 1px solid currentColor;
  color: inherit;
  font-family: var(--font-heading);
  font-size: 0.85rem;
  line-height: 1;
  padding: 0.2em 0.5em;
  border-radius: 999px;
  cursor: pointer;
  opacity: 0.6;
  transition: opacity var(--transition-fast, 150ms);
}

.mini-player__close:hover { opacity: 1; }

/* Freeze waveform bars while paused */
.mini-player--paused .mini-player__bar {
  animation-play-state: paused;
}

@media (prefers-reduced-motion: reduce) {
  .mini-player__bar { animation: none; height: 10px; }
  .mini-player { transition: none; }
}

@media (prefers-reduced-motion: reduce) {
  .album-reveal__pane { transition: opacity 0.2s ease; transform: none !important; }
}

/* Primary (large) cover image */
.album-reveal__cover-primary {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  background-color: var(--color-bg-alt);
  border: 1px solid var(--color-accent);
  border-radius: var(--border-radius, 0);
  overflow: hidden;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.18);
}

.album-reveal__cover-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  transition: opacity 250ms ease;
}

.album-reveal__cover-img.is-swapping { opacity: 0; }

.album-reveal__cover-caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: var(--space-xs) var(--space-sm);
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: 0.78rem;
  letter-spacing: 0.05em;
}

/* Thumbnail strip */
.album-reveal__thumbs {
  display: flex;
  gap: var(--space-sm);
  flex-wrap: wrap;
}

.album-reveal__thumb {
  flex: 0 0 auto;
  width: 64px;
  height: 64px;
  padding: 0;
  background: transparent;
  border: 2px solid transparent;
  border-radius: var(--border-radius, 0);
  cursor: pointer;
  overflow: hidden;
  opacity: 0.6;
  transition: opacity var(--transition-fast),
              border-color var(--transition-fast),
              transform var(--transition-fast);
}

.album-reveal__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.album-reveal__thumb:hover,
.album-reveal__thumb:focus-visible {
  opacity: 1;
  outline: none;
}

.album-reveal__thumb[aria-selected="true"] {
  opacity: 1;
  border-color: var(--color-accent);
  transform: translateY(-2px);
}

.album-reveal__thumb--placeholder::after {
  content: '·';
  position: absolute;
}

/* Player pane */
.album-reveal__player {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
  border-radius: var(--border-radius, 0);
  overflow: hidden;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
}

.album-reveal__player iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.album-reveal__close {
  /* Floats over the top-right of the iframe so it works regardless of
     whether the surrounding pane has any vertical room beyond the player. */
  position: absolute;
  top: var(--space-sm);
  right: var(--space-sm);
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.2em;
  height: 2.2em;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.4);
  font-family: var(--font-body);
  font-size: 1.1rem;
  line-height: 1;
  border-radius: 999px;
  cursor: pointer;
  transition: background-color var(--transition-fast),
              color var(--transition-fast);
}

.album-reveal__close:hover,
.album-reveal__close:focus-visible {
  background-color: var(--color-accent);
  color: var(--color-bg);
  outline: none;
}

.album-reveal__nowplaying {
  font-size: 0.85rem;
  opacity: 0.8;
  letter-spacing: 0.06em;
}

/* ── Tracklist ──────────────────────────────────────── */

.album-reveal__tracklist {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.album-reveal__tracks-heading {
  font-family: var(--font-heading);
  font-size: var(--text-sm);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--color-accent);
  opacity: 0.85;
  margin-bottom: var(--space-xs);
}

.album-tracks {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0;
  margin: 0;
  padding: 0;
}

.album-tracks__item + .album-tracks__item {
  border-top: 1px solid color-mix(in srgb, var(--color-text, #222) 12%, transparent);
}

.album-tracks__side {
  font-family: var(--font-heading);
  font-size: 0.7rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--color-accent);
  opacity: 0.7;
  margin-top: var(--space-md);
  margin-bottom: var(--space-xs);
  padding-bottom: var(--space-xs);
  border-bottom: 1px solid var(--color-accent);
}

.album-tracks__side:first-child { margin-top: 0; }

.album-tracks__item {
  list-style: none;
  /* Track button + copy-link button sit side by side. */
  display: flex;
  align-items: stretch;
  gap: 0;
}

.album-track {
  display: grid;
  grid-template-columns: auto auto 1fr auto;
  gap: var(--space-md);
  align-items: center;
  flex: 1;
  min-width: 0;
  padding: var(--space-sm) var(--space-md);
  background: transparent;
  border: 0;
  border-radius: var(--border-radius, 0);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background-color var(--transition-fast),
              color var(--transition-fast);
}

.album-track:hover:not(:disabled),
.album-track:focus-visible:not(:disabled) {
  background-color: var(--color-bg-alt);
  outline: none;
}

.album-track:disabled {
  cursor: default;
  opacity: 0.5;
}

.album-track__num {
  font-family: var(--font-heading);
  font-size: 0.78rem;
  letter-spacing: 0.1em;
  opacity: 0.55;
  min-width: 1.6em;
}

.album-track__play {
  position: relative;
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 1px solid var(--color-accent);
  color: var(--color-accent);
  transition: background-color var(--transition-fast),
              color var(--transition-fast),
              transform var(--transition-fast);
}

/* CSS-drawn play triangle, centred via the centroid of the triangle
   (1/3 from the wide edge), so it doesn't shift between fonts the way
   the U+25B6 ▶ glyph did. */
.album-track__play::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 5px 0 5px 8px;
  border-color: transparent transparent transparent currentColor;
  transform: translate(-33%, -50%);
}

.album-track:hover:not(:disabled) .album-track__play {
  background-color: var(--color-accent);
  color: var(--color-bg);
  transform: scale(1.06);
}

.album-track[aria-pressed="true"] .album-track__play {
  background-color: var(--color-accent);
  color: var(--color-bg);
}

/* Paused state: two vertical bars drawn from a single element via box-shadow */
.album-track.is-paused .album-track__play::before {
  border: none;
  width: 3px;
  height: 10px;
  background: currentColor;
  box-shadow: 5px 0 0 currentColor;
  border-radius: 1px;
  transform: translate(-4px, -50%);
}

.album-track__title {
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: 1.3;
}

.album-track[aria-pressed="true"] .album-track__title {
  color: var(--color-accent);
  font-weight: 700;
}

.album-track__duration {
  font-family: var(--font-heading);
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  opacity: 0.6;
}

.album-track__coming-soon,
.album-track__lost,
.album-track__unobtainable {
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-style: italic;
  opacity: 0.5;
}

/* Solo "play album" fallback (no tracks present) */
.album-reveal__tracklist--single {
  align-items: center;
  justify-content: center;
}

.album-track--solo {
  grid-template-columns: auto 1fr;
  border: 1px solid var(--color-accent);
  padding: var(--space-md) var(--space-lg);
}

.album-tracks__item--solo {
  /* Centred row matching the parent --single tracklist alignment. */
  justify-content: center;
}

/* ── "Show entire album" expansion ───────────────────────── */

.album-tracks__show-more {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-top: var(--space-md);
  padding: var(--space-sm) var(--space-md);
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--color-accent) 50%, transparent);
  border-radius: var(--border-radius, 0);
  color: var(--color-accent);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  cursor: pointer;
  transition: background-color var(--transition-fast), border-color var(--transition-fast);
}

.album-tracks__show-more:hover,
.album-tracks__show-more:focus-visible {
  background-color: color-mix(in srgb, var(--color-accent) 10%, transparent);
  border-color: var(--color-accent);
  outline: none;
}

.album-tracks__show-more-icon {
  display: inline-block;
  transition: transform var(--transition-fast);
  font-size: 0.9em;
  line-height: 1;
}

.album-tracks__show-more.is-open .album-tracks__show-more-icon {
  transform: rotate(180deg);
}

.album-tracks__full {
  margin-top: var(--space-md);
  border-top: 1px solid color-mix(in srgb, var(--color-accent) 30%, transparent);
  padding-top: var(--space-sm);
}

.album-tracks__full-heading {
  font-family: var(--font-heading);
  font-size: var(--text-sm);
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: var(--color-accent);
  opacity: 0.7;
  margin: 0 0 var(--space-xs) var(--space-md);
}

/* Featured (Swan Arcade) tracks within the full album listing */
.album-tracks__item--featured .album-track__title {
  font-weight: 600;
}

.album-tracks__item--featured .album-track__num {
  color: var(--color-accent);
  opacity: 0.9;
}

/* ── Other performances ───────────────────────────────────
   A track that exists elsewhere in the archive gets a disclosure beneath
   its row; opening it reveals a nested list of the other recordings,
   joined to the parent by a bracket — a vertical spur down the left with
   an elbow into each version. Everything is drawn from the era/album
   theme's custom properties so each reveal paints its own palette. */

.album-tracks {
  /* Where the bracket's spur sits: under the parent track's play icon
     (row padding + number column + gap + half the 26px icon). */
  --versions-spur-x: calc(3.25rem + 13px);
  --versions-elbow: 14px;
  /* Indent for the disclosure so it lines up with the track title. */
  --versions-indent: calc(var(--versions-spur-x) + 13px + var(--space-md));
  /* Indent for a version's timeline link: row padding + play icon + gap. */
  --versions-link-indent: calc(var(--space-md) + 26px + var(--space-md));
  --versions-line: color-mix(in srgb, var(--color-accent) 55%, transparent);
}

/* Header row: heading on the left, the section-wide toggle on the right. */
.album-reveal__tracks-header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-xs) var(--space-md);
}

.album-tracks__versions-all {
  padding: var(--space-xs) var(--space-sm);
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--color-accent) 50%, transparent);
  border-radius: var(--border-radius, 0);
  color: var(--color-accent);
  font-family: var(--font-body);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  cursor: pointer;
  transition: background-color var(--transition-fast),
              border-color var(--transition-fast),
              color var(--transition-fast);
}

.album-tracks__versions-all:hover,
.album-tracks__versions-all:focus-visible {
  background-color: color-mix(in srgb, var(--color-accent) 10%, transparent);
  border-color: var(--color-accent);
  outline: none;
}

.album-tracks__versions-all:focus-visible {
  box-shadow: 0 0 0 2px var(--color-bg), 0 0 0 4px var(--color-accent);
}

.album-tracks__versions-all[aria-expanded="true"] {
  background-color: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-bg);
}

/* The row keeps play / copy / playlist on its first line; the disclosure
   and the nested list wrap beneath at full width. */
.album-tracks__item--has-versions {
  flex-wrap: wrap;
}

.album-tracks__versions-toggle {
  flex-basis: 100%;
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  margin: calc(-1 * var(--space-xs)) 0 var(--space-xs) var(--versions-indent);
  padding: var(--space-xs) var(--space-sm) var(--space-xs) 0;
  background: transparent;
  border: 0;
  color: var(--color-accent);
  font-family: var(--font-body);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-align: left;
  cursor: pointer;
  opacity: 0.8;
  transition: opacity var(--transition-fast);
}

.album-tracks__versions-toggle:hover,
.album-tracks__versions-toggle:focus-visible {
  opacity: 1;
  outline: none;
}

.album-tracks__versions-toggle:focus-visible .album-tracks__versions-count {
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.album-tracks__versions-icon {
  display: inline-block;
  font-size: 0.9em;
  line-height: 1;
  transition: transform var(--transition-fast);
}

.is-versions-open > .album-tracks__versions-toggle .album-tracks__versions-icon {
  transform: rotate(180deg);
}

/* Expand / collapse: the wrap animates its single grid row from 0fr to 1fr
   while the inner clips. Visibility goes with it so the closed list is out
   of the tab order and the accessibility tree, not merely clipped. */
.album-tracks__versions-wrap {
  flex-basis: 100%;
  display: grid;
  grid-template-rows: 0fr;
  visibility: hidden;
  transition: grid-template-rows 320ms ease, visibility 0s linear 320ms;
}

.is-versions-open > .album-tracks__versions-wrap {
  grid-template-rows: 1fr;
  visibility: visible;
  transition: grid-template-rows 320ms ease, visibility 0s;
}

.album-tracks__versions-inner {
  min-height: 0;
  overflow: hidden;
}

.album-tracks__versions {
  list-style: none;
  margin: 0 0 var(--space-sm) var(--versions-spur-x);
  padding: var(--space-xs) 0 0 var(--versions-elbow);
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity 250ms ease, transform 250ms ease;
}

.is-versions-open .album-tracks__versions {
  opacity: 1;
  transform: none;
}

/* Bracket. Each version draws its own stretch of the vertical spur and
   the elbow into its play icon; the last one stops at the elbow. */
.album-tracks__version {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
}

.album-tracks__version + .album-tracks__version {
  border-top: 1px solid color-mix(in srgb, var(--color-text, #222) 8%, transparent);
}

.album-tracks__version::before,
.album-tracks__version::after {
  content: '';
  position: absolute;
  left: calc(-1 * var(--versions-elbow));
  background-color: var(--versions-line);
  pointer-events: none;
}

.album-tracks__version::before {
  top: 0;
  bottom: 0;
  width: 1px;
}

.album-tracks__version:last-child::before {
  bottom: auto;
  height: calc(var(--space-sm) + 13px);
}

.album-tracks__version::after {
  top: calc(var(--space-sm) + 13px);
  /* Reach past the button's padding so the elbow meets the play icon. */
  width: calc(var(--versions-elbow) + var(--space-md));
  height: 1px;
}

/* The play icon pins to the top of the row so the elbow always meets it,
   however many lines the source name wraps to. */
/* Play icon, then the year and source name taking everything else.

   The row spans the full width of its list item, and everything else about the
   recording — Live/Studio, running time, copy, playlist, timeline — sits on a
   second line beneath. These panels appear both in an album reveal's tracklist
   and inside timeline music cards, and those cards run to about 230px at
   desktop widths. Sharing the first line left the source name some 39px there,
   narrow enough that a single word overran it and printed through the running
   time. Nothing now competes with the name for that line at any width. */
.album-track--version {
  grid-template-columns: auto 1fr;
  align-items: start;
  flex-basis: 100%;
}

/* Duration and status captions sit on the action line beneath the row (they
   are appended to the list item, not the button — see buildVersionItem), so
   the source name has the whole of the first line whatever width the
   surrounding card happens to be. */
.album-tracks__version > .album-track__duration,
.album-tracks__version > .album-track__coming-soon,
.album-tracks__version > .album-track__lost,
.album-tracks__version > .album-track__unobtainable {
  flex: 0 0 auto;
  align-self: center;
  margin: 0 var(--space-sm) var(--space-xs) 0;
  padding-top: 0;
}

/* Year · source · badge flow as one line of text and wrap like prose, so a
   long session title costs a second line rather than a third and fourth. */
.album-track__version-meta {
  display: block;
  min-width: 0;
  padding-top: var(--space-xs);
  font-size: var(--text-sm);
  line-height: 1.4;
}

.album-track__version-year {
  font-family: var(--font-heading);
  font-size: 0.85rem;
  letter-spacing: 0.1em;
  opacity: 0.8;
}

.album-track__version-source {
  font-family: var(--font-body);
}

.album-track__version-source::before {
  content: '·';
  margin: 0 var(--space-sm);
  opacity: 0.45;
}

.album-track--version[aria-pressed="true"] .album-track__version-source {
  color: var(--color-accent);
  font-weight: 700;
}

.album-track__badge {
  display: inline-block;
  margin-left: var(--space-sm);
  vertical-align: 0.1em;
  padding: 0.05em 0.5em;
  border: 1px solid currentColor;
  border-radius: 999px;
  font-family: var(--font-heading);
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  line-height: 1.5;
}

.album-track__badge--live {
  color: var(--color-accent);
}

.album-track__badge--studio {
  color: inherit;
  opacity: 0.6;
}

/* Sits on its own line beneath the row, indented to the meta text, so the
   row itself keeps its width for the source name. */
/* The badge leads the row of actions beneath each version. Only a small
   indent: aligning it under the source name would eat 58px of a line that has
   to seat the badge, copy, playlist and timeline link inside roughly 270px. */
.album-tracks__version > .album-track__badge {
  flex: 0 0 auto;
  align-self: center;
  margin: 0 var(--space-sm) var(--space-xs) var(--space-md);
}

.album-tracks__version-link {
  /* Shares the action line rather than taking a third line of its own, and is
     pushed to the far end of it so the row reads: what kind of recording,
     what you can do with it, then where it lives. */
  flex-basis: auto;
  align-self: center;
  margin: 0 0 var(--space-xs) auto;
  padding: var(--space-xs) 0;
  color: var(--color-accent);
  font-family: var(--font-body);
  font-size: 0.74rem;
  letter-spacing: 0.03em;
  text-decoration: none;
  white-space: nowrap;
  opacity: 0.65;
  transition: opacity var(--transition-fast);
}

.album-tracks__version-link:hover,
.album-tracks__version-link:focus-visible {
  opacity: 1;
  text-decoration: underline;
  text-underline-offset: 0.2em;
  outline: none;
}

@media (prefers-reduced-motion: reduce) {
  .album-tracks__versions-wrap,
  .is-versions-open > .album-tracks__versions-wrap,
  .album-tracks__versions,
  .album-tracks__versions-icon {
    transition: none;
  }
}

/* ── YouTube source notice (inline footnote) ──────────────── */

.youtube-notice {
  /* Inline-flow text so the trailing icon sits naturally next to the
     last word and wraps with it as the column narrows. */
  display: block;
  margin: 0 0 var(--space-sm);
  padding: 0 var(--space-md);
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-style: italic;
  line-height: 1.5;
  color: var(--color-text);
  opacity: 0.62;
}

.youtube-notice__icon {
  display: inline-flex;
  align-items: center;
  vertical-align: -0.18em;
  margin-left: 0.25em;
  color: var(--color-accent);
  opacity: 0.85;
}

.youtube-notice__icon .copy-link-icon {
  width: 0.95em;
  height: 0.95em;
}

/* ── Per-track "copy YouTube link" button ────────────────── */

.copy-link-icon {
  width: 16px;
  height: 16px;
  display: block;
}

.track-copy-link {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  margin: 0;
  padding: 0 var(--space-sm);
  background: transparent;
  border: 0;
  border-radius: var(--border-radius, 0);
  color: var(--color-accent);
  cursor: pointer;
  opacity: 0.55;
  transition: opacity var(--transition-fast),
              background-color var(--transition-fast),
              color var(--transition-fast),
              transform var(--transition-fast);
}

.track-copy-link:hover:not(:disabled),
.track-copy-link:focus-visible:not(:disabled) {
  opacity: 1;
  background-color: var(--color-bg-alt);
  outline: none;
}

.track-copy-link:focus-visible {
  box-shadow: inset 0 0 0 2px var(--color-accent);
}

.track-copy-link:disabled {
  cursor: default;
  opacity: 0.2;
}

.track-copy-link--copied {
  opacity: 1;
  color: var(--color-bg);
  background-color: var(--color-accent);
}

.track-copy-link--copied .copy-link-icon {
  animation: copy-link-pop 280ms ease-out;
}

.track-copy-link--failed {
  opacity: 1;
  color: var(--color-bg);
  background-color: #8b2015;
}

@keyframes copy-link-pop {
  0%   { transform: scale(0.6); opacity: 0; }
  60%  { transform: scale(1.15); opacity: 1; }
  100% { transform: scale(1);    opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .track-copy-link,
  .track-copy-link--copied .copy-link-icon { transition: none; animation: none; }
}

/* Entry animation — fade hero in when section enters viewport. */
.album-reveal--animated .album-reveal__eyebrow,
.album-reveal--animated .album-reveal__title,
.album-reveal--animated .album-reveal__year,
.album-reveal--animated .album-reveal__description,
.album-reveal--animated .album-reveal__body {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.7s ease, transform 0.7s ease;
}

.album-reveal--animated .album-reveal__title { transition-delay: 0.15s; }
.album-reveal--animated .album-reveal__year  { transition-delay: 0.3s; }
.album-reveal--animated .album-reveal__description { transition-delay: 0.45s; }
.album-reveal--animated .album-reveal__body  { transition-delay: 0.55s; transform: translateY(24px); }

.album-reveal.is-visible .album-reveal__eyebrow,
.album-reveal.is-visible .album-reveal__title,
.album-reveal.is-visible .album-reveal__year,
.album-reveal.is-visible .album-reveal__description,
.album-reveal.is-visible .album-reveal__body {
  opacity: 1;
  transform: none;
}

.album-reveal.is-visible .album-reveal__description { opacity: 0.75; }

/* ── Site footer ─────────────────────────────────────────── */

.site-footer {
  padding: var(--space-xl) var(--space-lg);
  text-align: center;
  font-size: var(--text-sm);
  opacity: 0.6;
  border-top: 1px solid var(--color-accent, #c0392b);
}

/* ── Noscript ─────────────────────────────────────────────── */

.noscript-notice {
  padding: var(--space-lg);
  background: #fff3cd;
  color: #856404;
  text-align: center;
  border-bottom: 2px solid #ffc107;
}

/* ============================================================
   RESPONSIVE
   ============================================================ */

@media (max-width: 480px) {
  :root {
    --text-4xl: 2.5rem;
    --text-3xl: 2rem;
    --timeline-marker-size: 56px;
    --timeline-spine-offset: 24px;
  }

  /* Stay a single row on small screens so the burger keeps its place in
     the top-right corner. The logo wrap shrinks to the logo's own
     max-width and the header padding tightens to buy back the room. */
  .site-header {
    gap: var(--space-sm);
    padding: var(--space-sm) var(--space-md);
  }

  .site-header__logo-wrap { width: 140px; }

  .era-section { padding: var(--space-lg) var(--space-md) var(--space-2xl); }

  .timeline__lead-title { font-size: var(--text-base); }
  .timeline__card { padding: var(--space-md); }
  .gallery { margin-left: calc(var(--space-md) * -1); margin-right: calc(var(--space-md) * -1); padding: 0 var(--space-md); }
  .gallery__item { flex-basis: 180px; }

  /* Small phones: the album panes stack, so the nested "other
     performances" list gets a tighter indent to keep room for the rows. */
  .album-tracks {
    --versions-spur-x: var(--space-sm);
    --versions-elbow: 8px;
    --versions-indent: calc(var(--versions-spur-x) + var(--versions-elbow));
  }

}

/* All timeline cards — break out to full section width on mobile.
   The timeline node's left padding (spine-offset + marker/2 + space-lg)
   indents the card away from the left edge; a matching negative
   margin-left pulls it back, and width:auto then fills the full
   parent padding-box so every card type has maximum room. */
@media (max-width: 899px) {
  .timeline__card {
    margin-left: calc(-1 * (var(--timeline-spine-offset) + var(--space-lg) + var(--timeline-marker-size) / 2));
  }

  /* Tighter track-row gaps so title + action buttons all fit. */
  .album-track {
    gap: var(--space-sm);
    padding: var(--space-sm) var(--space-md);
  }

  /* The tighter gap moves the play icon left; keep the bracket under it. */
  .album-tracks {
    --versions-spur-x: calc(2.75rem + 13px);
    --versions-indent: calc(var(--versions-spur-x) + 13px + var(--space-sm));
    --versions-link-indent: calc(var(--space-md) + 26px + var(--space-sm));
  }
}

@media (min-width: 900px) {
  :root {
    --timeline-spine-offset: 50%;
    --timeline-marker-size: 76px;
    /* Two columns of nodes carry the page length, so the rows can sit
       tighter together than they do in the single mobile column. */
    --timeline-row-gap: 24px;
  }

  /* ── Packed two-column layout ──────────────────────────────
     js/timeline-layout.js takes the nodes out of flow and writes each
     one's --node-top (plus the list's --timeline-height), so a tall
     expanded card no longer pushes the node opposite it down the page.
     Without JS the class is never added and the nodes stay in ordinary
     flow, one full-width row each. */
  .timeline--columns {
    height: var(--timeline-height, auto);
  }

  .timeline--columns > .timeline__node {
    position: absolute;
    top: var(--node-top, 0px);
    margin: 0;
  }

  .timeline--columns > .timeline__node[data-side="left"] {
    left: 0;
    right: auto;
  }

  .timeline--columns > .timeline__node[data-side="right"] {
    left: auto;
    right: 0;
  }

  .timeline--columns > .timeline__node--full {
    left: 0;
    right: auto;
  }

  /* Spine sits at the visual centre */
  .timeline__spine {
    left: 50%;
    transform: translateX(-50%) scaleY(var(--timeline-draw, 1));
    transform-origin: center top;
  }

  /* Each node occupies one half of the row, alternating */
  .timeline__node {
    width: calc(50% - var(--timeline-marker-size) / 2 - var(--space-lg));
    padding-left: 0;
  }

  .timeline__node[data-side="left"] {
    margin-right: auto;
    padding-right: var(--space-lg);
    text-align: right;
  }

  .timeline__node[data-side="right"] {
    margin-left: auto;
    padding-left: var(--space-lg);
    text-align: left;
  }

  /* Card text always reads naturally left-to-right, even on left-side nodes */
  .timeline__node[data-side="left"] .timeline__card {
    text-align: left;
  }

  /* Full-width nodes span both halves of the timeline */
  .timeline__node--full {
    width: 100%;
    margin-left: 0;
    margin-right: 0;
    padding-left: 0;
    padding-right: 0;
    text-align: left;
  }

  .timeline__node--full .timeline__marker {
    left: calc(50% - var(--timeline-marker-size) / 2);
    right: auto;
  }

  /* Offset the toggle lead text to start after the centred marker */
  .timeline__node--full .timeline__toggle {
    padding-left: calc(50% + var(--timeline-marker-size) / 2 + var(--space-md));
  }

  .timeline__node--full .timeline__card {
    text-align: left;
    width: fit-content;
    max-width: 100%;
    margin-left: auto;
    margin-right: auto;
  }

  .timeline__node[data-side="left"] .timeline__lead { align-items: flex-end; }

  /* On left-side rows the +/− caret naturally sits on the right of its row,
     i.e. nearest the spine, which is the correct visual home. */

  /* Marker sits on the spine — same x for both sides */
  .timeline__marker {
    left: auto;
    right: auto;
  }

  /* Centre the marker on the 50% spine. The node's edge nearest the
     spine sits marker/2 + space-lg short of the spine centre, so the
     marker has to overhang by another marker/2 to be centred — total
     offset = marker + space-lg. */
  .timeline__node[data-side="left"] .timeline__marker {
    right: calc(-1 * (var(--timeline-marker-size) + var(--space-lg)));
    left: auto;
  }

  .timeline__node[data-side="right"] .timeline__marker {
    left: calc(-1 * (var(--timeline-marker-size) + var(--space-lg)));
    right: auto;
  }

  /* Card alignment when the node is on the left */
  .timeline__node[data-side="left"] .card-body__name-change-names,
  .timeline__node[data-side="left"] .card-body { align-items: flex-end; }
  .timeline__node[data-side="left"] .card-body__quote-text {
    border-left: 0;
    border-right: 3px solid var(--color-accent);
    padding-left: 0;
    padding-right: var(--space-md);
  }
  .timeline__node[data-side="left"] .card-body__quote-attribution {
    padding-left: 0;
    padding-right: calc(var(--space-md) + 3px);
  }

  .timeline__lead { flex: 1 1 auto; }

  .timeline__lead-title { font-size: var(--text-xl); }
}

@media (min-width: 1200px) {
  /* Wider screens get more horizontal breathing room around the section,
     but keep the tight vertical rhythm so the timeline doesn't drift. */
  .era-section {
    padding-left:  calc(var(--space-2xl) * 1.5);
    padding-right: calc(var(--space-2xl) * 1.5);
  }
}

/* ── Newspaper clipping cards ────────────────────────────────
   Applied when an event has clipping:true in JSON. The card
   stops blending into the era theme and instead renders as a
   pasted-in cutting from a paper, with a dedicated palette,
   serif typography, paper grain, and a soft fold line. Four
   variants — broadsheet / pink / yellowed / newsprint — are
   selected via clipping_style or a deterministic hash. */

.timeline__card--clipping {
  --clip-paper:        #f4ecd8;
  --clip-paper-edge:   #d8cba6;
  --clip-ink:          #1a1612;
  --clip-ink-soft:     #5a4a36;
  --clip-rule:         rgba(26, 22, 18, 0.35);
  --clip-rotation:     0deg;
  --clip-fold-angle:   90deg;
  --clip-fold-pos:     50%;
  --clip-fold-strength: 0.06;

  background-color: var(--clip-paper);
  background-image:
    /* faint vertical fold */
    linear-gradient(
      var(--clip-fold-angle),
      transparent calc(var(--clip-fold-pos) - 1px),
      rgba(0, 0, 0, var(--clip-fold-strength)) var(--clip-fold-pos),
      transparent calc(var(--clip-fold-pos) + 1px)
    ),
    /* paper grain — a tight repeating noise-like gradient */
    repeating-linear-gradient(
      0deg,
      rgba(0, 0, 0, 0.018) 0 1px,
      transparent 1px 3px
    ),
    repeating-linear-gradient(
      90deg,
      rgba(0, 0, 0, 0.014) 0 1px,
      transparent 1px 4px
    ),
    /* darker edges — gives the cutting a shadowed border */
    radial-gradient(
      ellipse at center,
      transparent 55%,
      var(--clip-paper-edge) 100%
    );
  background-blend-mode: normal, multiply, multiply, multiply;

  color: var(--clip-ink);
  /* Keep the standard card frame (rounded corners + accent left stripe)
     — only the background, colour, and shadow are overridden. */
  border-color: var(--color-accent);
  box-shadow:
    0 1px 0 rgba(0, 0, 0, 0.08) inset,
    0 14px 28px rgba(0, 0, 0, 0.18),
    0 4px 8px rgba(0, 0, 0, 0.12);
  transform: rotate(var(--clip-rotation));
  font-family: Georgia, 'Times New Roman', 'Playfair Display', serif;
  padding: var(--space-lg) var(--space-lg) var(--space-md);
}

/* Headline rule above the body to evoke a column break */
.timeline__card--clipping::before {
  content: '';
  display: block;
  height: 0;
  border-top: 1px solid var(--clip-rule);
  border-bottom: 3px double var(--clip-rule);
  margin: 0 0 var(--space-sm);
}

/* All inner text takes the clipping ink colour, not the era theme */
.timeline__card--clipping .card-body,
.timeline__card--clipping .card-body__text,
.timeline__card--clipping .card-body__byline,
.timeline__card--clipping .card-body__copyright,
.timeline__card--clipping .card-body__rating,
.timeline__card--clipping .card-body__quote-text,
.timeline__card--clipping .card-body__quote-attribution,
.timeline__card--clipping .card-body__name-from,
.timeline__card--clipping .card-body__name-to {
  color: var(--clip-ink);
}

.timeline__card--clipping .card-body__text {
  font-family: Georgia, 'Times New Roman', serif;
  font-size: 1.02rem;
  line-height: 1.55;
  text-align: justify;
  hyphens: auto;
}

.timeline__card--clipping .card-body__text::first-letter {
  font-family: 'Playfair Display', Georgia, serif;
  font-weight: 700;
  font-size: 2.6em;
  float: left;
  line-height: 0.9;
  padding: 0.05em 0.08em 0 0;
  color: var(--clip-ink);
}

.timeline__card--clipping .card-body__meta {
  font-family: 'Playfair Display', Georgia, serif;
  font-variant: small-caps;
  letter-spacing: 0.12em;
  font-size: 0.78rem;
  color: var(--clip-ink-soft);
  opacity: 1;
  border-bottom: 1px solid var(--clip-rule);
  padding-bottom: var(--space-xs);
  margin-bottom: var(--space-xs);
}

.timeline__card--clipping .card-body__rating {
  font-family: 'Playfair Display', Georgia, serif;
  color: var(--clip-ink);
}

.timeline__card--clipping .card-body__byline {
  font-style: italic;
  color: var(--clip-ink-soft);
  opacity: 1;
}

.timeline__card--clipping .card-body__copyright {
  font-family: Georgia, 'Times New Roman', serif;
  color: var(--clip-ink-soft);
  border-top: 1px solid var(--clip-rule);
  padding-top: var(--space-xs);
  opacity: 1;
}

.timeline__card--clipping .card-body__quote-text {
  border-left-color: var(--clip-rule);
  font-family: 'Playfair Display', Georgia, serif;
}

/* Captions and figures inside a clipping pick up the same paper feel */
.timeline__card--clipping .media-figure figcaption,
.timeline__card--clipping .gallery__caption {
  font-family: Georgia, 'Times New Roman', serif;
  color: var(--clip-ink-soft);
  font-style: italic;
}

/* ── Variant: broadsheet (default cream paper) ──────────── */
.timeline__card--clipping-broadsheet {
  --clip-paper:      #f4ecd8;
  --clip-paper-edge: #d8c89a;
  --clip-ink:        #15110b;
  --clip-ink-soft:   #4a3a24;
  --clip-rule:       rgba(21, 17, 11, 0.35);
  --clip-rotation:   0deg;
  --clip-fold-angle: 90deg;
  --clip-fold-pos:   50%;
}

/* ── Variant: pink (sporting / racing paper) ─────────────── */
.timeline__card--clipping-pink {
  --clip-paper:      #f6cfc7;
  --clip-paper-edge: #d99c91;
  --clip-ink:        #2c0d0a;
  --clip-ink-soft:   #6b2820;
  --clip-rule:       rgba(44, 13, 10, 0.4);
  --clip-rotation:   0deg;
  --clip-fold-angle: 0deg;
  --clip-fold-pos:   55%;
  --clip-fold-strength: 0.08;
}

/* ── Variant: yellowed (aged, sun-faded) ─────────────────── */
.timeline__card--clipping-yellowed {
  --clip-paper:      #ecdda0;
  --clip-paper-edge: #b89a4a;
  --clip-ink:        #2a1c08;
  --clip-ink-soft:   #6b5018;
  --clip-rule:       rgba(42, 28, 8, 0.45);
  --clip-rotation:   0deg;
  --clip-fold-angle: 78deg;
  --clip-fold-pos:   42%;
  --clip-fold-strength: 0.09;
}

/* Yellowed gets a slightly stronger browning vignette via an
   extra inner shadow rather than another background layer. */
.timeline__card--clipping-yellowed {
  box-shadow:
    inset 0 0 60px rgba(120, 70, 10, 0.18),
    0 14px 28px rgba(0, 0, 0, 0.22),
    0 4px 8px rgba(0, 0, 0, 0.14);
}

/* ── Variant: newsprint (cheap grey pulp) ────────────────── */
.timeline__card--clipping-newsprint {
  --clip-paper:      #d8d3c4;
  --clip-paper-edge: #a8a193;
  --clip-ink:        #0e0e0e;
  --clip-ink-soft:   #3a3a3a;
  --clip-rule:       rgba(14, 14, 14, 0.5);
  --clip-rotation:   0deg;
  --clip-fold-angle: 90deg;
  --clip-fold-pos:   38%;
  --clip-fold-strength: 0.1;
}

/* Reduced-motion: drop the slight rotation so the page doesn't
   feel jittery for users who've asked for less movement. */
@media (prefers-reduced-motion: reduce) {
  .timeline__card--clipping { transform: none; }
}

/* On narrow screens, ease back the rotation so cards don't poke
   into the timeline spine or over the screen edge. */
@media (max-width: 600px) {
  .timeline__card--clipping {
    --clip-rotation: 0deg;
    padding: var(--space-md);
  }
  .timeline__card--clipping .card-body__text { text-align: left; }
  .timeline__card--clipping .card-body__text::first-letter {
    font-size: 2.1em;
  }
}

/* ── Story chapters ───────────────────────────────────────

   Stories in the band's own voice (any story without clipping:true) are the
   biography the archive hangs on, so they leave the card format entirely.
   Each one runs across the whole timeline as a page of prose: a rule with the
   marker sitting on it, a centred chapter heading, a drop cap, and a fleuron
   on the closing rule where the spine picks up again.

   The node's solid era background covers the spine, so the line visibly
   stops for the chapter and resumes below it — the same way framed cards
   interrupt it. Unlike clippings, chapters take every colour and font from
   the era theme, so the biography changes voice as the band does. */

.timeline__node--chapter {
  --chapter-measure: 62ch;
  --chapter-rule: color-mix(in srgb, var(--color-accent) 55%, transparent);
  margin: calc(var(--timeline-row-gap) * 2) 0;
  padding: calc(var(--timeline-marker-size) + var(--space-md)) var(--space-sm) calc(var(--space-xl) + 1.5em);
  background-color: var(--color-bg);
  text-align: center;
}

/* Opening rule, drawn through the middle of the marker */
.timeline__node--chapter::before {
  content: '';
  position: absolute;
  top: calc(var(--timeline-marker-size) / 2);
  left: 0;
  right: 0;
  height: 1px;
  background-color: var(--chapter-rule);
}

/* Closing rule with a fleuron set into it */
.timeline__node--chapter::after {
  content: '❦';
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  font-size: 1.25rem;
  line-height: 1.5em;
  text-align: center;
  color: var(--color-accent);
  background:
    linear-gradient(var(--chapter-rule), var(--chapter-rule)) left center / calc(50% - 1.25em) 1px no-repeat,
    linear-gradient(var(--chapter-rule), var(--chapter-rule)) right center / calc(50% - 1.25em) 1px no-repeat,
    linear-gradient(var(--color-bg), var(--color-bg)) center / 2.5em 100% no-repeat;
}

/* The fleuron closes biography chapters only, not featured recordings. And
   back to back, the next chapter's opening rule already marks the break, so
   a closing rule here would draw a second line just above it. */
.timeline__node--featured::after,
.timeline__node--chapter:has(+ .timeline__node--chapter)::after {
  content: none;
}

/* A filled marker sets chapters apart when scanning down the spine */
.timeline__node--chapter .timeline__marker {
  transform: none;
  background-color: var(--color-accent);
  color: var(--color-bg);
}

/* Heading: centred, stacked, no +/− caret — "Read on" does that job */
.timeline__node--chapter .timeline__toggle {
  flex-direction: column;
  padding: 0;
}

/* [data-collapsed] matches the weight of the open-state "−" rule */
.timeline__node--chapter[data-collapsed] > .timeline__toggle::after { content: none; }

.timeline__node--chapter .timeline__lead {
  align-items: center;
  text-align: center;
  gap: var(--space-xs);
}

.timeline__node--chapter .timeline__type::before,
.timeline__node--chapter .timeline__type::after {
  content: '—';
  margin: 0 0.6em;
  opacity: 0.6;
}

.timeline__node--chapter .timeline__lead-title {
  font-size: clamp(1.6rem, 5vw, 2.5rem);
  line-height: 1.15;
  max-width: 24ch;
  text-wrap: balance;
}

.timeline__node--chapter .timeline__lead-date {
  font-style: italic;
  font-size: 0.9rem;
}

/* Shared prose setting for the opening and the full text */
.timeline__chapter-opening,
.timeline__node--chapter .timeline__card--chapter {
  max-width: var(--chapter-measure);
  margin: var(--space-lg) auto 0;
  text-align: left;
}

.timeline__chapter-opening-text,
.timeline__card--chapter .card-body__text {
  font-size: 1.0625rem;
  line-height: 1.8;
  max-width: none;
}

/* The teaser is a couple of lines under a centred heading, so it centres
   with it and reads a shade quieter than the story itself. */
.timeline__chapter-opening {
  max-width: 52ch;
  margin-top: var(--space-md);
  text-align: center;
}

.timeline__chapter-opening-text {
  font-style: italic;
  opacity: 0.85;
}

.timeline__card--chapter .card-body__text--rich p {
  margin-bottom: 1em;
}

/* Drop cap on the full text's first paragraph. The rule lands on the rich
   body's wrapping div, whose first formatted line is the first <p>'s. */
.timeline__card--chapter .card-body > .card-body__text::first-letter {
  float: left;
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: 3.4em;
  line-height: 0.85;
  margin: 0.06em 0.1em 0 0;
  color: var(--color-accent);
}

/* The teaser repeats the start of the card, so it gives way once the
   full text is open. */
.timeline__node--chapter[data-collapsed="false"] .timeline__chapter-opening {
  display: none;
}

.timeline__chapter-more,
.timeline__chapter-close {
  display: block;
  margin: var(--space-md) auto 0;
  padding: var(--space-xs) var(--space-sm);
  background: transparent;
  border: 0;
  border-radius: var(--border-radius);
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: 0.85rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--color-accent);
  cursor: pointer;
  transition: opacity var(--transition-fast);
}

.timeline__chapter-more::after  { content: ' →'; }
.timeline__chapter-close::after { content: ' ↑'; }

.timeline__chapter-more:hover,
.timeline__chapter-close:hover { opacity: 0.7; }

.timeline__chapter-more:focus-visible,
.timeline__chapter-close:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* The full text drops the card's frame — it is part of the page, not a box
   on it. Selector weight beats the mobile break-out margin and the desktop
   full-width fit-content rule. */
.timeline__node--chapter .timeline__card--chapter {
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  overflow: visible;
  width: auto;
}

.timeline__card--chapter .card-body__byline {
  text-align: right;
}

.timeline__card--chapter .timeline__chapter-close {
  margin-top: var(--space-lg);
}

@media (min-width: 768px) {
  .timeline__node--chapter {
    padding-left: var(--space-lg);
    padding-right: var(--space-lg);
  }

  .timeline__chapter-opening-text,
  .timeline__card--chapter .card-body__text {
    font-size: 1.125rem;
  }
}

/* Biography and featured-recording prose is set in Times New Roman rather
   than the era's body font, so long reading feels like a book. It is a
   system font, not a web font: the fallbacks are its metric-compatible
   stand-ins on Linux and Android. Times runs small for its size, so it is set
   up a step. The heading and drop cap keep the era's heading font. */
.timeline__node--chapter .timeline__lead-date,
.timeline__node--chapter .timeline__chapter-opening-text,
.timeline__node--chapter .card-body__text,
.timeline__node--chapter .card-body__byline {
  font-family: 'Times New Roman', Times, 'Liberation Serif', Tinos, serif;
}

.timeline__node--chapter .timeline__lead-date {
  font-size: 1rem;
}

.timeline__node--bio .timeline__chapter-opening-text,
.timeline__node--bio .card-body__text {
  font-size: 1.1875rem;
  line-height: 1.7;
}

@media (min-width: 768px) {
  .timeline__node--bio .timeline__chapter-opening-text,
  .timeline__node--bio .card-body__text {
    font-size: 1.25rem;
  }
}

/* ── Featured recordings ──────────────────────────────────

   A music item flagged featured:true whose writing reads as liner notes. It
   shares the chapter frame above (rules, centred heading, no box) but stays
   recognisably music: the ♪ marker, a "Recording" label, and an LP layout —
   the player as the sleeve, a record edging out from behind it, and the
   writing beside it as the notes.

   The sleeve is always shown, so the music plays without opening anything.
   Only the notes open and close: closed, the notes column holds a teaser;
   open, the full writing. Stacked on mobile, player first. */

.timeline__node--featured {
  --sleeve-peek: 14%;
}

.timeline__feature {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-xl);
  align-items: start;
  max-width: 1000px;
  margin: var(--space-lg) auto 0;
  text-align: left;
}

.featured-music__sleeve .music-player {
  margin-top: 0;
}

/* The record sits in the cover pane, behind the cover. The pane leaves room
   on the right for it to edge out, and the player pane is inset to match so
   a playing video is the same size as the cover was. */
.timeline__node--featured .album-reveal__pane--cover {
  position: relative;
  padding-right: var(--sleeve-peek);
}

.timeline__node--featured .album-reveal__pane--player {
  right: var(--sleeve-peek);
}

.timeline__node--featured .album-reveal__cover-primary {
  z-index: 1;
}

/* The record itself, drawn from gradients: a coloured label with a spindle
   hole, then grooves. */
.timeline__node--featured .album-reveal__pane--cover::before {
  content: '';
  position: absolute;
  top: 50%;
  right: 0;
  height: 92%;
  aspect-ratio: 1;
  border-radius: 50%;
  background:
    radial-gradient(circle,
      var(--color-bg) 0 2.5%,
      var(--color-accent) 3% 30%,
      transparent 31%),
    repeating-radial-gradient(circle,
      #161616 0 2px,
      #262626 2px 3px);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
  transform: translateY(-50%);
  z-index: 0;
}

@media (prefers-reduced-motion: no-preference) {
  /* The record slides out of the sleeve as the recording scrolls into view */
  .timeline__node--featured.is-visible .album-reveal__pane--cover::before {
    animation: record-slide-out 0.9s ease-out 0.3s backwards;
  }

  @keyframes record-slide-out {
    from { transform: translate(-45%, -50%); }
    to   { transform: translate(0, -50%); }
  }
}

.timeline__feature-notes {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.featured-music__notes-heading {
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: 0.8rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--color-accent);
  padding-bottom: var(--space-xs);
  border-bottom: 1px solid var(--chapter-rule);
}

/* "Recording coming soon" — why a closed recording has no player showing */
.timeline__feature-status {
  align-self: flex-start;
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: 0.8rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--color-accent);
  padding: 0.3em 0.8em;
  border: 1px dashed var(--color-accent);
  border-radius: var(--border-radius, 0);
}

/* Once open the placeholder player says the same thing */
.timeline__node--featured[data-collapsed="false"] .timeline__feature-status {
  display: none;
}

/* In the notes column the teaser and the full notes sit flush left with the
   heading, rather than centred and capped like a prose chapter's. */
.timeline__feature-notes .timeline__chapter-opening,
.timeline__node--featured .timeline__feature-notes .timeline__card--chapter {
  max-width: none;
  margin: 0;
  text-align: left;
}

.timeline__feature-notes .timeline__chapter-more,
.timeline__feature-notes .timeline__chapter-close {
  margin-left: 0;
  padding-left: 0;
}

.timeline__feature-notes .timeline__card--chapter .card-body {
  gap: var(--space-sm);
}

/* Sleeve notes are set a touch smaller and tighter than a chapter's prose,
   with no drop cap — they annotate the record rather than tell the story. */
.timeline__feature-notes .timeline__chapter-opening-text,
.timeline__card--chapter .featured-music__notes .card-body__text {
  font-size: 1.0625rem;
  line-height: 1.7;
}

/* No sleeve to sit beside: the notes alone, at a readable measure. */
.timeline__feature--notes-only {
  max-width: 680px;
}

@media (min-width: 900px) {
  .timeline__feature {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--space-2xl);
  }

  .timeline__feature--notes-only {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ── "Played here N other times" ──────────────────────────

   The band returned to the same rooms again and again, and each of those
   nights used to sit alone in the timeline with nothing marking it as a
   return. This opens the other appearances at the same venue from inside a
   gig or music card.

   Sized for the narrow case first. These cards sit in the packed two-column
   timeline and can be around 150px wide, so a row is a date, sometimes what
   the night was called, and a link — stacked, never competing for one line.
   The song version rows learned that the hard way. */

.venue-visits {
  margin-top: var(--space-md);
  padding-top: var(--space-sm);
  border-top: 1px solid color-mix(in srgb, var(--color-accent) 25%, transparent);
}

.venue-visits__toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  max-width: 100%;
  padding: var(--space-xs) var(--space-sm) var(--space-xs) 0;
  background: transparent;
  border: 0;
  color: var(--color-accent);
  font-family: var(--font-body);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-align: left;
  cursor: pointer;
  opacity: 0.85;
  transition: opacity var(--transition-fast);
}

.venue-visits__toggle:hover,
.venue-visits__toggle:focus-visible {
  opacity: 1;
}

.venue-visits__icon {
  display: inline-block;
  font-size: 0.7em;
  transition: transform var(--transition-fast);
}

.venue-visits.is-open .venue-visits__icon {
  transform: rotate(180deg);
}

/* Grid-row expand: the panel animates 0fr → 1fr and the inner clips, so the
   list slides rather than jumping. `visibility` keeps the closed rows out of
   the tab order — a link nobody can see should not be reachable by keyboard. */
.venue-visits__panel {
  display: grid;
  grid-template-rows: 0fr;
  visibility: hidden;
  /* Timing and the delayed `visibility` match the song versions panel: on the
     way out visibility waits for the slide to finish, on the way in it flips
     at once. Without the delay the rows vanish before the panel has closed. */
  transition: grid-template-rows 320ms ease, visibility 0s linear 320ms;
}

.venue-visits.is-open .venue-visits__panel {
  grid-template-rows: 1fr;
  visibility: visible;
  transition: grid-template-rows 320ms ease, visibility 0s;
}

.venue-visits__inner {
  overflow: hidden;
  min-height: 0;
}

.venue-visits__list {
  margin: var(--space-xs) 0 0;
  padding: 0 0 0 var(--space-md);
  list-style: none;
  /* The spur that ties the list back to the card it belongs to. */
  border-left: 1px solid color-mix(in srgb, var(--color-accent) 40%, transparent);
}

.venue-visits__item {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 var(--space-sm);
  padding: var(--space-xs) 0;
}

.venue-visits__item + .venue-visits__item {
  border-top: 1px solid color-mix(in srgb, var(--color-text, #222) 8%, transparent);
}

.venue-visits__date {
  font-family: var(--font-heading);
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  opacity: 0.85;
}

/* Takes the whole line: a title like "Anchor Inn — free Christmas concert"
   has nowhere to go beside a date in a 150px card. */
.venue-visits__title {
  flex-basis: 100%;
  font-family: var(--font-body);
  font-size: 0.78rem;
  line-height: 1.4;
  opacity: 0.75;
}

.venue-visits__link {
  margin-left: auto;
  padding: var(--space-xs) 0;
  color: var(--color-accent);
  font-family: var(--font-body);
  font-size: 0.74rem;
  letter-spacing: 0.03em;
  text-decoration: none;
  white-space: nowrap;
  opacity: 0.7;
  transition: opacity var(--transition-fast);
}

.venue-visits__link:hover,
.venue-visits__link:focus-visible {
  opacity: 1;
  text-decoration: underline;
}

.venue-visits__map-link {
  display: inline-block;
  margin: var(--space-sm) 0 0 var(--space-md);
  color: var(--color-accent);
  font-family: var(--font-body);
  font-size: 0.74rem;
  letter-spacing: 0.03em;
  text-decoration: none;
  opacity: 0.7;
  transition: opacity var(--transition-fast);
}

.venue-visits__map-link:hover,
.venue-visits__map-link:focus-visible {
  opacity: 1;
  text-decoration: underline;
}

@media (prefers-reduced-motion: reduce) {
  .venue-visits__panel,
  .venue-visits__icon {
    transition: none;
  }
}


/* ── Version rows in a narrow container ───────────────────

   Keyed on the width of the list itself, not the window. Those two do not
   track each other here: a version row is about 241px on a 375px phone but
   only 230px inside a timeline music card on a 1200px desktop, because the
   packed two-column timeline is narrower than a stacked phone layout. A
   viewport media query tightened the phone and left the desktop cards
   wrapping — twice today — so the query asks the question that actually
   matters: how much room does this row have?

   Below 300px the action line cannot hold badge, running time, copy, playlist
   and the timeline link at their full size, and the link drops to a line of
   its own. Everything here buys that line back. */

.album-tracks__versions {
  container-type: inline-size;
  container-name: versions;
}

@container versions (max-width: 300px) {
  .album-tracks__version > .album-track__badge {
    margin-left: 0;
    margin-right: var(--space-xs);
    /* The wide tracking that gives the badge its stamped look costs about 16px
       on "STUDIO" — two characters more than "LIVE", and the difference
       between the link fitting and not. Kept at roomier sizes. */
    padding-left: 0.35em;
    padding-right: 0.35em;
    letter-spacing: 0.08em;
  }

  .album-tracks__version > .album-track__duration,
  .album-tracks__version > .album-track__coming-soon,
  .album-tracks__version > .album-track__lost,
  .album-tracks__version > .album-track__unobtainable {
    margin-right: var(--space-xs);
  }

  /* The playlist button drops its "Playlist" label and keeps only the ＋,
     matching the copy button beside it, which has always been icon-only.
     That is the last ~35px, and it costs nothing: the button's aria-label and
     title both already read "Add to playlist", so the meaning survives for
     screen readers and on hover. The label is a bare text node rather than an
     element, hence zeroing the button's font size and restoring it on the
     glyph. */
  .album-tracks__version .playlist-add-btn {
    font-size: 0;
    gap: 0;
  }

  .album-tracks__version .playlist-add-btn > span {
    font-size: 0.65rem;
  }
}

/* Narrower still — the 165px lists inside a timeline music card on a mid-width
   desktop. Even fully tightened the line cannot seat the link, so rather than
   let it fall off the end it is given a line of its own, aligned with the
   controls above it. The drop then reads as the layout, not as a fault. */
@container versions (max-width: 210px) {
  .album-tracks__version-link {
    flex-basis: 100%;
    margin-left: 0;
    padding-top: 0;
  }
}

/* People chips in the timeline filter. The type chips carry a glyph that
   means something; a person's does not, so it is dimmed to a marker and the
   name does the work. */
.timeline-filter__chip--person .timeline-filter__chip-glyph {
  opacity: 0.5;
  font-size: 0.7em;
}

/* ── Filter list view ─────────────────────────────────────

   With a single event type selected that isn't gigs (only the biography,
   only the music), the timeline steps aside and the entries run on as one
   list: no intro, no era headings, no spine or markers, no packed columns
   (timeline-layout.js hands the nodes back to normal flow). The phase card
   for Before Swan Arcade goes too, and its contents are shown regardless of
   whether it was expanded. Each era still lends its colours and fonts to its
   own entries, so the list still changes voice as the band did.

   timeline-filter.js sets body.filter-list-view and shows the banner. */

.filter-list-banner {
  max-width: 760px;
  margin: 0 auto;
  padding: var(--space-xl) var(--space-md) var(--space-lg);
  text-align: center;
  background-color: var(--color-bg);
  color: var(--color-text);
}

.filter-list-banner[hidden] { display: none; }

.filter-list-banner__eyebrow {
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: 0.75rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--color-accent);
}

.filter-list-banner__title {
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: clamp(2rem, 7vw, var(--text-3xl));
  line-height: 1.1;
  margin: var(--space-xs) 0;
}

.filter-list-banner__count {
  font-style: italic;
  opacity: 0.8;
}

.filter-list-banner__reset {
  margin-top: var(--space-md);
  padding: 0.5em 1.2em;
  background: transparent;
  border: 1px solid var(--color-accent);
  border-radius: 999px;
  color: var(--color-accent);
  font-family: var(--font-heading, 'Bebas Neue', sans-serif);
  font-size: 0.8rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color var(--transition-fast), color var(--transition-fast);
}

.filter-list-banner__reset:hover,
.filter-list-banner__reset:focus-visible {
  background-color: var(--color-accent);
  color: var(--color-bg);
  outline: none;
}

.filter-list-view .intro-gallery,
.filter-list-view .intro-about,
.filter-list-view .phase-card,
.filter-list-view .era-section__header,
.filter-list-view .timeline__spine,
.filter-list-view .timeline__marker,
.filter-list-view .timeline__links {
  display: none;
}

/* The continuous spine is drawn by these sections' ::before */
.filter-list-view .era-section::before,
.filter-list-view .album-reveal::before,
.filter-list-view .phase-collapse::before {
  content: none;
}

/* Show the Before Swan Arcade entries whether or not it was expanded */
.filter-list-view .phase-collapse > .phase-collapse__content {
  display: block;
  animation: none;
}

.filter-list-view .phase-collapse {
  padding: 0;
}

.filter-list-view .era-section {
  padding-top: var(--space-xs);
  padding-bottom: var(--space-xs);
}

.filter-list-view .era-section--header-only {
  display: none;
}

/* One column, every entry reading left to right, no room kept for markers */
.filter-list-view .timeline__node {
  width: auto;
  max-width: 760px;
  margin: var(--space-lg) auto;
  padding-left: 0;
  padding-right: 0;
  text-align: left;
}

.filter-list-view .timeline__node .timeline__toggle {
  padding-left: 0;
}

/* Undo the mobile break-out margin; chapters centre their own text */
.filter-list-view .timeline__node:not(.timeline__node--chapter) .timeline__card {
  margin-left: 0;
}

/* Chapters keep their own frame; with no marker sitting on the opening rule,
   the rule moves to the top and the heading closes up under it. */
.filter-list-view .timeline__node--chapter {
  padding: var(--space-lg) var(--space-sm) calc(var(--space-xl) + 1.5em);
}

.filter-list-view .timeline__node--chapter::before {
  top: 0;
}

@media (min-width: 768px) {
  .filter-list-view .timeline__node--chapter {
    padding-left: var(--space-lg);
    padding-right: var(--space-lg);
  }
}

/* Featured recordings need the room for their sleeve and notes */
.filter-list-view .timeline__node--featured {
  max-width: none;
}
