@font-face {
  font-family: "Gap Sans";
  src: url("fonts/gap-sans-black.woff2") format("woff2");
  font-weight: 900;
  font-display: block; /* the name is the whole page: never flash a fallback */
}
@font-face {
  font-family: "Office Code Pro";
  src: url("fonts/office-code-pro-regular.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Office Code Pro";
  src: url("fonts/office-code-pro-medium.woff2") format("woff2");
  font-weight: 500;
  font-display: swap;
}

:root {
  --bg: #0b0b0a;
  --fg: #f1efe8;
  --dim: rgba(241, 239, 232, 0.38);
  --mono: "Office Code Pro", ui-monospace, Menlo, monospace;
  --display: "Gap Sans", "Helvetica Neue", Arial, sans-serif;
  --gut: 14px;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  overscroll-behavior: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

:focus-visible { outline: 1px solid currentColor; outline-offset: 3px; }

.app {
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom);
}

/* ---------- one full-bleed page per station ---------- */

.track {
  flex: 1;
  min-height: 0;
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior: contain;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.track::-webkit-scrollbar { display: none; }

.card {
  flex: 0 0 100%;
  scroll-snap-align: center;
  scroll-snap-stop: always;
  display: flex;
  flex-direction: column;
  padding: var(--gut);
  color: var(--spot);
}

.meta, .foot {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  line-height: 1;
}

.name {
  flex: 1;
  min-height: 0;
  margin: var(--gut) 0;
  overflow: hidden;
  font-family: var(--display);
  font-weight: 900;
  letter-spacing: -0.02em;
  text-transform: none;
}
.stack {
  display: block;
  width: max-content;
  transform-origin: 0 0;
  font-size: 100px; /* replaced by fitNames() in app.js */
}
/* Tight on purpose: stacked words overprint. Gap Sans has ascent .928em and descent .195em,
   so in a .8 line box the baseline sits 96% down. */
.word { display: block; line-height: 0.8; white-space: nowrap; }
.word i {
  display: inline-block;
  font-style: normal;
  transform-origin: 50% 96%; /* the baseline: letters grow up from it like level meters */
}
.playing .card.active .word i {
  animation: level var(--t) cubic-bezier(0.5, 0, 0.5, 1) infinite alternate;
  will-change: transform;
}
/* Real audio data is flowing: the script drives the letters instead. */
.app.reactive .card.active .word i { animation: none; }
@keyframes level { to { transform: scaleY(var(--lo)); } }


.foot a {
  color: inherit;
  text-decoration: none;
  padding: 10px 0 10px 20px;
  margin: -10px 0;
}
.foot a.open { color: var(--bg); background: var(--spot); padding: 10px 12px; margin: -10px 0; }

/* ---------- footer: one tick per station, one slab to play ---------- */

.controls { display: flex; flex-direction: column; }

/* Equal ticks, edge to edge. Each carries its station's ink; the current one fills. */
.marks { display: flex; gap: 4px; padding: 0 var(--gut); }
.mark {
  flex: 1;
  height: 28px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  position: relative;
}
.mark::after {
  content: "";
  position: absolute;
  inset: 11px 0;
  border: 1px solid var(--spot);
  opacity: 0.5;
  transition: opacity 0.2s, inset 0.2s, background-color 0.2s;
}
.mark.active::after { inset: 6px 0; background: var(--spot); opacity: 1; }

/* The slab: station ink, full bleed, the word set huge. */
.bar {
  position: relative;
  display: flex;
  align-items: stretch;
  margin-top: 6px;
  background: var(--spot);
  color: var(--bg);
  transition: background-color 0.3s;
}
.play {
  flex: 1;
  height: 92px;
  padding: 0 var(--gut);
  border: 0;
  border-radius: 0;
  background: none;
  color: inherit;
  font-family: var(--display);
  font-weight: 900;
  font-size: 60px;
  line-height: 1;
  letter-spacing: -0.03em;
  text-align: left;
  text-transform: none;
  display: flex;
  align-items: center;
  padding-bottom: 6px;
  cursor: pointer;
}
.play:active { opacity: 0.7; }
.play:focus-visible { outline-color: var(--bg); outline-offset: -6px; }

.status {
  position: absolute;
  top: var(--gut);
  right: var(--gut);
  margin: 0;
  line-height: 1;
  text-align: right;
  pointer-events: none;
}
.playing .status::before {
  content: "";
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-right: 8px;
  background: var(--bg);
  animation: blink 1s steps(1) infinite;
}
@keyframes blink { 50% { opacity: 0; } }

/* What's on: one quiet line under the slab, filled in by track recognition. */
.now {
  margin: 0;
  padding: 8px var(--gut) 2px;
  min-height: calc(1em + 10px); /* keeps its line even when empty: the slab never moves */
  line-height: 1;
  color: var(--dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.now b { font-weight: 500; color: var(--fg); }
.now.listening::before { content: "Listening…"; }

@media (prefers-reduced-motion: reduce) {
  .playing .card.active .word i,
  .playing .status::before { animation: none; }
}
