/* ───────────────────────────────────────────────────────────
   Yamanote Line — station melody player
   Visual system lifted from the Figma source:
     bg        rgb(25,25,23)      warm near-black
     JR green  rgb(50,205,102)
     control   linear-gradient(180deg, #474745, #353533)
     type      Space Grotesk (Medium / Bold)
   ─────────────────────────────────────────────────────────── */

:root {
  /* ── themeable surface tokens (dark is the default) ──────────────
     Light mode is applied via html[data-theme="light"] below; the app
     boot script stamps the attribute before first paint (no flash). */
  --bg:        rgb(25, 25, 23);     /* warm near-black surface         */
  --fg:        #fff;                /* primary text / active icons     */
  --panel:     rgb(34, 34, 31);     /* elevated surface (info modal)   */
  --panel-edge: rgba(255, 255, 255, 0.07);
  --green:     rgb(50, 205, 102);   /* JR green — constant in both modes */
  --ctrl-top:  rgb(71, 71, 69);     /* button gradient — top stop      */
  --ctrl-bot:  rgb(53, 53, 51);     /* button gradient — bottom stop   */
  --ctrl-ring: rgba(255, 255, 255, 0.2);   /* inset top highlight      */
  --ctrl-shadow: 0 0 0 0 transparent;       /* extra lift in light mode */
  /* control surface as gradient tokens so the pressed state can flip the
     gradient vertically. --ctrl-bg-active is the same stops, reversed. */
  --ctrl-bg:        linear-gradient(180deg, var(--ctrl-top), var(--ctrl-bot));
  --ctrl-bg-active: linear-gradient(180deg, var(--ctrl-bot), var(--ctrl-top));
  --ctrl-inset:     inset 0 1px 0 0 var(--ctrl-ring);   /* edge highlights */
  --ctrl-blur:      none;                                /* glass blur (light only) */
  --dim:       rgba(255, 255, 255, 0.30);   /* inactive text            */
  --spine:     rgba(255, 255, 255, 0.30);   /* dotted-line dots         */

  /* corner padding for the desktop dark-mode / info buttons */
  --corner-pad: 36px;

  --badge:     64px;    /* JY number box size (overridden by JS)         */
  --dot:       10px;    /* dotted-line period (set by JS)               */

  /* shared "halo" that lets chrome occlude the dotted spine,
     matching the figma 0 0 10px 10px bg shadow */
  --halo: 0 0 10px 11px var(--bg);

  --ease: cubic-bezier(0.32, 0.72, 0, 1);
  --dur:  0.62s;
  --theme-dur: 0.45s;   /* dark/light cross-fade duration (central, tunable) */

  /* intro reveal — plays once, after the layout is measured */
  --intro-dur: 0.9s;
  /* ribbon/stations fade a full second slower than the chrome slide-in */
  --intro-fade-dur: 1.9s;

  /* ── chrome spacing — TUNE THESE ──────────────────────────────
     The loop toggle (top) and media controls (bottom) need DIFFERENT
     spacing in Safari's browser view vs. the installed fullscreen PWA,
     so each mode gets its own values. These defaults target Safari:
     the browser toolbars eat into the viewport, so we pull both the
     toggle and the controls close to the edges to undo the squashed feel. */
  --toggle-top:      max(20px, env(safe-area-inset-top, 0px) + 8px);
  --controls-bottom: max(20px, env(safe-area-inset-bottom, 0px) + 16px);
}

/* ── light mode ───────────────────────────────────────────────────
   Warm near-white surface, warm near-black ink — the dark palette
   inverted while the JR green accent stays put. Only the surface
   tokens change; every rule below reads them, so the whole app
   (ribbon, badges, eraser, halo, buttons, modal) re-themes at once. */
html[data-theme="light"] {
  --bg:        rgb(242, 241, 237);   /* warm near-white — figma surface  */
  --fg:        rgb(25, 25, 23);
  --panel:     rgb(252, 251, 248);
  --panel-edge: rgba(25, 25, 23, 0.08);
  /* figma "glass" control: a translucent grey gradient over the bg, frosted
     with a backdrop blur, edged with a white top highlight and a dark hairline
     at the bottom. A soft drop-shadow keeps it lifted off the near-white bg. */
  --ctrl-bg:        linear-gradient(180deg, rgba(204, 204, 204, 0.2) 0%, rgba(102, 102, 102, 0.2) 100%);
  --ctrl-bg-active: linear-gradient(180deg, rgba(102, 102, 102, 0.2) 0%, rgba(204, 204, 204, 0.2) 100%);
  --ctrl-inset:     inset 0 1px 0 0 rgb(255, 255, 255), inset 0 -1px 0 0 rgba(25, 25, 23, 0.3);
  --ctrl-blur:      blur(16px);
  --ctrl-shadow: 0 4px 18px rgba(25, 25, 23, 0.16), 0 1px 4px rgba(25, 25, 23, 0.12);
  --dim:       rgba(25, 25, 23, 0.3);
  --spine:     rgba(25, 25, 23, 0.22);
}

/* installed PWA (Home Screen, fullscreen) ─────────────────────────
   Detected two ways for maximum coverage: the standalone display-mode
   media query (iOS 16.4+) and a `standalone` class that app boot stamps
   on <html> via navigator.standalone (older iOS). Either one wins.

   Here .app is deliberately over-extended past the bottom edge by the
   TOP inset (see the .app rule), so a plain `bottom:` lands too low and
   crowds the home indicator. We add that top inset back into the offset
   to cancel the over-extension, then sit a comfortable gap above the
   home indicator (its own bottom inset + breathing room). */
html.standalone {
  --toggle-top:      max(48px, env(safe-area-inset-top, 0px) + 24px);
  --controls-bottom: calc(env(safe-area-inset-top, 0px)
                        + env(safe-area-inset-bottom, 0px) + 38px);
}
@media (display-mode: standalone) {
  html {
    --toggle-top:      max(48px, env(safe-area-inset-top, 0px) + 24px);
    --controls-bottom: calc(env(safe-area-inset-top, 0px)
                          + env(safe-area-inset-bottom, 0px) + 38px);
  }
}

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

html, body {
  margin: 0;
  height: 100vh;
  background: var(--bg);
  transition: background-color var(--theme-dur, 0.45s) var(--ease);
  overscroll-behavior: none;
  overflow: hidden;
  /* disable the double-tap-to-zoom gesture (iOS ignores user-scalable=no).
     In Safari it stops the zoom; in the installed PWA it prevents the
     standalone viewport from snapping to a zoomed, mis-aligned state where
     the controls fall out from under your taps. */
  touch-action: manipulation;
}

body {
  font-family: "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  color: var(--fg);
  transition: color var(--theme-dur, 0.45s) var(--ease),
              background-color var(--theme-dur, 0.45s) var(--ease);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  user-select: none;
  /* pin the body to the viewport so a touch-swipe can never scroll the page
     (which is what flashes the iOS scrollbar). .app is position:fixed, so
     fixing the body too doesn't affect its layout. */
  position: fixed;
  inset: 0;
  width: 100%;
}

/* full-bleed app surface, centred on a phone-width column.
   In an iOS standalone PWA with a black-translucent status bar, content draws
   UNDER the status bar — and WebKit then under-reports every height reference
   (100vh, 100dvh, window.innerHeight) by the top safe-area inset, leaving a
   black gap at the BOTTOM. We add that inset back so the surface reaches the
   physical screen edge. In a normal browser the inset is 0, so this is a no-op. */
.app {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: calc(100vh + env(safe-area-inset-top, 0px));   /* fallback */
  height: calc(100dvh + env(safe-area-inset-top, 0px));  /* dynamic vh */
  overflow: hidden;
  background: var(--bg);
  transition: background-color var(--theme-dur, 0.45s) var(--ease);
  touch-action: manipulation;
}

.stage {
  position: absolute;
  inset: 0;
  margin: 0 auto;
  max-width: 460px;
}

/* ── dotted spine ─────────────────────────────────────────────
   Lives INSIDE the ribbon so it scrolls together with the names.
   --dot divides the slot height evenly, so the silent re-centre
   (a jump of N×slot) preserves the dot phase — no visible flicker. */
.spine {
  position: absolute;
  left: 50%;
  width: 4px;
  margin-left: -2px;
  transform: translateY(0);
  transition: transform var(--dur) var(--ease),
              opacity var(--theme-dur, 0.45s) var(--ease);
  background-image: radial-gradient(circle at center,
                    var(--spine) 1.5px, transparent 2px);
  background-size: 4px var(--dot, 10px);
  background-repeat: repeat-y;
  pointer-events: none;
  will-change: transform;
}

/* ── ribbon: static container; stations are positioned individually ─ */
.ribbon {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 0;
  transition: opacity var(--intro-fade-dur) var(--ease),
              transform var(--dur) var(--ease);
}
/* while a finger is down the ribbon must track instantly (no easing lag) */
.ribbon.dragging { transition: opacity var(--intro-fade-dur) var(--ease); }

/* ── intro reveal ─────────────────────────────────────────────
   While booting, the chrome is parked off-screen and the ribbon is
   transparent so the pre-font-load layout jump is never seen. JS drops
   the .booting class once the final layout is measured, and everything
   animates into place together. */
body.booting .ribbon { opacity: 0; }
body.booting .toggle {
  transform: translateX(-50%) translateY(calc(-100% - 140px));
}
body.booting .controls {
  transform: translateX(-50%) translateY(calc(100% + 160px));
}
.no-anim,
.no-anim * { transition: none !important; }

/* each station eases to the Y for its role (prev / current / next);
   far stations fade to 0 so only those three are ever visible */
.station {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 0;
  pointer-events: none;
  transition: transform var(--dur) var(--ease),
              opacity var(--dur) var(--ease);
  will-change: transform, opacity;
}
.station.far .name { pointer-events: none; }

/* Make station text bolder in light mode to compensate for the higher contrast. In dark mode the text is already quite punchy, so 500
   is plenty; in light mode the same weight looks thinner, so we boost it
   to 700. The JY badge text gets the same boost to keep it visually balanced. */
html[data-theme="light"] .station.active .name,
html[data-theme="light"] .station.active .badge .jy,
html[data-theme="light"] .station.active .badge .num { 
  font-weight: 700; 
}

/* a bg-coloured block that erases the dotted line around this
   station's content, leaving a clean vertical gap. It grows when
   the station becomes active to clear the name AND the JY badge. */
.station .eraser {
  position: absolute;
  left: 50%;
  width: 200px;
  transform: translateX(-50%);
  background: var(--bg);
  top: -24px;            /* 12px name half + 12px gap          */
  height: 48px;
  transition: top var(--dur) var(--ease),
              height var(--dur) var(--ease),
              background-color var(--theme-dur, 0.45s) var(--ease);
  pointer-events: none;
}
.station.active .eraser {
  top: calc(-50px - var(--badge));     /* clear box top + 16px gap        */
  height: calc(128px + var(--badge));  /* down past name to scrub bottom  */
}

/* the name sits exactly on the slot centre line */
.station .name {
  position: absolute;
  left: 0;
  right: 0;
  top: -4px;
  text-align: center;
  font-weight: 500;
  font-size: 36px;
  line-height: 1;
  letter-spacing: -0.01em;
  white-space: nowrap;
  color: var(--dim);
  transform: translateY(-50%) scale(0.667);   /* 36 → 24px when dim */
  transform-origin: center center;
  transition: color var(--dur) var(--ease),
              transform var(--dur) var(--ease);
  pointer-events: auto;
  cursor: pointer;
}

.station.active .name {
  color: var(--fg);
  transform: translateY(-50%) scale(1);
  cursor: default;
}

/* the JY badge floats above the name, fading + scaling into view
   only while its station is the active one */
.station .badge {
  position: absolute;
  bottom: calc(50% + 34px);       /* 18px name half + 16px gap */
  left: 50%;
  width: var(--badge);
  height: var(--badge);
  margin-left: calc(var(--badge) / -2);
  border-radius: 6px;
  background: var(--green);
  opacity: 0;
  transform: scale(0.55);
  transform-origin: center bottom;
  transition: opacity calc(var(--dur) * 0.85) var(--ease),
              transform var(--dur) var(--ease);
}
.station.active .badge {
  opacity: 1;
  transform: scale(1);
}

.badge .inner {
  position: absolute;
  inset: 5px;
  border-radius: 3px;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  transition: background-color var(--theme-dur, 0.45s) var(--ease);
}
.badge .jy {
  font-size: 15px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0.02em;
}
.badge .num {
  font-size: 31px;
  font-weight: 500;
  line-height: 0.86;
  letter-spacing: -0.02em;
}

/* ── top loop toggle ──────────────────────────────────────── */
.toggle {
  position: absolute;
  top: var(--toggle-top);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: stretch;
  border-radius: 115px;
  background: var(--ctrl-bg);
  -webkit-backdrop-filter: var(--ctrl-blur);
  backdrop-filter: var(--ctrl-blur);
  box-shadow: var(--halo), var(--ctrl-shadow), var(--ctrl-inset);
  z-index: 5;
  transition: transform var(--intro-dur) var(--ease),
              box-shadow var(--theme-dur, 0.45s) var(--ease);
}
.toggle button {
  position: relative;
  appearance: none;
  border: 0;
  background: none;
  font-family: inherit;
  font-weight: 700;
  font-size: 20px;
  letter-spacing: -0.01em;
  white-space: nowrap;
  color: var(--dim);
  padding: 13px 24px;
  cursor: pointer;
  transition: color 0.35s var(--ease);
}
.toggle button.on { color: var(--fg); }
.toggle .divider {
  width: 1px;
  align-self: stretch;
  margin: 8px 0 8px -4px;
  background: var(--dim);
  opacity: 0.5;
}

/* ── bottom media controls ────────────────────────────────── */
.controls {
  position: absolute;
  bottom: var(--controls-bottom);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 24px;
  z-index: 5;
  transition: transform var(--intro-dur) var(--ease);
}

.ctrl {
  appearance: none;
  border: 0;
  padding: 0;
  display: grid;
  place-items: center;
  cursor: pointer;
  color: var(--fg);
}
.ctrl svg { display: block; }

.ctrl.skip {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: var(--ctrl-bg);
  -webkit-backdrop-filter: var(--ctrl-blur);
  backdrop-filter: var(--ctrl-blur);
  /* box-shadow: var(--halo), var(--ctrl-shadow), var(--ctrl-inset); */
  box-shadow: var(--halo), var(--ctrl-inset);
  transition: transform 0.18s var(--ease), filter 0.2s var(--ease),
              box-shadow var(--theme-dur, 0.45s) var(--ease);
}
.ctrl.play {
  width: 96px;
  height: 96px;
  border-radius: 50%;
  color: #fff;                 /* white glyph on JR green in both themes */
  background: var(--green);
  box-shadow: var(--halo), inset 0 1px 0 0 rgba(255, 255, 255, 0.4);
  transition: transform 0.18s var(--ease);
}

html[data-theme="light"] .ctrl.play {
  box-shadow: none;
}

/* pressed feedback: shrink slightly and darken — consistent in both themes. */
.ctrl:active   { transform: scale(0.9); }
/* .ctrl.skip:active { filter: brightness(0.85); } */

/* play / pause icon swap */
.play .icon-pause { display: none; }
.play.playing .icon-play  { display: none; }
.play.playing .icon-pause { display: block; }

/* sun / moon icon swap — dark mode shows the SUN (tap → lighten),
   light mode shows the MOON (tap → darken). */
.theme-toggle .icon-moon { display: none; }
.theme-toggle .icon-sun  { display: block; }
html[data-theme="light"] .theme-toggle .icon-sun  { display: none; }
html[data-theme="light"] .theme-toggle .icon-moon { display: block; }

/* ── dark-mode toggle + info buttons ──────────────────────────────
   Two extra .ctrl.skip buttons. They are positioned WITHOUT transform
   (only left/right/bottom) so the shared .ctrl:active scale never
   fights their placement. Default (mobile) layout flanks the play
   button; the desktop media query relocates them to the corners. */
.corner-ctrl {
  position: absolute;
  z-index: 5;
  bottom: calc(var(--controls-bottom) + 16px);   /* centre 64px on 96px play */
  transition: transform 0.18s var(--ease), filter 0.2s var(--ease),
              background-color var(--theme-dur, 0.45s) var(--ease);
}
/* mobile: sit left & right of the play button (replacing prev/next).
   play centre is the viewport centre; centre-to-centre spacing is
   48 (play half) + 24 (gap) + 32 (skip half) = 104px. */
.theme-toggle { left: calc(50% - 136px); }   /* left edge = centre − 104 − 32 */
.info-btn     { left: calc(50% + 72px);  }    /* left edge = centre + 104 − 32 */

/* mobile: swipe replaces prev/next, so hide them */
@media (max-width: 639px) {
  #prev, #next { display: none; }
}

/* desktop: keep prev/next in the centre row, banish toggle + info to
   the bottom corners with comfortable edge padding (safe-area aware). */
@media (min-width: 640px) {
  .corner-ctrl {
    bottom: max(var(--corner-pad), env(safe-area-inset-bottom, 0px) + 12px);
  }
  .theme-toggle {
    left: max(var(--corner-pad), env(safe-area-inset-left, 0px) + 12px);
  }
  .info-btn {
    left: auto;
    right: max(var(--corner-pad), env(safe-area-inset-right, 0px) + 12px);
  }
}

/* desktop: zoom the ribbon (station names, badges, scrub bars) to 125%.
   Uses the standalone `scale` property — NOT `transform` — because JS
   drives .ribbon's `transform` directly for the drag gesture (see app.js);
   `scale` composes independently so it never gets clobbered.
   transform-origin is pinned to --ribbon-mid (the vertical centre of the
   toggle↔controls band, set by measure() in app.js) so the active station
   stays put at its calculated position instead of drifting as it scales.
   .toggle / .controls / .corner-ctrl / .modal are siblings of .ribbon, not
   descendants, so they are completely unaffected and stay at 100%. */
@media (min-width: 640px) {
  .ribbon {
    scale: 1.25;
    transform-origin: 50% var(--ribbon-mid, 50%);
  }
}

/* corner buttons slide up with the rest of the chrome on boot — a pure
   transform slide (no opacity fade) so they move in perfect lock-step with
   the playback controls instead of fading in ahead of them. They stay hidden
   simply by being parked off-screen, exactly like .controls.
   JS adds .intro before dropping .booting so the entrance uses
   var(--intro-dur) rather than the 0.18s press duration. */
body.booting .corner-ctrl {
  transform: translateY(calc(100% + 160px));
}
.corner-ctrl.intro {
  transition: transform var(--intro-dur) var(--ease),
              filter 0.2s var(--ease),
              background-color var(--theme-dur, 0.45s) var(--ease) !important;
}

/* ── info modal ───────────────────────────────────────────────── */
.modal[hidden] { display: none; }
.modal {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  opacity: 0;
  transition: opacity 0.3s var(--ease);
}
.modal.open .modal-backdrop { opacity: 1; }

.modal-card {
  position: relative;
  width: 100%;
  max-width: 416px;
  max-height: calc(100dvh - 48px);
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  background: var(--panel);
  color: var(--fg);
  border: 1px solid var(--panel-edge);
  border-radius: 24px;
  padding: 34px 30px 32px;
  box-shadow: 0 28px 70px rgba(0, 0, 0, 0.5),
              inset 0 1px 0 0 rgba(255, 255, 255, 0.06);
  transform: translateY(18px) scale(0.97);
  opacity: 0;
  transition: transform 0.38s var(--ease), opacity 0.38s var(--ease);
}
.modal.open .modal-card { transform: none; opacity: 1; }

.modal-close {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--fg);
  opacity: 0.45;
  cursor: pointer;
  transition: opacity 0.2s var(--ease), background-color 0.2s var(--ease);
}
.modal-close:hover { opacity: 1; }

/* small JY mark echoing the app icon */
.modal-mark {
  width: 52px;
  height: 52px;
  border-radius: 11px;
  background: var(--green);
  display: grid;
  place-items: center;
  margin-bottom: 18px;
}
.modal-mark span {
  display: grid;
  place-items: center;
  width: calc(100% - 9px);
  height: calc(100% - 9px);
  border-radius: 6px;
  background: var(--panel);
  color: var(--fg);
  font-weight: 700;
  font-size: 18px;
  letter-spacing: 0.03em;
}

.modal-eyebrow {
  display: block;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--green);
  margin-bottom: 8px;
}
.modal-card h2 {
  margin: 0 0 14px;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.1;
}
.modal-card p {
  margin: 0 0 14px;
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--fg);
  opacity: 0.78;
  text-wrap: pretty;
}
.modal-divider {
  height: 1px;
  margin: 22px 0;
  background: var(--panel-edge);
  border: 0;
}
.modal-section-label {
  display: block;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--fg);
  opacity: 0.5;
  margin-bottom: 10px;
}
.modal-download {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 13px 20px;
  border-radius: 14px;
  background: var(--green);
  color: #fff;
  font-family: inherit;
  font-weight: 700;
  font-size: 15px;
  white-space: nowrap;
  text-decoration: none;
  transition: filter 0.18s var(--ease), transform 0.18s var(--ease);
}
.modal-download:hover  { filter: brightness(1.08); }
.modal-download:active { transform: scale(0.97); }
.modal-download svg { display: block; }

.modal-contact {
  margin-top: 6px;
  font-size: 15px;
  line-height: 1.5;
  opacity: 0.78;
}
.modal-contact a {
  color: var(--green);
  text-decoration: none;
  font-weight: 500;
}
.modal-contact a:hover { text-decoration: underline; }

/* ── scrub bar ────────────────────────────────────────────────
   Lives INSIDE each .station element, positioned below the name.
   Layout: [current-time] [track (max 204px)] [total-time], centred.
   A chip label floats above the active segment; the thin playhead
   line shows exact position within the lit-up current section.
   Fades in/out with .station.active, just like the JY badge — but
   asymmetrically: hiding is quick and clean (the eye has already moved
   on to the incoming station), while showing eases in over the same
   duration as the slide so it arrives in step rather than snapping in
   ahead of it. ──── */
.station .scrub {
  position: absolute;
  top: 26px;    /* 18px name-half + 8px gap below name */
  left: 0;
  right: 0;
  opacity: 0;
  transition: opacity 0.15s var(--ease);
  pointer-events: none;
}

.station.active .scrub {
  opacity: 1;
  pointer-events: auto;
  transition: opacity calc(var(--dur) * 0.85) var(--ease);
}

/* Flex row — timestamps flank the track, whole row centred in the station */
.scrub-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding-top: 30px;    /* vertical space reserved for the chip above the track */
}

.scrub-time {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--fg);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  min-width: 28px;
  transition: color var(--theme-dur, 0.45s) var(--ease);
}

.scrub-time-cur   { text-align: right; }
.scrub-time-total { text-align: left; }

/* Section chip — floats above the track, centred on the active segment */
.scrub-chip {
  background-color: var(--green);
  position: absolute;
  bottom: calc(100% + 12px);
  transform: translateX(-50%);
  padding: 2px 8px 3px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: -0.04em;
  color: var(--bg);
  white-space: nowrap;
  pointer-events: none;
  z-index: 3;
  transition: color var(--theme-dur, 0.45s) var(--ease),
              border-color var(--theme-dur, 0.45s) var(--ease);
}

.scrub-track {
  position: relative;
  display: flex;
  gap: 4px;
  height: 4px;
  flex: 1;
  max-width: 204px;
}

.scrub-segment {
  flex-basis: 0;
  min-width: 0;
  position: relative;
  height: 4px;
  background: var(--dim);
  overflow: hidden;
}

.scrub-segment--first {
  border-top-left-radius: 999px;
  border-bottom-left-radius: 999px;
}

.scrub-segment--last {
  border-top-right-radius: 999px;
  border-bottom-right-radius: 999px;
}

.scrub-fill {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  width: 0%;
  background: var(--green);
  transition: background-color var(--theme-dur, 0.45s) var(--ease);
}

/* Transport head — a thin vertical line centred on the bar at the
   current playback position. margin-left / margin-top centre it. */
.scrub-head {
  position: absolute;
  width: 6px;
  height: 12px;
  margin-left: -1px;
  margin-top: -4px;    /* (12 − 4) / 2 — centres head on the 4px bar */
  border-radius: 1px;
  border-left: 2px solid var(--bg);
  border-right: 2px solid var(--bg);
  background: var(--fg);
  pointer-events: none;
  z-index: 2;
  left: 0;
  transition: background-color var(--theme-dur, 0.45s) var(--ease);
}


@media (prefers-reduced-motion: reduce) {
  .spine, .station, .station .name, .station .badge { transition-duration: 0.001s; }
  .ribbon, .toggle, .controls { transition-duration: 0.001s; }
  body.booting .toggle      { transform: translateX(-50%); }
  body.booting .controls    { transform: translateX(-50%); }
  body.booting .corner-ctrl { transform: none; }
  .corner-ctrl.intro        { transition-duration: 0.001s !important; }
  .modal-backdrop, .modal-card { transition-duration: 0.001s; }
  .station .scrub { transition-duration: 0.001s; }
}
