/* ==========================================================================
   Echo Signal — site styles
   Single stylesheet, no build step, no external font or CSS dependencies.
   Sections: tokens, reset, splash, layout, nav, components, pages, utilities.
   ========================================================================== */

:root {
  /* Surface ramp — near-black with a cold violet cast rather than pure grey. */
  --bg:        #08080a;
  --bg-raised: #0e0e12;
  --bg-inset:  #131319;

  /* Ink ramp */
  /* Contrast ratios below are measured against --bg-raised (#0e0e12), the
     worst case, since cards sit on it. All clear WCAG AA (4.5:1) for normal
     text — --ink-faint is used at small sizes for badges and meta, so it
     cannot be dimmer than this. */
  --ink:       #e4e0da;  /* headings / primary   14.9:1 */
  --ink-body:  #b8b3bd;  /* body copy             9.4:1 */
  --ink-muted: #8d8798;  /* secondary meta        5.6:1 */
  --ink-faint: #7e788b;  /* badges, captions      4.5:1 */

  /* The accent is reserved for interactive state. */
  --accent:      #8fb6da;
  --accent-dim:  #5f7f9e;
  --accent-glow: rgba(143, 182, 218, 0.10);

  /* Brand marks — never tinted. */
  --logo: #f2f0ec;

  /* Lockup proportions. */
  --lockup-word: 0.30;   /* wordmark font-size ÷ mark size */
  --lockup-gap:  0.06;   /* space beneath the mark ÷ mark size */

  --line:        rgba(228, 224, 218, 0.10);
  --line-strong: rgba(228, 224, 218, 0.20);

  /* Type — system stacks only; zero webfont requests. */
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* Constants */
  --measure: 68ch;
  --wrap-max: 67.5rem;
  --section-y: clamp(3.5rem, 9vw, 7rem);
  --radius: 0.25rem;
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
}

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

html {
  font-size: 100%;
  background: var(--bg);
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink-body);
  font-family: var(--font);
  font-size: clamp(1.075rem, 1rem + 0.2vw, 1.1875rem);
  font-weight: 400;
  line-height: 1.75;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-wrap: break-word;
}

img, svg, video { max-width: 100%; height: auto; display: block; }
button, input, textarea, select { font: inherit; color: inherit; }

h1, h2, h3, h4 {
  color: var(--ink);
  font-weight: 500;
  line-height: 1.2;
  margin: 0 0 0.6em;
  text-wrap: balance;
}

p { margin: 0 0 1.2em; }
p:last-child { margin-bottom: 0; }

ul, ol { margin: 0 0 1.2em; padding-left: 1.2em; }

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 0.22em; }
a:hover { color: var(--ink); }

/* Focus: always visible, never removed. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 0.1875rem;
  border-radius: var(--radius);
}
:focus:not(:focus-visible) { outline: none; }

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

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

.skip-link {
  position: absolute;
  left: 0.5rem;
  top: -100%;
  z-index: 999;
  padding: 0.7rem 1.1rem;
  background: var(--bg-inset);
  color: var(--ink);
  border: 1px solid var(--accent);
  text-decoration: none;
}
.skip-link:focus { top: 0.5rem; }

.wrap {
  width: min(100% - 2.5rem, var(--wrap-max));
  margin-inline: auto;
}

.eyebrow {
  margin: 0 0 0.75rem;
  font-size: 0.95rem;
  font-weight: 500;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--accent-dim);
}

.note {
  font-size: 0.95rem;
  line-height: 1.7;
  color: var(--ink-muted);
  border-left: 2px solid var(--line-strong);
  padding-left: 1rem;
  margin: 1.75rem 0 0;
}

/* ==========================================================================
   Splash
   Hidden by default. Only shown when JS confirms it is safe and wanted, so
   no-JS and reduced-motion users get the normal site with zero flash.
   ========================================================================== */
#splash { display: none; }

.splash-on #splash {
  display: grid;
  place-items: center;
  position: fixed;
  inset: 0;
  z-index: 200;
  background: var(--bg);
  will-change: opacity;
  animation: splash-out 0.8s linear both;
}

.splash-on #splash::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--bg);
}

.splash-on #splash { --mark: 66vw; }

.splash-mark-wrap {
  position: relative;
  z-index: 1;
  animation: splash-mark-wrap .8s cubic-bezier(0.12, 0.0, 0.08, 1) both;
}

.splash-on #splash .splash-mark {
  width: var(--mark);
  height: auto;
  animation: splash-mark .8s cubic-bezier(0.12, 0.0, 0.08, 1) both;
}

/* Note: deliberately no `overflow: hidden` on body here. The splash is a
   fixed full-viewport layer, so it already covers the page; locking scroll
   would strand the user if the animation never fired. */
/* .splash-on .site-header,
.splash-on .site-main { animation: fade-up 0.6s var(--ease) 1.25s both; } */

@keyframes splash-mark {
  0% { opacity: 0; transform: scale(0.82); }
  20% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(2.2); }
}

@keyframes splash-mark-wrap {
  0% { transform: scale(0.82); filter: none; }
  20% { transform: scale(1); filter: none; }
  100% { transform: scale(2.2); filter: none; }
}
@keyframes splash-out {
  0% { opacity: 1; visibility: visible; }
  50% { opacity: 1; visibility: visible; }
  72% { opacity: 0; visibility: visible; }
  100% { opacity: 0; visibility: hidden; }
}

@keyframes splash-overlay-fade {
  0% { opacity: 1; }
  50% { opacity: 1; }
  100% { opacity: 0; }
}

@keyframes splash-mark-wrap {
  0% { transform: scale(0.82); filter: none; }
  50% { transform: scale(1); filter: none; }
  100% { transform: scale(3); filter: none; }
}

@keyframes splash-mark {
  0% { opacity: 0; transform: scale(0.82); }
  50% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; visibility: hidden; }
}
@keyframes fade-up {
  from { opacity: 0; transform: translateY(0.375rem); }
  to   { opacity: 1; transform: none; }
}

/* ==========================================================================
   Header / navigation
   No JS. On narrow screens the list scrolls horizontally instead of
   collapsing into a toggle, which keeps every link reachable by keyboard.
   ========================================================================== */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(8, 8, 10, 0.9);
  border-bottom: 1px solid var(--line);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding-block: 0.85rem;
}

.menu-toggle {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.hamburger {
  display: none;
  position: relative;
  width: 2.5rem;
  height: 2.5rem;
  cursor: pointer;
  flex-shrink: 0;
  z-index: 1;
  border: 1px solid transparent;
  border-bottom-color: transparent;
  border-radius: 0;
  transition: transform 0.25s var(--ease), background 0.25s var(--ease), border-color 0.25s var(--ease), border-radius 0.25s var(--ease);
}

.hamburger span {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 1.5rem;
  height: 2px;
  background: currentColor;
  border-radius: 999px;
  transform: translate(-50%, -50%);
  transition: transform 0.25s var(--ease), opacity 0.2s var(--ease);
}
.hamburger span:nth-child(1) { transform: translate(-50%, calc(-50% - 0.4rem)); }
.hamburger span:nth-child(3) { transform: translate(-50%, calc(-50% + 0.4rem)); }

/* Checked = open: bars become an X, and the button dips down and takes the
   panel's background/border, reading as a tab rather than a floating icon. */
.menu-toggle:checked ~ .hamburger {
  /* Header's padding-bottom + border-width — lands the tab flush on the
     panel with no seam. */
  transform: translateY(calc(0.85rem + 1px));
  background: rgba(14, 14, 18, 0.975); /* --bg-raised at 97.5%, matches .nav__list */
  border-color: var(--line-strong);
  border-bottom-color: transparent;
  border-radius: var(--radius) var(--radius) 0 0;
}
.menu-toggle:checked ~ .hamburger span:nth-child(1) {
  transform: translate(-50%, -50%) rotate(45deg);
}
.menu-toggle:checked ~ .hamburger span:nth-child(2) {
  opacity: 0;
}
.menu-toggle:checked ~ .hamburger span:nth-child(3) {
  transform: translate(-50%, -50%) rotate(-45deg);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  text-decoration: none;
  color: var(--ink);
  flex-shrink: 0;
}
.brand { --mark: clamp(2.5rem, 8vw - 1.5rem, 3rem); }
.brand__mark { width: var(--mark); height: var(--mark); flex-shrink: 0; }
.brand__word {
  font-size: calc(var(--mark) * 0.5);
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
}
.brand:hover { color: #fff; }

.nav__list {
  display: flex;
  align-items: center;
  gap: clamp(0.9rem, 2.4vw, 1.9rem);
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.nav__list::-webkit-scrollbar { display: none; }

.nav__list a {
  display: block;
  padding: 0.35rem 0;
  font-size: 0.95rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  color: var(--ink-muted);
  border-bottom: 1px solid transparent;
  transition: color 0.18s var(--ease), border-color 0.18s var(--ease);
}
.nav__list a:hover { color: var(--ink); }
.nav__list a[aria-current="page"] {
  color: var(--ink);
  border-bottom-color: var(--accent);
}

@media (max-width: 50rem) {
  .hamburger {
    display: inline-block;
  }

  /* Anchored to .site-header, not .site-header__inner (inset by .wrap), so
     the panel runs full width; .nav__list re-adds that inset as padding.
     grid-template-rows 0fr/1fr animates the collapse; visibility rides the
     same transition with a delay so it leaves the a11y tree only once shut. */
  .top-menu {
    position: absolute;
    top: calc(100% + 1px);
    left: 0;
    right: 0;
    display: grid;
    grid-template-rows: 0fr;
    visibility: hidden;
    overflow: hidden;
    transition: grid-template-rows 0.25s var(--ease), visibility 0s linear 0.25s;
  }

  .nav__list {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    margin: 0;
    padding: 0.5rem 1.25rem 0.75rem;
    overflow: hidden;
    min-height: 0;
    background: rgba(14, 14, 18, 0.975); /* --bg-raised at 97.5% */
    border-bottom: 1px solid var(--line);
  }

  .nav__list a {
    padding: 0.55rem 0;
    border-bottom: 0;
  }

  .menu-toggle:checked ~ .top-menu {
    grid-template-rows: 1fr;
    visibility: visible;
    transition: grid-template-rows 0.25s var(--ease);
  }
}

/* ==========================================================================
   Hero
   ========================================================================== */
.hero {
  position: relative;
  display: grid;
  place-items: center;
  text-align: center;
  padding-block: clamp(4rem, 14vh, 8rem);
  border-bottom: 1px solid var(--line);
  background:
    radial-gradient(ellipse 60% 50% at 50% 42%, var(--accent-glow), transparent 70%),
    var(--bg);
  overflow: hidden;
}

/* Capped against viewport width because the wordmark,
   which scales with the mark, is what would overflow first. */
.hero { --mark: min(34vw, 15rem); }

.hero__mark {
  width: var(--mark);
  height: auto;
  margin-inline: auto;
}

.hero__title {
  margin: calc(var(--mark) * var(--lockup-gap)) 0 0;
  font-size: calc(0.6 * var(--mark) * var(--lockup-word));
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--logo);
  line-height: 1.05;
}

.hero__tagline {
  margin: 1.5rem auto 0;
  max-width: 46ch;
  color: var(--ink-muted);
}

.hero__meta {
  margin: 1.1rem 0 0;
  font-size: 0.9rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* --- Home highlight -------------------------------------------------------
   An optional "coming up" or news item under the hero's buttons, generated
   from tools/highlight.yml. Kept deliberately quiet: small type, muted ink,
   no box around it -- it should read as a footnote to the hero, not compete
   with the logo for attention. */
.highlight {
  width: min(100%, 34rem);
  margin: 3.25rem auto 0;
  padding-top: 2.25rem;
  border-top: 1px solid var(--line);
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--ink-muted);
}
/* Paragraph margins are zeroed here and set per line below; the selectors
   below carry .highlight too so they outrank this one. */
.highlight p { margin: 0; }
.highlight .highlight__eyebrow {
  margin-bottom: 1rem;
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--accent-dim);
}
.highlight__title {
  margin: 1.1rem 0 0.35rem;
  font-size: 1rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--ink-body);
}
.highlight__address { display: block; color: var(--ink-faint); }
.highlight .highlight__bill { margin-top: 0.5rem; }
.highlight .highlight__note { margin-top: 0.5rem; color: var(--ink-faint); }
.highlight .highlight__facts {
  margin-top: 0.75rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.5rem 0.9rem;
  color: var(--ink-faint);
}
.highlight .show__ticket { margin-top: 0; }

/* Click-to-play video. A link to YouTube until pressed; site.js then swaps in
   the player, so nothing loads from YouTube before someone asks for it. */
.video {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background:
    radial-gradient(ellipse 70% 60% at 50% 45%, var(--accent-glow), transparent 75%),
    var(--bg-raised);
}
.video__link,
.video__frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
.video__link {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.8rem;
  text-decoration: none;
  color: var(--ink-muted);
}
.video__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.55;
  transition: opacity 0.18s var(--ease);
}
.video__play {
  position: relative;
  display: grid;
  place-items: center;
  width: 3.5rem;
  height: 3.5rem;
  border: 1px solid var(--accent-dim);
  border-radius: 50%;
  background: rgba(8, 8, 10, 0.6);
  color: var(--accent);
  transition: border-color 0.18s var(--ease), background 0.18s var(--ease),
              color 0.18s var(--ease);
}
/* The triangle's visual centre sits left of its box centre; nudge it right. */
.video__play svg { width: 1.4rem; height: 1.4rem; margin-left: 0.15rem; }
.video__label {
  position: relative;
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.video__link:hover .video__play,
.video__link:focus-visible .video__play {
  border-color: var(--accent);
  background: var(--accent-glow);
  color: var(--ink);
}
.video__link:hover .video__label { color: var(--ink); }
.video__link:hover .video__poster { opacity: 0.75; }

/* ==========================================================================
   Buttons / link-tree
   ========================================================================== */
.actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.75rem;
  margin-top: 2.4rem;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.75rem 1.6rem;
  font-size: 0.95rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color 0.18s var(--ease), background 0.18s var(--ease), color 0.18s var(--ease);
}
.btn:hover { border-color: var(--accent); background: var(--accent-glow); color: var(--ink); }
.btn--primary { background: var(--accent); border-color: var(--accent); color: #14101c; }
.btn--primary:hover { background: var(--ink); border-color: var(--ink); color: #14101c; }
.btn svg { width: 0.9375rem; height: 0.9375rem; flex-shrink: 0; }

/* Link-tree grid on the home page */
/* ==========================================================================
   Page shell / section headings
   ========================================================================== */
.site-main { display: block; }
.section { padding-block: var(--section-y); }
.section + .section { padding-top: 0; }

.page-head {
  padding-block: clamp(2.5rem, 7vw, 4.5rem) clamp(1.5rem, 4vw, 2.5rem);
  border-bottom: 1px solid var(--line);
}
.page-head h1 {
  font-size: clamp(1.75rem, 5.5vw, 2.75rem);
  font-weight: 300;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.page-head p {
  max-width: var(--measure);
  margin-bottom: 0;
  color: var(--ink-muted);
}

.section__title {
  font-size: clamp(1.05rem, 2.6vw, 1.3rem);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin-bottom: 1.6rem;
}

.prose { max-width: var(--measure); }
.prose p { color: var(--ink-body); }

/* ==========================================================================
   Members
   ========================================================================== */
.members {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 12.8125rem), 1fr));
  gap: 1px;
  margin: 0;
  padding: 0;
  list-style: none;
  background: var(--line);
  border: 1px solid var(--line);
}

.member {
  display: flex;
  flex-direction: column;
  background: var(--bg-raised);
  padding: 1.4rem 1.3rem;
}
.member__name { margin: 0 0 0.3rem; font-size: 0.9375rem; font-weight: 500; color: var(--ink); }
.member__role {
  margin: 0;
  font-size: 0.95rem;
  letter-spacing: 0.1em;
  color: var(--ink-muted);
}
/* .member--former .member__name { color: var(--ink-muted); } */
.member__tag {
  display: inline-block;
  align-self: flex-start;
  margin-top: 0.8rem;
  padding: 0.15rem 0.55rem;
  font-size: 0.95rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
  border: 1px solid var(--line-strong);
}

/* Influence tags */
.tags { display: flex; flex-wrap: wrap; gap: 0.45rem; margin: 0; padding: 0; list-style: none; }
.tags li {
  padding: 0.3rem 0.8rem;
  font-size: 0.95rem;
  color: var(--ink-muted);
  border: 1px solid var(--line);
  background: var(--bg-raised);
}

/* ==========================================================================
   Releases
   ========================================================================== */
.releases { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); }

.release {
  display: grid;
  grid-template-columns: 5.25rem 1fr auto;
  align-items: start;
  gap: 1.25rem;
  padding: 1.25rem;
  background: var(--bg-raised);
}
.release__art {
  width: 5.25rem; height: 5.25rem;
  object-fit: cover;
  background: var(--bg-inset);
  border: 1px solid var(--line);
}
.release__title { margin: 0 0 0.2rem; font-size: 1rem; font-weight: 500; }
.release__title a { color: var(--ink); text-decoration: none; }
.release__title a:hover { color: var(--accent); text-decoration: underline; }
.release__meta { margin: 0; font-size: 0.95rem; color: var(--ink-muted); }
.release__badge {
  font-size: 0.95rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
  border: 1px solid var(--line-strong);
  padding: 0.2rem 0.6rem;
  white-space: nowrap;
}

/* Track list inside a <details> so it costs nothing until opened. */
.release__tracks { margin-top: 0.9rem; }
.release__tracks summary {
  cursor: pointer;
  font-size: 0.95rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent-dim);
  padding: 0.2rem 0;
}
.release__tracks summary:hover { color: var(--accent); }
.release__tracks ol {
  margin: 0.75rem 0 0;
  padding-left: 1.4rem;
  font-size: 0.875rem;
  color: var(--ink-muted);
}
.release__tracks li { padding: 0.15rem 0; }
/* Track rows carry a duration when one is published; the name and time sit at
   opposite ends with the time tabular so the column stays aligned. */
.release__tracks li:has(.track-len) {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
}
.release__tracks .track-len {
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}

@media (max-width: 35rem) {
  .release { grid-template-columns: 4rem 1fr; }
  .release__art { width: 4rem; height: 4rem; }
  .release__badge { grid-column: 2; justify-self: start; }
}

/* ==========================================================================
   Shows
   ========================================================================== */
.shows { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); list-style: none; margin: 0; padding: 0; }

.show {
  /* Named so the narrow-screen date strip can bleed out by exactly this much
     and reach the card's edges. Change the padding here and the strip follows. */
  --card-pad-y: 1.35rem;
  --card-pad-x: 1.25rem;

  display: grid;
  grid-template-columns: 5.25rem 1fr auto;
  gap: 1.25rem;
  padding: var(--card-pad-y) var(--card-pad-x);
  background: var(--bg-raised);
}
/* Each show has an id (date-venue) so it can be linked to directly. The
   sticky header would cover a card scrolled to the very top, so leave room
   for it, and mark the linked card so it can be found on arrival. */
.show { scroll-margin-top: 5.5rem; }
.show:target { box-shadow: inset 2px 0 0 var(--accent); }
.show__date { text-align: center; }
.show__month {
  display: block;
  font-size: 0.95rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.show__day { display: block; font-size: 1.6rem; font-weight: 300; color: var(--ink); line-height: 1.1; }
.show__year { display: block; font-size: 0.6875rem; letter-spacing: 0.14em; color: var(--ink-faint); }
/* A grid item defaults to min-width:auto, so a long bill would push the column
   wider than its track instead of wrapping. */
.show__body { min-width: 0; }

.show__venue { margin: 0 0 0.2rem; font-size: 0.9375rem; font-weight: 500; color: var(--ink); }
.show__place { margin: 0; font-size: 0.95rem; color: var(--ink-muted); }

/* Event, bill and presenter are one thought, so they are set tight against
   each other and the whole group is separated from what surrounds it. Evenly
   spacing every line made the card read as an undifferentiated list. */
/* Flex `gap` rather than margins between the three lines: it is measured
   between boxes, so it stays identical whatever the inline content does.
   Margins here left the event sitting further from the bill than the bill
   from the presenter. */
.show__lineup {
  margin-top: 0.7rem;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.show__lineup > p {
  margin: 0;
  line-height: 1.45;
  /* Hanging indent: the first line sits flush and any wrapped line is stepped
     in, so a long bill reads as one entry rather than two. */
  padding-left: 1.1rem;
  text-indent: -1.1rem;
}

.show__event { font-size: 0.95rem; font-style: italic; color: var(--ink-body); }

/* Optical, not geometric. A linked event carries an underline sitting in the
   descender space, which visually closes the gap to the bill beneath it.
   Unlinked, the identical gap reads as bigger, so the bare name is nudged down
   a pixel to match. Targets the span — which is exactly the unlinked form —
   rather than testing for the absence of a link.
   `position: relative` moves the glyphs without touching the line box, so the
   spacing of everything around it is unchanged. */
.show__event > span.show__event-link {
  position: relative;
  top: 1px;
}
.show__bill { font-size: 0.95rem; color: var(--ink-muted); }
.show__promoter { font-size: 0.95rem; color: var(--ink-faint); }

/* One act — name, and hometown if there is one — kept on a single line so the
   bill breaks between acts, never through a name. */
.show__act { white-space: nowrap; }

/* A visiting band's hometown. */
.show__from { color: var(--ink-faint); }

.show__note {
  margin: 0.7rem 0 0;
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--ink-faint);
}

/* Links inside a note ([text](url) in shows.yml) match the card's other
   links -- muted ink, dim underline -- so a credit stays as quiet as the
   note around it. */
.show__note a,
.highlight__note a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--accent-dim);
  text-underline-offset: 0.2em;
}
.show__note a:hover,
.highlight__note a:hover { color: var(--accent); text-decoration-color: var(--accent); }

/* Teaser videos for the night — quiet, like the note, since most shows will
   never have one. */
.show__teasers {
  margin: 0.7rem 0 0;
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--ink-faint);
}
a.show__teaser {
  color: var(--ink-muted);
  text-decoration: underline;
  text-decoration-color: var(--accent-dim);
  text-underline-offset: 0.2em;
}
a.show__teaser:hover { color: var(--accent); text-decoration-color: var(--accent); }

/* Tickets. A link, not a button, but it has to read as pressable — so it gets
   the accent, a border, and a ticket glyph, while staying quiet enough not to
   fight the flyer for attention. */
.show__ticket {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.15rem;
  padding: 0.3rem 0.6rem;
  border: 1px solid var(--accent-dim);
  border-radius: var(--radius);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  color: var(--accent);
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease),
              color 0.18s var(--ease);
}
.show__ticket svg { width: 1.05em; height: 1.05em; flex-shrink: 0; }
.show__ticket:hover,
.show__ticket:focus-visible {
  background: var(--accent-glow);
  border-color: var(--accent);
  color: var(--ink);
}

/* The practical facts — age, times, cover — stacked together in their own
   column so they can be scanned without reading the card. */
.show__meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.35rem;
  text-align: right;
}
.show__time,
.show__cover {
  font-size: 0.8125rem;
  color: var(--ink-faint);
  white-space: nowrap;
}
.show__cover { color: var(--ink-muted); }

/* Below the card's three-column layout, the meta column would be a narrow
   right-hand strip, so it turns into a row under the body instead. */
@media (max-width: 35rem) {
  .show__meta {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: flex-start;
    gap: 0.5rem 0.9rem;
    text-align: left;
    margin-top: 0.9rem;
  }
}

/* Venue, band, promoter and event names. Each is a link when that entity has a
   URL recorded and a plain span when it doesn't, so both forms have to sit on
   the same baseline and weight — only the underline should differ. */
.show__venue-link,
.show__band,
.show__promoter-link,
.show__event-link { color: inherit; }

a.show__venue-link,
a.show__band,
a.show__promoter-link,
a.show__event-link {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--accent-dim);
  text-underline-offset: 0.2em;
}
a.show__venue-link:hover,
a.show__band:hover,
a.show__promoter-link:hover,
a.show__event-link:hover {
  color: var(--accent);
  text-decoration-color: var(--accent);
}
.show__badge {
  font-size: 0.95rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  border: 1px solid var(--line-strong);
  /* Trailing letter-spacing adds a space after the last glyph, so the right
     padding is reduced by that much to keep the text optically centred in the
     pill and the pill's right edge flush with the lines below it. */
  padding: 0.2rem 0.46rem 0.2rem 0.6rem;
  white-space: nowrap;
}
.show__flyer { margin-top: 0.9rem; }
.show__flyer img { width: 100%; max-width: 13.75rem; border: 1px solid var(--line); }

.empty {
  padding: 3rem 1.5rem;
  text-align: center;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  color: var(--ink-muted);
}
.empty strong { display: block; color: var(--ink); font-weight: 500; margin-bottom: 0.4rem; }

@media (max-width: 35rem) {
  .show { grid-template-columns: 4rem 1fr; }
  .show__meta { grid-column: 2; justify-self: start; }
}

/* Under 450px the date column costs more width than it earns, so the card
   collapses to one column and the content runs the full width beneath the
   date. The date stops stacking and reads across instead — a tall three-line
   block on its own full-width row would waste the space the change just
   recovered.
   `grid-column: auto` on the meta group matters: the rule above pins it to
   column 2, and in a one-column grid that would conjure an implicit second
   column and undo the whole thing. */
@media (max-width: 28.125rem) {
  .show {
    grid-template-columns: 1fr;
    gap: 0.6rem;
  }

  /* The date becomes the card's header strip: it bleeds back out through the
     card's padding to touch both edges, and takes the next surface up from the
     card's own, so each show reads as a titled block instead of a run of text. */
  .show__date {
    margin: calc(var(--card-pad-y) * -1) calc(var(--card-pad-x) * -1) 0;
    padding: 0.45rem var(--card-pad-x);
    background: var(--bg-inset);
    border-bottom: 1px solid var(--line);
    text-align: left;
  }
  .show__date time {
    display: flex;
    align-items: baseline;
    gap: 0.45rem;
  }
  .show__month,
  .show__day,
  .show__year { display: inline; }
  .show__day { font-size: 1.25rem; }
  /* The ink ramp is measured against --bg-raised. On the brighter strip
     --ink-faint falls to 4.36:1, under AA, so the year steps up one. */
  .show__year { color: var(--ink-muted); }

  .show__meta {
    grid-column: auto;
    justify-self: start;
  }
}

/* ==========================================================================
   Gallery
   ========================================================================== */
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 13.75rem), 1fr));
  gap: 1rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.gallery a {
  display: block;
  position: relative;
  aspect-ratio: 1;
  overflow: hidden;
  background: var(--bg-inset);
  border: 1px solid var(--line);
  transition: border-color 0.18s var(--ease);
}
.gallery a:hover { border-color: var(--accent-dim); }
.gallery img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 0.35s var(--ease), opacity 0.18s var(--ease);
}
.gallery a:hover img { transform: scale(1.03); }


/* --- Lightbox (native <dialog>) ----------------------------------------- */
#lightbox {
  padding: 0;
  border: 0;
  background: transparent;
  max-width: 100vw;
  max-height: 100vh;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
#lightbox::backdrop { background: rgba(4, 4, 6, 0.93); }

.lightbox__inner {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  padding: clamp(1rem, 5vw, 3rem);
}
#lightbox img {
  max-width: 100%;
  max-height: 74vh;
  object-fit: contain;
  border: 1px solid var(--line-strong);
}
/* Stack the image and its caption, centred as a pair. The caption keeps its
   own measure rather than inheriting the image's width, so it reads as a
   paragraph under a very narrow or a very wide photo alike.
   The token lives on .lightbox__inner because both the stack and the caption
   need to read it. */
.lightbox__inner {
  /* Target line length for the caption: roughly 60-70 characters of running
     prose. Not 65ch -- 1ch is the width of "0", which is wider than the
     average lowercase letter, so 65ch measures out at ~85 characters. 50ch
     was calibrated against real caption text in this typeface. */
  --caption-measure: 50ch;
}
.lightbox__inner > div {
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: 100%;
  /* Floor the stack at the caption's measure so a tall, narrow photo does not
     squeeze the caption into a column of two or three words. */
  min-width: min(var(--caption-measure), 100%);
}
/* Left-aligned text in a centred box. Being centred and looking centred are
   different things once the text wraps, so two things close the gap:
   1. `text-wrap: balance` evens out the line lengths, so a two-line caption
      is two similar lines rather than one full line and a short stub.
   2. The box is shrink-wrapped to its longest line. CSS cannot do this on its
      own -- a box whose text wraps stays as wide as the space it was given,
      which leaves all the ragged slack on the right and the block reading as
      off-centre. site.js measures the rendered lines and sets the width.
      Without JS there is no viewer, so nothing else depends on it. */
.lightbox__caption {
  width: fit-content;
  max-width: min(var(--caption-measure), 100%);
  margin: 1rem auto 0;
  text-align: left;
  text-wrap: balance;
  font-size: 0.95rem;
  color: var(--ink-muted);
}
.lightbox__close,
.lightbox__nav {
  position: absolute;
  z-index: 1;
  background: var(--bg-raised);
  border: 1px solid var(--line-strong);
  color: var(--ink);
  cursor: pointer;
  transition: border-color 0.18s var(--ease), background 0.18s var(--ease);
}
.lightbox__close:hover,
.lightbox__nav:hover { border-color: var(--accent); background: var(--bg-inset); }

.lightbox__close {
  top: 1rem; right: 1rem;
  padding: 0.6rem 1rem;
  font-size: 0.95rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

/* Step controls. Hidden by JS when there is only one image to look at. */
.lightbox__nav {
  top: 50%;
  transform: translateY(-50%);
  width: 2.75rem;
  height: 3.5rem;
  display: grid;
  place-items: center;
  font-size: 1.5rem;
  line-height: 1;
}
.lightbox__nav[hidden] { display: none; }
.lightbox__nav--prev { left: 1rem; }
.lightbox__nav--next { right: 1rem; }

/* On narrow screens the arrows would sit on top of the image, so drop them to
   the bottom corners where the caption area gives them room. */
@media (max-width: 37.5rem) {
  .lightbox__nav { top: auto; bottom: 1rem; transform: none; }
}

/* ==========================================================================
   Connect
   ========================================================================== */
/* Listen / Follow rows -- on Connect and on the home page, both built from
   tools/links.yml. One panel per group with no dividers inside it: each link
   is only as wide as its content, the panel keeps the same inset at both ends
   (--pad-x, identical for every group), and the space left over is shared
   equally between the links -- so the gaps between the actual content blocks
   are equal, rather than equal boxes leaving a short name with a wide void.
   Four in a row when they fit, otherwise two columns, otherwise one; never
   three, which would strand the fourth on a row by itself.
   The switch is a container query, not flex-wrap, because wrapping would
   happily produce that three-and-one. The thresholds come from measured
   content: four-up needs the wider group's four links (Listen, 44.14rem)
   plus three minimum gaps, the inset and the border; two-up needs the widest
   link (Bandcamp, 14.03rem) twice plus one gap. Measure each link in place
   (its width less 2 x --hit-x); a copy outside .connect loses the icon size
   and comes out ~2rem short. A longer handle means re-measuring -- a stale value shows as a handle wrapping. Queries can't
   read custom properties, so the numbers are written out. */
.connect-wrap { container-type: inline-size; }
.connect {
  --pad-x: 1.5rem;
  --pad-y: 1.25rem;
  /* Each link's hover area reaches this far past its content, and is pulled
     back by the same amount so the content itself sits on the inset. */
  --hit-x: 0.75rem;
  --hit-y: 0.6rem;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.1rem 2.5rem;
  margin: 0;
  padding: var(--pad-y) var(--pad-x);
  list-style: none;
  background: var(--bg-raised);
  border: 1px solid var(--line);
}
@container (min-width: 33.75rem) {   /* 2 x 14.03rem + 2.5rem gap + 3rem inset + border */
  .connect { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@container (min-width: 53.375rem) {  /* 44.14rem + 3 x 2rem min gap + 3rem inset + border */
  .connect {
    display: flex;
    justify-content: space-between;
    gap: 2rem;
  }
}
.connect a {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin: calc(var(--hit-y) * -1) calc(var(--hit-x) * -1);
  padding: var(--hit-y) var(--hit-x);
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--ink-body);
  transition: background 0.18s var(--ease), color 0.18s var(--ease);
}
.connect a:hover { background: var(--bg-inset); color: var(--ink); }
.connect svg { width: 1.25rem; height: 1.25rem; flex-shrink: 0; color: var(--accent); }
.connect__label { font-size: 0.95rem; font-weight: 500; letter-spacing: 0.1em; white-space: nowrap; }
.connect__handle { display: block; margin-top: 0.1rem; font-size: 0.75rem; color: var(--ink-faint); }

/* The line under the home page's Follow grid pointing at the Shows page. */
.connect__more {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 1.25rem 0 0;
  font-size: 0.875rem;
  color: var(--ink-muted);
}
.connect__more svg { width: 1.1rem; height: 1.1rem; flex-shrink: 0; color: var(--accent); }

/* ==========================================================================
   Footer
   ========================================================================== */
.site-footer {
  margin-top: var(--section-y);
  padding-block: 2.5rem;
  border-top: 1px solid var(--line);
  color: var(--ink-faint);
  font-size: 0.95rem;
}
.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
.site-footer a { color: var(--ink-muted); text-decoration: none; }
.site-footer a:hover { color: var(--ink); text-decoration: underline; }
.site-footer__links { display: flex; flex-wrap: wrap; gap: 1.2rem; margin: 0; padding: 0; list-style: none; }

/* --- Forced-colors / high-contrast -------------------------------------- */
@media (forced-colors: active) {
  .btn, .connect a, .member, .release, .show { border: 1px solid CanvasText; }
  .nav__list a[aria-current="page"] { border-bottom: 2px solid Highlight; }
}
