/* ==========================================================================
   ADDITIONS FOR THE REACT REBUILD
   style.css is the original, copied unchanged. Every new style lives here.
   Palette: Muscadine as fill and border only, never as text; accent text
   uses the passing tint (--muscadine-lt). Only colours, glass and type
   already in style.css are used.
   ========================================================================== */

/* --------------------------------------------------------------------------
   SMALL ROUND BUTTONS — back, restart, skip, shuffle, repeat, queue
   -------------------------------------------------------------------------- */
.ctl {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 1px solid var(--glass-brd);
  background: rgba(244, 240, 235, .03);
  color: var(--ink-mid);
  transition: color .25s, border-color .25s, background .25s, transform .3s var(--ease);
}
.ctl svg { width: 14px; height: 14px; display: block; }
.ctl:hover:not(:disabled) {
  color: var(--ink);
  border-color: var(--glass-brd-hi);
  background: rgba(244, 240, 235, .08);
  transform: translateY(-1px);
}
/* A switch that's on (shuffle, repeat): lit the way the active embed tab is. */
.ctl[aria-pressed="true"] {
  color: var(--cotton);
  border-color: rgba(199, 123, 171, .6);
  background: rgba(127, 42, 101, .32);
}
/* Hovering a switch that's on keeps it lit — a little brighter — instead of
   the plain hover wiping the fill away (owner, 8 Oct). */
.ctl[aria-pressed="true"]:hover:not(:disabled) {
  color: var(--cotton);
  border-color: rgba(199, 123, 171, .85);
  background: rgba(127, 42, 101, .46);
}
.ctl:disabled { opacity: .35; cursor: default; }

/* With its name written next to it (home and Listen pages). */
.ctl--label {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  width: auto;
  padding: 0 .85rem 0 .65rem;
  border-radius: 999px;
  font-size: var(--t-xs);
}

.transport, .modes { display: flex; align-items: center; gap: .4rem; }

/* Position over length (over time left) — mono, like the original times. */
.times {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: .1rem;
  flex: none;
  line-height: 1.2;
}
.times .readout { font-size: 11px; }
/* Every line as wide as the longest time in the playlist (--chars, set by
   Times), so the seconds ticking never resize the waveform beside it. */
.times .readout { min-width: calc(var(--chars, 4) * (1ch + .02em)); text-align: right; }

/* --------------------------------------------------------------------------
   THE MASTHEAD PLAY/PAUSE — same colours as the home page's Play button
   -------------------------------------------------------------------------- */
.mini-play {
  display: grid;
  place-items: center;
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 1px solid rgba(199, 123, 171, .45);
  background: rgba(127, 42, 101, .2);
  transition: background .3s, border-color .3s;
}
.mini-play:hover:not(:disabled) { background: rgba(127, 42, 101, .35); border-color: var(--muscadine-lt); }
.mini-play .play__icon { margin-left: 2px; }
.mini-play[aria-pressed="true"] .play__icon {
  width: 9px;
  height: 11px;
  margin-left: 0;
  border: none;
  border-left: 3px solid var(--cotton);
  border-right: 3px solid var(--cotton);
}
.mini-play:disabled {
  cursor: default;
  background: rgba(244, 240, 235, .05);
  border-color: var(--glass-brd);
}
.mini-play:disabled .play__icon { opacity: .4; }

/* --------------------------------------------------------------------------
   WAVEFORM — the owner's image, or the original flat bars
   -------------------------------------------------------------------------- */
.wave--image { position: relative; display: block; }
.wave__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: fill;
  opacity: .35;
  pointer-events: none;
  user-select: none;
}
.wave__played, .wave__drag, .wave__hover { position: absolute; inset: 0; }
.wave__played .wave__img, .wave__drag .wave__img, .wave__hover .wave__img { opacity: 1; }
/* The paused / playing versions are both in the page (so neither has to
   load at the moment of switching); only one shows. */
.wave__img.is-off { visibility: hidden; }
/* Held song (owner, 12 Oct): the pictures are many times wider than the
   waveform and slid sideways; each layer cuts them off at its edges, so
   they never widen the page. */
.wave__base { position: absolute; inset: 0; }
.wave__base, .wave--slice .wave__played, .wave--slice .wave__drag, .wave--slice .wave__hover { overflow: hidden; }
/* The marker: a line where the song would land, and the time there. */
.wave { position: relative; }
.wave__mark {
  position: absolute;
  top: -3px;
  bottom: -3px;
  width: 2px;
  margin-left: -1px;
  border-radius: 1px;
  background: var(--muscadine-lt);
  pointer-events: none;
  z-index: 2;
}
.wave__mark--drag { background: var(--sorghum-lt); }
.wave__tip {
  position: absolute;
  bottom: calc(100% + 4px);
  left: 0;
  padding: 3px 5px;
  border: 1px solid var(--muscadine-lt);
  border-radius: 4px;
  background: var(--char-deep);
  color: var(--cotton);
  font-family: var(--mono);
  font-size: 10px;
  line-height: 1;
  letter-spacing: .02em;
  white-space: nowrap;
}
.wave__mark--drag .wave__tip { border-color: var(--sorghum-lt); }
/* The owner's own played/unplayed pair: both exactly as drawn. */
.wave--own .wave__img { opacity: 1; }
/* Dragging sideways seeks; up-and-down still scrolls the page on a phone. */
.wave.is-seekable { touch-action: pan-y; user-select: none; }
.wave.is-dragging { cursor: grabbing; }
.wave.is-dragging span { transition: none; }

/* --------------------------------------------------------------------------
   THE HEADER PLAYER — every page
   Wide screens: between the name and the menu. Narrower: its own row under
   the menu. Phones: under the menu, two lines, no back/skip (swipe instead).
   -------------------------------------------------------------------------- */
.hp {
  position: relative;
  display: flex;
  align-items: center;
  gap: .6rem;
  min-width: 0;
}
.hp .wave { flex: 0 1 120px; width: 120px; height: 24px; gap: 1px; }
.hp .wave span { min-width: 1px; }
.hp .transport, .hp .modes { gap: .3rem; }
.hp__now { position: relative; display: flex; align-items: center; gap: .35rem; min-width: 0; }
/* The title on top of invisible copies of every title line it can show. */
.hp__title { display: grid; min-width: 0; }
.hp__title > * { grid-area: 1 / 1; min-width: 0; }
.hp__size { visibility: hidden; }
.hp__text {
  font-size: var(--t-xs);
  color: var(--ink-mid);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 15rem;
}
.ctl--queue svg { width: 13px; height: 13px; transition: transform .3s var(--ease); }
.ctl--queue[aria-expanded="true"] svg { transform: rotate(180deg); }

/* The queue: glass, like the masthead it drops from. */
.queue {
  position: absolute;
  top: calc(100% + .6rem);
  left: 0;
  z-index: 70;
  width: min(320px, calc(100vw - 2.5rem));
  max-height: min(60vh, 420px);
  overflow-y: auto;
  padding: .45rem;
  border-radius: 14px;
  background: rgba(28, 24, 21, .97);
  border: 1px solid var(--glass-brd-hi);
  box-shadow: 0 22px 48px -18px rgba(0, 0, 0, .9);
}
.queue__head {
  display: flex;
  justify-content: space-between;
  padding: .3rem .55rem .5rem;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .06em;
  color: var(--ink-dim);
}
.queue__list { list-style: none; margin: 0; padding: 0; }
.queue__item {
  display: grid;
  grid-template-columns: 1.6em 1fr auto;
  gap: .5rem;
  align-items: center;
  width: 100%;
  padding: .55rem;
  border-radius: 8px;
  text-align: left;
  font-size: var(--t-sm);
  color: var(--ink-mid);
  transition: background .25s, color .25s;
}
.queue__item:hover { background: rgba(244, 240, 235, .06); color: var(--ink); }
.queue__item.is-current { background: rgba(127, 42, 101, .24); color: var(--cotton); }
.queue__n, .queue__sub { font-family: var(--mono); font-size: 10px; color: var(--ink-dim); }
.queue__title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Not enough room beside the menu: the player takes its own row. */
@media (max-width: 1679px) {
  .hp { order: 4; flex: 1 1 100%; }
  .hp .wave { flex: 1 1 auto; width: auto; }
  .hp__text { max-width: 22rem; }
}
@media (min-width: 721px) and (max-width: 1679px) {
  .masthead { flex-wrap: wrap; row-gap: .75rem; }   /* phones already wrap, with their own spacing */
}

/* --------------------------------------------------------------------------
   HOME + LISTEN PLAYER — ▶︎‖ over back/restart/skip, waveform, times;
   shuffle and repeat underneath
   -------------------------------------------------------------------------- */
.fp { display: flex; flex-direction: column; gap: .75rem; margin-top: .4rem; min-width: 0; }

/* Keep the home cover inside its panel at every width (owner, 7 Oct: it was
   overlapping the panel's edge). The hero's text column may now be narrower
   than its widest line, the player sizes itself to its own column (so it
   can't push the column wider), the waveform's bars may thin to 1px, and when
   the column is really narrow the waveform takes its own line, as on phones. */
.panel--hero .panel__body { min-width: 0; }
.fp { container-type: inline-size; }
.fp .wave span { min-width: 1px; }
@container (max-width: 380px) {
  .fp .player { flex-wrap: wrap; }
  .fp .wave { order: 3; flex: 1 1 100%; }
  .fp .times { margin-left: auto; }
}
.fp .player { margin-top: 0; }
.fp__buttons { display: flex; flex-direction: column; align-items: center; gap: .5rem; flex: none; }

/* Listen page: the song's cover on the left of its player. */
.lp { display: flex; align-items: center; gap: clamp(1rem, 1.6vw, 1.5rem); }
.lp .cover {
  width: clamp(140px, 13vw, 200px);
  margin: 0;
  flex: none;
  align-self: center;
  transform: none;
}
.lp .fp { flex: 1; margin-top: 0; }

/* --------------------------------------------------------------------------
   COVER ART — the song's name shows until (or instead of) the picture
   -------------------------------------------------------------------------- */
.cover__square > * { grid-area: 1 / 1; }
.cover.has-cover .cover__soon { display: block; }   /* stays underneath the picture */
.cover__art { position: relative; z-index: 1; background: var(--char-deep); }
.cover__link { position: absolute; inset: 0; z-index: 3; border-radius: inherit; }
.cover__link:focus-visible { outline: 2px solid var(--muscadine-lt); outline-offset: 3px; }
.cover:has(.cover__link) { transition: border-color .3s; }
.cover:has(.cover__link):hover { border-color: rgba(199, 123, 171, .55); }

/* Titles that are links look exactly as before: the gradient shows through. */
.page-head__link, .hero__link { color: inherit; text-decoration: none; }

/* PAGE TITLES (owner, 29 Oct): every title shows its Cotton → tan gradient
   across its own words, as "Press Kit" does in the press kit. style.css's
   title stretched the gradient over the page's whole width, so a short
   title ("Shows") only ever showed the Cotton end. */
.page-head h1 { width: fit-content; }

/* Home tiles: a logo never runs edge to edge in a narrow tile, and is never
   squeezed out of shape — it shrinks whole instead (owner, 7 Oct: SoundCloud
   filled its tile at laptop widths). No other logo is wide enough to notice. */
.platforms--tiles .platform img { max-width: calc(100% - 1rem); object-fit: contain; object-position: left center; }
/* Shorter tiles now that they're logo only (owner, 27 Oct: "make the vertical
   length smaller… keep the horizontal"): 116px → 84px tall, same widths and
   side padding. */
.platforms--tiles .platform { min-height: 84px; padding-top: 1rem; padding-bottom: 1rem; }

/* Listen page: songs in the Releases list are links, each with a small
   square of its cover in front (owner, 7 Oct). The empty frame is Char only:
   covers often show the artist, so no Sorghum behind them. */
a.tracklist__item { color: inherit; text-decoration: none; grid-template-columns: auto 1fr auto; gap: .75rem; }
.thumb {
  position: relative;
  display: block;
  width: 40px;
  height: 40px;
  flex: none;
  border-radius: 6px;
  overflow: hidden;
  border: 1px solid rgba(244, 240, 235, .12);
  background: linear-gradient(160deg, var(--char-lift2), var(--char-deep));
}
.thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.tracklist__item.is-viewed { background: rgba(244, 240, 235, .05); }

/* Phones-only swipe row on the home page (hidden elsewhere). */
.hero-swipe { display: none; }

/* --------------------------------------------------------------------------
   PHONES
   -------------------------------------------------------------------------- */
@media (max-width: 720px) {
  /* Seven tabs since "Shop": the gaps shrink with the screen so they stay on
     one line (a very narrow phone wraps Shop rather than cutting it off). */
  .masthead__nav { flex-wrap: wrap; gap: .45rem clamp(.55rem, 2.8vw, 1.1rem); }

  /* Header player: under the menu. Back and skip are swipes here. */
  .hp {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto auto;
    grid-template-areas:
      "play now  now   restart"
      "wave wave times modes";
    align-items: center;
    gap: .5rem .55rem;
    touch-action: pan-y;
  }
  .hp .mini-play { grid-area: play; }
  .hp__now { grid-area: now; }
  .hp__text { max-width: none; }
  /* Play sits inside the back · restart · play · next group (wide screens,
     owner 11 Oct); here the group opens up so play keeps its place on the
     left, as before. */
  .hp .transport { display: contents; }
  .hp .transport > .ctl:not(.ctl--step) { grid-area: restart; }
  .hp .ctl--step { display: none; }
  .hp .wave { grid-area: wave; width: auto; height: 22px; }
  .hp .times { grid-area: times; }
  .hp .modes { grid-area: modes; }
  .hp .times .readout { font-size: 10px; }

  /* Home: swipe through the songs; the big cover and title move into it.
     min-width: 0 keeps the row (and the player under it) inside the panel. */
  .panel--hero .panel__body, .lp .fp { min-width: 0; }
  .hero-swipe {
    min-width: 0;
    display: flex;
    gap: 2rem;                       /* no sliver of the next song at the edge */
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
  }
  .hero-swipe::-webkit-scrollbar { display: none; }
  .hero-swipe__slide {
    flex: 0 0 100%;
    scroll-snap-align: start;
    display: flex;
    align-items: center;
    gap: 1rem;
    min-width: 0;
    color: inherit;
    text-decoration: none;
  }
  .hero-swipe .cover { width: 104px; margin: 0; flex: none; align-self: center; }
  .hero-swipe__text { display: flex; flex-direction: column; gap: .35rem; min-width: 0; }
  .hero-swipe .hero__title { font-family: var(--display); font-size: clamp(1.55rem, 7vw, 2.1rem); }
  .panel--hero > .cover { display: none; }
  .hero__now {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  .fp .times { margin-left: auto; }
  .lp { align-items: flex-start; }
  .lp .cover { width: 96px; }
}

/* --------------------------------------------------------------------------
   PICTURES (7 Oct, evening): AVIF / WebP / JPG in a <picture> that doesn't
   change the layout. Covers show their small copy at once; the sharp copy
   fades in over it when it's allowed to load (lib/net.js).
   -------------------------------------------------------------------------- */
.pic { display: contents; }
.cover__square .cover__art { grid-area: 1 / 1; }
.cover__art--sharp { z-index: 2; opacity: 0; transition: opacity .45s ease; }
.cover__art--sharp.is-loaded { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .cover__art--sharp { transition: none; } }

/* --------------------------------------------------------------------------
   SOUND QUALITY, NOTICES, MOVING HEADER TEXT (7 Oct, evening)
   -------------------------------------------------------------------------- */
/* Long and short headings: "Sound quality" / "Sound", "Up next" / "Q". */
.label-short { display: none; }

/* The quality button: the same pill as Shuffle and Repeat. In the masthead
   it's a compact pill that shows only the words ("Auto · 128k"). */
.qsel { position: relative; display: inline-flex; }
/* Equal padding both sides, so the words sit in the middle (owner, 8 Oct). */
.ctl--quality { position: relative; padding: 0 .95rem; font-family: var(--mono); font-size: 11px; letter-spacing: .02em; }
.ctl--quality.ctl--compact { height: 30px; padding: 0 .85rem; font-size: 10px; }
.ctl--quality:disabled { cursor: default; }
/* The label sits on top of invisible copies of every label it can show:
   the button is always as wide as the longest. */
.qsel__now { display: inline-grid; justify-items: center; }
.qsel__now > span { grid-area: 1 / 1; }
.qsel__size { visibility: hidden; }
/* The pick isn't what's playing yet: a short bright stretch sweeps along a
   faint line under the words (owner, 11 Oct: "C, underline sweep" —
   replaces 8 Oct's figure-8). It sits inside the button, between its
   padding, and takes no room: the button never changes size. */
.ctl--quality .qsel__wait {
  position: absolute;
  left: .95rem;
  right: .95rem;
  bottom: 4px;
  height: 2px;
  border-radius: 1px;
  overflow: hidden;
  background: rgba(199, 123, 171, .22);
  visibility: hidden;
  pointer-events: none;
}
.ctl--quality.ctl--compact .qsel__wait { left: .85rem; right: .85rem; bottom: 3px; }
.qsel__wait.is-on { visibility: visible; }
/* Brighter and wider since 11 Oct ("a little more visibly moving"). */
.qsel__run {
  position: absolute; top: 0; left: 0;
  width: 40%; height: 100%;
  border-radius: 1px;
  background: linear-gradient(90deg, rgba(199, 123, 171, 0), var(--muscadine-lt) 30%, #E4B3D2 60%, var(--muscadine-lt) 80%, rgba(199, 123, 171, 0));
  animation: qrun 1s ease-in-out infinite;
}
@keyframes qrun { from { transform: translateX(-100%); } to { transform: translateX(250%); } }
/* Less motion asked for (the owner's own Windows has animation effects off):
   it still moves — it's a small loading sign, not scenery — but slower. */
@media (prefers-reduced-motion: reduce) {
  /* style.css stops every animation then, with !important — this one is
     let through on purpose. */
  .ctl--quality .qsel__run { animation-duration: 1.8s !important; animation-iteration-count: infinite !important; }
}
/* The Repeat button sits on an invisible "Repeat Song" button: it keeps its
   own size (growing to the right for Repeat Song), the room stays fixed. */
.ctl-slot { display: inline-grid; justify-items: start; align-items: center; }
.ctl-slot > * { grid-area: 1 / 1; }
.ctl-slot--plain { display: contents; }
.ctl-slot__size { visibility: hidden; pointer-events: none; }
.qsel__menu { left: auto; right: 0; width: min(260px, calc(100vw - 2.5rem)); }
.queue__foot {
  margin: .4rem .5rem .1rem;
  font-family: var(--mono);
  font-size: 10px;
  color: var(--ink-dim);
}

/* Moving header text: the words slide inside the same box. */
.hp__text .marquee { display: inline-flex; white-space: nowrap; will-change: transform; }

/* Notices: a chat bubble in the bottom-right corner (owner, 8 Oct), a
   little bigger than before. A notice catches its own clicks — nothing
   underneath it is touched. */
.notice-slot {
  position: fixed;
  right: max(1rem, env(safe-area-inset-right));
  bottom: max(1rem, env(safe-area-inset-bottom));
  z-index: 200;
  width: min(340px, calc(100vw - 2rem));
  display: flex;
  justify-content: flex-end;
  pointer-events: none;
}
.notice {
  pointer-events: auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .35rem;
  max-width: 100%;
  padding: .85rem 1.15rem 1rem;
  border-radius: 18px 18px 6px 18px;   /* the corner nearest the screen's corner, tucked in */
  background: rgba(28, 24, 21, .95);
  border: 1px solid rgba(127, 42, 101, .6);
  box-shadow: 0 22px 48px -18px rgba(0, 0, 0, .9), 0 0 28px -10px rgba(127, 42, 101, .65);
  backdrop-filter: blur(var(--blur)) saturate(130%);
  -webkit-backdrop-filter: blur(var(--blur)) saturate(130%);
  color: var(--ink);
  font-size: 1rem;
  line-height: 1.35;
  text-align: left;
  transform-origin: 100% 100%;
  animation: noticeIn .3s var(--ease);
}
/* "● hewy.rocks" — who it's from, like a name over a chat message. */
.notice__from {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .08em;
  color: var(--ink-dim);
}
.notice__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--muscadine-lt); box-shadow: 0 0 8px var(--muscadine); }
.notice__text { font-weight: 500; }
.notice__sub { color: var(--ink-mid); font-size: .92rem; }
.notice__link { color: var(--muscadine-lt); font-size: .95rem; text-decoration: underline; text-underline-offset: 3px; }
.notice__link:hover { color: var(--cotton); }
@keyframes noticeIn { from { opacity: 0; transform: translateY(12px) scale(.97); } }
@media (prefers-reduced-motion: reduce) { .notice { animation: none; } }

/* Very narrow screens: the short headings. */
@media (max-width: 360px) {
  .label-long { display: none; }
  .label-short { display: inline; }
}

/* --------------------------------------------------------------------------
   CLICKS LAND WHERE THE BUTTONS ARE (owner, 8 Oct)
   style.css floats panel content "above the glass" (translateZ 40px, the
   cover 25px) under the deck's 1400px perspective. The glass (overflow:
   hidden + backdrop-filter) makes browsers DRAW that flat — but Chrome and
   Edge still work out clicks as if it floated, magnified ~3% out from the
   middle of the page: on the home player every click area sat ~13px left
   and ~17px above its button (measured: Shuffle drawn 52–136, clicks landed
   39–124), so the right of Shuffle pressed Repeat. Without the float,
   drawing and clicking agree. Nothing looks different in Chrome or Edge,
   where the float was never drawn. Phones never had it (no perspective).
   -------------------------------------------------------------------------- */
.panel__body, .cover { transform: none; }

/* --------------------------------------------------------------------------
   THE FOOTER STAYS AT THE FOOT (owner, 9 Oct)
   On a page shorter than the window (zoomed out, or a very tall screen),
   the spare space goes above the footer instead of below it. Pages taller
   than the window are unchanged: every block measured in the same place
   before and after, on all fourteen pages, at 1440 and 390 wide.
   -------------------------------------------------------------------------- */
body { display: flex; flex-direction: column; }
body > .pagewrap { flex: 1 0 auto; }

/* --------------------------------------------------------------------------
   ZOOMED OUT: THE SCENE KEEPS ITS SHAPE (owner, 9 Oct)
   Zooming out makes the window wider and taller in page pixels, but the
   scenery's size limits in style.css are fixed pixel counts. So the land
   stayed short while the window grew, and to fill the width the browser
   magnified just the bottom strip of each picture: the "boxy" shapes were
   the bottoms of the trees, poles and buildings, five times too big.
   --zk is how far out the visitor has zoomed (1 at normal size, 2 at 50%,
   4 at 25%; set by a small script in root.jsx, which says how it's
   measured). Every fixed size in the scene is multiplied by it, so the
   scene keeps its shape at every zoom. At --zk 1
   these rules are exactly style.css's own. Sizes given in parts of the
   window (vw, vh, %) already keep their shape and aren't repeated here.
   -------------------------------------------------------------------------- */
.bloom { filter: blur(calc(90px * var(--zk, 1))); }
.bloom--muscadine { min-width: calc(420px * var(--zk, 1)); min-height: calc(420px * var(--zk, 1)); }
.bloom--sorghum   { min-width: calc(360px * var(--zk, 1)); min-height: calc(360px * var(--zk, 1)); }
.bloom--stone     { min-width: calc(300px * var(--zk, 1)); min-height: calc(300px * var(--zk, 1)); }

.treeline--far {
  height: clamp(calc(120px * var(--zk, 1)), 23vh, calc(260px * var(--zk, 1)));
  filter: blur(calc(1.7px * var(--zk, 1)));
}
.treeline--near { height: clamp(calc(165px * var(--zk, 1)), 31vh, calc(350px * var(--zk, 1))); }

.moon {
  width: clamp(calc(76px * var(--zk, 1)), 7.5vw, calc(132px * var(--zk, 1)));
  box-shadow:
    0 0 calc(26px * var(--zk, 1)) calc(3px * var(--zk, 1)) rgba(244, 240, 235, .30),
    0 0 calc(70px * var(--zk, 1)) calc(16px * var(--zk, 1)) rgba(244, 240, 235, .16),
    0 0 calc(160px * var(--zk, 1)) calc(50px * var(--zk, 1)) rgba(214, 208, 199, .07);
}
.moonglow { width: clamp(calc(360px * var(--zk, 1)), 46vw, calc(760px * var(--zk, 1))); }

.city     { height: clamp(calc(175px * var(--zk, 1)), 32vh, calc(360px * var(--zk, 1))); }
.juke     { height: clamp(calc(150px * var(--zk, 1)), 27vh, calc(300px * var(--zk, 1))); }
.lines    { height: clamp(calc(215px * var(--zk, 1)), 41vh, calc(460px * var(--zk, 1))); }
.landmark { height: clamp(calc(180px * var(--zk, 1)), 34vh, calc(380px * var(--zk, 1))); }
.fence    { height: clamp(calc(74px * var(--zk, 1)), 13vh, calc(150px * var(--zk, 1))); }
.kudzu    { width: clamp(calc(88px * var(--zk, 1)), 13vw, calc(210px * var(--zk, 1))); }

.train { bottom: clamp(calc(60px * var(--zk, 1)), 10vh, calc(122px * var(--zk, 1))); }
.train__beam {
  bottom: calc(-1px * var(--zk, 1));
  width: calc(7px * var(--zk, 1)); height: calc(4px * var(--zk, 1));
  box-shadow:
    0 0 calc(6px * var(--zk, 1)) calc(2px * var(--zk, 1)) rgba(255, 244, 214, .85),
    0 0 calc(16px * var(--zk, 1)) calc(5px * var(--zk, 1)) rgba(248, 224, 168, .55),
    0 0 calc(40px * var(--zk, 1)) calc(13px * var(--zk, 1)) rgba(226, 186, 120, .26);
}
.train__cars {
  right: calc(5px * var(--zk, 1)); bottom: calc(-1px * var(--zk, 1));
  width: clamp(calc(90px * var(--zk, 1)), 11vw, calc(170px * var(--zk, 1)));
  height: calc(5px * var(--zk, 1));
  background: repeating-linear-gradient(90deg,
    #0E0C0A 0 calc(15px * var(--zk, 1)), rgba(14, 12, 10, .2) calc(15px * var(--zk, 1)) calc(18px * var(--zk, 1)));
}

/* Fireflies and foxfire: their size, glow and drift all grow together. */
.motes span { scale: var(--zk, 1); }

/* HOVER LIKE THE OWNER'S PC (owner, 19 Oct, from a screen recording of
   their own PC, where reduced motion is on): hovering a tile lights its
   border and lifts it at once; the panel around it lights at once too.
   No tilt (lib/tilt.js), no shine following the mouse, no purple wash
   wiping across the tile. Only these hovers changed — everything else
   still moves as before, and reduced motion still stops the rest. */
.panel, .panel__sheen, .platform { transition: none; }
.platform::before { display: none; }

/* PRIVACY (owner, 19 Oct): the footer link sits to the right of the
   copyright line; the page's lists and links read like its text. */
.footer__sep { margin: 0 .55em; }
.prose ul { margin: 0; padding-left: 1.1rem; list-style: disc; font-size: var(--t-sm); color: var(--ink-mid); max-width: 64ch; }
.prose li::marker { color: var(--muscadine-lt); }
.prose li + li { margin-top: .4rem; }
.prose a { color: var(--sorghum-lt); }

/* MAILING LIST (owner, 11 Oct): the signup form uses style.css's own .signup
   look (13d). These two only place it in the panel. */
/* The form, then the social icons, spaced like the "Coming soon" version. */
.listbox { display: flex; flex-direction: column; gap: .7rem; }
/* A box only robots fill in: out of sight, out of the tab order, and
   skipped by screen readers. */
.signup__trap {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* HOVER DESCRIPTIONS (owner, 11 Oct): one bubble for every `title` on the
   site (components/tips.jsx) — dark, a thin Muscadine-tint border and a small
   pointer, in the spirit of the waveform's time bubble. Also the volume's
   live percentage (.vol__val). */
.tipbox, .vol__val {
  border: 1px solid rgba(199, 123, 171, .75);
  border-radius: 8px;
  background: rgba(22, 19, 16, .96);
  color: var(--cotton);
  box-shadow: 0 10px 26px -12px rgba(0, 0, 0, .9), 0 0 0 3px rgba(127, 42, 101, .12);
  white-space: nowrap;
  pointer-events: none;
}
.tipbox {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 200;
  max-width: min(320px, calc(100vw - 16px));
  padding: .38rem .62rem;
  font-family: var(--body);
  font-size: 12px;
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: .01em;
  white-space: normal;
  opacity: 0;
  visibility: hidden;
  margin-top: 4px;
  transition: opacity .16s ease, margin-top .16s ease, visibility 0s linear .16s;
}
.tipbox.is-below { margin-top: -4px; }
.tipbox.is-on { opacity: 1; visibility: visible; margin-top: 0; transition: opacity .16s ease, margin-top .16s ease; }
.tipbox.is-measuring { visibility: hidden; transition: none; }
/* The small pointer, under the bubble (over it when the bubble is below). */
.tipbox::after {
  content: "";
  position: absolute;
  left: var(--ax, 50%);
  top: 100%;
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  background: inherit;
  border-right: 1px solid rgba(199, 123, 171, .75);
  border-bottom: 1px solid rgba(199, 123, 171, .75);
  transform: rotate(45deg);
}
.tipbox.is-below::after { top: auto; bottom: 100%; margin: 0 0 -4px -4px; transform: rotate(225deg); }

/* VOLUME (owner, 11 Oct): a speaker button and a fader, after the sound
   quality button. Home and Listen: side by side. The bar at the top: the
   speaker opens the fader in a small glass bubble under it. */
.vol { display: inline-flex; align-items: center; gap: .45rem; }
/* Home and Listen: when the row runs out of room (phones), the volume moves
   down to a line of its own instead of running over the quality button. */
.modes { flex-wrap: wrap; row-gap: .55rem; }
.hp .modes { flex-wrap: nowrap; }
.vol__fader {
  -webkit-appearance: none; appearance: none;
  width: 84px; height: 18px; margin: 0;
  background: transparent; cursor: pointer;
}
.vol__fader::-webkit-slider-runnable-track {
  height: 3px; border-radius: 2px;
  background: linear-gradient(90deg, var(--muscadine-lt) var(--v, 100%), rgba(244, 240, 235, .16) var(--v, 100%));
}
.vol__fader::-moz-range-track { height: 3px; border-radius: 2px; background: rgba(244, 240, 235, .16); }
.vol__fader::-moz-range-progress { height: 3px; border-radius: 2px; background: var(--muscadine-lt); }
.vol__fader::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 12px; height: 12px; margin-top: -4.5px;
  border: 0; border-radius: 50%;
  background: var(--cotton);
  box-shadow: 0 0 0 3px rgba(127, 42, 101, .35);
  transition: box-shadow .2s;
}
.vol__fader::-moz-range-thumb {
  width: 12px; height: 12px; border: 0; border-radius: 50%;
  background: var(--cotton);
  box-shadow: 0 0 0 3px rgba(127, 42, 101, .35);
}
.vol__fader:hover::-webkit-slider-thumb { box-shadow: 0 0 0 5px rgba(127, 42, 101, .45); }
/* The live percentage over the knob (the knob is 12px: its centre runs from
   6px to the width less 6px). */
.vol__track { position: relative; display: inline-flex; align-items: center; }
.vol__val {
  position: absolute;
  bottom: calc(100% + 7px);
  left: calc(6px + (100% - 12px) * var(--v-num, 1));
  transform: translateX(-50%);
  padding: .22rem .42rem;
  font-family: var(--mono);
  font-size: 10px;
  line-height: 1;
  opacity: 0;
  transition: opacity .15s ease;
  z-index: 5;
}
.vol__track:hover .vol__val,
.vol__track:active .vol__val,
.vol__track:has(.vol__fader:focus-visible) .vol__val { opacity: 1; }
.vol__fader:focus-visible { outline: 2px solid var(--muscadine-lt); outline-offset: 4px; border-radius: 4px; }
.vol--compact { position: relative; }
.vol__pop {
  position: absolute;
  top: calc(100% + .6rem);
  right: 0;
  z-index: 70;
  display: flex; align-items: center; gap: .5rem;
  padding: .4rem .8rem .4rem .45rem;
  border-radius: 999px;
  background: rgba(28, 24, 21, .97);
  border: 1px solid var(--glass-brd-hi);
  box-shadow: 0 22px 48px -18px rgba(0, 0, 0, .9);
}
.vol__pop .vol__fader { width: 110px; }
/* Phones: the bar at the top stays as it was — the phone's own volume
   buttons are right there. (Home and Listen keep their fader.) */
@media (max-width: 720px) { .hp .vol { display: none; } }

/* RELEASE PAGE (owner, 11 Oct) — /listen/<song>/release. The night scene,
   and in the middle: the cover with its play/stop button, the title, then
   one full-width row per platform. Glass and colours as the panels. */
body > .release { flex: 1 0 auto; }
.release {
  position: relative;
  z-index: 10;
  display: flex;
  justify-content: center;
  align-items: center;
  padding: clamp(4.5rem, 9vh, 5.5rem) 16px clamp(2rem, 9vh, 5rem);
}
/* Home, top right (owner, 11 Oct). */
.release__home {
  position: absolute;
  top: 16px;
  right: 16px;
  display: inline-flex; align-items: center; gap: .45rem;
  padding: .5rem .95rem .5rem .75rem;
  border-radius: 999px;
  font-size: var(--t-sm);
  color: var(--ink);
  text-decoration: none;
  background: var(--glass-bg);
  border: 1px solid var(--glass-brd);
  backdrop-filter: blur(var(--blur)) saturate(130%);
  -webkit-backdrop-filter: blur(var(--blur)) saturate(130%);
  transition: transform .3s var(--ease), border-color .3s, background .3s;
}
.release__home svg { width: 16px; height: 16px; display: block; }
.release__home:hover { transform: translateY(-2px); border-color: var(--glass-brd-hi); background: rgba(244, 240, 235, .07); }
.release__home:focus-visible { outline: 2px solid var(--muscadine-lt); outline-offset: 3px; }
/* The mailing-list box, under the rows: the site's own signup form. */
.release__list {
  margin-top: .35rem;
  padding: 1rem 1rem .6rem;
  border-radius: 14px;
  background: var(--glass-bg);
  border: 1px solid var(--glass-brd);
  backdrop-filter: blur(var(--blur)) saturate(130%);
  -webkit-backdrop-filter: blur(var(--blur)) saturate(130%);
}
.release__list-h {
  margin: 0 0 .7rem;
  font-family: var(--display);
  font-size: 1.15rem;
  font-weight: 600;
  font-variation-settings: "SOFT" 30, "WONK" 1;
}
.release__card { width: min(440px, 100%); display: flex; flex-direction: column; gap: 1.15rem; }
.release__cover {
  position: relative;
  margin: 0;
  aspect-ratio: 1;
  border-radius: 16px;
  overflow: hidden;
  background: var(--glass-bg);
  box-shadow: 0 0 0 1px var(--glass-brd), 0 34px 70px -30px rgba(0, 0, 0, .95);
  animation: riseIn .65s var(--ease) backwards;
}
.release__cover .pic, .release__cover img { display: block; width: 100%; height: 100%; object-fit: cover; }
.release__nocover {
  position: absolute; inset: 0; display: grid; place-items: center; padding: 1rem; text-align: center;
  font-family: var(--display); color: var(--ink-mid);
}
.release__play {
  position: absolute; inset: 0; margin: auto;
  width: 78px; height: 78px; border-radius: 50%;
  display: grid; place-items: center;
  color: var(--cotton);
  background: rgba(40, 35, 31, .5);
  border: 1px solid rgba(244, 240, 235, .38);
  backdrop-filter: blur(10px) saturate(130%);
  -webkit-backdrop-filter: blur(10px) saturate(130%);
  box-shadow: 0 14px 34px -14px rgba(0, 0, 0, .9);
  cursor: pointer;
  transition: transform .35s var(--ease), background .3s, border-color .3s, box-shadow .3s;
}
.release__play svg { width: 30px; height: 30px; display: block; }
.release__play:not(.is-playing) svg { margin-left: 3px; }
.release__play:hover, .release__play.is-playing {
  background: rgba(127, 42, 101, .62);
  border-color: var(--muscadine-lt);
  box-shadow: 0 0 0 6px rgba(127, 42, 101, .22), 0 14px 34px -14px rgba(0, 0, 0, .9);
}
.release__play:hover { transform: scale(1.06); }
.release__play:focus-visible { outline: 2px solid var(--muscadine-lt); outline-offset: 4px; }
.release__chip {
  position: absolute; left: 12px; bottom: 12px;
  padding: .42rem .65rem; border-radius: 999px;
  font: 500 10px/1 var(--mono); letter-spacing: .08em; text-transform: uppercase;
  color: var(--cotton);
  background: rgba(28, 24, 21, .72);
  border: 1px solid var(--glass-brd);
}
.release__head { text-align: center; }
.release__title {
  margin: 0;
  font-family: var(--display);
  font-size: clamp(1.6rem, 6.4vw, 2.25rem);
  font-weight: 700;
  font-variation-settings: "SOFT" 30, "WONK" 1;
  letter-spacing: -.01em;
  line-height: 1.1;
}
.release__artist { margin: .35rem 0 0; color: var(--ink-mid); }
.release__date { margin: .5rem 0 0; font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .06em; color: var(--ink-dim); }
.release__links { display: flex; flex-direction: column; gap: .55rem; }
.rel-link {
  display: flex; align-items: center; justify-content: center; gap: .85rem;
  min-height: 3.35rem;
  padding: .65rem .8rem;
  border-radius: 14px;
  color: var(--ink);
  text-decoration: none;
  background: var(--glass-bg);
  border: 1px solid var(--glass-brd);
  backdrop-filter: blur(var(--blur)) saturate(130%);
  -webkit-backdrop-filter: blur(var(--blur)) saturate(130%);
  transition: transform .3s var(--ease), border-color .3s, background .3s;
}
.rel-link:hover { transform: translateY(-2px); border-color: var(--glass-brd-hi); background: rgba(244, 240, 235, .07); }
.rel-link:focus-visible { outline: 2px solid var(--muscadine-lt); outline-offset: 3px; }
.rel-link__logo { display: block; height: 24px; width: auto; max-width: 150px; object-fit: contain; }
.rel-link__logo.is-invert { filter: invert(1); }
.rel-link__name { min-width: 0; font-weight: 500; }
/* The social icons, centred under the mailing-list box. */
.release__socials { justify-content: center; margin-top: .5rem; }

/* CONFIRM PAGE (owner, 15 Oct) — /confirm: the release page's layout, with
   one glass box (routes/confirm.jsx). It keeps its height while the page
   reads its link, so nothing jumps. */
.confirm__box { min-height: 6.4rem; }
.confirm__label { flex-basis: 100%; font-size: var(--t-sm); color: var(--ink-mid); }
.confirm__note { margin: 0 0 .7rem; color: var(--ink); line-height: 1.5; }

/* PRESS KIT (owner, 28–29 Oct): the Press page and the kit share their own
   stylesheet, styles/press-kit.css. */
