/* The Loft Library — David Mancuso's record collection
   Palette: ink #060504 · cream #EDE4D3 · gold #C9A96A */

:root {
  --ink: #060504;
  --cream: #EDE4D3;
  --gold: #C9A96A;
  --serif: 'Playfair Display', Georgia, serif;
  --sans: Inter, system-ui, sans-serif;
  --mono: 'Space Mono', monospace;
}

html, body { margin: 0; padding: 0; background: var(--ink); }
body { font-family: var(--sans); -webkit-font-smoothing: antialiased; }
html { scrollbar-width: none; }
html::-webkit-scrollbar, body::-webkit-scrollbar { display: none; }
a { color: var(--cream); text-decoration: none; }
a:hover { color: var(--gold); }
::selection { background: var(--gold); color: var(--ink); }
button { font: inherit; }
:focus-visible { outline: 1px solid var(--gold); outline-offset: 3px; }

@keyframes loftGrain {
  0%   { transform: translate(0, 0); }
  25%  { transform: translate(-2%, -3%); }
  50%  { transform: translate(-4%, 1%); }
  75%  { transform: translate(2%, -2%); }
  100% { transform: translate(0, 0); }
}
@keyframes loftHint {
  0%, 100% { transform: scaleY(0.25); opacity: 0.2; }
  50%      { transform: scaleY(1); opacity: 0.9; }
}

/* ---------- scroll spacer + webgl mount ---------- */
#scroll-space { height: 12000px; width: 1px; }
#scene { position: fixed; inset: 0; z-index: 0; }

/* ---------- film grade ---------- */
.stage-light,
.vignette,
.grain { position: fixed; pointer-events: none; }
.stage-light {
  inset: 0; z-index: 2;
  background: radial-gradient(ellipse 78% 46% at 50% -18%, rgba(255, 236, 202, 0.07), rgba(255, 232, 194, 0.022) 42%, transparent 74%);
}
.vignette {
  inset: 0; z-index: 2;
  background: radial-gradient(ellipse at 50% 44%, transparent 52%, rgba(3, 2, 1, 0.62) 100%);
}
.grain {
  inset: -60%; width: 220%; height: 220%; z-index: 3; opacity: 0.07;
  animation: loftGrain 0.85s steps(5) infinite;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ---------- decade signage (driven by scene) ---------- */
.signage {
  position: fixed; inset: 0; z-index: 5; pointer-events: none; opacity: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center;
}
.sign-title {
  font-family: var(--serif); font-weight: 500; font-size: clamp(44px, 6.5vw, 86px);
  color: var(--cream); letter-spacing: 0.05em;
}
.sign-sub {
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.32em;
  color: var(--gold); margin-top: 20px; white-space: nowrap;
}

/* ---------- closing colophon ---------- */
.colophon {
  position: fixed; inset: 0; z-index: 6; pointer-events: none; opacity: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; padding: 90px 24px 0;
}
.colophon .eyebrow { font-family: var(--mono); font-size: 10px; letter-spacing: 0.4em; color: rgba(237, 228, 211, 0.5); }
.colophon h2 {
  font-family: var(--serif); font-style: italic; font-weight: 500;
  font-size: clamp(42px, 6vw, 78px); color: var(--cream); margin: 26px 0 0;
}
.colophon-stats { font-family: var(--mono); font-size: 11px; letter-spacing: 0.3em; color: var(--gold); margin-top: 30px; }
.colophon p {
  font-family: var(--sans); font-size: 11px; letter-spacing: 0.22em;
  color: rgba(237, 228, 211, 0.45); margin: 44px 0 0; line-height: 2.2;
}

/* ---------- hero scroll hint ---------- */
.hero-caption { position: fixed; bottom: 16px; left: 0; right: 0; z-index: 8; pointer-events: none; text-align: center; }
.hero-caption span {
  font-family: var(--sans); font-size: 11px; font-weight: 500;
  letter-spacing: 0.36em; color: rgba(237, 228, 211, 0.72);
}
.hero-caption i {
  display: block; width: 1px; height: 26px; background: var(--gold);
  margin: 14px auto 0; transform-origin: top;
  animation: loftHint 2.4s ease-in-out infinite;
}

/* ---------- hover tooltip ---------- */
.tooltip {
  position: fixed; left: 0; top: 0; z-index: 30; pointer-events: none; opacity: 0;
  transform: translate(-50%, -130%); background: rgba(6, 5, 4, 0.88);
  border: 1px solid rgba(201, 169, 106, 0.32); padding: 12px 18px;
  text-align: center; max-width: 280px; transition: opacity 0.16s ease;
}
.tooltip div {
  font-family: var(--serif); font-size: 17px; color: var(--cream);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 250px;
}
.tooltip small {
  display: block; font-family: var(--mono); font-size: 9.5px;
  letter-spacing: 0.12em; color: rgba(237, 228, 211, 0.55); margin-top: 5px;
}

/* ---------- top navigation ---------- */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 10;
  display: flex; align-items: center; justify-content: space-between;
  padding: 26px 44px; pointer-events: none;
}
.nav > * { pointer-events: auto; }
.wordmark {
  background: none; border: none; cursor: pointer; padding: 0; white-space: nowrap;
  font-family: var(--sans); font-size: 12px; font-weight: 500;
  letter-spacing: 0.28em; color: var(--cream);
}
.nav-links { display: flex; align-items: center; gap: 38px; }
.nav-links button {
  background: none; border: none; cursor: pointer; padding: 0;
  display: flex; align-items: center; gap: 7px;
  font-family: var(--sans); font-size: 10.5px; letter-spacing: 0.22em;
  color: rgba(237, 228, 211, 0.72); transition: color 0.2s ease;
}
.nav-links button:hover { color: var(--cream); }
.nav-tools { display: flex; align-items: center; gap: 26px; }
.sound-btn {
  background: none; border: none; cursor: pointer; padding: 0;
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.24em; color: var(--gold);
}
.sound-btn.is-muted { color: rgba(237, 228, 211, 0.4); }
.icon-btn { background: none; border: none; cursor: pointer; padding: 0; display: flex; }

/* ---------- decade pills ---------- */
.decade-pills {
  position: fixed; top: 86px; left: 44px; z-index: 10;
  display: flex; gap: 9px; flex-wrap: wrap; max-width: 34vw;
}
.decade-pills button {
  background: rgba(237, 228, 211, 0.03); border: 1px solid rgba(237, 228, 211, 0.22);
  border-radius: 999px; cursor: pointer; padding: 8px 16px; white-space: nowrap;
  font-family: var(--sans); font-size: 10px; letter-spacing: 0.14em;
  color: rgba(237, 228, 211, 0.72); transition: border-color 0.25s ease, color 0.25s ease;
}
.decade-pills button:hover { border-color: rgba(201, 169, 106, 0.8); color: var(--cream); }
.decade-pills button.is-active { border-color: rgba(201, 169, 106, 0.85); color: var(--cream); }

/* ---------- current band readout ---------- */
.current-band { position: fixed; top: 86px; left: 50%; transform: translateX(-50%); z-index: 10; text-align: center; }
.current-band .pill {
  display: inline-block; border: 1px solid rgba(201, 169, 106, 0.5); border-radius: 999px;
  padding: 8px 20px; font-family: var(--sans); font-size: 10px; letter-spacing: 0.16em;
  color: var(--cream); background: rgba(6, 5, 4, 0.4); white-space: nowrap;
}
.current-band button {
  background: none; border: none; cursor: pointer; padding: 0; margin-top: 12px;
  font-family: var(--sans); font-size: 9px; letter-spacing: 0.3em;
  color: rgba(237, 228, 211, 0.5); transition: color 0.2s ease;
}
.current-band button:hover { color: var(--gold); }

.tagline {
  position: fixed; top: 96px; right: 44px; z-index: 10; pointer-events: none;
  font-family: var(--sans); font-size: 10px; letter-spacing: 0.3em; color: rgba(237, 228, 211, 0.5);
}

/* ---------- edge arrows: turn the nearest shelf ---------- */
.ring-arrow {
  position: fixed; top: 50%; transform: translateY(-50%); z-index: 12;
  display: flex; flex-direction: column; align-items: center; gap: 22px;
  width: 124px; padding: 58px 0; background: none; border: none; cursor: pointer;
  color: rgba(237, 228, 211, 0.34);
  transition: color 0.45s ease, opacity 0.45s ease;
}
.ring-arrow.is-left { left: 0; }
.ring-arrow.is-right { right: 0; }
.ring-arrow svg { width: 24px; height: 62px; overflow: visible; }
.ring-arrow i {
  display: block; width: 1px; height: 64px; background: currentColor;
  opacity: 0.5; transform: scaleY(0.45); transform-origin: center;
  transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.2, 1), opacity 0.5s ease;
}
.ring-arrow:hover, .ring-arrow:focus-visible { color: var(--gold); }
.ring-arrow:hover i, .ring-arrow:focus-visible i { transform: scaleY(1); opacity: 0.9; }
.ring-arrow[hidden] { display: none; }

/* ---------- the story ---------- */
.story-modal {
  position: fixed; inset: 0; z-index: 45; display: none;
  background: rgba(4, 3, 2, 0.94);
}
.story-modal.is-open { display: block; }
.story-scroll { position: absolute; inset: 0; overflow-y: auto; padding: 16vh 24px 14vh; }
.story-inner { width: min(660px, 86vw); margin: 0 auto; }
.story-inner .eyebrow { font-family: var(--mono); font-size: 9px; letter-spacing: 0.34em; color: var(--gold); }
.story-inner h1 {
  font-family: var(--serif); font-weight: 500; font-size: clamp(46px, 7vw, 82px);
  line-height: 1.02; color: var(--cream); margin: 20px 0 0;
}
.story-dates { font-family: var(--mono); font-size: 11px; letter-spacing: 0.3em; color: rgba(237, 228, 211, 0.5); margin-top: 16px; }
.story-inner p {
  font-family: var(--sans); font-size: 15px; line-height: 1.85;
  color: rgba(237, 228, 211, 0.74); margin: 26px 0 0;
}
.story-inner p.story-lede {
  font-family: var(--serif); font-size: clamp(19px, 2.3vw, 23px); font-style: italic;
  line-height: 1.6; color: var(--cream); margin-top: 40px;
}
.story-inner em { color: var(--cream); font-style: italic; }
.story-rule { height: 1px; background: rgba(201, 169, 106, 0.35); margin: 54px 0 0; }
.story-inner h2 {
  font-family: var(--serif); font-weight: 500; font-size: clamp(28px, 3.6vw, 40px);
  color: var(--cream); margin: 34px 0 0;
}
.story-stats { font-family: var(--mono); font-size: 11px; letter-spacing: 0.28em; color: var(--gold); margin-top: 52px; }
.story-foot { font-family: var(--sans); font-size: 10px; letter-spacing: 0.26em; color: rgba(237, 228, 211, 0.4); margin-top: 14px; }

/* ---------- focus panel ---------- */
.focus-card {
  position: fixed; left: 44px; bottom: 44px; z-index: 20; width: 384px;
  background: rgba(6, 5, 4, 0.8); border: 1px solid rgba(201, 169, 106, 0.35);
  padding: 26px 28px; box-sizing: border-box; display: none;
  /* the tracklist is the elastic part — the panel never grows past the window */
  flex-direction: column; max-height: calc(100vh - 108px);
}
.focus-card.is-visible { display: flex; }
.focus-card .eyebrow { font-family: var(--mono); font-size: 9px; letter-spacing: 0.3em; color: var(--gold); }
.focus-card h2 {
  font-family: var(--serif); font-weight: 600; font-size: 27px; line-height: 1.1;
  color: var(--cream); margin: 11px 0 0;
}
.focus-title {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.06em;
  color: rgba(237, 228, 211, 0.85); margin-top: 9px; text-transform: uppercase;
}
.focus-meta { font-family: var(--mono); font-size: 10px; letter-spacing: 0.1em; color: rgba(237, 228, 211, 0.5); margin-top: 14px; }

/* while a record is held up, everything else steps out of the frame */
.decade-pills, .current-band, .tagline, .ring-arrow {
  transition: opacity 0.4s ease, visibility 0.4s;
}
body.is-focused .decade-pills,
body.is-focused .current-band,
body.is-focused .tagline,
body.is-focused .ring-arrow {
  opacity: 0; visibility: hidden; pointer-events: none;
}
/* Same again under a full-screen overlay: the scrim is deliberately not opaque
   — the room should still be felt behind the search — but the chrome reading
   through it lands on top of the results, which is a different thing entirely. */
body.is-overlay .decade-pills,
body.is-overlay .current-band,
body.is-overlay .tagline,
body.is-overlay .ring-arrow,
body.is-overlay .hero-caption {
  opacity: 0; visibility: hidden; pointer-events: none;
}

/* the scan, lifted straight off the release it was matched to */
.focus-head { display: flex; gap: 18px; align-items: flex-start; }
.focus-head-text { min-width: 0; flex: 1; }
.focus-cover {
  position: relative; flex: 0 0 116px; width: 116px; height: 116px; display: block;
  background: #0a0806; border: 1px solid rgba(237, 228, 211, 0.16);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.6); overflow: hidden;
  transition: border-color 0.25s ease, transform 0.25s ease;
}
.focus-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.focus-cover.is-empty img { display: none; }
.focus-cover-fallback {
  position: absolute; inset: 0; display: none; align-items: center; justify-content: center;
  font-family: var(--mono); font-size: 8px; letter-spacing: 0.24em; color: rgba(237, 228, 211, 0.35);
}
.focus-cover.is-empty .focus-cover-fallback { display: flex; }
.focus-cover[href]:hover { border-color: rgba(201, 169, 106, 0.75); transform: translateY(-2px); }

/* primary action — deliberately the loudest thing in the panel */
.discogs-btn {
  display: flex; align-items: center; gap: 10px; margin-top: 18px;
  padding: 11px 14px; box-sizing: border-box;
  border: 1px solid rgba(201, 169, 106, 0.55); background: rgba(201, 169, 106, 0.07);
  color: var(--gold); cursor: pointer; text-decoration: none;
  transition: background 0.22s ease, border-color 0.22s ease, color 0.22s ease;
}
.discogs-btn:hover { background: rgba(201, 169, 106, 0.18); border-color: var(--gold); color: var(--cream); }
.discogs-mark { width: 15px; height: 15px; flex: 0 0 15px; }
.discogs-label {
  flex: 1; font-family: var(--mono); font-size: 10px; letter-spacing: 0.22em;
}
.discogs-arrow { width: 11px; height: 11px; flex: 0 0 11px; opacity: 0.8; }
.discogs-btn.is-off {
  pointer-events: none; border-color: rgba(237, 228, 211, 0.14);
  background: none; color: rgba(237, 228, 211, 0.3);
}
.play-note { font-family: var(--mono); font-size: 9px; letter-spacing: 0.2em; color: var(--gold); margin-top: 16px; }
.play-note:empty { display: none; }

/* the panel is a column: only the tracklist may take the slack */
.focus-head, .focus-meta, .focus-links, .discogs-btn, .play-note, .focus-actions { flex: 0 0 auto; }
/* the exits travel together; on the desk the column changes nothing */
.focus-links { display: flex; flex-direction: column; }

/* ---------- tracklist ---------- */
/* Every cut on the record, sides and running times straight off the release. */
.tracklist {
  flex: 1 1 auto; min-height: 64px; overflow-y: auto; margin-top: 16px;
  border-top: 1px solid rgba(237, 228, 211, 0.12); padding-top: 4px;
  scrollbar-width: thin; scrollbar-color: rgba(201, 169, 106, 0.4) transparent;
}
.tracklist::-webkit-scrollbar { width: 3px; }
.tracklist::-webkit-scrollbar-thumb { background: rgba(201, 169, 106, 0.4); }
.tracklist-empty {
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.2em;
  color: rgba(237, 228, 211, 0.35); padding: 12px 2px;
}
.side-head {
  display: flex; justify-content: space-between; align-items: baseline;
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.28em;
  color: var(--gold); padding: 12px 2px 6px;
  border-bottom: 1px solid rgba(201, 169, 106, 0.22);
}
.side-head:first-child { padding-top: 6px; }
.side-head small { letter-spacing: 0.12em; color: rgba(237, 228, 211, 0.4); }
/* the way out to the recording itself — same shape as the Discogs button so the
   pair reads as one row of exits, tinted apart so they are not confusable */
.discogs-btn.is-tube { margin-top: 8px; border-color: rgba(237, 228, 211, 0.3);
  background: rgba(237, 228, 211, 0.04); color: rgba(237, 228, 211, 0.82); }
.discogs-btn.is-tube:hover { background: rgba(237, 228, 211, 0.11);
  border-color: rgba(237, 228, 211, 0.6); color: var(--cream); }
.discogs-btn.is-tube .discogs-mark { width: 18px; height: 13px; flex: 0 0 18px; }

/* a cut is a row now: the sillon on the left, its way out on the right */
.track { display: flex; align-items: stretch; gap: 2px;
  border-left: 2px solid transparent; transition: background 0.15s ease, border-color 0.15s ease; }
.tk-main { flex: 1 1 auto; min-width: 0; display: grid;
  grid-template-columns: 26px 1fr auto; align-items: baseline; gap: 10px;
  background: none; border: none; cursor: pointer; text-align: left;
  padding: 7px 4px 7px 6px; }
.tk-yt { flex: 0 0 26px; display: flex; align-items: center; justify-content: center;
  color: rgba(237, 228, 211, 0.28); transition: color 0.18s ease; }
.tk-yt svg { width: 15px; height: 11px; }
.tk-yt:hover { color: var(--cream); }
.track:hover { background: rgba(201, 169, 106, 0.08); }
.track:hover .tk-yt { color: rgba(237, 228, 211, 0.55); }
.track.is-playing .tk-yt { color: rgba(201, 169, 106, 0.8); }
.tk-pos {
  font-family: var(--mono); font-style: normal; font-size: 9px; letter-spacing: 0.08em;
  color: rgba(237, 228, 211, 0.4);
}
.tk-title {
  font-family: var(--sans); font-size: 11.5px; line-height: 1.3; color: rgba(237, 228, 211, 0.86);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tk-dur {
  font-family: var(--mono); font-style: normal; font-size: 9px;
  color: rgba(237, 228, 211, 0.42);
}
.tk-dur.is-est { border-bottom: 1px dotted rgba(237, 228, 211, 0.3); cursor: help; }
.track.is-playing { border-left-color: var(--gold); background: rgba(201, 169, 106, 0.1); }
.track.is-playing .tk-title { color: var(--gold); }
.track.is-playing .tk-pos, .track.is-playing .tk-dur { color: rgba(201, 169, 106, 0.75); }

.transport { display: flex; align-items: center; gap: 11px; margin-top: 16px; flex: 0 0 auto; }
.tk-btn {
  width: 22px; height: 22px; flex-shrink: 0; padding: 0; border: none; background: none;
  cursor: pointer; color: rgba(237, 228, 211, 0.55); transition: color 0.2s ease;
  display: flex; align-items: center; justify-content: center;
}
.tk-btn svg { width: 12px; height: 12px; }
.tk-btn:hover { color: var(--gold); }
.play-btn {
  width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0; padding: 0;
  border: 1px solid rgba(201, 169, 106, 0.6); background: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center; transition: background 0.2s ease;
}
.play-btn:hover { background: rgba(201, 169, 106, 0.14); }
.prog-bar { flex: 1; height: 20px; display: flex; align-items: center; cursor: pointer; }
.prog-track { width: 100%; height: 2px; background: rgba(237, 228, 211, 0.16); position: relative; }
.prog-fill { position: absolute; left: 0; top: -1px; height: 4px; background: var(--gold); width: 0%; }
.prog-time {
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.06em;
  color: rgba(237, 228, 211, 0.55); flex-shrink: 0;
}

/* ---------- all night ----------
   The switch that makes the page a room rather than a page: the end of a side
   turns the record over, the end of the record fetches the next one, and the
   tab can be left behind other windows all evening. Off, the arm lifts at the
   last groove and stays there, which is what it used to do. */
.continuous { display: flex; align-items: center; gap: 12px; margin-top: 13px; flex: 0 0 auto; }
.cont-toggle {
  display: flex; align-items: center; gap: 9px; padding: 0; border: none; background: none;
  cursor: pointer; white-space: nowrap; transition: color 0.2s ease;
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.2em;
  color: rgba(237, 228, 211, 0.5);
}
.cont-toggle:hover { color: var(--cream); }
.cont-toggle i {
  width: 26px; height: 13px; flex-shrink: 0; position: relative; border-radius: 7px;
  border: 1px solid rgba(237, 228, 211, 0.28);
  transition: border-color 0.25s ease, background 0.25s ease;
}
.cont-toggle i::after {
  content: ''; position: absolute; top: 2px; left: 2px; width: 7px; height: 7px;
  border-radius: 50%; background: rgba(237, 228, 211, 0.5);
  transition: transform 0.25s ease, background 0.25s ease;
}
.cont-toggle[aria-pressed='true'] { color: var(--gold); }
.cont-toggle[aria-pressed='true'] i {
  border-color: rgba(201, 169, 106, 0.7); background: rgba(201, 169, 106, 0.14);
}
.cont-toggle[aria-pressed='true'] i::after { transform: translateX(13px); background: var(--gold); }
.cont-note {
  font-family: var(--mono); font-size: 8px; letter-spacing: 0.16em; line-height: 1.5;
  color: rgba(237, 228, 211, 0.38);
}

.focus-actions {
  display: flex; gap: 22px; margin-top: 18px;
  border-top: 1px solid rgba(237, 228, 211, 0.12); padding-top: 16px;
}
.focus-actions button {
  background: none; border: none; cursor: pointer; padding: 0; white-space: nowrap;
  font-family: var(--sans); font-size: 10px; letter-spacing: 0.22em;
  color: rgba(237, 228, 211, 0.75); transition: color 0.2s ease;
}
.focus-actions button:hover { color: var(--gold); }
.focus-actions #close-btn { margin-left: auto; color: rgba(237, 228, 211, 0.55); }
.focus-actions #close-btn:hover { color: var(--cream); }

/* ---------- search ---------- */
.search-modal {
  position: fixed; inset: 0; z-index: 40; background: rgba(4, 3, 2, 0.9);
  display: none; flex-direction: column; align-items: center; padding-top: 15vh;
}
.search-modal.is-open { display: flex; }
.close-search {
  position: absolute; top: 30px; right: 44px; background: none; border: none;
  cursor: pointer; padding: 0; font-family: var(--sans); font-size: 11px;
  letter-spacing: 0.24em; color: rgba(237, 228, 211, 0.6); transition: color 0.2s ease;
}
.close-search:hover { color: var(--cream); }
.search-inner { width: min(580px, 84vw); }
.search-inner .eyebrow { font-family: var(--mono); font-size: 9px; letter-spacing: 0.34em; color: var(--gold); }
.search-modal input {
  width: 100%; box-sizing: border-box; margin-top: 16px; background: transparent;
  border: none; border-bottom: 1px solid rgba(201, 169, 106, 0.55);
  font-family: var(--serif); font-size: 30px; color: var(--cream); padding: 10px 2px; outline: none;
}
.search-modal input::-webkit-search-cancel-button,
.search-modal input::-webkit-search-decoration { -webkit-appearance: none; appearance: none; }
.search-modal input::placeholder {
  color: rgba(237, 228, 211, 0.3); font-family: var(--mono); font-size: 13px; letter-spacing: 0.14em;
}
/* the aided way in: browse a decade with one tap, or narrow what you typed */
.search-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 18px; }
.search-chips button {
  background: rgba(237, 228, 211, 0.03); border: 1px solid rgba(237, 228, 211, 0.22);
  border-radius: 999px; cursor: pointer; padding: 7px 14px; white-space: nowrap;
  font-family: var(--sans); font-size: 10px; letter-spacing: 0.14em;
  color: rgba(237, 228, 211, 0.72); transition: border-color 0.25s ease, color 0.25s ease, background 0.25s ease;
}
.search-chips button:hover { border-color: rgba(201, 169, 106, 0.8); color: var(--cream); }
.search-chips button.is-active {
  border-color: rgba(201, 169, 106, 0.85); color: var(--cream); background: rgba(201, 169, 106, 0.12);
}
.search-count {
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.24em;
  color: rgba(237, 228, 211, 0.4); margin-top: 14px; min-height: 11px;
}
#search-results {
  margin-top: 14px; display: flex; flex-direction: column; max-height: 52vh;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
}
#search-results button {
  display: flex; justify-content: space-between; align-items: center; gap: 16px;
  background: none; border: none; border-bottom: 1px solid rgba(237, 228, 211, 0.08);
  cursor: pointer; padding: 11px 6px; text-align: left; transition: background 0.15s ease;
}
.result-thumb {
  flex: 0 0 38px; width: 38px; height: 38px; display: block; overflow: hidden;
  background: #100d0a; border: 1px solid rgba(237, 228, 211, 0.12);
}
.result-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
#search-results button:hover { background: rgba(201, 169, 106, 0.07); }
#search-results span {
  font-family: var(--serif); font-size: 18px; color: var(--cream);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 0 1 auto;
}
#search-results small {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.08em;
  color: rgba(237, 228, 211, 0.55); text-align: right;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 0 1 auto;
}

/* ---------- reduced-motion fallback ---------- */
.reduced-gallery { position: relative; z-index: 5; max-width: 1360px; margin: 0 auto; padding: 140px 40px 100px; display: none; }
.reduced-gallery.is-visible { display: block; }
.reduced-heading { font-family: var(--serif); font-weight: 500; font-size: 56px; color: var(--cream); text-align: center; }
.reduced-gallery .eyebrow {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.3em;
  color: var(--gold); text-align: center; margin-top: 16px;
}
.gallery-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 18px; margin-top: 60px; }
.gallery-tile {
  position: relative; aspect-ratio: 1; border: 1px solid rgba(237, 228, 211, 0.2);
  box-sizing: border-box; overflow: hidden; scroll-margin: 120px; background: #0a0806;
}
.gallery-body {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center; padding: 22px;
}
.gallery-art { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* a label scan can be bright white or acid yellow, so the caption needs a real
   scrim under it — not a whisper of one */
.gallery-tile.has-art .gallery-body {
  justify-content: flex-end; padding: 16px 14px 15px;
  background: linear-gradient(to top,
    rgba(6, 5, 4, 0.96) 0%, rgba(6, 5, 4, 0.94) 42%,
    rgba(6, 5, 4, 0.62) 66%, rgba(6, 5, 4, 0) 88%);
  text-shadow: 0 1px 5px rgba(0, 0, 0, 0.85);
}
.gallery-tile.has-art strong { font-size: 17px; }
.gallery-tile.has-art span { margin-top: 7px; }
.gallery-tile.has-art small { margin-top: 6px; }
.gallery-tile strong { font-family: var(--serif); font-weight: 700; font-size: 19px; line-height: 1.15; color: var(--cream); }
.gallery-tile span { font-family: var(--mono); font-size: 10px; color: rgba(237, 228, 211, 0.7); margin-top: 10px; text-transform: uppercase; }
.gallery-tile small { font-family: var(--mono); font-size: 9px; color: rgba(237, 228, 211, 0.45); margin-top: 8px; }
.gallery-link {
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.2em; color: var(--gold);
  margin-top: 12px; padding: 6px 10px; border: 1px solid rgba(201, 169, 106, 0.45);
  transition: background 0.2s ease, color 0.2s ease;
}
.gallery-link:hover { background: rgba(201, 169, 106, 0.16); color: var(--cream); }

/* ---------- loader: "Le Sillon" ----------
   Opacity on .loader is written every frame from JS (the Sillon dissolve), so
   there is deliberately no transition on it here — one would fight the other.
   The lockup scale rides --lock-scale, set by the loader from the viewport, so
   the DOM type tracks the cover-fitted canvas field behind it. */
.loader {
  position: fixed; inset: 0; z-index: 100; background: var(--ink);
  overflow: hidden; --lock-scale: 1;
}
.loader-field { position: absolute; inset: 0; z-index: 2; display: block; }

.loader-lockup {
  position: absolute; inset: 0; z-index: 1; opacity: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
}
.loader-wordmark { position: relative; text-align: center; }
.loader-wordmark span {
  font-family: var(--serif); font-weight: 500;
  font-size: calc(88px * var(--lock-scale));
  letter-spacing: 0.1em; color: var(--cream); white-space: nowrap;
}
/* the gold ghost drifting a couple of pixels off the cream — the piece's shimmer */
.loader-echo { position: absolute; inset: 0; color: var(--gold) !important; opacity: 0.22; }
/* The wordmark can scale linearly with the frame — 88px down to 26px still
   reads. The caption cannot: the same factor would put it at 3px. It gets a
   floor, and gives up some of its tracking on narrow screens so a line that is
   set nowrap still fits the frame. */
.loader-caption {
  margin-top: calc(28px * var(--lock-scale)); opacity: 0;
  font-family: var(--sans); font-weight: 500;
  /* the line is set larger than the credits under it, and long — size it off
     the frame, then cap it against the viewport so a nowrap line can never
     run past the edge on an in-between window */
  font-size: min(max(9px, calc(13px * var(--lock-scale))), 1.72vw);
  letter-spacing: 0.4em; color: rgba(237, 228, 211, 0.68); white-space: nowrap;
}
.loader-caption b { font-weight: 700; color: var(--cream); }

/* the credit block: a step down from the caption, the three names carrying
   the weight so they read before the sentence around them does */
.loader-credits {
  margin-top: calc(20px * var(--lock-scale)); opacity: 0;
  display: flex; flex-direction: column; align-items: center;
  gap: calc(9px * var(--lock-scale));
  font-family: var(--sans); font-weight: 500;
  font-size: min(max(8px, calc(10.5px * var(--lock-scale))), 1.5vw);
  letter-spacing: 0.3em; color: rgba(237, 228, 211, 0.52); white-space: nowrap;
}
.loader-credits b { font-weight: 700; color: var(--cream); }

@media (max-width: 620px) {
  .loader-caption { font-size: min(9px, 2.05vw); letter-spacing: 0.2em; margin-top: 16px; }
  .loader-credits {
    font-size: min(8px, 1.85vw); letter-spacing: 0.16em; margin-top: 12px; gap: 6px;
  }
}

.loader-readout {
  position: absolute; left: 0; right: 0; bottom: clamp(44px, 8vh, 72px); z-index: 3;
  display: flex; flex-direction: column; align-items: center; gap: 16px; opacity: 0;
}
.loader-count {
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.34em;
  color: var(--gold); white-space: nowrap;
}
.loader-line { width: min(220px, 56vw); height: 1px; background: rgba(237, 228, 211, 0.14); }
.loader-line span { display: block; height: 1px; background: var(--gold); width: 0%; }
.loader-stage {
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.3em;
  color: rgba(237, 228, 211, 0.32); white-space: nowrap; min-height: 11px;
  transition: opacity 0.4s ease;
}

/* film grade: a warm wash from above, then the frame closed down at the edges */
.loader-grade, .loader-vignette, .loader-grain { position: absolute; z-index: 4; pointer-events: none; }
.loader-grade {
  inset: 0;
  background: radial-gradient(ellipse 78% 46% at 50% -18%, rgba(255, 236, 202, 0.07), rgba(255, 232, 194, 0.022) 42%, transparent 74%);
}
.loader-vignette {
  inset: 0;
  background: radial-gradient(ellipse at 50% 44%, transparent 52%, rgba(3, 2, 1, 0.62) 100%);
}
.loader-grain {
  inset: -60%; width: 220%; height: 220%; z-index: 5; opacity: 0.07;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23n)'/%3E%3C/svg%3E");
}
@media (prefers-reduced-motion: reduce) {
  .loader { transition: opacity 0.45s ease; }
  .loader-grain { display: none; }
}

.sr-only { position: fixed; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* ---------- narrow viewports ---------- */
@media (max-width: 939px) {
  .nav-links, .tagline { display: none; }
  .nav {
    padding: calc(14px + env(safe-area-inset-top, 0px)) calc(18px + env(safe-area-inset-right, 0px))
             14px calc(18px + env(safe-area-inset-left, 0px));
  }
  /* One row that scrolls sideways. Wrapped onto two rows they climbed into the
     lockup, which is the one thing the hero cannot afford to lose. */
  .decade-pills {
    top: 138px; left: 0; right: 0; max-width: none;
    flex-wrap: nowrap; overflow-x: auto; overscroll-behavior-x: contain;
    justify-content: safe center; gap: 8px; padding: 2px 18px;
    scrollbar-width: none; -webkit-overflow-scrolling: touch;
  }
  .decade-pills::-webkit-scrollbar { display: none; }
  .decade-pills button { flex: 0 0 auto; }
  .current-band { top: 74px; }
  /* pills run across the middle here, so anchor the colophon below the chrome
     instead of centring it into them */
  .colophon { justify-content: flex-start; padding-top: 190px; }
  .colophon h2 { font-size: clamp(30px, 7vw, 52px); margin-top: 14px; }
  .colophon-stats { margin-top: 16px; letter-spacing: 0.2em; }
  .colophon p { margin-top: 24px; line-height: 1.8; }
}
@media (max-width: 640px) {
  /* The hero, composed for a tall frame: the chrome closes ranks at the top —
     wordmark, band, timeline — and the decade strip goes to the bottom, out of
     the lockup's air and into the thumb's. What is left between them is the
     room: the lockup, the ring under it, the deck on the floor. */
  .decade-pills {
    top: auto; bottom: calc(14px + env(safe-area-inset-bottom, 0px));
    padding: 2px 16px;
  }
  /* the strip now says what this said, and says it where the thumb is */
  .hero-caption { display: none; }
  body.is-touch .touch-coach { bottom: calc(74px + env(safe-area-inset-bottom, 0px)); }
  .ring-arrow { width: 60px; padding: 34px 0; gap: 14px; }
  .ring-arrow svg { width: 18px; height: 46px; }
  .ring-arrow i { height: 40px; }
  .story-scroll { padding: calc(10vh + env(safe-area-inset-top, 0px)) 20px 12vh; }
  /* the decade titles: 44px served the desktop clamp floor; a 390px screen
     needs "THE TWO THOUSANDS" on one legible line, not bled off both edges */
  .sign-title { font-size: clamp(28px, 9.5vw, 44px); letter-spacing: 0.04em; }
  .sign-sub { font-size: 10px; letter-spacing: 0.22em; margin-top: 14px; }
  /* the focus card becomes a true bottom sheet: full width, flush to the glass,
     the tracklist still the only elastic part */
  /* The player is a fixed panel, not a drawer you wrestle with: two authored
     heights, --sheet-h written by the app, and the tab is the only thing that
     moves between them. Nothing here resizes under a finger. */
  .focus-card {
    left: 0; right: 0; bottom: 0; width: auto;
    border-left: none; border-right: none; border-bottom: none;
    background: rgba(6, 5, 4, 0.9);
    -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
    padding: 14px 18px calc(12px + env(safe-area-inset-bottom, 0px));
    height: calc(var(--sheet-h, 214px) + env(safe-area-inset-bottom, 0px));
    max-height: none; overflow: visible;
    transition: height 0.38s cubic-bezier(0.2, 0.7, 0.2, 1);
  }
  .tracklist { max-height: none; }
  .focus-card h2 { font-size: 22px; }
  .focus-cover { flex-basis: 96px; width: 96px; height: 96px; }
  .hero-caption { bottom: calc(14px + env(safe-area-inset-bottom, 0px)); }
  .close-search { right: 22px; top: calc(18px + env(safe-area-inset-top, 0px)); }
  .search-modal { padding-top: calc(64px + env(safe-area-inset-top, 0px)); }
  .search-inner {
    width: calc(100vw - 40px); display: flex; flex-direction: column;
    flex: 1 1 auto; min-height: 0;
  }
  .search-modal input { font-size: 22px; }
  /* results take whatever the keyboard leaves — interactive-widget=resizes-content
     shrinks the viewport, and this column follows it */
  #search-results { max-height: none; flex: 1 1 auto; padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px)); }
  .search-chips button { padding: 9px 14px; font-size: 10.5px; }
  .reduced-gallery { padding: 100px 20px 80px; }
  .reduced-heading { font-size: 36px; }
  .gallery-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; }
}

/* a cut we hold the record for, against one we only have a preview of —
   but never at the cost of the playing row's gold, which this rule used to
   override from below purely by coming later in the file */
.track.has-audio .tk-pos { color: rgba(201, 169, 106, 0.75); }
.track:not(.has-audio):not(.is-playing) .tk-title { color: rgba(237, 228, 211, 0.55); }

/* The shelf sticker, restated on the record's own scan — top-right, stuck over
   the artwork the way a shop tags a sleeve, so opening the record cannot lose
   the mark you scanned the room for. */
.soon-badge {
  position: absolute; top: 5px; right: 5px; z-index: 2; pointer-events: none;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  width: 42px; height: 42px; border-radius: 50%; transform: rotate(-11deg);
  background: #F2C43D; color: var(--ink);
  border: 1.5px solid rgba(6, 5, 4, 0.5);
  font-family: var(--mono); font-size: 7px; font-weight: 700; letter-spacing: 0.06em;
  line-height: 1.25; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
}
.soon-badge[hidden] { display: none; }
.soon-badge b { font-weight: 700; }

/* A record we hold only in part is marked cut by cut instead: the sleeve is not
   what is missing, this line is. Only the marked rows get the fourth column —
   a track gap is not free, and on every other row it pushed the running times
   10px off the side-head's right edge. */
.track.is-missing .tk-main { grid-template-columns: 26px 1fr auto auto; }
.tk-soon {
  font-family: var(--mono); font-style: normal; font-size: 7.5px; font-weight: 700;
  letter-spacing: 0.1em; white-space: nowrap;
  background: #F2C43D; color: var(--ink);
  padding: 2px 7px 3px; border-radius: 999px; transform: rotate(-1.5deg);
  align-self: center;
}
/* the rows a visitor most needs to read are the missing ones — no extra fade
   on top of the not-has-audio dim, which already stacks to illegible */
.track.is-missing .tk-pos { color: rgba(242, 196, 61, 0.75); }

/* ---------- the touch build ----------
   Same room, same palette, same type — retuned for a thumb. Everything in here
   hangs off .is-touch (set once from pointer:coarse), so a desktop with a
   touchscreen mouse still gets the desktop chrome. */
.sheet-tab { display: none; }
.touch-coach { display: none; }

html.is-touch { overscroll-behavior-y: none; }   /* the descent, not pull-to-refresh */
body.is-touch button, body.is-touch a { -webkit-tap-highlight-color: transparent; touch-action: manipulation; }
body.is-touch .tooltip { display: none; }        /* a finger never hovers */

/* hit targets: 44px of touchable area without moving a pixel of the layout */
body.is-touch .icon-btn { padding: 12px; margin: -12px; }
body.is-touch .sound-btn { padding: 12px 10px; margin: -12px -10px; }
body.is-touch .decade-pills button { padding: 10px 15px; font-size: 10.5px; }
body.is-touch .current-band button { padding: 10px; margin: 2px -10px -10px; }

/* the transport at finger size */
body.is-touch .tk-btn { width: 40px; height: 40px; }
body.is-touch .tk-btn svg { width: 15px; height: 15px; }
body.is-touch .play-btn { width: 46px; height: 46px; }
body.is-touch .prog-bar { height: 32px; }
body.is-touch .transport { gap: 8px; margin-top: 12px; }
body.is-touch .tk-main { padding: 11px 4px 11px 8px; }
body.is-touch .tk-title { font-size: 13px; }
body.is-touch .tk-yt { flex-basis: 42px; }
body.is-touch .tk-yt svg { width: 18px; height: 13px; }
/* No switch on the phone sheet — not yet. Both of its heights are stated and
   both are already full to the pixel: collapsed leaves seven, and expanded is
   over its own edge before this row is added. A control that renders below the
   glass is worse than no control, so the phone gets the behaviour without the
   switch — ALL NIGHT is on by default, and `?allnight=0` turns it off — until
   there is a row for it to live in. */
body.is-touch .continuous { display: none; }
body.is-touch .focus-actions { gap: 18px; padding-top: 12px; margin-top: 12px; }
body.is-touch .focus-actions button { font-size: 11px; padding: 8px 0; }
body.is-touch .discogs-btn { padding: 13px 14px; }

/* larger search rows — each one is a jump into the shelves. The desktop row
   spreads artist and meta across one line; a phone stacks them beside the
   scan so neither truncates the other into "Al G…". */
body.is-touch #search-results button {
  display: grid; grid-template-columns: 66px 1fr; grid-template-rows: auto auto;
  column-gap: 15px; row-gap: 5px; align-items: center; padding: 12px 6px;
}
/* the scan is what you actually recognise a record by — give it the room to be
   recognised, and the shadow of a sleeve rather than the outline of a swatch */
body.is-touch .result-thumb {
  grid-row: 1 / span 2; flex-basis: 66px; width: 66px; height: 66px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.55); border-color: rgba(237, 228, 211, 0.18);
}
body.is-touch #search-results span { grid-column: 2; text-align: left; justify-self: start; max-width: 100%; }
body.is-touch #search-results small { grid-column: 2; text-align: left; justify-self: start; max-width: 100%; }

/* the bottom sheet's two states (phones and small tablets) */
@media (max-width: 939px) {
  /* the languette: a real tab standing proud of the panel's edge, labelled, so
     it reads as something to press rather than something to drag */
  body.is-touch .sheet-tab {
    position: absolute; top: -29px; left: 50%; transform: translateX(-50%);
    display: flex; align-items: center; gap: 9px; height: 30px; padding: 0 20px;
    background: rgba(6, 5, 4, 0.92); cursor: pointer;
    border: 1px solid rgba(201, 169, 106, 0.42); border-bottom: none;
    border-radius: 11px 11px 0 0; color: var(--gold);
    font-family: var(--mono); font-size: 9px; letter-spacing: 0.22em;
    -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  }
  /* the tab is 30px of paint and 54px of target */
  body.is-touch .sheet-tab::after { content: ''; position: absolute; inset: -12px -16px -6px; }
  body.is-touch .sheet-tab svg { width: 13px; height: 8px; transition: transform 0.34s ease; }
  body.is-touch .focus-card.is-expanded .sheet-tab svg { transform: rotate(180deg); }
  /* A stated height is only honest if the content cannot outgrow it: two lines
     of artist is the most the collapsed panel has room for, and a third would
     push the exits off the bottom of the glass. */
  body.is-touch .focus-card h2 {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
    overflow: hidden;
  }
  /* collapsed: the record, the transport, prev/next/close — the deck stays in view */
  body.is-touch .focus-card:not(.is-expanded) .focus-meta,
  body.is-touch .focus-card:not(.is-expanded) .focus-links,
  body.is-touch .focus-card:not(.is-expanded) .play-note,
  body.is-touch .focus-card:not(.is-expanded) .tracklist { display: none; }
  /* expanded: the two exits close ranks into one row. Stacked full-width they
     were most of the sheet, and the tracklist — the thing the tab was pressed
     FOR — was left a slot and a half of scroll. */
  body.is-touch .focus-links { flex-direction: row; gap: 10px; margin-top: 14px; }
  body.is-touch .focus-links .discogs-btn {
    flex: 1 1 0; min-width: 0; margin-top: 0;
    justify-content: center; padding: 12px 8px;
  }
  body.is-touch .focus-links .discogs-arrow { display: none; }
  body.is-touch .focus-links .discogs-label {
    flex: 0 1 auto; white-space: nowrap; overflow: hidden; font-size: 0;
  }
  /* half a row holds seven letters, not fifteen: the label yields to the short
     form the app writes alongside it */
  body.is-touch .focus-links .discogs-label::before {
    content: attr(data-short);
    font-size: 10px; letter-spacing: 0.18em;
  }
}

/* the one-time coach: three gestures, named once, gone at the first touch */
body.is-touch .touch-coach {
  position: fixed; left: 50%; bottom: calc(96px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%); z-index: 35; pointer-events: none;
  display: flex; flex-direction: column; gap: 10px; text-align: center;
  padding: 16px 24px; background: rgba(6, 5, 4, 0.84);
  border: 1px solid rgba(201, 169, 106, 0.4);
  opacity: 0; transition: opacity 0.6s ease;
}
body.is-touch .touch-coach.is-visible { opacity: 1; }
.touch-coach div { font-family: var(--mono); font-size: 9px; letter-spacing: 0.22em; color: rgba(237, 228, 211, 0.62); white-space: nowrap; }
.touch-coach b { color: var(--gold); font-weight: 700; }

/* ---------- the tonearm cue ----------
   The deck has one moving part and nothing on the page says so — a cursor over
   a 3D tube is still just a cursor. Once ever, beside the arm: a pointer that
   drags itself back and forth, and the sentence that names the gesture. The
   app positions it from the scene, so it sits next to the arm wherever the
   staging has put the deck. */
.arm-hint {
  --sx: 18px; --sy: -50%;
  position: fixed; left: 0; top: 0; z-index: 30;
  display: none; align-items: center; gap: 11px;
  box-sizing: border-box; max-width: min(272px, 46vw);
  padding: 10px 14px 11px 11px;
  background: rgba(6, 5, 4, 0.9);
  -webkit-backdrop-filter: blur(9px); backdrop-filter: blur(9px);
  border: 1px solid rgba(201, 169, 106, 0.42);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.55);
  pointer-events: none;
  opacity: 0;
  transform: translate(var(--sx), var(--sy)) scale(0.92);
  transition: opacity 0.55s ease, transform 0.55s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.arm-hint.is-live { display: flex; }
.arm-hint.is-shown { opacity: 1; transform: translate(var(--sx), var(--sy)) scale(1); }
/* the arm lives on the right of the deck: on that side the card goes the other
   way rather than off the glass */
.arm-hint.is-left { --sx: calc(-100% - 18px); }
/* two gold ticks — a reticle, not a speech bubble */
.arm-hint::before, .arm-hint::after {
  content: ''; position: absolute; width: 9px; height: 9px; border: 1px solid var(--gold);
}
.arm-hint::before { top: -1px; left: -1px; border-right: 0; border-bottom: 0; }
.arm-hint::after { bottom: -1px; right: -1px; border-left: 0; border-top: 0; }

.arm-hint-glyph {
  position: relative; flex: 0 0 30px; width: 30px; height: 30px;
  display: grid; place-items: center;
  border: 1px solid rgba(201, 169, 106, 0.28);
  background: rgba(201, 169, 106, 0.07);
}
/* the cursor is holding something and pulling it: a short travel held at each
   end, so it reads as a drag and not as a shiver */
.arm-hint-glyph svg {
  width: 15px; height: 17px;
  animation: loftArmDrag 2.6s ease-in-out infinite;
}
.arm-hint-glyph svg path {
  fill: var(--cream); stroke: rgba(6, 5, 4, 0.92); stroke-width: 1.6; stroke-linejoin: round;
}
.arm-hint-glyph::after {
  content: ''; position: absolute; left: 50%; top: 50%;
  width: 16px; height: 16px; margin: -8px 0 0 -8px;
  border: 1px solid var(--gold); border-radius: 50%;
  opacity: 0; animation: loftArmGrab 2.6s ease-out infinite;
}
@keyframes loftArmDrag {
  0%, 14%   { transform: translateX(-5px); }
  46%, 60%  { transform: translateX(5px); }
  92%, 100% { transform: translateX(-5px); }
}
@keyframes loftArmGrab {
  0%   { opacity: 0.8; transform: scale(0.35); }
  24%  { opacity: 0;   transform: scale(1.55); }
  100% { opacity: 0;   transform: scale(1.55); }
}
.arm-hint-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.arm-hint-text b {
  font-family: var(--mono); font-size: 10px; font-weight: 700;
  letter-spacing: 0.2em; color: var(--gold);
}
.arm-hint-text i {
  font-family: var(--mono); font-style: normal; font-size: 8.5px;
  letter-spacing: 0.13em; line-height: 1.5; color: rgba(237, 228, 211, 0.62);
}
body.is-touch .arm-hint { max-width: min(238px, 64vw); }
@media (prefers-reduced-motion: reduce) {
  .arm-hint-glyph svg, .arm-hint-glyph::after { animation: none; }
}

/* ---------- share this track ----------
   The gift. While a record is up, the top-right corner holds a wrapped box —
   a live 3D scene (share-gift.js) — and one legible sentence. Hovering cracks
   the lid; clicking takes the address of the exact cut under the stylus and
   pops the box open. Type is deliberately louder than the rest of the chrome:
   this button is the one thing on the page meant to leave the page. */
.share-btn {
  position: fixed; top: 78px; right: 36px; z-index: 21;
  display: flex; align-items: center; gap: 6px; text-align: left;
  background: rgba(6, 5, 4, 0.58); border: 1px solid rgba(201, 169, 106, 0.45);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  padding: 8px 22px 8px 6px; cursor: pointer;
  /* the liquid-glass top light: a thin inner sheen along the upper edge */
  box-shadow: inset 0 1px 1px rgba(237, 228, 211, 0.09), 0 12px 34px rgba(0, 0, 0, 0.4);
  opacity: 0; visibility: hidden; transform: translateY(-10px); pointer-events: none;
  transition: opacity 0.45s ease, visibility 0.45s, transform 0.28s cubic-bezier(0.22, 1, 0.36, 1),
              border-color 0.25s ease, background 0.25s ease, box-shadow 0.3s ease;
}
body.is-focused .share-btn { opacity: 1; visibility: visible; transform: none; pointer-events: auto; }
body.is-overlay .share-btn { opacity: 0; visibility: hidden; pointer-events: none; }
.share-btn:hover {
  border-color: rgba(201, 169, 106, 0.95); background: rgba(6, 5, 4, 0.78);
  transform: translateY(-2px);
  box-shadow: inset 0 1px 1px rgba(237, 228, 211, 0.12), 0 16px 44px rgba(0, 0, 0, 0.5),
              0 0 34px rgba(201, 169, 106, 0.14);
}
.share-btn:active { transform: translateY(0) scale(0.97); }
.share-gift-canvas { width: 92px; height: 92px; display: block; flex: 0 0 92px; }
.share-gift-flat { display: none; }
/* the flat build: no canvas, a drawn box that still lifts and glows on press */
.share-btn.is-flat .share-gift-canvas { display: none; }
.share-btn.is-flat .share-gift-flat {
  display: block; width: 30px; height: 30px; flex: 0 0 30px; margin: 0 12px 0 14px;
  fill: none; stroke: var(--gold); stroke-width: 1.5;
  stroke-linejoin: round; stroke-linecap: round;
  transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
.share-btn.is-flat:active .share-gift-flat { transform: scale(0.88) rotate(-4deg); }
.share-btn.is-flat.is-given .share-gift-flat { animation: giftPop 0.7s cubic-bezier(0.22, 1, 0.36, 1); }
@keyframes giftPop {
  0% { transform: scale(1); }
  35% { transform: scale(1.3) rotate(-7deg); }
  100% { transform: scale(1); }
}
.share-btn-text b {
  display: block; font-family: var(--mono); font-weight: 700;
  font-size: 11px; letter-spacing: 0.3em; color: var(--gold);
}
.share-btn-text i {
  display: block; font-family: var(--serif); font-style: italic; font-weight: 500;
  font-size: 20px; line-height: 1.15; color: var(--cream); margin-top: 5px;
  transition: color 0.25s ease;
}
.share-btn:hover .share-btn-text i { color: #fff; }

/* the small heart on every cut: each line of the tracklist can be given away */
.tk-share {
  flex: 0 0 26px; display: flex; align-items: center; justify-content: center;
  background: none; border: none; padding: 0; cursor: pointer;
  color: rgba(237, 228, 211, 0.24); transition: color 0.18s ease, transform 0.18s ease;
}
.tk-share svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linejoin: round; stroke-linecap: round; }
.track:hover .tk-share { color: rgba(237, 228, 211, 0.5); }
.tk-share:hover { color: var(--gold); transform: scale(1.22) translateY(-1px); }
.tk-share:active { transform: scale(0.9); }
.track.is-playing .tk-share { color: rgba(201, 169, 106, 0.8); }

/* the confirmation, under the button it answers */
.share-toast {
  position: fixed; top: 196px; right: 36px; z-index: 26;
  display: flex; align-items: center; gap: 13px;
  background: rgba(6, 5, 4, 0.92); border: 1px solid rgba(201, 169, 106, 0.55);
  box-shadow: inset 0 1px 1px rgba(237, 228, 211, 0.08), 0 14px 40px rgba(0, 0, 0, 0.5);
  padding: 15px 19px; pointer-events: none;
  opacity: 0; transform: translateY(-10px);
  transition: opacity 0.4s ease, transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.share-toast.is-visible { opacity: 1; transform: none; }
.share-toast-heart { width: 16px; height: 16px; flex: 0 0 auto; fill: var(--gold); }
.share-toast.is-visible .share-toast-heart { animation: shareBeat 1.9s ease-in-out infinite; }
.share-toast b { display: block; font-family: var(--mono); font-weight: 700; font-size: 10px; letter-spacing: 0.2em; color: var(--cream); }
.share-toast small { display: block; font-family: var(--mono); font-size: 8.5px; letter-spacing: 0.24em; color: var(--gold); margin-top: 5px; }
/* two soft systoles, then rest — the same 1.9s figure the 3D hearts beat */
@keyframes shareBeat {
  0%, 48%, 100% { transform: scale(1); }
  12% { transform: scale(1.22); }
  24% { transform: scale(1.04); }
  34% { transform: scale(1.16); }
}

/* ---------- arriving on a shared link ----------
   The room does not autoplay at a stranger — it stages the offer. The canvas
   plays the unwrapping (share-gift.js); this layer owns the words, the PLAY,
   and the glass. Ink-solid while the library loads unseen behind it; once the
   room has begun it thins to a blur, and accepting PLAY wipes it clear. */
.share-arrival {
  position: fixed; inset: 0; z-index: 120; display: none;
  background: var(--ink);
  transition: background 1.1s ease, opacity 0.9s ease,
              -webkit-backdrop-filter 1.1s ease, backdrop-filter 1.1s ease;
}
.share-arrival.is-open { display: block; }
/* the loading screen never plays on this channel — the unwrapping IS the wait */
body.is-share-arrival .loader { visibility: hidden; }
.share-arrival.is-room-behind {
  background: rgba(6, 5, 4, 0.6);
  -webkit-backdrop-filter: blur(22px); backdrop-filter: blur(22px);
}
/* PLAY has been accepted: the glass clears fast, because the deck is already
   turning underneath it and the record is already making a sound */
.share-arrival.is-leaving {
  background: rgba(6, 5, 4, 0);
  -webkit-backdrop-filter: blur(0px); backdrop-filter: blur(0px);
  opacity: 0; pointer-events: none;
  transition: background 0.45s ease, opacity 0.5s ease,
              -webkit-backdrop-filter 0.45s ease, backdrop-filter 0.45s ease;
}
.share-arrival-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }

.share-arrival-copy {
  position: absolute; top: 9%; left: 0; right: 0; text-align: center;
  padding: 0 24px; pointer-events: none;
}
.share-arrival-eyebrow {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.36em; color: var(--gold);
  animation: arrivalRise 1s cubic-bezier(0.16, 1, 0.3, 1) backwards;
}
/* the message, in characters that can be read across a room */
.share-arrival-msg {
  font-family: var(--serif); font-weight: 600;
  font-size: clamp(30px, 5.4vw, 56px); line-height: 1.08;
  color: var(--cream); margin: 22px 0 0;
  text-shadow: 0 4px 40px rgba(0, 0, 0, 0.65);
  animation: arrivalRise 1.1s cubic-bezier(0.16, 1, 0.3, 1) 0.12s backwards;
}
.share-arrival-msg em { font-style: italic; font-weight: 500; color: var(--gold); }
[hidden].share-arrival-msg { display: none; }

/* The cartel: who it is and what the cut is, standing beside the record the
   way a card stands beside a piece. Position comes from the scene (it tracks
   the sleeve's projected edge); everything here is the typography. */
.share-arrival-credit {
  /* left/top stay at the origin: the scene writes the whole position into the
     transform, which composites instead of laying the page out every frame */
  position: absolute; left: 0; top: 0; margin: 0; text-align: left; pointer-events: none;
  animation: arrivalFade 0.8s ease 0.1s backwards;
}
[hidden].share-arrival-credit { display: none; }
.share-arrival-credit::before {
  content: ''; display: block; width: 34px; height: 1px;
  background: var(--gold); margin-bottom: 15px;
}
.credit-eyebrow {
  font-family: var(--mono); font-size: 8.5px; letter-spacing: 0.34em; color: var(--gold);
}
.credit-artist {
  font-family: var(--mono); font-weight: 700; font-size: 13.5px; letter-spacing: 0.28em;
  text-transform: uppercase; color: var(--cream); margin-top: 16px;
}
/* the thing that was given, set the biggest of anything on this screen */
.credit-track {
  font-family: var(--serif); font-weight: 600; font-style: italic;
  font-size: clamp(24px, 2.5vw, 36px); line-height: 1.1; color: var(--cream);
  margin-top: 8px; text-shadow: 0 3px 26px rgba(0, 0, 0, 0.7);
}
.credit-meta {
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.2em; text-transform: uppercase;
  color: rgba(237, 228, 211, 0.5); margin-top: 14px;
}
.credit-meta:empty { display: none; }
/* the blur-rise: borrowed figure — up out of a soft focus, never a hard pop */
@keyframes arrivalRise {
  from { opacity: 0; transform: translateY(26px); filter: blur(12px); }
  to { opacity: 1; transform: none; filter: blur(0); }
}

/* PLAY, offered on the sleeve itself: the one triangle everyone knows */
.share-arrival-play {
  /* the scene writes the transform each frame — the button rides the record,
     so it lands on the sleeve at any aspect ratio, and moving it costs a
     composite rather than a layout */
  position: absolute; left: 0; top: 0; transform: translate(-50%, -50%);
  width: 96px; height: 96px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(6, 5, 4, 0.44); border: 1.5px solid var(--gold);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  box-shadow: inset 0 1px 1px rgba(237, 228, 211, 0.16), 0 10px 44px rgba(0, 0, 0, 0.55);
  cursor: pointer;
  /* fades in only — the transform belongs to the scene, so an entrance that
     animated it would yank the button off the record */
  animation: arrivalFade 0.7s ease 0.2s backwards;
  transition: background 0.25s ease, opacity 0.25s ease;
}
@keyframes arrivalFade { from { opacity: 0; } to { opacity: 1; } }
.share-arrival-play svg { width: 34px; height: 34px; margin-left: 6px; fill: var(--gold); transition: fill 0.25s ease; }
.share-arrival-play::after {
  content: ''; position: absolute; inset: -7px; border-radius: 50%;
  border: 1px solid rgba(201, 169, 106, 0.65);
  animation: arrivalRing 2.3s ease-out infinite;
}
@keyframes arrivalRing {
  0% { transform: scale(1); opacity: 0.9; }
  70%, 100% { transform: scale(1.45); opacity: 0; }
}
.share-arrival-play:hover { background: var(--gold); }
.share-arrival-play:hover svg { fill: var(--ink); }
.share-arrival-play:active { opacity: 0.85; }
[hidden].share-arrival-play { display: none; }

/* ---------- the wait, counted ----------
   A real percentage of the library's own load. Small and quiet while the gift
   is being read; on PLAY it takes the button's place and holds the screen by
   itself, because a record that starts playing with nothing moving on screen
   reads as broken — and this is the room saying "still coming". */
.share-loading {
  /* Small mode lives in the top corner, clear of the message, the record and
     the cartel — all three of which the scene positions from the 3D, so
     anything sharing their column ends up colliding with one of them. */
  position: absolute; top: calc(14px + env(safe-area-inset-top, 0px)); right: 16px;
  display: flex; flex-direction: column; align-items: center;
  pointer-events: none; animation: arrivalFade 0.7s ease backwards;
  transition: opacity 0.4s ease, top 0.55s cubic-bezier(0.22, 1, 0.36, 1),
              right 0.55s cubic-bezier(0.22, 1, 0.36, 1);
  --ring: 52px;
}
.share-loading-dial { position: relative; width: var(--ring); height: var(--ring); display: block; }
[hidden].share-loading { display: none; }
.share-loading-ring {
  width: var(--ring); height: var(--ring); display: block;
  transform: rotate(-90deg);   /* the arc opens from twelve o'clock */
  transition: width 0.55s cubic-bezier(0.22, 1, 0.36, 1), height 0.55s cubic-bezier(0.22, 1, 0.36, 1);
}
.share-loading-ring .ring-track { fill: none; stroke: rgba(201, 169, 106, 0.18); stroke-width: 3; }
.share-loading-ring .ring-arc {
  fill: none; stroke: var(--gold); stroke-width: 3; stroke-linecap: round;
  /* 2πr for r=54; the offset is written from JS, this is only the resting state */
  stroke-dasharray: 339.29; stroke-dashoffset: 339.29;
}
.share-loading-pct {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--mono); font-weight: 700; font-size: 13px; letter-spacing: 0.06em;
  color: var(--cream);
  transition: font-size 0.55s cubic-bezier(0.22, 1, 0.36, 1);
}
.share-loading-pct i { font-style: normal; font-size: 0.62em; color: var(--gold); margin-left: 1px; }
.share-loading-note {
  /* Out of the flow entirely. In the corner the caption is invisible, but a
     flex column still takes its width from it — which made the small dial a
     137px box that ran under the eyebrow at the top of the screen. Absolute,
     it contributes nothing until the dial owns the screen. */
  position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  margin-top: 20px;
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.34em;
  color: rgba(237, 228, 211, 0.45); white-space: nowrap;
  opacity: 0; transition: opacity 0.3s ease;   /* only the full dial says a word */
}
.share-loading.is-full .share-loading-note { opacity: 1; }

/* PLAY accepted: the ring is the only thing left, dead centre */
/* PLAY accepted: the dial leaves the corner and takes the whole screen */
.share-loading.is-full {
  --ring: 132px;
  top: 50%; right: 50%; transform: translate(50%, -50%);
}
.share-loading.is-full .share-loading-pct { font-size: 27px; }
.share-loading.is-full .share-loading-note { margin-top: 22px; font-size: 9px; letter-spacing: 0.34em; }

/* everything else steps off the stage, the canvas included — a second WebGL
   context is the last thing a phone needs while the room is still loading */
.share-arrival.is-loading .share-arrival-copy,
.share-arrival.is-loading .share-arrival-credit,
.share-arrival.is-loading .share-arrival-play,
.share-arrival.is-loading .share-arrival-skip { opacity: 0; pointer-events: none; transition: opacity 0.4s ease; }
.share-arrival.is-loading .share-arrival-canvas { opacity: 0; transition: opacity 0.5s ease; }

.share-arrival-note {
  position: absolute; left: 50%; bottom: calc(74px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.3em; white-space: nowrap;
  color: rgba(237, 228, 211, 0.55);
  animation: arrivalRise 0.8s cubic-bezier(0.16, 1, 0.3, 1) backwards;
}
.share-arrival-skip {
  position: absolute; left: 50%; bottom: calc(30px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  background: none; border: none; cursor: pointer;
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.3em;
  color: rgba(237, 228, 211, 0.4); transition: color 0.25s ease;
}
.share-arrival-skip:hover { color: var(--cream); }

@media (max-width: 640px) {
  /* the sheet holds the bottom, the nav the top — the gift compacts and slots
     just under the nav, still unmistakably a button */
  .share-btn { top: 62px; right: 12px; gap: 2px; padding: 6px 14px 6px 2px; }
  .share-gift-canvas { width: 64px; height: 64px; flex-basis: 64px; }
  .share-btn-text b { font-size: 9px; letter-spacing: 0.2em; }
  .share-btn-text i { font-size: 15px; margin-top: 3px; }
  .share-toast {
    top: 148px; right: 12px; max-width: calc(100vw - 24px);
    padding: 12px 15px; box-sizing: border-box;
  }
  .share-toast b { font-size: 8.5px; letter-spacing: 0.14em; }
  /* the room behind is already dim; a lighter blur reads the same here and
     costs a fraction of the fill rate */
  .share-arrival.is-room-behind {
    background: rgba(6, 5, 4, 0.72);
    -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  }
  /* portrait hands the top third to the words: tighter tracking so the
     eyebrow holds one line, and a track name that can wrap without shoving
     the record off the glass */
  /* the dial in the corner is pushed down by the notch inset; the copy has to
     be pushed by the same amount or the two converge on a notched phone */
  .share-arrival-copy { top: calc(5% + env(safe-area-inset-top, 0px)); }
  .share-arrival-eyebrow { font-size: 8.5px; letter-spacing: 0.2em; }
  .share-arrival-msg { font-size: clamp(26px, 8vw, 34px); margin-top: 16px; }
  .share-arrival-credit { text-align: center; }
  .share-arrival-credit::before { margin: 0 auto 11px; width: 28px; }
  .credit-eyebrow { font-size: 8px; letter-spacing: 0.26em; }
  .credit-artist { font-size: 10.5px; letter-spacing: 0.2em; margin-top: 11px; }
  .credit-track { font-size: clamp(19px, 5.6vw, 26px); margin-top: 5px; }
  .credit-meta { display: none; }   /* the phone keeps the two lines that matter */
  .share-arrival-play { width: 74px; height: 74px; }
  .share-arrival-play svg { width: 26px; height: 26px; margin-left: 5px; }
  .share-arrival-note { font-size: 8px; letter-spacing: 0.18em; }
  .share-arrival-skip { font-size: 8px; letter-spacing: 0.16em; white-space: nowrap; }
  .share-loading { top: calc(10px + env(safe-area-inset-top, 0px)); right: 12px; --ring: 46px; }
  .share-loading.is-full { --ring: 112px; top: 50%; right: 50%; }
  .share-loading.is-full .share-loading-pct { font-size: 23px; }
}
@media (prefers-reduced-motion: reduce) {
  .share-toast.is-visible .share-toast-heart { animation: none; }
  .share-arrival-eyebrow, .share-arrival-msg, .share-arrival-track,
  .share-arrival-play, .share-arrival-note { animation: none; }
  .share-arrival-play::after { animation: none; opacity: 0.5; }
}
