/* Synopsist: the website's look, a song-select screen in the style of rhythm-game menus: slanted
   bars and buttons, diamond shapes, and glass panels, in the colors of an album cover (four themes,
   each with a light and a dark side). Colors and sizes are set once, under :root. */

/* --- fonts (player/fonts/, SIL Open Font License): Kazesawa for text, Exo for display --------- */

@font-face { font-family: "Kazesawa"; src: url("fonts/kazesawa-regular.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Kazesawa"; src: url("fonts/kazesawa-semibold.woff2") format("woff2"); font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: "Kazesawa"; src: url("fonts/kazesawa-bold.woff2") format("woff2"); font-weight: 700; font-style: normal; font-display: swap; }
@font-face { font-family: "Kazesawa"; src: url("fonts/kazesawa-extrabold.woff2") format("woff2"); font-weight: 800; font-style: normal; font-display: swap; }
@font-face { font-family: "Exo"; src: url("fonts/exo-variable.woff2") format("woff2"); font-weight: 100 900; font-style: normal; font-display: swap; }

/* --- sizes and motion ------------------------------------------------------------------------- */

:root {
  --font: "Kazesawa", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --display: "Exo", "Kazesawa", ui-sans-serif, system-ui, sans-serif;
  --ease-out: cubic-bezier(0.2, 0.9, 0.25, 1);
  --skew: -14deg;
  --radius: 3px;
  --bar-h: 96px; /* measured by app.js */
  /* js/vibe.js hands the backgrounds new light this often. The thin outline turning behind the
     full-screen cover glides from one value to the next (it would otherwise grow and shrink in
     visible steps); on the soft lights everywhere else the steps are too small to see, and gliding
     them would cost work on every frame. */
  --vibe-step: 50ms;
}
:root[data-graphics="low"] { --vibe-step: 100ms; }

/* --- colors: four themes from the album covers, each with a light and a dark side -------------- */

/* Settings > Theme puts data-palette on <html> (js/settings.js; none for Hollow Journey, the
   default). A theme sets only the colors below, as light-dark() pairs: the light side when the device
   or the listener asks for it (data-theme), the dark side otherwise. Everything else (lines, glass,
   tints) is mixed from them further down. The colors shown for each theme on the settings page
   (data-palette-preview) come from here too. Browsers without light-dark() get Hollow Journey's dark
   side. */
:root {
  color-scheme: dark;
  --bg: #050b15;
  --bg-2: #071325;
  --surface-solid: #0a1a30;
  --text: #ecf4fd;
  --muted: #a1b9d3;
  --faint: #7890ae;
  --accent: #43abf5; /* links, the play button, what's chosen or playing */
  --accent-strong: #178de0; /* the deep end of the accent */
  --accent-2: #d5eaff; /* two more lights from the cover: the moon, and the moonlight */
  --accent-3: #70b8f0;
  --on-accent: #001024; /* text on the accent */
  --shadow: rgba(0, 0, 0, 0.45);
}

@supports (color: light-dark(#000, #fff)) {
  :root {
    color-scheme: light dark; /* follows the device until the listener picks one */
    --shadow: light-dark(rgba(20, 40, 56, 0.13), rgba(0, 0, 0, 0.45));
  }
  :root[data-theme="light"] { color-scheme: light; }
  :root[data-theme="dark"] { color-scheme: dark; }

  /* Hollow Journey (the default): a moonlit night in deep baby blue (bluer and darker than
     Planetary Fields' teal, so the two don't look alike) */
  :root, [data-palette-preview="hollow-journey"] {
    --bg: light-dark(#e8f1fa, #050b15);
    --bg-2: light-dark(#d9e6f5, #071325);
    --surface-solid: light-dark(#fff, #0a1a30);
    --text: light-dark(#08182a, #ecf4fd);
    --muted: light-dark(#37506a, #a1b9d3);
    --faint: light-dark(#4d647e, #7890ae);
    --accent: light-dark(#0066b5, #43abf5);
    --accent-strong: light-dark(#005395, #178de0);
    --accent-2: light-dark(#3e8fdd, #d5eaff);
    --accent-3: light-dark(#1f84d1, #70b8f0);
    --on-accent: light-dark(#fff, #001024);
  }
  /* Hollow Journey Beyond: a green-lit cave and a lantern */
  :root[data-palette="hollow-journey-beyond"], [data-palette-preview="hollow-journey-beyond"] {
    --bg: light-dark(#eef4f0, #0a110e);
    --bg-2: light-dark(#e1ebe5, #0f1a15);
    --surface-solid: light-dark(#fff, #111d18);
    --text: light-dark(#152620, #e3f0e8);
    --muted: light-dark(#465f53, #9db8aa);
    --faint: light-dark(#546b60, #7b9689);
    --accent: light-dark(#22704a, #8fdcae);
    --accent-strong: light-dark(#1b5c3c, #67c08f);
    --accent-2: light-dark(#b7781f, #f2c46b);
    --accent-3: light-dark(#3a9466, #76c293);
    --on-accent: light-dark(#fff, #08130d);
  }
  /* Planetary Fields: a pale sun over a silver field */
  :root[data-palette="planetary-fields"], [data-palette-preview="planetary-fields"] {
    --bg: light-dark(#f2f5f5, #0b1213);
    --bg-2: light-dark(#e6eced, #111c1e);
    --surface-solid: light-dark(#fff, #132022);
    --text: light-dark(#111a1c, #e6f1f2);
    --muted: light-dark(#4a5c60, #a0b5b8);
    --faint: light-dark(#56686c, #7d9497);
    --accent: light-dark(#216a74, #a6e0e8);
    --accent-strong: light-dark(#1a5860, #79c6d1);
    --accent-2: light-dark(#5c8f97, #f1fbfc);
    --accent-3: light-dark(#3f97a4, #67b4c1);
    --on-accent: light-dark(#fff, #071214);
  }
  /* Eternal Synopsis: a neon grid, orange and red on black */
  :root[data-palette="eternal-synopsis"], [data-palette-preview="eternal-synopsis"] {
    --bg: light-dark(#fbf3ef, #0d0807);
    --bg-2: light-dark(#f4e6e0, #170d0b);
    --surface-solid: light-dark(#fff, #1b100d);
    --text: light-dark(#2a1511, #f6ebe6);
    --muted: light-dark(#6e4b43, #c2a79e);
    --faint: light-dark(#7c5d55, #a08379);
    --accent: light-dark(#b83d14, #ff8a4c);
    --accent-strong: light-dark(#9c3210, #ff6a2a);
    --accent-2: light-dark(#d93a2a, #ff3b2a);
    --accent-3: light-dark(#d98a3d, #ffb36b);
    --on-accent: light-dark(#fff, #1a0703);
  }
}

/* the rest, mixed from the theme's colors */
:root {
  --surface: color-mix(in srgb, var(--surface-solid) 72%, transparent); /* glass */
  --surface-2: color-mix(in srgb, var(--surface-solid) 92%, transparent);
  --line: color-mix(in srgb, var(--text) 12%, transparent);
  --line-strong: color-mix(in srgb, var(--text) 26%, transparent);
  --hover: color-mix(in srgb, var(--text) 7%, transparent);
  --selected: color-mix(in srgb, var(--accent) 15%, transparent);
  --header-bg: color-mix(in srgb, var(--bg) 80%, transparent);
  --bar-bg: color-mix(in srgb, var(--bg-2) 88%, transparent);
  --track: color-mix(in srgb, var(--text) 20%, transparent);
  /* glass, for the buttons: clear, so the background shows through, with a border and a thin line
     of light along the top edge. The main action's glass has a faint tint of the accent and a border
     in it; the others are clear with a plain border. (No blur behind it: there's little behind a
     button to blur, and a blur would cost the graphics card work on every frame anything moves
     under it.) */
  --clear: rgba(255, 255, 255, 0.03);
  --clear-hover: rgba(255, 255, 255, 0.07);
  --glass: color-mix(in srgb, var(--accent) 10%, transparent);
  --glass-hover: color-mix(in srgb, var(--accent) 18%, transparent);
  --glass-line: color-mix(in srgb, var(--accent) 70%, transparent);
  --glass-text: var(--text);
  --sheen: rgba(255, 255, 255, 0.07); /* a little light in the top of the glass */
  --edge: rgba(255, 255, 255, 0.2); /* the light along its top edge */
}
/* on the light side the glass is frosted white, or it would vanish into the pale page */
@supports (color: light-dark(#000, #fff)) {
  :root {
    --clear: light-dark(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.03));
    --clear-hover: light-dark(rgba(255, 255, 255, 0.85), rgba(255, 255, 255, 0.07));
    --glass: light-dark(color-mix(in srgb, var(--accent) 6%, rgba(255, 255, 255, 0.55)), color-mix(in srgb, var(--accent) 10%, transparent));
    --glass-hover: light-dark(color-mix(in srgb, var(--accent) 12%, rgba(255, 255, 255, 0.7)), color-mix(in srgb, var(--accent) 18%, transparent));
    --glass-line: light-dark(color-mix(in srgb, var(--accent) 80%, transparent), color-mix(in srgb, var(--accent) 70%, transparent));
    --glass-text: light-dark(var(--accent-strong), var(--text));
    --sheen: light-dark(rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0.07));
    --edge: light-dark(rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.2));
  }
}

/* --- base ------------------------------------------------------------------------------------ */

*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }

html { scroll-behavior: smooth; scroll-padding-top: calc(84px + env(safe-area-inset-top)); }
/* no zooming, by a double tap or a pinch: the site works like an app (iPhones pinch-zoom anyway,
   so app.js stops that there) */
html { touch-action: pan-x pan-y; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font: 400 16px/1.55 var(--font);
  -webkit-font-smoothing: antialiased;
}
/* no text is picked out, by dragging, a double click, or Select All, or by pressing and holding on
   a phone (which pops up no menu either), as in an app or Spotify's site (the user asked, for phones
   and then for computers); the search box and the playlist name box still can */
body { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
:is(input, textarea) { -webkit-user-select: text; user-select: text; }
body[data-view="player"], body:is([data-view="settings"], [data-view="app"]).has-track { padding-bottom: calc(var(--bar-h) + 24px); }
body.visual-open { overflow: hidden; }

/* the pages: the title screen, the music, the settings, and the app's (the last two share the music
   page's top bar) */
body[data-view="home"] .player-page { display: none; }
body:not([data-view="home"]) .landing { display: none; }
body:is([data-view="settings"], [data-view="app"]) main, body:not([data-view="settings"]) .settings-page,
body:not([data-view="app"]) .app-page { display: none; }
body:is([data-view="home"], [data-view="settings"], [data-view="app"]):not(.has-track) .player-bar { display: none; }

svg { width: 1.25em; height: 1.25em; fill: currentColor; flex-shrink: 0; }
.icons { position: absolute; width: 0; height: 0; overflow: hidden; }
a { color: var(--accent); }
button { font: inherit; color: inherit; }
button:not(:disabled) { cursor: pointer; }
/* the focus outline is for moving around with the keyboard: app.js turns it on when Tab is pressed
   and off again with a click, so clicking a button never leaves a box around it */
:focus-visible { outline: none; }
:root[data-input="keyboard"] :focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
::selection { background: color-mix(in srgb, var(--accent) 35%, transparent); }

.skip-link {
  position: absolute;
  left: 1rem;
  top: -4rem;
  z-index: 100;
  padding: 0.6rem 1rem;
  background: var(--surface-solid);
}
.skip-link:focus { top: 1rem; }
/* words only screen readers read (like the unit after a bare number) */
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* --- the scenery behind every page: the night of the Hollow Journey cover ------------------------ */

/* A twelve-sided moon high in a corner of the sky, its light, and a skyline of leaning monoliths
   along the bottom (index.html), in the theme's colors: on the dark side a moonlit night, the nearer
   slabs darker; on the light side the same skyline in a pale mist, the nearer slabs deeper. It's
   painted once and holds still (but for the title screen's parallax, while it scrolls); only the
   moonlight changes, with the music. */
.scenery {
  --moon-size: clamp(24px, 2.4vw, 36px);
  --moon-x: 4.5vw; /* the moon's distance from the right edge */
  --moon-y: 12vh; /* and from the top */
  --moon: color-mix(in srgb, var(--accent-2) 92%, var(--bg));
  --moonlight: color-mix(in srgb, var(--accent-3) 22%, transparent);
  --bloom: color-mix(in srgb, var(--accent-2) 24%, transparent);
  --far: color-mix(in srgb, var(--accent) 4%, var(--bg-2));
  --mid: color-mix(in srgb, #000 15%, var(--bg));
  --near: color-mix(in srgb, #000 45%, var(--bg));
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  background: linear-gradient(180deg, var(--bg) 35%, var(--bg-2));
}
@supports (color: light-dark(#000, #fff)) {
  .scenery {
    --moon: light-dark(#fff, color-mix(in srgb, var(--accent-2) 92%, var(--bg)));
    --moonlight: light-dark(rgba(255, 255, 255, 0.8), color-mix(in srgb, var(--accent-3) 22%, transparent));
    --bloom: light-dark(rgba(255, 255, 255, 0.9), color-mix(in srgb, var(--accent-2) 24%, transparent));
    --far: light-dark(color-mix(in srgb, var(--accent) 6%, var(--bg-2)), color-mix(in srgb, var(--accent) 4%, var(--bg-2)));
    --mid: light-dark(color-mix(in srgb, var(--accent) 11%, var(--bg-2)), color-mix(in srgb, #000 15%, var(--bg)));
    --near: light-dark(color-mix(in srgb, var(--accent) 14%, var(--bg-2)), color-mix(in srgb, #000 45%, var(--bg)));
  }
}
/* The moonlight follows the music (--bass and --mids, 0 to 1, which js/vibe.js sets on .sky alone,
   so the skyline isn't restyled with every change): the halo around the moon brightens with the
   bass, the haze behind the skyline with the mids. */
.sky { --bass: 0.35; --mids: 0.35; --highs: 0.35; position: absolute; inset: 0; }
.sky i { position: absolute; will-change: opacity; }
.halo {
  top: calc(var(--moon-y) + var(--moon-size) / 2);
  right: calc(var(--moon-x) + var(--moon-size) / 2);
  width: 80vmin;
  aspect-ratio: 1;
  translate: 50% -50%;
  /* a bright bloom close around the moon, in a wide, faint glow */
  background:
    radial-gradient(closest-side, var(--bloom), transparent 22%),
    radial-gradient(closest-side, var(--moonlight), transparent);
  opacity: calc(0.45 + 0.65 * var(--bass));
}
/* the haze: the sky lit along the whole horizon, most under the moon, so the slabs stand dark against it */
.haze {
  left: 0;
  right: 0;
  bottom: 0;
  height: 75vh;
  background:
    radial-gradient(70% 65% at 78% 100%, var(--moonlight), transparent),
    linear-gradient(to top, color-mix(in srgb, var(--moonlight) 50%, transparent), transparent 55%);
  opacity: calc(0.4 + 0.7 * var(--mids));
}
.moon {
  position: absolute;
  top: var(--moon-y);
  right: var(--moon-x);
  width: var(--moon-size);
  aspect-ratio: 1;
  clip-path: polygon(98.3% 62.9%, 85.4% 85.4%, 62.9% 98.3%, 37.1% 98.3%, 14.6% 85.4%, 1.7% 62.9%, 1.7% 37.1%, 14.6% 14.6%, 37.1% 1.7%, 62.9% 1.7%, 85.4% 14.6%, 98.3% 37.1%);
  background: var(--moon);
}
/* the skyline fills the screen (its viewBox is a screen's shape), keeping its foot on the bottom edge
   and its middle in view on narrow screens; the ground under each layer, below the screen, shows
   only when the parallax lifts it */
.skyline { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.skyline.far { fill: var(--far); }
.skyline.mid { fill: var(--mid); }
.skyline.near { fill: var(--near); }
/* The title screen's parallax: as the page scrolls down to the summary below it, the night moves up,
   the nearer layers faster and the moon hardly at all. The scrolling itself drives it (a
   scroll-driven animation, which the graphics card runs: the page does no work for it), so it moves
   only while the page does. Where a browser can't, the night holds still, as on the other pages. */
@supports (animation-timeline: scroll()) {
  body[data-view="home"] :is(.halo, .haze, .moon, .skyline) {
    animation: parallax linear both;
    animation-timeline: scroll(root);
    animation-range: 0px 100vh;
  }
}
@keyframes parallax { to { transform: translateY(calc(var(--rise) * -100vh)); } }
.halo, .moon { --rise: 0.06; }
.haze, .skyline.far { --rise: 0.14; }
.skyline.mid { --rise: 0.26; }
.skyline.near { --rise: 0.42; }
/* some songs' own particles and light (js/effects.js), here and in layers in the full-screen view */
.effects { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.visual .effects { z-index: -1; }
.visual .effects-front { z-index: auto; } /* over the cover */

/* --- shared pieces ----------------------------------------------------------------------------- */

/* "◆ Title ———" */
.section-title {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin: 0;
  font: 650 clamp(1.3rem, 3vw, 1.75rem) / 1.15 var(--display);
  letter-spacing: 0.02em;
}
.section-title::before {
  content: "";
  flex: none;
  width: 0.42em;
  height: 0.42em;
  transform: rotate(45deg);
  background: var(--accent);
}
.section-title::after { content: ""; flex: 1; min-width: 2rem; height: 1px; background: var(--line); }

/* slanted buttons: the shape is a skewed layer of glass behind the (upright) label; the main action's
   glass is tinted with the accent, the others are clear */
.btn {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 38px;
  padding: 0 1.2rem;
  border: 0;
  background: none;
  color: var(--text);
  font: 600 0.86rem / 1 var(--display);
  letter-spacing: 0.02em;
  text-decoration: none;
  white-space: nowrap;
  transition: transform 0.2s var(--ease-out);
}
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  transform: skewX(var(--skew));
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: linear-gradient(var(--sheen), transparent 60%) var(--clear);
  box-shadow: inset 0 1px 0 var(--edge);
  transition: border-color 0.2s, background-color 0.2s;
}
.btn:hover::before { border-color: color-mix(in srgb, var(--text) 45%, transparent); background-color: var(--clear-hover); }
.btn:active { transform: scale(0.96); }
.btn svg { width: 1.05em; height: 1.05em; }
.btn-primary { color: var(--glass-text); }
.btn-primary::before { border-color: var(--glass-line); background-color: var(--glass); }
.btn-primary:hover::before { border-color: var(--accent); background-color: var(--glass-hover); }
.btn-large { min-height: 52px; padding: 0 2.3rem; font-size: 1.02rem; letter-spacing: 0.06em; }

/* diamond buttons (settings): pointing at one lights its border, nothing turns */
.diamond-button {
  position: relative;
  isolation: isolate;
  display: grid;
  place-items: center;
  flex: none;
  width: 46px;
  height: 46px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text);
  text-decoration: none;
}
.diamond-button::before {
  content: "";
  position: absolute;
  inset: 9px;
  z-index: -1;
  transform: rotate(45deg);
  border: 1px solid var(--line-strong);
  border-radius: 4px;
  background: var(--surface-2);
  transition: border-color 0.2s;
}
.diamond-button:hover::before { border-color: var(--accent); }
.diamond-button svg { width: 18px; height: 18px; }
.diamond-button[aria-current="page"] { color: var(--accent); }
.diamond-button[aria-current="page"]::before { border-color: var(--accent); background: var(--selected); }

/* icon buttons: a slanted plate grows in behind them on hover */
.icon-button {
  position: relative;
  isolation: isolate;
  display: grid;
  place-items: center;
  flex: none;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--muted);
  transition: color 0.2s, opacity 0.3s;
}
.icon-button::before {
  content: "";
  position: absolute;
  inset: 4px;
  z-index: -1;
  transform: skewX(var(--skew)) scale(0.55);
  border-radius: var(--radius);
  background: var(--hover);
  opacity: 0;
  transition: transform 0.3s var(--ease-out), opacity 0.2s;
}
/* (only where something can point without pressing: a finger leaves a button "pointed at" after
   tapping it, and it would stay lit) */
@media (hover: hover) {
  .icon-button:hover:not(:disabled) { color: var(--text); }
  .icon-button:hover:not(:disabled)::before { transform: skewX(var(--skew)) scale(1); opacity: 1; }
}
.icon-button:active:not(:disabled) svg { transform: scale(0.88); }
.icon-button svg { transition: transform 0.2s var(--ease-out); }
.icon-button:disabled { opacity: 0.35; }
.icon-button.small { width: 34px; height: 34px; }
.icon-button.small svg { width: 18px; height: 18px; }
.icon-button.active, .toggle-button[aria-pressed="true"] { color: var(--accent); }
.toggle-button[aria-pressed="true"]::after {
  content: "";
  position: absolute;
  bottom: 2px;
  width: 5px;
  height: 5px;
  transform: rotate(45deg);
  background: currentColor;
}

/* small slanted links: the artist's profiles */
.pill {
  position: relative;
  isolation: isolate;
  display: inline-block;
  padding: 0.34rem 0.9rem;
  color: var(--text);
  font: 600 0.8rem / 1.2 var(--display);
  letter-spacing: 0.02em;
  text-decoration: none;
}
.pill::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  transform: skewX(var(--skew));
  border: 1px solid var(--line-strong);
  border-radius: 2px;
  transition: border-color 0.2s, background-color 0.2s;
}
.pill:hover::before { border-color: var(--accent); background-color: var(--selected); }
.artist-links { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.6rem; margin: 0; padding: 0; list-style: none; }
/* "Also on" your store profiles */
.also-on { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 0.6rem 0.9rem; }
.also-on > span { color: var(--faint); font: 600 0.82rem var(--display); }
.also-on:has(.artist-links[hidden]) { display: none; }

/* --- moving color backdrop (the full-screen view) --------------------------------------------- */

.backdrop {
  --blob: 0.75; /* how strongly the glows show */
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
}
.backdrop i {
  position: absolute;
  width: 75vmax;
  height: 75vmax;
  border-radius: 50%;
  will-change: transform;
  /* The glows drift in steps, 30 a second, all at the same moments (each animation is --frames
     thirtieths of a second long), like the title screen's emblem: the screen is redrawn for them
     half as often as it would be, and slow, soft motion looks the same. */
  animation: drift calc(var(--frames, 900) / 30 * 1s) steps(var(--frames, 900)) infinite alternate;
}
/* each glow follows a band of the music (--bass, --mids, --highs from js/vibe.js): the two low ones
   the bass, the one at the side the mids, the top one the highs. --shine is how brightly the whole
   backdrop shines; it's in each glow's own opacity rather than on the backdrop, because an opacity
   or filter over the whole backdrop would make the graphics card redraw all of it on every frame. */
.backdrop i:nth-child(1) { top: -30vmax; left: -20vmax; background: radial-gradient(circle, var(--c-1), transparent 66%); --frames: 810; opacity: calc(var(--blob) * var(--shine, 1) * (0.7 + 0.5 * var(--highs, 0.35))); }
.backdrop i:nth-child(2) { bottom: -35vmax; right: -25vmax; background: radial-gradient(circle, var(--c-3), transparent 66%); --frames: 990; animation-delay: -9s; opacity: calc(var(--blob) * var(--shine, 1) * (0.6 + 0.65 * var(--bass, 0.35))); scale: calc(0.9 + 0.2 * var(--bass, 0.35)); }
.backdrop i:nth-child(3) { top: 10%; right: -35vmax; background: radial-gradient(circle, var(--c-4), transparent 62%); --frames: 1170; animation-delay: -17s; opacity: calc(var(--blob) * var(--shine, 1) * (0.7 + 0.5 * var(--mids, 0.35))); }
.backdrop i:nth-child(4) { bottom: -30vmax; left: -25vmax; background: radial-gradient(circle, var(--c-2), transparent 62%); --frames: 870; animation-delay: -4s; opacity: calc(var(--blob) * var(--shine, 1) * (0.65 + 0.55 * var(--bass, 0.35))); }
@keyframes drift {
  0% { transform: translate(0, 0) scale(1) rotate(0deg); }
  50% { transform: translate(14vmax, 9vmax) scale(1.18) rotate(30deg); }
  100% { transform: translate(-8vmax, 16vmax) scale(0.9) rotate(-20deg); }
}

/* --- landing page: a title screen, and a short summary below it ------------------------------- */

.landing { position: relative; isolation: isolate; padding: 0 1.25rem; overflow: hidden; }
body.has-track .landing { padding-bottom: var(--bar-h); }
.landing-settings { position: absolute; top: calc(1rem + env(safe-area-inset-top)); right: 1rem; }
/* the title screen fills the screen, its middle above the player bar when there is one */
.landing-hero { display: grid; place-items: center; min-height: 100vh; min-height: 100svh; padding: 4rem 0 2.5rem; }
body.has-track .landing-hero { padding-bottom: calc(var(--bar-h) + 1.5rem); }
.landing-content { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1rem; max-width: 46rem; text-align: center; }
.landing.intro .landing-content > * { animation: rise 0.9s var(--ease-out) backwards; animation-delay: calc(var(--n, 0) * 110ms + 100ms); }
/* (no blur on the way in: blurring text as it moves is the most expensive thing the page could do
   while it's still loading) */
@keyframes rise { from { opacity: 0; transform: translateY(22px); } }

/* the emblem is a link back to the artist's main site (the portfolio): pointing at it lights its rings */
.emblem { position: relative; display: grid; place-items: center; width: clamp(160px, 22vw, 220px); aspect-ratio: 1; margin-bottom: 0.4rem; }
.emblem-ring { position: absolute; inset: 3%; border: 1px solid var(--line-strong); animation: turn 26s steps(780) infinite; transition: border-color 0.2s; }
.emblem-ring + .emblem-ring { inset: 13%; border-color: color-mix(in srgb, var(--accent-3) 55%, transparent); animation-duration: 17s; animation-timing-function: steps(510); animation-direction: reverse; }
.emblem:hover .emblem-ring { border-color: var(--accent); }
@keyframes turn { from { transform: rotate(45deg); } to { transform: rotate(405deg); } }
/* the profile picture, as a diamond */
.avatar-glow { position: relative; width: 68%; aspect-ratio: 1; filter: drop-shadow(0 0 26px color-mix(in srgb, var(--accent-3) 45%, transparent)); animation: float 8s steps(30) infinite; }
.avatar-glow::after { content: ""; position: absolute; inset: 12%; border: 1px solid color-mix(in srgb, var(--accent-3) 70%, transparent); transform: rotate(45deg); pointer-events: none; }
.emblem .avatar { display: block; width: 100%; height: 100%; object-fit: cover; clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); }
/* up and down like a sine wave, in 8 one-second parts of 30 steps each */
@keyframes float {
  12.5%, 87.5% { transform: translateY(-1.2px); }
  25%, 75% { transform: translateY(-4px); }
  37.5%, 62.5% { transform: translateY(-6.8px); }
  50% { transform: translateY(-8px); }
}
.logo-word {
  margin: 0;
  padding-left: 0.24em; /* balances the letter spacing after the last letter */
  color: var(--text);
  font: 350 clamp(2.1rem, 7.4vw, 5.2rem) / 1.05 var(--display);
  letter-spacing: 0.24em;
  text-transform: uppercase;
}
.landing-tagline { margin: 0.2rem 0 0; color: var(--muted); font-size: clamp(1.05rem, 2.6vw, 1.3rem); }
.landing-stats { margin: 0; color: var(--faint); font: 500 0.92rem var(--display); letter-spacing: 0.02em; }
.landing-content .btn { margin: 0.9rem 0 0.5rem; }

/* below the title screen: the artist's few words, three short points, and the releases */
.landing-more { display: grid; justify-items: center; gap: clamp(3rem, 7vw, 4.5rem); max-width: 1040px; margin: 0 auto; padding-top: clamp(1rem, 4vw, 2.5rem); }
.landing-bio { max-width: 38rem; margin: 0; color: var(--muted); font-size: 1.05rem; text-align: center; }
.landing-points { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 2rem clamp(1.5rem, 4vw, 3rem); width: 100%; margin: 0; padding: 0; list-style: none; }
.landing-points h3 { display: flex; align-items: center; gap: 0.55rem; margin: 0 0 0.45rem; font: 650 1.02rem / 1.3 var(--display); letter-spacing: 0.01em; }
.landing-points h3::before { content: ""; flex: none; width: 0.42em; height: 0.42em; transform: rotate(45deg); background: var(--accent); }
.landing-points p { margin: 0; color: var(--muted); font-size: 0.94rem; line-height: 1.6; }
/* "RELEASES", lettered like the artist's name above */
.landing-heading { margin: 0 0 1.5rem; padding-left: 0.3em; color: var(--muted); font: 500 0.82rem var(--display); letter-spacing: 0.3em; text-transform: uppercase; text-align: center; }
.release-list { display: flex; flex-wrap: wrap; justify-content: center; gap: 1.75rem 1.5rem; margin: 0; padding: 0; list-style: none; }
.release { display: flex; flex-direction: column; gap: 0.15rem; width: clamp(130px, 17vw, 168px); color: var(--text); text-decoration: none; }
.release-cover { display: block; aspect-ratio: 1; margin-bottom: 0.55rem; overflow: hidden; border-radius: 2px; box-shadow: 0 0 0 1px var(--line), 0 10px 28px var(--shadow); }
.release:hover .release-cover { outline: 1px solid var(--accent); outline-offset: -1px; }
.release-title { font: 650 0.95rem / 1.3 var(--display); }
.release-meta { color: var(--muted); font-size: 0.84rem; }
/* the end of the page: only the copyright, centered, well clear of what's above it */
.landing-footer { margin-top: clamp(4rem, 9vw, 6rem); padding: 1.5rem 0 2rem; color: var(--faint); font: 500 0.85rem var(--display); text-align: center; }

/* --- music page: top bar ---------------------------------------------------------------------- */

.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: clamp(0.75rem, 3vw, 2.25rem);
  height: calc(64px + env(safe-area-inset-top)); /* (where the page runs under a phone's clock, clear of it) */
  padding: env(safe-area-inset-top) clamp(0.75rem, 3vw, 2rem) 0;
  background: var(--header-bg);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-bottom: 1px solid var(--line);
}
.brand {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-right: auto;
  color: var(--text);
  font: 650 0.88rem var(--display);
  letter-spacing: 0.14em;
  text-decoration: none;
  text-transform: uppercase;
}
/* the profile picture as a diamond, with a soft light around its edges (the glow is on the wrapper,
   because a clip-path would cut off a shadow on the picture itself) */
.brand-mark {
  flex: none;
  width: 40px;
  height: 40px;
  filter: drop-shadow(0 0 1px color-mix(in srgb, var(--accent-3) 70%, transparent)) drop-shadow(0 0 7px color-mix(in srgb, var(--accent-3) 40%, transparent));
  transition: transform 0.5s var(--ease-out), filter 0.25s;
}
.brand:hover .brand-mark { transform: scale(1.1); filter: drop-shadow(0 0 1px color-mix(in srgb, var(--accent-3) 85%, transparent)) drop-shadow(0 0 10px color-mix(in srgb, var(--accent-3) 55%, transparent)); }
.brand-mark .avatar { display: block; width: 100%; height: 100%; object-fit: cover; clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); }
.topbar nav { display: flex; gap: clamp(0.9rem, 3vw, 1.75rem); }
.topbar nav a { position: relative; padding: 0.35rem 0.1rem; color: var(--muted); font: 600 0.92rem var(--display); letter-spacing: 0.02em; text-decoration: none; transition: color 0.2s; }
.topbar nav a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.3s var(--ease-out);
}
.topbar nav a:hover { color: var(--text); }
.topbar nav a:hover::after { transform: scaleX(1); }

main { max-width: 1240px; margin: 0 auto; padding: 0 clamp(1rem, 3.5vw, 3rem); }

/* --- music page: header and tools ------------------------------------------------------------- */

.music { padding-top: clamp(1.75rem, 4vw, 3rem); }
.music-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 1rem 2rem; margin-bottom: clamp(1.5rem, 3vw, 2.25rem); }
.music-title { flex: 1 1 320px; min-width: 0; }
.music-title .section-title { font-size: clamp(1.7rem, 4vw, 2.4rem); }
.music-stats { margin: 0.4rem 0 0; color: var(--muted); font-size: 0.95rem; }
.tools-right { display: flex; flex-wrap: wrap; align-items: center; gap: 0.7rem; padding-right: 0.4rem; }

.search { position: relative; isolation: isolate; display: flex; align-items: center; gap: 0.55rem; width: 240px; min-height: 38px; padding: 0 1rem; color: var(--faint); }
.search::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  transform: skewX(var(--skew));
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--clear);
  box-shadow: inset 0 1px 0 var(--edge);
  transition: border-color 0.2s;
}
.search:focus-within::before { border-color: var(--accent); }
.search svg { width: 1rem; height: 1rem; }
.search input { flex: 1; min-width: 0; border: 0; outline: 0; background: none; color: var(--text); font: inherit; font-size: 0.95rem; }
.search input::placeholder { color: var(--faint); }
/* the songs' order: by album, or one list (a dropdown like the settings', styled with them below) */
.dropdown.sort { width: 12.5rem; }
.dropdown-icon { width: 16px; height: 16px; color: var(--muted); }
.status { color: var(--muted); }

/* --- music page: the selected song and the list ----------------------------------------------- */

.select { display: grid; grid-template-columns: minmax(240px, 300px) minmax(0, 1fr); gap: clamp(1.5rem, 3.5vw, 3.25rem); align-items: start; }
.focus { position: sticky; top: calc(88px + env(safe-area-inset-top)); }
/* the cover, whole */
.jacket { aspect-ratio: 1; margin-bottom: 1.15rem; overflow: hidden; border-radius: var(--radius); background: var(--surface-solid); box-shadow: 0 0 0 1px var(--line), 0 14px 36px var(--shadow); }
.jacket > * { width: 100%; height: 100%; }
.jacket.swap > * { animation: jacket-in 0.5s var(--ease-out); }
@keyframes jacket-in { from { opacity: 0; transform: scale(1.04); } }
/* "Now playing", "Paused", or where the song is on its album */
.focus-state { display: flex; align-items: center; gap: 0.5rem; margin: 0 0 0.25rem; color: var(--faint); font: 600 0.82rem var(--display); letter-spacing: 0.03em; }
.focus-state.playing { color: var(--accent); }
.focus-state.playing::before { content: ""; width: 0.45em; height: 0.45em; transform: rotate(45deg); background: currentColor; }
.focus-title { margin: 0; font: 700 clamp(1.35rem, 2.3vw, 1.8rem) / 1.2 var(--font); overflow-wrap: anywhere; }
.focus-album { margin: 0.3rem 0 0; color: var(--muted); }
.focus-meta { margin: 0.1rem 0 1.1rem; color: var(--faint); font-size: 0.9rem; font-variant-numeric: tabular-nums; }
.focus-actions { display: flex; align-items: center; gap: 0.4rem; }
.focus-text-swap { animation: fade-slide 0.4s var(--ease-out); }
@keyframes fade-slide { from { opacity: 0; transform: translateX(-10px); } }

/* an album: a heading like a list's (its cover, its name, what it is and how long, and two small
   buttons to play or shuffle it) over a hairline, then its songs */
.group { margin: 0 0 2.25rem; }
.group-head {
  display: grid;
  grid-template-columns: 52px minmax(0, 1fr) auto;
  align-items: center;
  gap: 1rem;
  margin: 0 0 0.35rem;
  padding: 0 0.55rem 0.75rem 1rem;
  border-bottom: 1px solid var(--line);
}
.group-cover { width: 52px; aspect-ratio: 1; overflow: hidden; border-radius: 2px; box-shadow: 0 0 0 1px var(--line); }
.group-text { min-width: 0; }
.group-title { margin: 0; font: 650 1.2rem / 1.25 var(--display); letter-spacing: 0.01em; overflow-wrap: anywhere; }
.group-meta { margin: 0.15rem 0 0; color: var(--muted); font-size: 0.86rem; }
/* play and shuffle are the same in every album, so they stay small and quiet until pointed at */
.group-actions { display: flex; }
.group-actions .icon-button { color: var(--faint); }
.group-actions .icon-button svg { width: 16px; height: 16px; }
/* an album folded to its heading (its chevron, or its cover or title; the user asked for it, to
   scroll past albums): a search still shows its matches */
.group-cover, .group-text { cursor: pointer; }
.artist-group :is(.group-cover, .group-text), .playlist-group :is(.group-cover, .group-text) { cursor: default; }
/* an artist's heading: their picture as a diamond, like the logo (another artist's first letter) */
.artist-mark { display: grid; place-items: center; overflow: visible; box-shadow: none; }
.artist-picture { width: 100%; height: 100%; object-fit: cover; clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); }
.artist-letter { display: grid; place-items: center; width: 74%; aspect-ratio: 1; rotate: 45deg; border: 1px solid var(--line-strong); color: var(--muted); font: 650 1.1rem var(--display); }
/* a playlist: the first song's cover, or a mark while it has none */
.playlist-mark { display: grid; place-items: center; width: 100%; height: 100%; background: var(--clear); color: var(--faint); }
.playlist-mark svg { width: 22px; height: 22px; }
.playlists-head { display: flex; justify-content: flex-end; margin: 0 0 1rem; }
.playlist-group { margin-bottom: 1.25rem; }
.playlist-empty { margin: 0.4rem 1rem 0; color: var(--faint); font-size: 0.88rem; }
.fold svg { transition: rotate 0.2s var(--ease-out), transform 0.2s var(--ease-out); }
.group.collapsed { margin-bottom: 1.25rem; }
.group.collapsed .fold svg { rotate: -90deg; }
.group.collapsed .tracklist { display: none; }
#albums.searching .group.collapsed .tracklist { display: grid; }

/* the songs: plain rows; a slanted plate shows behind one on hover, and the one playing is lit */
.tracklist { display: grid; gap: 2px; margin: 0; padding: 0; list-style: none; }
.track {
  position: relative;
  isolation: isolate;
  display: grid;
  grid-template-columns: minmax(0, 1fr); /* the song; then a column for each part after it */
  grid-auto-flow: column;
  grid-auto-columns: auto;
  align-items: center;
  gap: 0.3rem;
  min-height: 50px;
  padding: 0 0.9rem 0 0.6rem;
  scroll-margin: 140px;
}
.track::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  transform: skewX(var(--skew));
  border-radius: var(--radius);
  transition: background-color 0.2s;
}
@media (hover: hover) {
  .track:hover::before { background-color: var(--hover); }
}
.track.current::before { background-color: var(--selected); box-shadow: inset 3px 0 var(--accent); }
.track-main { display: flex; align-items: center; gap: 0.9rem; min-width: 0; padding: 0.75rem 0.4rem; border: 0; background: none; text-align: left; }
.track-no { display: grid; place-items: center; flex: none; width: 1.8rem; color: var(--faint); font: 600 0.85rem var(--display); font-variant-numeric: tabular-nums; }
.track-no > * { grid-area: 1 / 1; }
.track-no svg { display: none; width: 1rem; height: 1rem; color: var(--text); }
.track:hover .track-no .num { visibility: hidden; }
.track:hover .track-no svg { display: block; }
.track-title { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
/* in one list of all the songs (sorted), each names its album, under a hairline like an album's */
.track-album { margin-left: 0.7rem; color: var(--faint); font-size: 0.86rem; }
.all-songs { padding-top: 0.35rem; border-top: 1px solid var(--line); }
.track.current .track-title { font-weight: 700; }
.track.current .num, .track.current:hover .track-no svg { display: none; }
.track.current .eq { display: flex; }
.track-actions { display: flex; opacity: 0; transition: opacity 0.2s; }
.track:hover .track-actions, .track:focus-within .track-actions { opacity: 1; }
.track-time { min-width: 2.8rem; color: var(--muted); font: 500 0.88rem var(--display); font-variant-numeric: tabular-nums; text-align: right; }
.track.current .track-time { color: var(--text); }
/* a song's streams (site.js), when known: a quiet number before its length, like a plays column */
.track-streams { color: var(--faint); font: 500 0.82rem var(--display); font-variant-numeric: tabular-nums; text-align: right; }
.track-streams:not(:empty) { min-width: 4.6rem; }

/* favorites: a heart beside each song (it shows while pointing at the row) and the one in focus,
   filled in the accent once the song is one */
.heart svg { fill: none; }
.heart[aria-pressed="true"] { color: var(--accent); }
.heart[aria-pressed="true"] svg { fill: currentColor; }
.track .heart { opacity: 0; }
.track:hover .heart, .track:focus-within .heart, .track .heart[aria-pressed="true"] { opacity: 1; }
/* a song's "···" (playlists, downloads, and the rest): always there on phones, on hover here */
.track-more { opacity: 0; color: var(--faint); transition: opacity 0.2s; }
.track:hover .track-more, .track:focus-within .track-more, .track-more[aria-expanded="true"] { opacity: 1; }
/* a downloaded song: a small tick after its title, the accent toned down toward the "···" so it's
   there to see without standing out like the buttons (the user asked); offline, the songs that
   aren't are dimmed */
.track-saved { display: none; flex: none; margin-left: 0.45rem; color: color-mix(in srgb, var(--accent) 55%, var(--faint)); }
.track-saved svg { width: 15px; height: 15px; vertical-align: -0.2em; }
.track.saved .track-saved { display: inline-block; }
body.offline .track:not(.saved) { opacity: 0.4; }
.icon-button.download[aria-pressed="true"] { color: var(--accent); }
.icon-button.download.busy { opacity: 0.5; }

/* a song's menu, from the "···" on its row (phones): a plain list like the dropdowns' */
/* a short question (a playlist's name, or "Delete …?"): a plain box like the menus */
.ask {
  width: min(24rem, calc(100vw - 2rem));
  padding: 1.25rem 1.25rem 1.1rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface-solid);
  color: var(--text);
  box-shadow: 0 14px 36px var(--shadow);
}
.ask::backdrop { background: rgb(2 6 14 / 0.45); }
.ask-form { display: grid; gap: 0.9rem; margin: 0; }
.ask-title { margin: 0; font: 650 1.1rem / 1.3 var(--display); }
.ask-text { margin: -0.3rem 0 0; color: var(--muted); font-size: 0.9rem; }
.ask-input { width: 100%; min-height: 40px; padding: 0 0.8rem; border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--clear); color: var(--text); font: inherit; font-size: 1rem; outline: 0; }
.ask-input:focus { border-color: var(--accent); }
.ask-buttons { display: flex; justify-content: flex-end; gap: 0.6rem; }
.downloads-status { margin: 0 0 0.8rem; font-size: 0.92rem; }
.downloads-actions { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.downloads-actions .btn:disabled { opacity: 0.45; }

.song-menu {
  position: fixed;
  z-index: 61; /* over the player bar */
  display: grid;
  gap: 2px;
  min-width: 13.5rem;
  padding: 0.35rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface-solid);
  box-shadow: 0 14px 36px var(--shadow);
  animation: list-in 0.12s ease-out;
}
.menu-item { position: relative; isolation: isolate; display: flex; align-items: center; gap: 0.8rem; padding: 0.7rem 0.75rem; border: 0; background: none; color: var(--text); font: 600 0.92rem / 1.2 var(--display); text-align: left; }
.menu-item::before { content: ""; position: absolute; inset: 0; z-index: -1; transform: skewX(var(--skew)); border-radius: var(--radius); }
.menu-item:active::before, :root[data-input="keyboard"] .menu-item:focus-visible::before { background: var(--hover); }
.menu-item svg { width: 18px; height: 18px; color: var(--muted); }
.menu-rating { display: flex; justify-content: center; padding: 0.35rem 0.5rem 0.2rem; border-top: 1px solid var(--line); }
.menu-rating .star { width: 38px; height: 40px; }
.menu-rating .star svg { width: 22px; height: 22px; }
@media (hover: hover) {
  .menu-item:hover::before { background: var(--hover); }
}

/* star ratings (Settings > Your songs, off at first), like iTunes: a song's own stars, and while
   pointing at its row all five to choose from; pointing at one lights it and the ones before it */
.rating { display: inline-flex; align-items: center; }
.star { display: grid; place-items: center; width: 17px; height: 30px; padding: 0; border: 0; background: none; color: var(--faint); cursor: pointer; }
.star svg { width: 13px; height: 13px; fill: none; }
.star.on { color: var(--muted); }
.star.on svg { fill: currentColor; }
.rating:has(.star:hover) .star { color: var(--accent); }
.rating:has(.star:hover) .star svg { fill: currentColor; }
.rating .star:hover ~ .star { color: var(--faint); }
.rating .star:hover ~ .star svg { fill: none; }
.track .star:not(.on) { opacity: 0; }
.track:hover .star, .track:focus-within .star { opacity: 1; }
.focus-rating { margin: -0.75rem 0 0.8rem -0.2rem; }
.focus-rating .star { width: 23px; height: 30px; }
.focus-rating .star svg { width: 17px; height: 17px; }
.track.highlight::before { animation: highlight 2.6s ease-out; }
@keyframes highlight { from { background-color: color-mix(in srgb, var(--accent) 40%, transparent); } }

.eq { display: none; align-items: flex-end; gap: 2px; height: 14px; }
/* the bars grow and shrink by scaling, which doesn't make the page lay itself out again */
.eq i { width: 3px; height: 100%; border-radius: 1px; background: var(--accent); transform: scaleY(0.35); transform-origin: bottom; }
body.is-playing .track.current .eq i { animation: eq 0.85s ease-in-out infinite alternate; }
.eq i:nth-child(2) { animation-delay: -0.3s !important; }
.eq i:nth-child(3) { animation-delay: -0.55s !important; }
@keyframes eq { from { transform: scaleY(0.2); } to { transform: scaleY(1); } }

/* songs slide in when the music page opens */
.song-list.enter .group-head, .song-list.enter .track { animation: bar-in 0.6s var(--ease-out) backwards; animation-delay: calc(var(--d, 0) * 1ms); }
@keyframes bar-in { from { opacity: 0; transform: translateX(40px); } }
.focus.enter { animation: focus-in 0.7s var(--ease-out) backwards; }
@keyframes focus-in { from { opacity: 0; transform: translateX(-30px); } }

/* placeholder art: a planet in the album's own colors */
.cover-img { display: block; width: 100%; height: 100%; object-fit: cover; }
.cover-art {
  position: relative;
  display: flex;
  align-items: flex-end;
  width: 100%;
  height: 100%;
  aspect-ratio: 1;
  padding: 8%;
  overflow: hidden;
  background:
    radial-gradient(130% 100% at 100% 100%, var(--c2), transparent 70%),
    linear-gradient(150deg, var(--c1), var(--c2));
}
.cover-art::before {
  content: "";
  position: absolute;
  top: 12%;
  right: 12%;
  width: 46%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle at 32% 30%, rgba(255, 255, 255, 0.75), var(--c1) 42%, var(--c2) 100%);
  box-shadow: 0 0 60px color-mix(in srgb, var(--c1) 60%, transparent);
}
.cover-art::after {
  content: "";
  position: absolute;
  top: 29%;
  right: 3%;
  width: 64%;
  height: 12%;
  border: 2px solid rgba(255, 255, 255, 0.55);
  border-radius: 50%;
  transform: rotate(-18deg);
}
.cover-art span { position: relative; z-index: 1; color: #fff; font: 700 clamp(1rem, 2.2vw, 1.45rem) / 1.1 var(--display); letter-spacing: 0.08em; text-transform: uppercase; text-shadow: 0 2px 16px rgba(0, 0, 0, 0.5); }
.group-cover .cover-art span, .now-cover .cover-art span, .queue-cover .cover-art span { display: none; }
.group-cover .cover-art::after, .now-cover .cover-art::after, .queue-cover .cover-art::after { border-width: 1px; }

/* --- about, privacy ---------------------------------------------------------------------------- */

.panel {
  max-width: 780px;
  margin-top: clamp(3rem, 7vw, 4.5rem);
  padding: clamp(1.4rem, 3.5vw, 2.2rem);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}
.panel .section-title { margin-bottom: 1rem; font-size: clamp(1.15rem, 2.5vw, 1.4rem); }
.panel p { color: var(--muted); }
.panel strong { color: var(--text); }
.about .artist-links { justify-content: flex-start; margin-top: 1rem; }
.fine { font-size: 0.9rem; }
.toggle { display: inline-flex; align-items: center; gap: 0.85rem; margin: 0.4rem 0 0; cursor: pointer; font-weight: 600; }
.toggle input {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  flex: none;
  width: 50px;
  height: 24px;
  margin: 0;
  cursor: pointer;
}
.toggle input::before {
  content: "";
  position: absolute;
  inset: 0;
  transform: skewX(var(--skew));
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background-color: var(--surface-2);
  transition: background-color 0.3s, border-color 0.3s;
}
.toggle input::after {
  content: "";
  position: absolute;
  top: 5px;
  left: 8px;
  width: 14px;
  height: 14px;
  transform: rotate(45deg);
  border-radius: 2px;
  background-color: var(--muted);
  transition: transform 0.4s var(--ease-out), background-color 0.3s;
}
.toggle input:checked::before { border-color: transparent; background-color: var(--accent); }
.toggle input:checked::after { transform: translateX(22px) rotate(225deg); background-color: var(--on-accent); }

.site-footer { max-width: 1240px; margin: 4rem auto 0; padding: 2rem clamp(1rem, 3.5vw, 3rem); border-top: 1px solid var(--line); color: var(--faint); font-size: 0.88rem; }
.site-footer p { margin: 0.35rem 0; }
.site-footer .also-on { justify-content: flex-start; margin-bottom: 1.1rem; }
kbd { padding: 0.05rem 0.4rem; border: 1px solid var(--line); border-radius: 3px; background: var(--surface); font: 600 0.9em var(--display); }

/* --- player bar ------------------------------------------------------------------------------- */

.player-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 50;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr) minmax(0, 1fr);
  align-items: center;
  gap: 1rem;
  padding: 0.8rem clamp(1rem, 2.5vw, 1.75rem) calc(0.8rem + env(safe-area-inset-bottom));
  background: var(--bar-bg);
  color: var(--text);
  backdrop-filter: blur(22px);
  -webkit-backdrop-filter: blur(22px);
  border-top: 1px solid var(--line);
}
.now { display: flex; align-items: center; gap: 0.9rem; min-width: 0; }
.now-cover {
  flex: none;
  width: 56px;
  height: 56px;
  padding: 0;
  overflow: hidden;
  border: 0;
  border-radius: 2px;
  background: var(--surface-2);
  transition: transform 0.3s var(--ease-out);
}
@media (hover: hover) {
  .now-cover:not(:disabled):hover { transform: scale(1.06); }
}
.now-text { min-width: 0; }
.now-title, .now-album { margin: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.now-title { font-weight: 700; }
.now-album { color: var(--muted); font-size: 0.85rem; }

.transport { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; min-width: 0; }
.controls { display: flex; align-items: center; gap: 0.45rem; }
.repeat-one { display: none; position: absolute; top: 4px; right: 3px; font: 800 0.6rem var(--display); }
#repeat[data-mode="one"] .repeat-one { display: block; }
#fullscreen[aria-pressed="true"]::after { display: none; }

/* the play button is a diamond of the main buttons' glass, the light along its two top edges */
.play-button {
  position: relative;
  isolation: isolate;
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  margin: 0 0.35rem;
  padding: 0;
  border: 0;
  background: none;
  color: var(--glass-text);
}
.play-button::before {
  content: "";
  position: absolute;
  inset: 9px;
  z-index: -1;
  transform: rotate(45deg);
  border: 1px solid var(--glass-line);
  border-radius: 5px;
  /* turned with the diamond: 135deg runs from its top corner down, and its top and right sides are
     the two on top once it's turned */
  background: linear-gradient(135deg, var(--sheen), transparent 60%) var(--glass);
  box-shadow: inset 0 1px 0 var(--edge), inset -1px 0 0 var(--edge);
  transition: transform 0.45s var(--ease-out), border-color 0.2s, background-color 0.2s;
}
@media (hover: hover) {
  .play-button:hover::before { transform: rotate(45deg) scale(1.08); border-color: var(--accent); background-color: var(--glass-hover); }
}
.play-button:active::before { transform: rotate(135deg) scale(0.94); }
.play-button svg { width: 20px; height: 20px; }
.play-button svg.pop { animation: pop 0.35s var(--ease-out); }
@keyframes pop { from { transform: scale(0.4); opacity: 0.2; } }
.buffering .play-button::after {
  content: "";
  position: absolute;
  inset: 3px;
  border: 2px solid transparent;
  border-top-color: var(--accent-3);
  border-right-color: var(--accent);
  transform: rotate(45deg);
  animation: spin-diamond 0.9s linear infinite;
}
@keyframes spin-diamond { to { transform: rotate(405deg); } }

.progress { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 0.75rem; width: 100%; max-width: 640px; }
/* the seek bar: the song's wave behind the line (js/wave.js), with the part already played lit up.
   The wave is glass like the buttons: an outline with only a faint tint inside. The line runs 60%
   of the way down, leaving the wave room above it: the slider (whose line is always in its middle)
   is taller than the bar and hangs below it. */
.seek { position: relative; height: 38px; touch-action: none; cursor: pointer; }
.seek:has(.slider:disabled) { cursor: default; }
/* app.js seeks from anywhere along the bar, pressed and dragged as in a phone's music app; the slider
   draws the line and its handle, and is there for the keyboard and screen readers */
.seek .slider { position: absolute; top: 0; left: 0; z-index: 1; height: 120%; pointer-events: none; }
.seek::after { content: ""; position: absolute; inset: -8px 0; } /* a little more to press on */
/* while it's held: the line in the accent, and its handle bigger */
.seek.scrubbing .slider { --done: var(--accent); }
.seek.scrubbing .slider::-webkit-slider-thumb { opacity: 1; transform: rotate(45deg) scale(1.4); }
.seek.scrubbing .slider::-moz-range-thumb { opacity: 1; }
.wave { position: absolute; inset: 0; pointer-events: none; opacity: 0; transition: opacity 0.5s, filter 0.25s; }
.wave.ready { opacity: 1; }
.wave svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
/* (the stroke stays 1px however far the bar stretches the drawing) */
.wave path { fill: color-mix(in srgb, var(--text) 3%, transparent); stroke: var(--track); stroke-width: 1px; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.wave .wave-reflection { opacity: 0.45; }
.wave-played { clip-path: inset(0 calc(100% - var(--fill, 0%)) 0 0); }
.wave-played path { fill: color-mix(in srgb, var(--accent) 7%, transparent); stroke: url(#wave-fill); }
.wave-from { stop-color: var(--accent-3); }
.wave-to { stop-color: var(--accent); }
@media (hover: hover) {
  .seek:hover .wave { filter: brightness(1.3) saturate(1.2); }
}
/* the time under the pointer */
.seek-tip {
  position: absolute;
  bottom: calc(100% + 2px);
  left: 0;
  z-index: 2;
  isolation: isolate;
  padding: 0.16rem 0.55rem;
  transform: translateX(-50%);
  color: var(--text);
  font: 650 0.7rem var(--display);
  letter-spacing: 0.05em;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}
.seek-tip::before { content: ""; position: absolute; inset: 0; z-index: -1; transform: skewX(var(--skew)); border: 1px solid var(--line-strong); border-radius: 2px; background: var(--surface-solid); box-shadow: 0 4px 14px var(--shadow); }
.time { min-width: 2.6rem; color: var(--faint); font: 650 0.74rem var(--display); letter-spacing: 0.05em; font-variant-numeric: tabular-nums; text-align: center; }
.progress .time { position: relative; top: 4px; } /* level with the seek line, which runs below the middle */

.slider {
  --done: var(--text);
  width: 100%;
  height: 18px;
  margin: 0;
  background: transparent;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
}
.slider:focus-visible, .slider:active { --done: var(--accent); }
@media (hover: hover) {
  .slider:hover, .seek:hover .slider { --done: var(--accent); }
}
.slider:disabled { cursor: default; opacity: 0.5; }
.slider::-webkit-slider-runnable-track {
  height: 3px;
  background: linear-gradient(to right, var(--done) var(--fill, 0%), var(--track) var(--fill, 0%));
}
.slider::-webkit-slider-thumb {
  width: 11px;
  height: 11px;
  margin-top: -4px;
  border: 0;
  border-radius: 2px;
  background: var(--text);
  opacity: 0;
  transform: rotate(45deg) scale(0.6);
  transition: opacity 0.2s, transform 0.25s var(--ease-out);
  -webkit-appearance: none;
}
.slider::-moz-range-track { height: 3px; background: var(--track); }
.slider::-moz-range-progress { height: 3px; background: var(--done); }
.slider::-moz-range-thumb { width: 11px; height: 11px; border: 0; border-radius: 2px; background: var(--text); opacity: 0; transform: rotate(45deg); }
.slider:hover::-webkit-slider-thumb, .seek:hover .slider::-webkit-slider-thumb, .slider:focus-visible::-webkit-slider-thumb, .slider:active::-webkit-slider-thumb { opacity: 1; transform: rotate(45deg) scale(1); }
.slider:hover::-moz-range-thumb, .seek:hover .slider::-moz-range-thumb, .slider:focus-visible::-moz-range-thumb, .slider:active::-moz-range-thumb { opacity: 1; }

.extras { display: flex; align-items: center; justify-content: flex-end; gap: 0.3rem; }
.volume { display: flex; align-items: center; gap: 0.25rem; width: 150px; }

/* --- the live EQ (Settings > Sound; js/liveeq.js) ---------------------------------------------- */

/* see-through bars along the bottom of the screen, on a strip just above the player bar, over the
   pages, the panels, and the full-screen view (never under the bar's glass, which would have to blur
   them again on every frame); in full screen they follow the bar down when it slides away. In the
   theme's accent, and in full screen the album's color (app.js, showPalette). */
.live-eq {
  position: fixed;
  left: 0;
  right: 0;
  bottom: var(--bar-h);
  z-index: 52;
  width: 100%;
  height: 56px;
  pointer-events: none;
  color: var(--accent);
  transition: transform 0.5s var(--ease-out);
}
body.visual-open .live-eq { color: var(--album-color, #fff); }
body.controls-hidden .live-eq { transform: translateY(var(--bar-h)); }
body:not(.has-track) .live-eq { display: none; }

/* --- full-screen view ------------------------------------------------------------------------- */

.visual {
  --bass: 0.35;
  --mids: 0.35;
  --highs: 0.35;
  position: fixed;
  inset: 0;
  z-index: 46;
  isolation: isolate;
  display: grid;
  place-items: center;
  padding: calc(7vh + env(safe-area-inset-top)) 4vw calc(var(--bar-h) + 7vh);
  background: #06040c;
  color-scheme: dark;
  touch-action: none; /* nothing here scrolls, and dragging on it doesn't move the page underneath */
  /* the album's colors (app.js sets them on the backdrops and the frame) */
  --c-1: #7b67ff;
  --c-2: #1c1a4d;
  --c-3: #3fb6e0;
  --c-4: #b05cff;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.45s var(--ease-out), visibility 0s linear 0.45s;
}
.visual.open { opacity: 1; visibility: visible; transition: opacity 0.45s var(--ease-out), visibility 0s; }
.visual:not(.open) .backdrop i, .visual:not(.open) .scene .light, .visual:not(.open) .visual-frame::after { animation-play-state: paused; }
.visual.idle { cursor: none; }
/* The album's colors are on two copies of the backdrop (and of the glow behind the cover). When the
   album changes, the new colors go on the copy that isn't showing and it fades in over the other:
   only opacity changes, which the graphics card does for free, where fading the colors themselves
   would repaint every glow on every frame. The copy out of sight is transparent, not hidden, so its
   glows keep drifting on the graphics card in step with the other's (hidden, they'd be moved by the
   page itself, frame by frame). */
.palette { opacity: 0; transition: opacity 1.6s ease; }
.palette.on { opacity: 1; }
/* the backdrops give way to a song's own scene, the same way */
.backdrops { position: absolute; inset: 0; z-index: -1; transition: opacity 1.6s ease; }
.visual[data-scene] .backdrops { opacity: 0; }
/* the music lights the backdrop: the bass sets how brightly it shines */
.visual .backdrop { --shine: calc((0.45 + 0.6 * var(--bass)) * (0.72 + 0.5 * var(--bass))); }
/* a song's scene (song-effects.js, built by js/effects.js): lights composed for that song, which
   brighten with the bass, the chorus (--chorus), and the drop (--swell: the light building into
   it and a quick pop on it), and go dark in a break just before a drop (--dark). It sits level
   with the cover, so it moves with it when the player bar slides away. */
.scenes { position: absolute; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
.scene {
  position: absolute;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  translate: 0 calc(var(--bar-h) / -2);
  transition: opacity 1.6s ease, visibility 0s linear 1.6s, translate 0.5s var(--ease-out);
}
.scene.shown { opacity: 1; visibility: visible; transition: opacity 1.6s ease, visibility 0s, translate 0.5s var(--ease-out); }
.scene:not(.shown) .light { animation-play-state: paused; }
.scene.asleep { display: none; } /* faded out: not styled, laid out, or drawn until it shows again */
body.controls-hidden .scene { translate: none; }
.scene .light {
  position: absolute;
  translate: -50% -50%;
  opacity: calc((var(--o) + (var(--oc) - var(--o)) * var(--chorus, 0)) * (1 + 0.35 * var(--swell, 0)) * (0.7 + 0.6 * var(--bass, 0.35)) * (1 - var(--dark, 0)));
  animation: breathe 20s ease-in-out infinite alternate;
  will-change: opacity;
}
@keyframes breathe { to { scale: 1.1; } }
/* a slight darkening toward the edges, on every song; songs with their own effects lift it as their
   chorus brightens (js/effects.js sets --vignette) */
.visual-vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(ellipse at center, transparent 50%, rgb(2 1 6 / 0.8) 100%);
  opacity: var(--vignette, 0.7);
}
.visual-frame {
  position: relative;
  width: min(86vw, calc(100vh - var(--bar-h) - 16vh - env(safe-area-inset-top)));
  width: min(86vw, calc(100svh - var(--bar-h) - 16vh - env(safe-area-inset-top)));
  aspect-ratio: 1;
  transition: translate 0.5s var(--ease-out);
}
/* when the player bar slides away, the cover glides down to the middle of the screen */
body.controls-hidden .visual-frame { translate: 0 calc(var(--bar-h) / 2); }
/* thin corner brackets around the cover */
.visual-frame::before {
  content: "";
  position: absolute;
  inset: -20px;
  pointer-events: none;
  --b: rgba(255, 255, 255, 0.55);
  background:
    linear-gradient(var(--b), var(--b)) top left / 36px 2px, linear-gradient(var(--b), var(--b)) top left / 2px 36px,
    linear-gradient(var(--b), var(--b)) top right / 36px 2px, linear-gradient(var(--b), var(--b)) top right / 2px 36px,
    linear-gradient(var(--b), var(--b)) bottom left / 36px 2px, linear-gradient(var(--b), var(--b)) bottom left / 2px 36px,
    linear-gradient(var(--b), var(--b)) bottom right / 36px 2px, linear-gradient(var(--b), var(--b)) bottom right / 2px 36px;
  background-repeat: no-repeat;
}
/* a soft light behind the cover that swells with the bass (two copies, one for each palette) */
.visual-glow {
  position: absolute;
  inset: -22%;
  z-index: -1;
  pointer-events: none;
  opacity: calc(0.12 + 0.88 * var(--bass));
  will-change: opacity;
}
.visual-glow i { position: absolute; inset: 0; background: radial-gradient(circle, color-mix(in srgb, var(--c-1) 60%, transparent), transparent 62%); }
/* a faint diamond slowly turning behind it, brighter with the highs (out of sight while a new cover
   unfolds: app.js, unfoldCover) */
.visual-frame::after {
  content: "";
  position: absolute;
  inset: 9%;
  z-index: -1;
  border: 1px solid rgba(255, 255, 255, 0.16);
  box-shadow: 0 0 60px color-mix(in srgb, var(--c-1) 30%, transparent);
  opacity: calc(0.3 + 0.85 * var(--highs));
  scale: calc(0.97 + 0.07 * var(--bass));
  transition: opacity var(--vibe-step) linear, scale var(--vibe-step) linear;
  animation: turn 70s linear infinite;
}
/* the cover's shadow and glow in its album's color: a box shadow on a square under it, painted once
   (a drop-shadow filter, which could follow the unfolding shape, would be worked out again on every
   frame anything in the cover changes). So it only fades in once the cover has filled the square
   (app.js, unfoldCover). */
.visual-art { position: absolute; inset: 0; }
.visual-art::before { content: ""; position: absolute; inset: 0; box-shadow: 0 30px 50px rgba(0, 0, 0, 0.5), 0 0 70px color-mix(in srgb, var(--c-1) 40%, transparent); }
.visual-cover { position: absolute; inset: 0; overflow: hidden; }
/* the cover unfolds from a diamond whenever it changes (app.js, unfoldCover) */
.visual-cover > * { position: absolute; inset: 0; width: 100%; height: 100%; }
/* when the song changes, a soft light in the album's color swells and fades over the cover, and a
   thin diamond drifts out from it (app.js, flashCover) */
.visual-flash {
  position: absolute;
  inset: -25%;
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(circle, color-mix(in srgb, var(--c-1) 30%, rgba(255, 255, 255, 0.12)), color-mix(in srgb, var(--c-1) 14%, transparent) 40%, transparent 62%);
}
.visual-flash::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44%;
  aspect-ratio: 1;
  border: 1px solid rgba(255, 255, 255, 0.7);
  box-shadow: 0 0 14px rgba(255, 255, 255, 0.3);
  opacity: 0;
  transform: translate(-50%, -50%) rotate(45deg) scale(0.8);
}
.visual-cover .cover-art span { font-size: clamp(1.6rem, 5vmin, 3.4rem); }
/* in full screen the bar is dark glass over the backdrop, whatever the theme, and it slides away
   while the music plays and nothing moves, like a video player's controls (app.js: wake, rest) */
body.visual-open .player-bar { color-scheme: dark; background: rgba(6, 4, 12, 0.5); border-top-color: rgba(255, 255, 255, 0.06); touch-action: none; transition: transform 0.5s var(--ease-out), opacity 0.4s; }
body.controls-hidden .player-bar { transform: translateY(100%); opacity: 0; pointer-events: none; }

/* Once the full-screen view has faded in over everything, the page under it isn't drawn or kept up
   to date at all (app.js adds .visual-covered, and takes it off before the view fades out again).
   The page keeps its size while it's skipped (contain-intrinsic-size), so nothing moves. */
.player-page { contain-intrinsic-size: auto 100vw auto 100vh; }
body.visual-covered .player-page { content-visibility: hidden; }
body.visual-covered .scenery { visibility: hidden; }

/* --- side panels: the queue, and song details ------------------------------------------------- */

.side-panel {
  position: fixed;
  top: 0;
  right: 0;
  bottom: var(--bar-h);
  z-index: 47;
  display: flex;
  flex-direction: column;
  width: min(430px, 100%);
  padding-top: env(safe-area-inset-top);
  background: var(--bar-bg);
  color: var(--text);
  backdrop-filter: blur(22px);
  -webkit-backdrop-filter: blur(22px);
  border-left: 1px solid var(--line);
  box-shadow: -24px 0 60px var(--shadow);
  transform: translateX(calc(100% + 40px));
  visibility: hidden;
  transition: transform 0.5s var(--ease-out), visibility 0s linear 0.5s;
}
.side-panel.open { transform: none; visibility: visible; transition: transform 0.5s var(--ease-out), visibility 0s; }
.side-panel.entering .queue-row, .side-panel.entering .side-body > * { animation: bar-in 0.5s var(--ease-out) backwards; animation-delay: calc(var(--d, 0) * 1ms + 120ms); }
.side-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 1.1rem 1rem 0.6rem 1.25rem; }
.side-head .section-title { flex: 1; font-size: 1.15rem; }
.side-body { flex: 1; overflow-y: auto; padding: 0 0.9rem 1.5rem 1.1rem; }
.side-body h3 { margin: 1.25rem 0.4rem 0.55rem; color: var(--text); font: 650 0.92rem var(--display); letter-spacing: 0.02em; }
.queue-section-head { display: flex; align-items: baseline; justify-content: space-between; }
.text-button { margin-right: 0.5rem; padding: 0.2rem 0.4rem; border: 0; background: none; color: var(--muted); font: 600 0.82rem var(--display); }
.text-button:hover { color: var(--accent); }
.queue-list { display: grid; gap: 0.4rem; margin: 0; padding: 0; list-style: none; }
.queue-row { position: relative; isolation: isolate; display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center; padding: 0 0.3rem 0 0.5rem; transition: transform 0.3s var(--ease-out); }
.queue-row::before { content: ""; position: absolute; inset: 0; z-index: -1; transform: skewX(var(--skew)); border: 1px solid transparent; border-radius: 3px; transition: background 0.25s, border-color 0.25s; }
.queue-row:hover { transform: translateX(6px); }
.queue-row:hover::before { border-color: var(--line); background: var(--hover); }
.queue-row.current::before { border-color: color-mix(in srgb, var(--accent) 50%, transparent); background: color-mix(in srgb, var(--accent) 14%, transparent); }
.queue-main { display: flex; align-items: center; gap: 0.75rem; min-width: 0; padding: 0.45rem 0.3rem; border: 0; background: none; text-align: left; }
.queue-cover { flex: none; width: 40px; height: 40px; overflow: hidden; border-radius: 2px; }
.queue-text { display: flex; flex-direction: column; min-width: 0; }
.queue-title, .queue-album { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.queue-title { font-weight: 600; }
.queue-album { color: var(--muted); font-size: 0.82rem; }
.queue-row.current .queue-title { color: var(--accent); }
.queue-row .track-time { min-width: 2.8rem; font-size: 0.82rem; }
.side-body .status { margin: 1.5rem 0.4rem; font-size: 0.92rem; }
.inline-icon { width: 1em; height: 1em; vertical-align: -0.15em; }

/* song details: the summary, the measurements, then moment by moment (js/details.js) */
.details-panel { width: min(500px, 100%); }
.details-song { display: flex; align-items: center; gap: 0.9rem; margin: 0.5rem 0.4rem 0.3rem; }
.details-cover { flex: none; width: 64px; height: 64px; overflow: hidden; border-radius: 2px; }
.details-title { margin: 0; font-size: 1.1rem; font-weight: 700; }
.details-album { margin: 0.1rem 0 0; color: var(--muted); font-size: 0.88rem; }
.details-summary { margin: 0.4rem 0.4rem 0.8rem; color: var(--muted); font-size: 0.93rem; line-height: 1.65; }
.measures { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.45rem 1.2rem; margin: 0.3rem 0.4rem 0.4rem; }
.measures dt { color: var(--muted); font: 600 0.82rem / 1.75 var(--display); }
.measures dd { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.15rem 0.6rem; margin: 0; font-weight: 600; font-variant-numeric: tabular-nums; }
.measures .note { color: var(--faint); font-size: 0.82rem; font-weight: 400; }
.balance { display: flex; gap: 3px; width: 110px; height: 8px; align-self: center; }
.balance i { min-width: 3px; transform: skewX(var(--skew)); border-radius: 1px; }
.balance i:nth-child(1), .moment-eq i:nth-child(1) { background: var(--accent); }
.balance i:nth-child(2), .moment-eq i:nth-child(2) { background: color-mix(in oklab, var(--accent) 68%, var(--bg)); }
.balance i:nth-child(3), .moment-eq i:nth-child(3) { background: color-mix(in oklab, var(--accent) 42%, var(--bg)); }
/* complexity: a bar out of 100, and an info tag that opens how it's rated, part by part */
.meter { flex: none; width: 110px; height: 8px; align-self: center; transform: skewX(var(--skew)); background: var(--track); }
.meter i { display: block; height: 100%; background: linear-gradient(90deg, var(--accent-3), var(--accent)); }
.info-tag {
  position: relative;
  isolation: isolate;
  display: inline-grid;
  place-items: center;
  width: 18px;
  height: 18px;
  margin-left: 0.5rem;
  padding: 0;
  border: 0;
  background: none;
  color: var(--accent);
  font: 700 0.62rem / 1 var(--display);
  letter-spacing: 0;
  text-transform: none;
  vertical-align: 1px;
}
.info-tag::before { content: ""; position: absolute; inset: 2px; z-index: -1; transform: rotate(45deg); border: 1px solid currentColor; border-radius: 2px; transition: background 0.2s; }
.info-tag:hover::before, .info-tag[aria-expanded="true"]::before { background: color-mix(in srgb, var(--accent) 22%, transparent); }
.measures .info-box { grid-column: 1 / -1; display: block; margin: 0 0 0.35rem; padding: 0.85rem 0.95rem; border-left: 2px solid var(--accent); background: var(--hover); font-weight: 400; }
.info-box p { margin: 0 0 0.7rem; color: var(--muted); font-size: 0.86rem; line-height: 1.55; }
.parts { display: grid; gap: 0.6rem; margin: 0; padding: 0; list-style: none; }
.parts li { display: grid; grid-template-columns: 6.4rem 110px 2rem minmax(0, 1fr); align-items: center; justify-content: start; gap: 0.15rem 0.75rem; }
.part-name { color: var(--text); font: 600 0.8rem var(--display); }
.part-value { font-weight: 700; font-variant-numeric: tabular-nums; }
.part-about { grid-column: 1 / -1; color: var(--faint); font-size: 0.8rem; line-height: 1.45; }
.part-about b { color: var(--muted); font-weight: 600; }
.moments-head .fine { margin: -0.2rem 0.4rem 0.6rem; color: var(--faint); font-size: 0.8rem; }
.moment-columns, .moment-main { display: grid; grid-template-columns: 2.7rem minmax(0, 1fr) 2.6rem 2.3rem 3.7rem; align-items: center; gap: 0.7rem; }
.moment-columns { padding: 0 0.7rem 0.35rem; color: var(--faint); font: 600 0.72rem var(--display); }
.moments { display: grid; gap: 2px; margin: 0; padding: 0; list-style: none; }
.moment-main { position: relative; isolation: isolate; width: 100%; padding: 0.3rem 0.7rem; border: 0; background: none; font-size: 0.82rem; text-align: left; }
.moment-main::before { content: ""; position: absolute; inset: 0; z-index: -1; transform: skewX(var(--skew)); border-radius: 2px; transition: background 0.2s; }
.moment-main:hover::before { background: var(--hover); }
.moment.current .moment-main::before { background: color-mix(in srgb, var(--accent) 16%, transparent); box-shadow: inset 3px 0 var(--accent); }
.moment-time { color: var(--muted); font: 650 0.74rem var(--display); font-variant-numeric: tabular-nums; }
.moment.current .moment-time { color: var(--accent); }
.moment-level { height: 6px; transform: skewX(var(--skew)); background: var(--track); }
.moment-level i { display: block; height: 100%; background: linear-gradient(90deg, var(--accent-3), var(--accent)); }
.moment-eq { display: flex; align-items: flex-end; gap: 3px; height: 16px; }
.moment-eq i { flex: 1; height: calc(var(--h) * 100%); min-height: 2px; border-radius: 1px; }
.moment-bpm, .moment-key { color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.moment-key.faint { color: var(--faint); }

/* --- messages --------------------------------------------------------------------------------- */

.toast {
  position: fixed;
  left: 50%;
  bottom: calc(var(--bar-h) + 18px);
  z-index: 60;
  isolation: isolate;
  max-width: calc(100% - 2.5rem);
  padding: 0.75rem 1.4rem;
  color: var(--text);
  font: 600 0.88rem var(--display);
  letter-spacing: 0.01em;
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, 14px) skewX(-6deg);
  transition: opacity 0.25s, transform 0.4s var(--ease-out);
}
.toast::before { content: ""; position: absolute; inset: 0; z-index: -1; transform: skewX(var(--skew)); border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--surface-solid); box-shadow: 0 10px 30px var(--shadow); }
.toast.show { opacity: 1; transform: translate(-50%, 0); }

/* --- page changes, light and dark, and the themes: a quick cross-fade (with view transitions; else
   at once) -------------------------------------------------------------------------------------- */

html.cross-fading::view-transition-old(root),
html.cross-fading::view-transition-new(root) { animation-duration: 0.35s; animation-timing-function: ease; }

/* --- settings page ("#settings"; js/app.js, js/settings.js) ------------------------------------ */

/* Compact and quiet: each setting's name, then a dropdown of its choices (a few small words under
   some of them in the list), and small notes. */
.settings-page, .app-page { max-width: 820px; margin: 0 auto; padding: clamp(1.25rem, 3.5vw, 2.25rem) clamp(1rem, 3.5vw, 3rem) 0; }
.back-link { display: inline-flex; align-items: center; gap: 0.45rem; margin: 0 0 1rem -0.15rem; padding: 0.3rem 0.15rem; color: var(--muted); font: 600 0.92rem var(--display); text-decoration: none; transition: color 0.2s; }
.back-link:hover { color: var(--text); }
.back-link svg { width: 1.1em; height: 1.1em; transform: scaleX(-1); }
.settings-page > .section-title, .app-page > .section-title { font-size: clamp(1.5rem, 3.5vw, 2rem); }
/* the first group's line is the one under the title */
.settings-page > .section-title::after, .app-page > .section-title::after { display: none; }
.settings-intro { margin: 0.35rem 0 1.5rem; color: var(--faint); font-size: 0.86rem; }

.settings-group { padding: 1.4rem 0 1.6rem; border-top: 1px solid var(--line); }
.settings-group h2 { display: flex; align-items: center; gap: 0.55rem; margin: 0 0 0.9rem; font: 650 1.05rem / 1.3 var(--display); }
.settings-group h2::before { content: ""; flex: none; width: 0.45em; height: 0.45em; transform: rotate(45deg); background: var(--accent); }
.settings-group .toggle { font-size: 0.92rem; }
.settings-note { margin: 0.6rem 0 0; color: var(--faint); font-size: 0.8rem; line-height: 1.5; }
/* the app page (#app): the steps for adding the site to the Home Screen, numbered, with the buttons'
   own icons in the text */
.app-steps { display: grid; gap: 0.75rem; margin: 0; padding-left: 1.4rem; font-size: 0.95rem; line-height: 1.55; }
.app-steps li { padding-left: 0.2rem; }
.app-steps li::marker { color: var(--accent); font: 650 0.95rem var(--display); }
.glyph { width: 1.15em; height: 1.15em; vertical-align: -0.22em; color: var(--accent); fill: currentColor; }
.app-installed { margin: 0 0 1.25rem; color: var(--accent); font: 600 0.95rem var(--display); }
.app-in-app { margin: 0 0 1rem; color: var(--muted); font-size: 0.92rem; line-height: 1.55; }
.app-other { margin: 0; font-size: 0.95rem; line-height: 1.55; }
.app-install { margin: 0 0 1rem; }
.app-link a { color: inherit; text-underline-offset: 0.2em; }
.app-link a:hover { color: var(--text); }
:root[data-app] .app-link { display: none; } /* in the app already */
/* the graphics level recommended for this device (js/device.js): a line with a button to use it, then
   what it's based on, small; and in the list, a tag by its name */
.settings-note.advice { color: var(--muted); }
.settings-note.advice + .settings-note { margin-top: 0.15rem; }
.advice .text-button { margin: 0 0 0 0.15rem; padding: 0.1rem 0.25rem; color: var(--accent); font-size: 0.8rem; }
.advice .text-button:hover { color: var(--text); }
.option-tag { color: var(--accent); font: 600 0.72rem / 1 var(--display); }
/* a setting: its name on the left, its choices on the right */
.setting { display: grid; grid-template-columns: 6.5rem minmax(0, 1fr); align-items: start; gap: 0.5rem 1.25rem; padding: 0.5rem 0; }
.setting h3 { margin: 0; padding-top: 0.6rem; color: var(--muted); font: 600 0.88rem / 1.3 var(--display); }
.setting:has(.toggle) h3 { padding-top: 0.15rem; } /* level with a switch, which is lower than a dropdown */

/* the dropdowns (js/dropdown.js): a slanted field showing the choice, like the search, and under it
   a plain list of the choices, one lit on a slanted plate where the pointer (or the arrow keys) is,
   the chosen one in the accent with its diamond filled */
.dropdown { position: relative; width: min(17rem, 100%); }
.dropdown-button {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  min-height: 38px;
  padding: 0 0.8rem 0 1rem;
  border: 0;
  background: none;
  color: var(--text);
  font: 600 0.88rem / 1.2 var(--display);
  text-align: left;
}
.dropdown-button::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  transform: skewX(var(--skew));
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--clear);
  box-shadow: inset 0 1px 0 var(--edge);
  transition: border-color 0.2s, background-color 0.2s;
}
.dropdown-button:hover::before { border-color: color-mix(in srgb, var(--text) 45%, transparent); background-color: var(--clear-hover); }
.dropdown-button[aria-expanded="true"]::before { border-color: var(--accent); }
.dropdown-value { display: flex; flex: 1; align-items: center; gap: 0.65rem; min-width: 0; }
.dropdown-value .option-name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.dropdown-arrow { width: 16px; height: 16px; color: var(--muted); }
.dropdown-list {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  z-index: 60; /* over the player bar */
  display: grid;
  gap: 2px;
  margin: 0;
  padding: 0.35rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface-solid);
  box-shadow: 0 14px 36px var(--shadow);
  list-style: none;
  animation: list-in 0.12s ease-out;
}
.dropdown.up .dropdown-list { top: auto; bottom: calc(100% + 6px); }
@keyframes list-in { from { opacity: 0; } }
:root[data-input] .dropdown-list:focus-visible { outline: none; } /* the lit option shows where the keys are */
.dropdown-list [role="option"] {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.1rem 0.65rem;
  padding: 0.5rem 0.75rem;
  cursor: pointer;
  font: 600 0.88rem / 1.25 var(--display);
}
.dropdown-list [role="option"]::before { content: ""; position: absolute; inset: 0; z-index: -1; transform: skewX(var(--skew)); border-radius: var(--radius); }
.dropdown-list .active::before { background: var(--hover); }
:root[data-input="keyboard"] .dropdown-list .active::before { box-shadow: inset 0 0 0 1px var(--accent); }
.dropdown-list .option-name { flex: 1; min-width: 0; }
.dropdown-list [role="option"]::after { content: ""; flex: none; width: 7px; height: 7px; transform: rotate(45deg); }
.dropdown-list [aria-selected="true"] { color: var(--accent); }
.dropdown-list [aria-selected="true"]::after { background: currentColor; }
.option-about { order: 1; flex-basis: 100%; color: var(--faint); font: 400 0.76rem / 1.35 var(--font); }
/* a theme's colors: its background, then its three lights */
.swatches { display: flex; flex: none; width: 2.6rem; height: 0.85rem; overflow: hidden; border-radius: 2px; box-shadow: 0 0 0 1px var(--line-strong); }
.swatches i { flex: 1; }
.swatches i:nth-child(1) { background: var(--bg); }
.swatches i:nth-child(2) { background: var(--accent-strong); }
.swatches i:nth-child(3) { background: var(--accent); }
.swatches i:nth-child(4) { background: var(--accent-2); }

.shortcut-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 2rem; margin: 0; font-size: 0.86rem; }
.shortcut-list div { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.4rem 0; border-bottom: 1px solid var(--line); }
.shortcut-list dt { color: var(--muted); }
.shortcut-list dd { margin: 0; white-space: nowrap; }

/* --- graphics (settings): Low and Off ------------------------------------------------------------- */

/* Low and Off: no glass blur (the bars and panels get nearly solid backgrounds instead), and the
   decoration around the page holds still */
:root[data-graphics] .topbar { background: color-mix(in srgb, var(--bg) 94%, transparent); backdrop-filter: none; -webkit-backdrop-filter: none; }
:root[data-graphics] .player-bar, :root[data-graphics] .side-panel { background: color-mix(in srgb, var(--bg-2) 96%, transparent); backdrop-filter: none; -webkit-backdrop-filter: none; }
:root[data-graphics] .panel { background: var(--surface-solid); backdrop-filter: none; -webkit-backdrop-filter: none; }
:root[data-graphics] :is(.backdrop i, .emblem-ring, .avatar-glow, .scene .light, .halo, .haze, .moon, .skyline),
:root[data-graphics] .visual-frame::after { animation: none !important; }
/* Off: nothing animates at all (the controls still respond), and no song effects (js/effects.js) */
:root[data-graphics="off"] *, :root[data-graphics="off"] *::before, :root[data-graphics="off"] *::after { animation: none !important; }
:root[data-graphics="off"] .effects, :root[data-graphics="off"] .scenes { display: none; }

/* --- iPhones and iPads (index.html's first script marks them: data-ios) ------------------------- */

/* As a Home Screen app the page runs up under the clock and battery (index.html), and iOS blurs and
   darkens what's behind them, as in Spotify and Apple Music (the user asked for that look): the
   full-screen art, the title screen as it scrolls, and the top bar's glass, which goes up behind
   them (its padding). But wherever something pinned spans the top of the screen, iOS paints a solid
   box there in its color instead (WebKit looks 4px below the middle of the top edge, for a fixed
   element; a full-screen one, like the full-screen view, keeps the color there was): the user saw a
   white or dark box on the music page, the settings, and in full screen, and the blur only on the
   title screen, which has nothing pinned there. So the band is pinned over everything at the top,
   empty and clear and taking taps: WebKit finds it, skips it (nothing to take a color from), and
   looks no further, the pinned top bar and full-screen view under it never read. */
.status-band { display: none; }
:root[data-ios] .status-band { display: block; position: fixed; inset: 0 0 auto; z-index: 2147483647; height: 12px; }
/* Behind the clock iOS shows the page that's scrolled up there, never the scenery pinned behind it
   (the sky, the moon's light, a song's effects), only the page's color: so the scenery fades up into
   that color at the top of the screen, or its lights end in a line under the clock (the user saw one
   on the dark side, and on the light side in Facades' effects) */
:root[data-ios] .scenery::after { content: ""; position: absolute; inset: 0 0 auto; height: 4rem; background: linear-gradient(var(--bg), transparent); pointer-events: none; }
/* The top bar is fixed: when the page bounces it stays under the clock, the page moving beneath it
   (the user wanted the bounce, not the bar coming away from the clock). */
:root[data-ios] .topbar { position: fixed; inset: 0 0 auto; }
:root[data-ios] .player-page { padding-top: calc(64px + env(safe-area-inset-top)); }
/* Full screen: iOS draws only the page behind the clock, what's scrolled up under it, never what's
   pinned (pinned, the view had the hidden page's background there, a cream bar on the light side,
   which the user sent a screenshot of). So the open view is laid into the page (app.js,
   inlineVisual), where the screen is, and reaches 120px above it, its art running up behind the
   clock under iOS's fade: its glows and a song's scene lights carry on past the top of the screen
   (their boxes reach up, each light still where it was), over a dark base, darkened as the view's
   edges are. The page under it stays scrollable, as on the other pages, though nothing scrolls it:
   the view and the bar take no swipes, and a panel's list doesn't pass its scrolling on. */
:root[data-ios] body.visual-open { overflow: visible; }
:root[data-ios] body.visual-inline { padding-top: 120px; }
:root[data-ios] body.visual-inline .visual { position: absolute; inset: auto 0; height: 100%; }
:root[data-ios] .side-body { overscroll-behavior: contain; }
/* and 120px below it: an iPhone app's page can end above the bottom of the screen (by the home bar),
   where iOS still draws the page, and the view stopped there over a strip of the page's color (the
   user's screenshot) */
:root[data-ios] .visual :is(.backdrops, .scenes) { top: -120px; bottom: -120px; overflow: hidden; }
:root[data-ios] .visual :is(.backdrop, .scene) { top: 120px; bottom: 120px; }
:root[data-ios] .visual .backdrop { overflow: visible; }
:root[data-ios] .visual::before, :root[data-ios] .visual::after { content: ""; position: absolute; inset: auto 0 100%; z-index: -2; height: 120px; background: rgb(6 4 12); pointer-events: none; } /* (the view's own background) */
:root[data-ios] .visual::after { inset: 100% 0 auto; }
/* the vignette reaches up there too, darkening it as it darkens the view's top edge (most at the
   corners), and lifting with it in a chorus (js/effects.js sets --vignette): a fixed shade stayed
   dark over the brightened art (the user saw it in Facades' chorus) */
:root[data-ios] .visual-vignette {
  inset: -120px 0;
  background:
    linear-gradient(to right, rgb(2 1 6 / 0.8), rgb(2 1 6 / 0.46) 25%, rgb(2 1 6 / 0.33) 50%, rgb(2 1 6 / 0.46) 75%, rgb(2 1 6 / 0.8)) top / 100% 120px no-repeat,
    linear-gradient(to right, rgb(2 1 6 / 0.8), rgb(2 1 6 / 0.46) 25%, rgb(2 1 6 / 0.33) 50%, rgb(2 1 6 / 0.46) 75%, rgb(2 1 6 / 0.8)) bottom / 100% 120px no-repeat,
    radial-gradient(ellipse at center, transparent 50%, rgb(2 1 6 / 0.8) 100%) center / 100% calc(100% - 240px) no-repeat;
}
/* the particles and rays too (js/effects.js draws them that much lower), so a ray falling in from
   above, like Facades' shaft, isn't cut off at the top of the screen (the user's screenshot) */
:root[data-ios] .visual .effects { top: -120px; height: calc(100% + 120px); }
/* iOS lays a haze over what's behind the clock, in the page's background color: white on the light
   side, black on the dark one, a bar over the art either way (the user's screenshots). In full screen
   the page takes its dark side, and the body the album's color, deepened (app.js, showClockTint), so
   the haze is the art's own color, as on the list it's the page's. */
:root[data-ios]:has(body.visual-open) { color-scheme: dark; }

/* --- smaller screens -------------------------------------------------------------------------- */

@media (max-width: 1080px) {
  .select { grid-template-columns: minmax(220px, 260px) minmax(0, 1fr); }
}

@media (max-width: 860px) {
  .select { grid-template-columns: minmax(0, 1fr); }
  .focus { display: none; }
  .tools-right { flex: 1 1 100%; }
  .search { flex: 1 1 100%; width: auto; }
  .landing-points { grid-template-columns: minmax(0, 1fr); max-width: 30rem; }
}

@media (max-width: 720px) {
  .moon { opacity: 0.6; } /* the song list runs right over it here */
  .side-panel { width: 100%; border-left: 0; }
  .shortcut-list { grid-template-columns: minmax(0, 1fr); }

  /* iPhones: no bar along the top, as in Apple Music and Spotify (the user asked): the logo and the
     gear float as buttons of their own, over the page, which scrolls up behind them and the clock */
  :root[data-ios] .topbar { background: none; border-bottom: 0; backdrop-filter: none; -webkit-backdrop-filter: none; pointer-events: none; }
  :root[data-ios] .topbar > * { pointer-events: auto; }
  :root[data-ios] .topbar .diamond-button::before { background-color: var(--header-bg); -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); }
  :root[data-ios] .player-page { padding-top: calc(52px + env(safe-area-inset-top)); }
  :root[data-ios] .music { padding-top: 0.75rem; }

  /* Phones: laid out like a phone's music app (the user asked for Apple Music's, in the site's own
     look, with room for the songs' titles). The music page's head: its title with the songs' order
     beside it, the count, the search across, then Play all and Shuffle side by side. */
  .music-head { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-areas: "title sort" "stats stats" "search search" "play shuffle"; align-items: center; gap: 0.85rem 0.75rem; }
  .music-title, .tools-right { display: contents; }
  .music-title .section-title { grid-area: title; }
  .music-title .section-title::after { display: none; }
  .music-stats { grid-area: stats; margin: -0.45rem 0 0.1rem; }
  .dropdown.sort { grid-area: sort; justify-self: end; width: auto; max-width: 100%; }
  .dropdown.sort .dropdown-list { left: auto; width: min(17rem, calc(100vw - 2rem)); }
  .search { grid-area: search; width: auto; min-height: 42px; }
  #play-all { grid-area: play; }
  #shuffle-all { grid-area: shuffle; }
  :is(#play-all, #shuffle-all) { min-height: 42px; }

  /* the songs: a row is its number and its title (in one list of all the songs, its album under it),
     a favorite's heart, and a "···" with the rest (app.js, openMenu) */
  .track { min-height: 52px; padding-right: 0.2rem; }
  .track-actions, .track-streams, .track .rating, .track-time { display: none; }
  .track .heart:not([aria-pressed="true"]) { display: none; }
  .track-more { display: grid; opacity: 1; color: var(--faint); }
  .track-main { gap: 0.75rem; padding: 0.75rem 0.3rem; }
  .track-title { white-space: normal; }
  .track-name { display: -webkit-box; overflow: hidden; line-height: 1.3; -webkit-box-orient: vertical; -webkit-line-clamp: 2; } /* (a long title on two lines) */
  .track-album { display: block; margin: 0.1rem 0 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: 0.8rem; }
  .group-head { grid-template-columns: 44px minmax(0, 1fr) auto; gap: 0.85rem; padding: 0 0.15rem 0.7rem 0.6rem; }
  .group-cover { width: 44px; }
  /* iPhones: the list denser, its text smaller, as in Apple Music, for more songs at once; and the
     small player bar without its seek bar, which is in the full-screen view (the user asked) */
  :root[data-ios] .track { min-height: 44px; }
  :root[data-ios] .track-main { gap: 0.65rem; padding: 0.5rem 0.3rem; }
  :root[data-ios] .track-no { width: 1.5rem; font-size: 0.78rem; }
  :root[data-ios] .track-name { font-size: 0.92rem; }
  :root[data-ios] .track-album { font-size: 0.74rem; }
  :root[data-ios] .group { margin-bottom: 1.6rem; }
  :root[data-ios] .group-head { grid-template-columns: 40px minmax(0, 1fr) auto; gap: 0.75rem; padding-bottom: 0.55rem; }
  :root[data-ios] .group-cover { width: 40px; }
  :root[data-ios] .group-title { font-size: 0.98rem; }
  :root[data-ios] .group-meta { font-size: 0.74rem; }
  :root[data-ios] body:not(.visual-open) .progress { display: none; }

  /* The player bar (a blend of the old one and Apple Music's, as the user asked): the song across the
     top, the whole title (pressed, it opens the full-screen view, as the cover does) with its heart,
     then the buttons as they were, and the seek bar with the times at its ends. Details, the queue,
     and full screen are in the full-screen view, and a song's link in its menu. */
  .player-bar { grid-template-columns: minmax(0, 1fr); grid-template-areas: "now" "transport"; row-gap: 0.3rem; padding-top: 0.65rem; }
  body:not(.visual-open) :is(#copy-link, .extras) { display: none; }
  .volume { display: none; }
  .now { grid-area: now; gap: 0.8rem; }
  .now-cover { width: 44px; height: 44px; }
  .now-text { flex: 1; }
  .now-title { font-size: 0.95rem; }
  .now-album { font-size: 0.8rem; }
  .transport { grid-area: transport; }
  .controls { gap: 0.9rem; }
  .progress .seek { height: 32px; }
}

/* --- full screen on phones ---------------------------------------------------------------------- */

/* Laid out like a phone's music app (the user asked for Apple Music's layout, in the site's own
   look): the cover big at the top; under it the song with its heart, the seek bar across the whole
   width with the times under its ends, then previous, play, and next, bigger, and the rest in one
   quiet row along the bottom. No glass: the controls sit on the backdrop, a little darker toward the
   bottom so they stay readable (and nothing has to blur the moving backdrop on every frame). */
@media (max-width: 720px) {
  .visual { padding: calc(1rem + env(safe-area-inset-top)) 2rem calc(var(--bar-h) + 0.5rem); }
  .visual-frame {
    width: min(100vw - 4rem, 100vh - var(--bar-h) - 2.5rem - env(safe-area-inset-top));
    width: min(100vw - 4rem, 100svh - var(--bar-h) - 2.5rem - env(safe-area-inset-top));
  }
  .visual-frame::before { inset: -14px; }

  body.visual-open .player-bar {
    grid-template-columns: repeat(5, minmax(0, 1fr));
    grid-template-areas: "now now now now now" "progress progress progress progress progress" "prev prev play next next";
    gap: 0.5rem 0;
    padding: 1rem 1.5rem calc(0.75rem + env(safe-area-inset-bottom));
    background: linear-gradient(transparent, rgb(4 3 10 / 0.5) 55%);
    border-top: 0;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  /* the buttons' groups step aside, so each button takes its own place in the bar's grid */
  body.visual-open :is(.transport, .controls, .extras) { display: contents; }
  body.visual-open :is(.now-cover, #copy-link) { display: none; }
  body.visual-open .now { gap: 0.5rem; }
  body.visual-open .now-title { font-size: 1.05rem; }
  body.visual-open .now-album { font-size: 0.85rem; }
  body.visual-open #now-favorite { width: 44px; height: 44px; margin-right: -11px; } /* the heart's edge on the seek bar's end */
  body.visual-open #now-favorite svg { width: 22px; height: 22px; }
  body.visual-open .progress { grid-area: progress; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-areas: "seek seek" "current total"; gap: 0; max-width: none; }
  body.visual-open .progress .seek { grid-area: seek; height: 40px; }
  body.visual-open .progress .time { top: 0; min-width: 0; font-size: 0.7rem; }
  body.visual-open #time-current { grid-area: current; text-align: left; }
  body.visual-open #time-total { grid-area: total; text-align: right; }
  body.visual-open #prev { grid-area: prev; }
  body.visual-open #play { grid-area: play; }
  body.visual-open #next { grid-area: next; }
  body.visual-open :is(#prev, #play, #next) { justify-self: center; }
  body.visual-open :is(#prev, #next) { width: 60px; height: 60px; color: var(--text); }
  body.visual-open :is(#prev, #next) svg { width: 30px; height: 30px; }
  body.visual-open #play { width: 84px; height: 84px; margin: 0; }
  body.visual-open #play::before { inset: 13px; }
  body.visual-open #play svg { width: 30px; height: 30px; }
  /* the quiet row: shuffle, repeat, the song's details, the queue, and back to the page */
  body.visual-open :is(#shuffle, #repeat, #details-toggle, #queue-toggle, #fullscreen) { grid-row: 4; justify-self: center; }
  body.visual-open #shuffle { grid-column: 1; }
  body.visual-open #repeat { grid-column: 2; }
  body.visual-open #details-toggle { grid-column: 3; }
  body.visual-open #queue-toggle { grid-column: 4; }
  body.visual-open #fullscreen { grid-column: 5; }
}

@media (max-width: 520px) {
  .brand [data-site] { display: none; }
  .topbar nav { gap: 1rem; }
  .setting { grid-template-columns: minmax(0, 1fr); gap: 0.6rem; }
  .setting h3 { padding-top: 0; }
  .group-title { font-size: 1.05rem; }
  .group-meta { font-size: 0.8rem; }
}

@media (hover: none) {
  .track-actions, .track .heart, .track .star { opacity: 1; }
  .slider::-webkit-slider-thumb { opacity: 1; transform: rotate(45deg) scale(1); }
  .slider::-moz-range-thumb { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation: none !important; transition: none !important; }
}
