/* NaaniRM · Just Us Two
   One full screen per song: the album's animated worlds under the promo videos' iOS glass. */

@font-face {
  font-family: "Geist";
  src: url("../fonts/Geist.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("../fonts/GeistMono.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* The accent travels from world to world, so it is a typed property that can transition. */
@property --accent {
  syntax: "<color>";
  inherits: true;
  initial-value: #ff2a3d;
}

:root {
  color-scheme: dark;
  --ink: #050505;
  --text: #fff;
  --text-2: rgba(255, 255, 255, 0.76);
  --text-3: rgba(255, 255, 255, 0.62);
  --accent: #ff2a3d;
  --gold-rgb: 220, 174, 85;
  --peek: 34px;
  --card-h: 196px;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --glass-fill: linear-gradient(140deg, rgba(255, 255, 255, 0.17), rgba(255, 255, 255, 0.06) 55%, rgba(255, 255, 255, 0.03));
  --glass-edge: rgba(255, 255, 255, 0.22);
  --glass-lift: 0 24px 60px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.34);
  --glass-blur: blur(26px) saturate(1.7);
  --safe-b: env(safe-area-inset-bottom, 0px);
  transition: --accent 0.9s var(--ease-out);
}

/* ---------- moving between the two pages ---------- */

/* A tab cross-fades the pages; the bar is its own group, so it stays put while the page changes. */
@view-transition {
  navigation: auto;
}
.top {
  view-transition-name: top-bar;
}
/* Reached by a sideways drag (common.js, enter.js): the page comes in from the side it was pulled from. */
@keyframes enter-from-left {
  from {
    transform: translateX(-28%);
    opacity: 0;
  }
}
@keyframes enter-from-right {
  from {
    transform: translateX(28%);
    opacity: 0;
  }
}
html[data-enter="left"]::view-transition-new(root) {
  animation: enter-from-left 0.34s var(--ease-out) both;
}
html[data-enter="right"]::view-transition-new(root) {
  animation: enter-from-right 0.34s var(--ease-out) both;
}

/* On touch screens a sideways drag moves the page itself; vertical drags still scroll natively. */
@media (pointer: coarse) {
  body {
    touch-action: pan-y pinch-zoom;
  }
}
.is-dragging main,
.is-dragging .card,
.is-dragging .rail,
.is-dragging .foot {
  will-change: translate;
}
.is-settling main,
.is-settling .card,
.is-settling .rail,
.is-settling .foot,
.is-settling .swipe-hint {
  transition: translate 0.26s cubic-bezier(0.2, 0.8, 0.2, 1), opacity 0.26s cubic-bezier(0.2, 0.8, 0.2, 1);
}
/* The other page's name, in the gap the drag opens. */
.swipe-hint {
  position: fixed;
  z-index: 45;
  top: 50%;
  left: 0;
  display: none;
  align-items: center;
  height: 44px;
  padding: 0 18px;
  border-radius: 999px;
  background: #fff;
  color: var(--ink);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.01em;
  white-space: nowrap;
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.42);
  pointer-events: none;
}
.swipe-hint[data-side="right"] {
  left: 100%;
}
.is-dragging .swipe-hint {
  display: flex;
}

*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* Nothing may widen the page sideways, not even a page being dragged off screen: html sets the
   viewport's rule, so body keeps its own clip (clip, unlike hidden, doesn't make body a scroller). */
html {
  overflow-x: clip;
  background: var(--ink);
  scroll-behavior: smooth;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.28) transparent;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
/* The album page pages like a feed: one swipe, one song, never two at once (.world has
   scroll-snap-stop). Past the worlds (.is-after, set by site.js) it only settles between song 5 and the
   making-of, never half-way; below that it scrolls freely (see .making). */
html:has(.world) {
  scroll-snap-type: y proximity;
}
html:has(.world):not(.is-after) {
  scroll-snap-type: y mandatory;
}
/* With a mouse, past the worlds the page doesn't snap at all: inside a snap container a browser judges a
   turn of the wheel again once the wheel stops, and the free part below the making-of answered the wheel
   badly. There site.js pages the wheel between song 5 and the making-of and settles any stop in between. */
@media (hover: hover) and (pointer: fine) {
  html:has(.world).is-after {
    scroll-snap-type: none;
  }
}

/* body keeps its own ink (an embedding viewer may paint white behind a transparent one); the light
   (.halo) sits on it, and the page's content sits on the light. */
body {
  font-family: "Geist", system-ui, sans-serif;
  color: var(--text);
  background: var(--ink);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  overflow-x: clip;
}

img,
video,
svg {
  display: block;
}

a {
  color: inherit;
}

button {
  font: inherit;
  color: inherit;
  border: 0;
  background: none;
  cursor: pointer;
}

ul,
ol {
  list-style: none;
}

::selection {
  background: var(--accent);
  color: var(--ink);
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

::-webkit-scrollbar {
  width: 8px;
}
::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.24);
  border-radius: 8px;
}
::-webkit-scrollbar-track {
  background: transparent;
}

.sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

.num,
.times {
  font-family: "Geist Mono", ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
}

.glass {
  background: var(--glass-fill);
  border: 1px solid var(--glass-edge);
  box-shadow: var(--glass-lift);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
}

/* Inside the card the glass is already blurred once: its pills only need the fill and the edge.
   They don't lift either, so no shadow spills onto the white Listen pill below them. */
.card .glass {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3);
}

/* In the bar the glass is smoked, so the white icons and words hold on the brightest world. */
.top .glass {
  background: linear-gradient(140deg, rgba(255, 255, 255, 0.14), rgba(255, 255, 255, 0.03) 60%), rgba(10, 10, 12, 0.48);
  border-color: rgba(255, 255, 255, 0.2);
}
/* Where the bar has its own ink there is nothing to see through: no live blur behind its pills, which keeps
   scrolling light on a phone. */
.is-after .top .glass,
.page-plain .top .glass {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

.skip {
  position: fixed;
  z-index: 60;
  left: 12px;
  top: 12px;
  padding: 12px 16px;
  border-radius: 999px;
  background: #fff;
  color: var(--ink);
  font-weight: 600;
  text-decoration: none;
  transform: translateY(-180%);
  transition: transform 0.3s var(--ease-out);
}
.skip:focus-visible {
  transform: none;
}

/* ---------- top bar ---------- */

.top {
  position: fixed;
  z-index: 40;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: calc(12px + env(safe-area-inset-top, 0px)) 14px 12px 20px;
  pointer-events: none;
  transition: opacity 0.5s var(--ease-out), visibility 0.5s, background-color 0.5s var(--ease-out);
}
.top > * {
  pointer-events: auto;
}
/* Over the worlds the bar has no ground of its own: a soft shade at the top of the screen keeps the name,
   the icons and the controls readable on the brightest world. Past the worlds the bar's ink takes over. */
.top::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0 0 -32px;
  background: linear-gradient(180deg, rgba(5, 5, 5, 0.6), rgba(5, 5, 5, 0.3) 55%, rgba(5, 5, 5, 0));
  pointer-events: none;
  transition: opacity 0.5s var(--ease-out);
}
.is-after .top::before,
.page-plain .top::before {
  opacity: 0;
}

.pages {
  display: none;
}
.actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}
.actions > .lang,
.pick {
  display: none;
}

/* Sound: a labelled pill while silent; once the music plays, the speaker and a volume slider. */
.sound-wrap {
  display: inline-flex;
  align-items: center;
  padding: 4px;
  border-radius: 999px;
}
/* Same build as the language switch: 4px of glass around 34px controls. */
.sound {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 34px;
  height: 34px;
  padding: 0 14px 0 11px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 600;
  transition: background 0.3s var(--ease-out);
}
.volume {
  --vol: 80%;
  -webkit-appearance: none;
  appearance: none;
  width: 84px;
  height: 28px;
  margin: 0 14px 0 2px;
  background: transparent;
  cursor: pointer;
}
.volume[hidden] {
  display: none;
}
.volume::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 2px;
  background: linear-gradient(90deg, #fff var(--vol), rgba(255, 255, 255, 0.25) var(--vol));
}
.volume::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 14px;
  height: 14px;
  margin-top: -5px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
}
.volume::-moz-range-track {
  height: 4px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.25);
}
.volume::-moz-range-progress {
  height: 4px;
  border-radius: 2px;
  background: #fff;
}
.volume::-moz-range-thumb {
  width: 14px;
  height: 14px;
  border: 0;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
}
.volume:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 6px;
}
.sound svg {
  flex: none;
  width: 20px;
  height: 20px;
}
.sound .i-on,
.sound[aria-pressed="true"] .i-off,
.sound[aria-pressed="true"] .sound-label {
  display: none;
}
.sound[aria-pressed="true"] .i-on {
  display: block;
}
.sound[aria-pressed="true"] {
  width: 34px;
  padding: 0;
}
.sound:hover,
.menu-btn:hover {
  background: rgba(255, 255, 255, 0.22);
}

.menu-btn {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  transition: background 0.3s var(--ease-out);
}
.menu-btn svg {
  width: 20px;
  height: 20px;
}

/* Phone menu: the pages and the language in plain, regular type on a solid ground,
   so it reads the same over a bright world or the big Discography title. */
.menu {
  position: absolute;
  top: calc(100% - 4px);
  right: 14px;
  width: 224px;
  padding: 6px;
  border-radius: 20px;
  background: rgba(18, 18, 20, 0.97);
  border: 1px solid rgba(255, 255, 255, 0.12);
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.55);
  display: none;
  flex-direction: column;
  gap: 4px;
}
.top.is-open .menu {
  display: flex;
}
.menu nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.menu nav a {
  display: flex;
  align-items: center;
  height: 44px;
  padding: 0 12px;
  border-radius: 14px;
  font-size: 15px;
  font-weight: 500;
  color: var(--text-2);
  text-decoration: none;
}
.menu nav a[aria-current="page"] {
  color: #fff;
  background: rgba(255, 255, 255, 0.1);
}
.menu nav a:hover {
  color: #fff;
}
.menu-lang {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 4px 2px 12px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  font-size: 13px;
  color: var(--text-3);
}
.menu .lang {
  background: rgba(255, 255, 255, 0.07);
}
.menu .lang button {
  min-width: 40px;
  height: 30px;
}

/* Instagram and TikTok sit right after the name, the way an artist's profile header reads. */
.top-socials {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-left: -10px;
}
.top-socials a {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  transition: background 0.3s var(--ease-out);
}
.top-socials a:hover {
  background: rgba(255, 255, 255, 0.16);
}
.top-socials svg {
  width: 18px;
  height: 18px;
  filter: drop-shadow(0 1px 6px rgba(0, 0, 0, 0.5));
}

.wordmark {
  padding: 10px 0;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.46em;
  text-decoration: none;
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.5);
}

.lang {
  display: flex;
  gap: 2px;
  padding: 4px;
  border-radius: 999px;
}
.lang button {
  min-width: 42px;
  height: 34px;
  padding: 0 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  transition: background 0.35s var(--ease-out), color 0.35s var(--ease-out);
}
.lang button[aria-pressed="true"] {
  background: rgba(255, 255, 255, 0.94);
  color: var(--ink);
}
.lang button:hover:not([aria-pressed="true"]) {
  color: #fff;
}

/* ---------- song rail ---------- */

.rail {
  position: fixed;
  z-index: 35;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  transition: opacity 0.5s var(--ease-out), visibility 0.5s;
}
.rail ol {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
}
.rail a {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 32px;
  padding: 0 6px 0 10px;
  font-size: 11px;
  font-weight: 500;
  color: var(--text-3);
  text-decoration: none;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.6);
}
.rail .name {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.rail i {
  display: block;
  width: 14px;
  height: 2px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.45);
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.4);
  transition: width 0.6s var(--ease-out), background 0.6s var(--ease-out);
}
.rail a[aria-current="true"] {
  color: #fff;
}
.rail a[aria-current="true"] i {
  width: 30px;
  background: var(--accent);
}
.rail a:hover i {
  background: #fff;
}
.is-after .rail {
  opacity: 0;
  visibility: hidden;
}

/* ---------- worlds ---------- */

.world {
  position: relative;
  height: calc(100vh - var(--peek));
  height: calc(100svh - var(--peek));
  min-height: 540px;
  overflow: hidden;
  isolation: isolate;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  /* Some viewers (the Claude app) snap below their own controls at the top. Pulling the snap point up by
     that inset puts each world back at the very top of the screen, so its title sits above the card;
     in an ordinary browser the inset is 0 and nothing moves. */
  scroll-margin-top: calc(-1 * env(safe-area-inset-top, 0px));
  background: var(--ink);
}

.stage {
  position: absolute;
  inset: 0;
  z-index: -1;
}
.ambient {
  display: none;
}

.portal {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: var(--ink);
}
.portal > img,
.portal > video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* While a picture loads, its own blurred colours show in its place, painted under it (site.js gives each
   world's picture its ambient frame; the making-of and the photo have theirs below): never a black hole. */
.portal > img {
  background: center / cover no-repeat;
}
.portal > video {
  opacity: 0;
  transition: opacity 0.9s var(--ease-out);
}
/* An episode shows once it really plays (.is-rolling, site.js): until then the loop's last frame or the
   world's picture holds the screen instead of black. */
.world.is-looping .loop,
.world.is-rolling .episode {
  opacity: 1;
}
.world.is-rolling .loop {
  opacity: 0;
}

/* Text scrim on phones: the world plays full-bleed under the copy and the card. */
.stage::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgba(5, 5, 5, 0.5) 0%,
    rgba(5, 5, 5, 0) 15%,
    rgba(5, 5, 5, 0) 50%,
    rgba(5, 5, 5, 0.6) 68%,
    rgba(5, 5, 5, 0.88) 88%,
    rgba(5, 5, 5, 0.94) 100%
  );
  pointer-events: none;
  transition: opacity 0.8s var(--ease-out);
}
.world.is-episode .stage::after {
  opacity: 0.3;
}

.peek {
  position: absolute;
  z-index: 2;
  top: 0;
  left: 0;
  right: 0;
  height: var(--peek);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -0.01em;
  background: linear-gradient(180deg, rgba(5, 5, 5, 0.6), rgba(5, 5, 5, 0.15));
  cursor: pointer;
  transition: opacity 0.5s var(--ease-out);
}
.peek .num {
  font-size: 11px;
  font-weight: 500;
  color: var(--text-2);
}
.peek svg {
  width: 14px;
  height: 14px;
}
.is-active > .peek {
  opacity: 0;
  pointer-events: none;
}

.copy {
  position: absolute;
  z-index: 1;
  left: max(20px, calc(50% - 246px));
  right: 64px;
  /* Clear of the card wherever it sits: the card rises by the bottom inset (the iPhone's home bar, an
     in-app viewer's own bar), so the words rise with it, with room to breathe. */
  bottom: calc(var(--card-h) + 34px + var(--safe-b));
  transition: opacity 0.6s var(--ease-out), transform 0.6s var(--ease-out);
}
/* Phone sizes stay modest so the world, not the type, fills the screen. */
.world h1,
.world h2 {
  font-size: clamp(36px, 11.5vw, 52px);
  line-height: 0.92;
  font-weight: 800;
  letter-spacing: -0.04em;
  text-wrap: balance;
  text-shadow: 0 2px 24px rgba(0, 0, 0, 0.35);
}
.world #w3-title {
  font-size: clamp(32px, 9.4vw, 42px);
}
.lede {
  margin-top: 10px;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.line {
  margin-top: 6px;
  max-width: 30ch;
  font-size: 14px;
  line-height: 1.4;
  color: var(--text-2);
}
.world.is-episode .copy {
  opacity: 0;
  transform: translateY(8px);
  pointer-events: none;
}

/* What a world offers besides listening: its lyrics, its own link to share, and on the first one the whole
   album. Smoked like the bar's pills, with no live blur: they ride on the moving world. */
.world-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 16px;
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 38px;
  padding: 0 15px 0 12px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.2);
  background: linear-gradient(140deg, rgba(255, 255, 255, 0.14), rgba(255, 255, 255, 0.03) 60%), rgba(10, 10, 12, 0.52);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.28);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  transition: background-color 0.3s var(--ease-out), transform 0.35s var(--ease-out);
}
.chip svg {
  flex: none;
  width: 16px;
  height: 16px;
}
.chip:hover {
  background-color: rgba(30, 30, 34, 0.6);
}
.chip:active {
  transform: scale(0.97);
}
/* A song without its lyrics yet (site-tools/songs) shows no Lyrics button. */
.world:not(:has(.lyrics)) .lyrics-btn {
  display: none;
}
.is-reading .copy {
  opacity: 0;
}

/* ---------- a song's lyrics ---------- */

/* Over the world, which keeps playing behind the glass: on a phone the panel takes the screen, on a wide
   screen the words' column, leaving the world in view. The page holds still while it is open. */
html.is-reading {
  overflow: hidden;
}
.lyrics {
  position: fixed;
  inset: 0;
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  color: var(--text);
  background: rgba(5, 5, 5, 0.64);
  -webkit-backdrop-filter: blur(30px) saturate(1.4);
  backdrop-filter: blur(30px) saturate(1.4);
  overflow: hidden;
}
.lyrics[open] {
  display: flex;
  flex-direction: column;
}
.lyrics::backdrop {
  background: transparent;
}
.lyrics-head {
  flex: none;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: calc(18px + env(safe-area-inset-top, 0px)) 16px 16px 20px;
}
.lyrics-head h2 {
  font-size: 32px;
  line-height: 1;
  font-weight: 800;
  letter-spacing: -0.04em;
  text-wrap: balance;
}
.lyrics-sub {
  margin-top: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-2);
}
.lyrics-close {
  flex: none;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  transition: background 0.3s var(--ease-out);
}
.lyrics-close svg {
  width: 18px;
  height: 18px;
}
.lyrics-close:hover {
  background: rgba(255, 255, 255, 0.2);
}
.lyrics-text {
  flex: 1;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 10px 20px calc(48px + var(--safe-b));
  scrollbar-width: thin;
}
.lyrics-text:focus-visible {
  outline-offset: -4px;
}
.lyrics-text p + p {
  margin-top: 30px;
}
/* Sung lines big, as on a phone's lyrics screen; the French translation under each one in the French site;
   the backing voices a step back. */
.lyrics-text .l {
  display: block;
  margin-top: 10px;
  font-size: 23px;
  line-height: 1.22;
  font-weight: 700;
  letter-spacing: -0.02em;
}
.lyrics-text p > .l:first-child {
  margin-top: 0;
}
.lyrics-text .tr {
  display: none;
  margin-top: 3px;
  font-size: 15px;
  line-height: 1.35;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--text-2);
}
:root[lang="fr"] .lyrics-text .tr {
  display: block;
}
.lyrics-text .bv {
  color: var(--text-3);
}

/* ---------- the card: listen, episode, mini player ---------- */

.card {
  position: fixed;
  z-index: 30;
  left: 12px;
  right: 12px;
  bottom: calc(var(--peek) + 10px + var(--safe-b));
  max-width: 520px;
  margin-inline: auto;
  padding: 14px;
  border-radius: 30px;
}

.card-head {
  display: flex;
  align-items: center;
  gap: 12px;
}
.card-art {
  flex: none;
  width: 52px;
  height: 52px;
  border-radius: 13px;
  object-fit: cover;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
.card-meta {
  flex: 1;
  min-width: 0;
}
.card-title,
.mini-title {
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.card-sub {
  margin-top: 2px;
  font-size: 13px;
  color: var(--text-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.watch {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 40px;
  padding: 0 14px 0 12px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 600;
  transition: background 0.3s var(--ease-out);
}
.watch svg {
  width: 14px;
  height: 14px;
}
.watch:hover {
  background: rgba(255, 255, 255, 0.22);
}

.listen-main {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  height: 54px;
  margin-top: 12px;
  border-radius: 999px;
  background: #fff;
  color: var(--ink);
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.02em;
  text-decoration: none;
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.42);
  transition: transform 0.35s var(--ease-out), box-shadow 0.5s var(--ease-out);
}
.listen-main svg {
  width: 22px;
  height: 22px;
}
.listen-main:hover {
  transform: translateY(-1px);
  box-shadow: 0 14px 32px rgba(0, 0, 0, 0.5);
}
.listen-main:active {
  transform: scale(0.98);
}

.listen-more {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  margin-top: 8px;
}
.listen-more a {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  height: 42px;
  padding: 0 8px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3);
  transition: background 0.3s var(--ease-out);
}
.listen-more svg {
  flex: none;
  width: 15px;
  height: 15px;
}
.listen-more a:hover {
  background: rgba(255, 255, 255, 0.2);
}

.mini {
  display: none;
  align-items: center;
  gap: 10px;
}
.mini-meta {
  flex: 1;
  min-width: 0;
  padding-left: 4px;
}
.bar {
  height: 4px;
  margin-top: 9px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.2);
  overflow: hidden;
}
.bar-fill {
  width: 100%;
  height: 100%;
  background: #fff;
  transform: scaleX(0);
  transform-origin: 0 50%;
}
.times {
  display: flex;
  justify-content: space-between;
  margin-top: 6px;
  font-size: 11px;
  color: var(--text-2);
}
.ctl {
  flex: none;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  transition: background 0.3s var(--ease-out);
}
.ctl:hover {
  background: rgba(255, 255, 255, 0.22);
}
.ctl svg {
  width: 18px;
  height: 18px;
}
.ctl-toggle .i-play,
.card.is-paused .ctl-toggle .i-pause {
  display: none;
}
.card.is-paused .ctl-toggle .i-play {
  display: block;
}

.card.is-playing .mini {
  display: flex;
}
/* As a player (an episode, the album) the card keeps every platform under it. */
.card.is-playing .card-head {
  display: none;
}
.card.is-playing .listen-main {
  height: 46px;
  margin-top: 10px;
  font-size: 15px;
}

/* After the five worlds the card folds into one pill. It sits on the ink fade at the foot of the screen,
   so its glass needs no live blur there. */
.card.is-compact {
  bottom: calc(12px + var(--safe-b));
  padding: 6px;
  border-radius: 999px;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  background: var(--glass-fill), rgba(14, 14, 16, 0.86);
}
.card.is-compact .card-head,
.card.is-compact .listen-more,
.card.is-compact .mini {
  display: none;
}
.card.is-compact .listen-main {
  height: 50px;
  margin-top: 0;
}

/* Folded on a phone (on a wide screen the bar's own chooser takes over), the pill offers every platform:
   it opens the card's four above it (site.js). */
.card-pick {
  display: none;
  width: 100%;
  align-items: center;
  justify-content: center;
  gap: 10px;
  height: 50px;
  border-radius: 999px;
  background: #fff;
  color: var(--ink);
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -0.02em;
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.42);
}
.card-pick svg {
  width: 18px;
  height: 18px;
}
.card-pick .pick-chev {
  rotate: 180deg;
  transition: rotate 0.35s var(--ease-out);
}
.card.is-choosing .card-pick .pick-chev {
  rotate: 0deg;
}
.card.is-compact .card-pick {
  display: flex;
}
.card.is-compact .listen-main {
  display: none;
}
.card.is-compact.is-choosing {
  padding: 8px;
  border-radius: 28px;
}
.card.is-compact.is-choosing .listen-main {
  display: flex;
  height: 46px;
  background: var(--glass-fill);
  border: 1px solid var(--glass-edge);
  color: #fff;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
.card.is-compact.is-choosing .listen-more {
  display: grid;
  margin-top: 8px;
}
.card.is-compact.is-choosing .card-pick {
  margin-top: 8px;
}
.card.is-choosing .listen-main,
.card.is-choosing .listen-more {
  animation: choose-in 0.32s var(--ease-out) both;
}
@keyframes choose-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
}

/* ---------- after the worlds ---------- */

.after {
  position: relative;
  isolation: isolate;
  padding: 104px 20px calc(96px + var(--safe-b));
  /* The bar's height; where a viewer reserves a top inset, it already snaps below it. */
  scroll-margin-top: 64px;
}
/* Where the swipe after the fifth song lands, one screen below the bar. Its snap area also reaches far
   below it, past the end of the page: inside a snap area taller than the screen the page scrolls freely,
   so everything from the making-of down scrolls freely and no edge down there catches a slow scroll. */
.making {
  scroll-snap-align: start;
  scroll-margin-bottom: 1000vh;
}

/* Past the worlds the bar gets a ground of ink, so text scrolls under it instead of into it (dense enough
   that no ghost of it shows behind the name). */
.is-after .top {
  background-color: rgba(5, 5, 5, 0.97);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06);
}
.is-after .making > .peek {
  opacity: 0;
}

/* And the Listen pill sits on a fade of ink, never straight on a line of text. */
body::after {
  content: "";
  position: fixed;
  z-index: 25;
  left: 0;
  right: 0;
  bottom: 0;
  height: calc(92px + var(--safe-b));
  background: linear-gradient(180deg, rgba(5, 5, 5, 0), rgba(5, 5, 5, 0.92) 58%);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.5s var(--ease-out);
}
.is-after body::after {
  opacity: 1;
}

/* ---------- the background: colour in slow, smooth gradients, and gold dust ---------- */

/* Behind the discography and the bio, and behind the album page once past the worlds: deep colour flowing in wide, soft
   gradients over black (violet, blue, magenta, a little rose and gold), with gold dust rising through it.
   Only transforms and opacity move, nothing is blended or masked live, so a phone keeps up while the page
   scrolls. On the discography a playing preview adds its own cover's glow (disco.js). */
.halo {
  position: fixed;
  z-index: 0;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  background: var(--ink);
  opacity: 0;
  visibility: hidden;
  transition: opacity 1.2s var(--ease-out), visibility 1.2s;
}
.is-after .halo,
.page-plain .halo {
  opacity: 1;
  visibility: visible;
}
main,
.foot {
  position: relative;
  z-index: 1;
}
.halo-mix,
.halo-velvet,
.halo-now {
  position: absolute;
  inset: 0;
  background: center / cover no-repeat;
}
/* Two sheets of deep colour kept toward the edges, each a few wide fields that fade over most of their
   reach (several stops, so no edge ever shows), drifting against each other; the middle stays black under
   the words. Larger than the screen, so the drift never shows a side. */
.halo-mix {
  inset: -15%;
  background: radial-gradient(
      55% 45% at 10% 14%,
      rgba(124, 58, 237, 0.36),
      rgba(124, 58, 237, 0.17) 40%,
      rgba(124, 58, 237, 0.05) 70%,
      transparent
    ),
    radial-gradient(
      50% 40% at 92% 30%,
      rgba(37, 99, 235, 0.3),
      rgba(37, 99, 235, 0.13) 40%,
      rgba(37, 99, 235, 0.04) 70%,
      transparent
    ),
    radial-gradient(
      55% 45% at 22% 92%,
      rgba(219, 39, 119, 0.26),
      rgba(219, 39, 119, 0.11) 40%,
      rgba(219, 39, 119, 0.03) 70%,
      transparent
    ),
    radial-gradient(45% 38% at 92% 94%, rgba(147, 51, 234, 0.24), rgba(147, 51, 234, 0.1) 45%, transparent);
  animation: flow-a 38s var(--ease-in-out) infinite alternate;
}
.halo-velvet {
  inset: -15%;
  background: radial-gradient(42% 34% at 66% 52%, rgba(244, 114, 182, 0.08), rgba(244, 114, 182, 0.03) 55%, transparent),
    radial-gradient(40% 32% at 36% 40%, rgba(148, 163, 184, 0.06), rgba(148, 163, 184, 0.02) 55%, transparent),
    radial-gradient(36% 28% at 80% 12%, rgba(var(--gold-rgb), 0.08), rgba(var(--gold-rgb), 0.03) 55%, transparent),
    radial-gradient(38% 30% at 8% 60%, rgba(56, 189, 248, 0.06), transparent 70%);
  animation: flow-b 52s var(--ease-in-out) infinite alternate;
}
@keyframes flow-a {
  from {
    transform: translate(-4%, -3%) rotate(-4deg);
  }
  to {
    transform: translate(4%, 3%) rotate(4deg) scale(1.08);
  }
}
@keyframes flow-b {
  from {
    transform: translate(5%, 2%) rotate(3deg) scale(1.06);
  }
  to {
    transform: translate(-5%, -3%) rotate(-5deg);
  }
}
/* A tenth larger than the screen through its inset, not a scale transform, and hidden between previews: in the
   Claude iPhone viewer a scaled, masked layer here left the whole discography drawn to 1/1.1 of the screen. */
.halo-now {
  inset: -5%;
  opacity: 0;
  visibility: hidden;
  transition: opacity 1.4s var(--ease-out), visibility 1.4s;
  /* A glow rising from the middle, not a flood: the dark frame stays. */
  -webkit-mask-image: radial-gradient(ellipse 75% 55% at 50% 58%, #000 0%, transparent 100%);
  mask-image: radial-gradient(ellipse 75% 55% at 50% 58%, #000 0%, transparent 100%);
}
.halo-now.is-on {
  opacity: 0.75;
  visibility: visible;
}
/* Gold dust: specks rising slowly through the colour, swaying and catching the light as they go, each with
   its own place, size and pace (--x, --y, --s, --rise, --sway, --twinkle, --delay; common.js scatters them). */
.dust {
  position: absolute;
  z-index: 1;
  inset: 0;
  opacity: 0;
  transition: opacity 2s var(--ease-out);
}
.dust.is-on {
  opacity: 0.7;
}
.is-playing .dust.is-on {
  opacity: 0.95;
}
.mote {
  position: absolute;
  left: var(--x);
  top: 100%;
  width: var(--s);
  height: var(--s);
  border-radius: 50%;
  /* A bright core in its own small gold glow. */
  background: radial-gradient(
    circle closest-side,
    #fff2c8 0 16%,
    rgba(var(--gold-rgb), 0.95) 32%,
    rgba(var(--gold-rgb), 0.3) 60%,
    transparent
  );
  animation: mote-rise var(--rise) linear var(--delay) infinite,
    mote-sway var(--sway) var(--ease-in-out) var(--delay) infinite alternate,
    mote-twinkle var(--twinkle) var(--ease-in-out) var(--delay) infinite alternate;
}
/* A few out of focus: wide, soft and slow, in the background's own colours. */
.mote.is-blur {
  background: radial-gradient(circle closest-side, rgba(var(--tint), 0.16) 0 55%, rgba(var(--tint), 0.06) 85%, transparent);
  animation: mote-rise var(--rise) linear var(--delay) infinite,
    mote-sway var(--sway) var(--ease-in-out) var(--delay) infinite alternate;
}
@keyframes mote-rise {
  to {
    transform: translateY(-115vh);
  }
}
@keyframes mote-sway {
  from {
    translate: -14px 0;
  }
  to {
    translate: 14px 0;
  }
}
@keyframes mote-twinkle {
  from {
    opacity: 0.25;
  }
  to {
    opacity: 0.85;
  }
}
/* The album page keeps all of it still while its worlds fill the screen. */
html:has(.world):not(.is-after) :is(.halo-mix, .halo-velvet, .mote) {
  animation-play-state: paused;
}
/* Ink pooled in the middle, under the words, and at the top and the foot keeps everything clear (the dust
   rides above it); a fine grain keeps the dark gradients from banding. The grain is a still image of faint
   light and dark specks, laid plainly on top. */
.halo::before,
.halo::after {
  content: "";
  position: absolute;
  z-index: 1;
  inset: 0;
}
.halo::after {
  z-index: 2;
  background: url("../img/grain.png");
}
.halo::before {
  background: radial-gradient(ellipse 75% 60% at 50% 50%, rgba(5, 5, 5, 0.5), rgba(5, 5, 5, 0.2) 55%, transparent 80%),
    linear-gradient(180deg, rgba(5, 5, 5, 0.5) 0%, rgba(5, 5, 5, 0) 24%, rgba(5, 5, 5, 0) 76%, rgba(5, 5, 5, 0.45) 100%);
}

.after :is(h1, h2) {
  font-size: clamp(38px, 10.5vw, 72px);
  line-height: 0.95;
  font-weight: 800;
  letter-spacing: -0.04em;
  text-wrap: balance;
}
.after p {
  font-size: 17px;
  line-height: 1.5;
  color: var(--text-2);
}

.making {
  padding-top: calc(var(--peek) + 88px);
}
.making .peek {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0));
}
.making-text p {
  max-width: 36ch;
  margin-top: 18px;
}
.steps {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 24px;
  counter-reset: step;
}
.steps li {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 36px;
  padding: 0 14px 0 8px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: rgba(255, 255, 255, 0.06);
  font-size: 13px;
  font-weight: 600;
  counter-increment: step;
}
.steps li::before {
  content: counter(step);
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.92);
  color: var(--ink);
  font-family: "Geist Mono", ui-monospace, monospace;
  font-size: 11px;
}

.making-film {
  margin-top: 40px;
}
.making-film .portal {
  position: relative;
  inset: auto;
  width: min(100%, 360px);
  aspect-ratio: 9 / 16;
  border-radius: 30px;
  box-shadow: 0 30px 70px rgba(0, 0, 0, 0.55);
}
/* Framed views draw their glass edge over the picture, so the frame sits on top of the world. */
.making-film .portal::after,
.about-photo::before {
  content: "";
  position: absolute;
  z-index: 3;
  inset: 0;
  border-radius: inherit;
  border: 1px solid var(--glass-edge);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3);
  pointer-events: none;
}
.making-film .poster {
  background-image: url("../img/making-of-blur.jpg");
}
.making-film.is-rolling .film {
  opacity: 1;
}
.film-play {
  position: absolute;
  z-index: 2;
  left: 50%;
  bottom: 22px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 50px;
  padding: 0 22px 0 18px;
  border-radius: 999px;
  font-size: 15px;
  font-weight: 600;
  white-space: nowrap;
  transform: translateX(-50%);
  transition: background 0.3s var(--ease-out);
}
.film-play svg {
  width: 16px;
  height: 16px;
}
.film-play:hover {
  background: rgba(255, 255, 255, 0.24);
}
.making-film.is-playing .film-play {
  display: none;
}

.about-photo {
  position: relative;
  overflow: hidden;
  border-radius: 30px;
  box-shadow: 0 30px 70px rgba(0, 0, 0, 0.5);
}
/* The artist's photo keeps its own colours (their call). */
.about-photo img {
  background: var(--ink) url("../img/artist-blur.jpg") center / cover no-repeat;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}
.about-text {
  margin-top: 36px;
}
.about-text p {
  max-width: 38ch;
  margin-top: 18px;
}
.about-text .motto {
  margin-top: 18px;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: #fff;
}
/* Under the bio's words, "Follow me" is a step below the name. */
.about-text .follow {
  margin-top: 34px;
}
.about-text .follow h2 {
  font-size: clamp(28px, 7.5vw, 40px);
}

/* No contact address: just the two places NaaniRM posts, where anyone can also write. */
.follow ul {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 32px;
  margin-top: 20px;
}
.follow a {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-size: clamp(21px, 6.2vw, 34px);
  font-weight: 700;
  letter-spacing: -0.02em;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 7px;
  text-decoration-color: rgba(255, 255, 255, 0.35);
}
.follow svg {
  flex: none;
  width: 26px;
  height: 26px;
}
.follow a:hover {
  text-decoration-color: var(--accent);
}

.foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 12px 20px;
  padding: 28px 20px calc(104px + var(--safe-b));
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  font-size: 13px;
  color: var(--text-3);
}
.foot a {
  text-underline-offset: 3px;
  text-decoration-color: rgba(255, 255, 255, 0.4);
}
.foot p:last-child {
  display: flex;
  gap: 18px;
}

.more-link {
  display: flex;
  width: max-content;
  align-items: center;
  gap: 8px;
  margin-top: 24px;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -0.01em;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 6px;
  text-decoration-color: rgba(255, 255, 255, 0.35);
}
.more-link svg {
  width: 18px;
  height: 18px;
  transition: transform 0.35s var(--ease-out);
}
.more-link:hover {
  text-decoration-color: var(--accent);
}
.more-link:hover svg {
  transform: translateX(3px);
}

/* ---------- pages without worlds: discography, bio ---------- */

.page-plain .top {
  background-color: rgba(5, 5, 5, 0.97);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06);
}

/* The bio: the photo, the words and "Follow me" open the page, then the email list as on the album page. The
   bar grows by the top safe area (notch, in-app viewers), so the page starts below it by as much. */
.page-bio .about {
  padding-top: calc(104px + env(safe-area-inset-top, 0px));
  padding-bottom: 24px;
}
.more-link + .more-link {
  margin-top: 14px;
}

/* The discography. */
.disco {
  padding: calc(104px + env(safe-area-inset-top, 0px)) 20px 0;
}
.disco-intro h1 {
  font-size: clamp(40px, 12vw, 84px);
  line-height: 0.95;
  font-weight: 800;
  letter-spacing: -0.04em;
}
.disco-intro p {
  max-width: 40ch;
  margin-top: 14px;
  font-size: 17px;
  line-height: 1.5;
  color: var(--text-2);
}

.feature {
  position: relative;
  isolation: isolate;
  display: grid;
  gap: 26px;
  margin-top: 40px;
}
.feature::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: -12% 0;
  background: url("../img/world-1-ambient.jpg") center / cover;
  opacity: 0.24;
  -webkit-mask-image: radial-gradient(ellipse 60% 50% at 40% 50%, #000 0%, transparent 100%);
  mask-image: radial-gradient(ellipse 60% 50% at 40% 50%, #000 0%, transparent 100%);
  pointer-events: none;
}
.feature-cover,
.release-cover {
  position: relative;
  display: block;
  overflow: hidden;
  box-shadow: 0 24px 56px rgba(0, 0, 0, 0.55);
  transition: transform 0.45s var(--ease-out);
}
/* Radii from the system: framed media 30px, cover art 13px. */
.feature-cover {
  width: min(100%, 440px);
  border-radius: 30px;
}
.release-cover {
  width: 100%;
  border-radius: 13px;
}
.feature-cover img,
.release-cover img {
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
}
.feature-cover::after,
.release-cover::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  border: 1px solid var(--glass-edge);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28);
  pointer-events: none;
}
.feature-cover:hover,
.release-cover:hover {
  transform: translateY(-3px);
}

/* A single's cover is its own small player: a press plays the 30-second preview in place. */
.release-cover::before {
  content: "";
  position: absolute;
  inset: 45% 0 0;
  background: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.62));
  opacity: 0;
  transition: opacity 0.4s var(--ease-out);
  pointer-events: none;
}
.cover-ctl {
  position: absolute;
  left: 10px;
  right: 12px;
  bottom: 10px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.cover-play {
  flex: none;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: rgba(5, 5, 5, 0.55);
  border: 1px solid var(--glass-edge);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.28);
  transition: background 0.3s var(--ease-out), color 0.3s var(--ease-out);
}
.cover-play svg {
  width: 16px;
  height: 16px;
}
.cover-play .i-pause,
.release-cover.is-playing .i-play {
  display: none;
}
.release-cover.is-playing .i-pause {
  display: block;
}
.release-cover.is-playing .cover-play {
  background: #fff;
  color: var(--ink);
}
@media (hover: hover) {
  .release-cover:hover .cover-play {
    background: #fff;
    color: var(--ink);
  }
}
.cover-ctl .bar {
  flex: 1;
  margin-top: 0;
  opacity: 0;
  transition: opacity 0.4s var(--ease-out);
}
.release-cover.is-current::before,
.release-cover.is-current .bar {
  opacity: 1;
}
.feature-text h2 {
  font-size: clamp(36px, 10.5vw, 68px);
  line-height: 0.95;
  font-weight: 800;
  letter-spacing: -0.04em;
}
.release-meta {
  margin-top: 6px;
  font-size: 14px;
  color: var(--text-2);
}
.tracks {
  max-width: 460px;
  margin-top: 18px;
}
.tracks li {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 9px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.tracks .num {
  font-size: 12px;
  color: var(--text-3);
}
.feature-actions {
  max-width: 460px;
  margin-top: 22px;
}
.feature-actions .listen-main {
  margin-top: 0;
}

.singles {
  margin-top: 96px;
  padding-bottom: 96px;
  /* Reached by the skip link or a scroll into view, the title and the switch land below the bar. */
  scroll-margin-top: calc(96px + env(safe-area-inset-top, 0px));
}
/* The title and the covers-or-list switch share a line; the hint goes under them. */
.singles-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 14px 24px;
}
.singles-head h2 {
  font-size: clamp(32px, 9vw, 56px);
  line-height: 0.95;
  font-weight: 800;
  letter-spacing: -0.04em;
}
/* On a narrow phone the hint wraps: the icon stays on the first line. */
.singles-hint {
  flex-basis: 100%;
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;
  color: var(--text-2);
}
.singles-hint svg {
  flex: none;
  width: 14px;
  height: 14px;
  margin-top: 3px;
}
.releases {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 30px 14px;
  margin-top: 28px;
}
.release h3 {
  margin-top: 12px;
  font-size: 16px;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.02em;
  transition: color 0.4s var(--ease-out);
}
/* The single that is playing wears a touch of gold. */
.release:has(.release-cover.is-current) h3 {
  color: #ecc978;
}
.release .release-meta {
  margin-top: 3px;
  font-size: 13px;
}
.release-links {
  display: flex;
  gap: 6px;
  margin-top: 10px;
}
/* Four per single, so they give a little on the narrowest phones instead of overflowing. */
.release-links li {
  flex: 0 1 36px;
  min-width: 0;
}
/* Forty small buttons on plain ink: the fill and edge are enough, no live blur. */
.release-links .glass {
  display: grid;
  place-items: center;
  width: 100%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  transition: background 0.3s var(--ease-out);
}
.release-links .glass:hover {
  background: rgba(255, 255, 255, 0.2);
}
.release-links svg {
  width: 16px;
  height: 16px;
}

/* Covers or list: built like the language switch, the chosen one in white. */
.view-switch {
  display: flex;
  gap: 2px;
  padding: 4px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.1);
}
.view-switch button {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 12px 0 10px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-2);
  transition: background 0.35s var(--ease-out), color 0.35s var(--ease-out);
}
.view-switch button[aria-pressed="true"] {
  background: rgba(255, 255, 255, 0.94);
  color: var(--ink);
}
.view-switch button:hover:not([aria-pressed="true"]) {
  color: #fff;
}
.view-switch svg {
  width: 15px;
  height: 15px;
}
/* The narrowest phones keep the icons; the words stay for screen readers. */
@media (max-width: 380px) {
  .view-switch button {
    padding: 0 10px;
  }
  .view-switch .label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* The singles as a list: a small cover that still plays its preview, the title and date beside it, the
   platforms under them (beside them where the screen is wide enough). */
[data-singles="list"] .releases {
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
  margin-top: 18px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}
[data-singles="list"] .release {
  display: grid;
  grid-template-columns: 64px minmax(0, 1fr);
  grid-template-areas: "cover title" "cover meta" "cover links";
  align-items: center;
  column-gap: 14px;
  padding: 14px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
[data-singles="list"] .release-cover {
  grid-area: cover;
  align-self: start;
  border-radius: 10px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.45);
}
[data-singles="list"] .release h3 {
  grid-area: title;
  margin-top: 0;
}
[data-singles="list"] .release .release-meta {
  grid-area: meta;
  margin-top: 2px;
}
[data-singles="list"] .release-links {
  grid-area: links;
  margin-top: 8px;
}
[data-singles="list"] .release-links li {
  flex-basis: 32px;
}
[data-singles="list"] .release-links svg {
  width: 15px;
  height: 15px;
}
/* On the small cover the badge sits in the middle and the progress runs along its foot. */
[data-singles="list"] .cover-ctl {
  inset: 0;
  justify-content: center;
}
[data-singles="list"] .cover-play {
  width: 30px;
  height: 30px;
}
[data-singles="list"] .cover-play svg {
  width: 12px;
  height: 12px;
}
[data-singles="list"] .cover-ctl .bar {
  position: absolute;
  left: 6px;
  right: 6px;
  bottom: 5px;
}
@media (min-width: 600px) {
  [data-singles="list"] .release {
    grid-template-columns: 64px minmax(0, 1fr) auto;
    grid-template-areas: "cover title links" "cover meta links";
    column-gap: 20px;
  }
  [data-singles="list"] .release-cover {
    align-self: center;
  }
  /* The title and the date hug the row's middle line. */
  [data-singles="list"] .release h3 {
    align-self: end;
  }
  [data-singles="list"] .release .release-meta {
    align-self: start;
  }
  [data-singles="list"] .release-links {
    margin-top: 0;
  }
  /* A fixed size: in a column sized to its content, the pills would shrink to their icons. */
  [data-singles="list"] .release-links li {
    flex: none;
    width: 36px;
  }
}

/* ---------- legal pages ---------- */

.doc {
  max-width: 680px;
  margin: 0 auto;
  padding: calc(120px + env(safe-area-inset-top, 0px)) 20px 80px;
}
.doc h1 {
  font-size: clamp(36px, 9vw, 60px);
  line-height: 0.95;
  font-weight: 800;
  letter-spacing: -0.04em;
}
.doc p {
  margin-top: 18px;
  font-size: 17px;
  line-height: 1.55;
  color: var(--text-2);
}
.doc a {
  text-underline-offset: 3px;
}

/* ---------- phones ---------- */

@media (max-width: 1023px) {
  /* While an episode plays full-bleed, the bar and the rail step aside. */
  .is-watching .top,
  .is-watching .rail {
    opacity: 0;
    visibility: hidden;
  }
  /* The episode takes the whole screen (no band of the next song). After a moment the player slides
     down off the screen (site.js) and a tap on the episode slides it back up. It moves without fading,
     so its frosted glass never flickers: an easy start and stop on the way out, a soft landing back. */
  .world.is-episode {
    height: 100vh;
    height: 100svh;
  }
  .is-watching .card {
    transition: translate 0.5s var(--ease-out);
  }
  .is-immersed .card {
    translate: 0 calc(100% + var(--peek) + 24px + var(--safe-b));
    transition: translate 0.6s var(--ease-in-out);
    pointer-events: none;
  }
  .is-immersed .world.is-episode .stage::after {
    opacity: 0;
  }

  /* On a phone the card is two slim rows: Spotify with the episode beside it, then the other apps.
     The big title already names the song, so the cover and title row are left to wide screens. */
  .card {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 8px;
    padding: 10px;
    border-radius: 28px;
  }
  .card-head {
    display: contents;
  }
  .card-art,
  .card-meta {
    display: none;
  }
  /* Scoped to the card: the discography page's own Listen buttons keep their normal spacing. */
  .card .listen-main {
    grid-column: 1;
    grid-row: 1;
    height: 48px;
    margin-top: 0;
    font-size: 15px;
  }
  .card .listen-main svg {
    width: 20px;
    height: 20px;
  }
  .card .watch {
    grid-column: 2;
    grid-row: 1;
    height: 48px;
    padding: 0 16px 0 14px;
  }
  .card .listen-more {
    grid-column: 1 / -1;
    grid-row: 2;
    margin-top: 0;
  }
  .card .listen-more a {
    height: 38px;
  }
  .card .mini {
    grid-column: 1 / -1;
    grid-row: 1;
  }
  /* As a player the card keeps two rows on a phone: the player, and one pill for every platform (as when it
     folds after the worlds), which opens the four above it. */
  .card.is-playing .listen-main,
  .card.is-playing .listen-more {
    display: none;
  }
  .card.is-playing .card-pick {
    display: flex;
    grid-column: 1 / -1;
    grid-row: 2;
    height: 44px;
    font-size: 15px;
  }
  .card.is-playing.is-choosing .listen-main {
    display: flex;
    grid-column: 1 / -1;
    grid-row: 2;
    height: 44px;
    margin-top: 0;
    background: var(--glass-fill);
    border: 1px solid var(--glass-edge);
    color: #fff;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3);
  }
  .card.is-playing.is-choosing .listen-more {
    display: grid;
    grid-column: 1 / -1;
    grid-row: 3;
  }
  .card.is-playing.is-choosing .card-pick {
    grid-row: 4;
  }
  .card.is-compact {
    display: block;
    padding: 6px;
    border-radius: 999px;
  }
  /* The words keep clear of the song rail on the right; the buttons under them sit below the rail, so they
     take the whole width. */
  .world-actions {
    margin-right: -44px;
  }
}

.listen-more .short,
.chip .short {
  display: none;
}
@media (max-width: 439px) {
  .listen-more .long,
  .chip .long {
    display: none;
  }
  .listen-more .short,
  .chip .short {
    display: inline;
  }
}

/* On a phone the name, Instagram, TikTok, Sound and the menu fill the bar: the volume slider
   would push it off screen, and the phone's own volume buttons already do that job. */
@media (max-width: 480px) {
  .volume {
    display: none;
  }
}
/* The narrowest phones keep the speaker without its word. */
@media (max-width: 340px) {
  .sound .sound-label {
    display: none;
  }
  .sound {
    width: 34px;
    padding: 0;
  }
}

@media (max-width: 380px) {
  .listen-more a {
    font-size: 11px;
    gap: 5px;
  }
}

@media (max-height: 640px) and (max-width: 1023px) {
  .line {
    display: none;
  }
  .world h1,
  .world h2 {
    font-size: clamp(32px, 10vw, 44px);
  }
}

/* ---------- desktop: copy and card on the left, the world as a tall portal ---------- */

@media (min-width: 1024px) {
  :root {
    --peek: 0px;
  }

  .top {
    padding: 22px 28px 22px 8vw;
  }

  .rail {
    right: 22px;
  }
  .rail a {
    gap: 12px;
  }
  .rail .name {
    position: static;
    width: auto;
    height: auto;
    clip-path: none;
    max-width: 0;
    opacity: 0;
    font-family: "Geist", system-ui, sans-serif;
    font-size: 13px;
    font-weight: 600;
    transition: max-width 0.6s var(--ease-out), opacity 0.4s var(--ease-out);
  }
  .rail a:hover .name,
  .rail a:focus-visible .name {
    max-width: 240px;
    opacity: 1;
  }

  .world {
    height: 100vh;
    min-height: 640px;
  }
  .peek,
  body::after {
    display: none;
  }

  /* Paint order inside the stage: ambient light, then the ink scrim, then the portal. */
  .ambient {
    display: block;
    position: absolute;
    z-index: 0;
    inset: -8%;
    width: 116%;
    height: 116%;
    object-fit: cover;
    opacity: 0.7;
    filter: blur(28px) saturate(1.25);
  }
  .stage::before {
    content: "";
    position: absolute;
    z-index: 1;
    inset: 0;
    background:
      linear-gradient(90deg, rgba(5, 5, 5, 0.92) 0%, rgba(5, 5, 5, 0.62) 46%, rgba(5, 5, 5, 0.36) 100%),
      linear-gradient(180deg, rgba(5, 5, 5, 0.5) 0%, rgba(5, 5, 5, 0) 22%, rgba(5, 5, 5, 0) 78%, rgba(5, 5, 5, 0.6) 100%);
  }
  .stage::after {
    display: none;
  }

  .world .portal {
    z-index: 2;
    inset: auto;
    top: 50%;
    right: max(10vw, 150px);
    height: min(84vh, 900px);
    aspect-ratio: 9 / 16;
    border-radius: 36px;
    box-shadow: 0 44px 110px rgba(0, 0, 0, 0.6);
    transform: translateY(-50%);
  }
  .world .portal::after {
    content: "";
    position: absolute;
    z-index: 3;
    inset: 0;
    border-radius: inherit;
    border: 1px solid var(--glass-edge);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3);
    pointer-events: none;
  }

  .copy {
    left: 8vw;
    right: auto;
    bottom: calc(7vh + var(--card-h) + 52px);
    width: min(40vw, 620px);
  }
  .world h1,
  .world h2 {
    font-size: clamp(64px, 6.4vw, 96px);
  }
  .world #w3-title {
    font-size: clamp(54px, 5.2vw, 82px);
  }
  .lede {
    font-size: 20px;
  }
  .line {
    max-width: 34ch;
    font-size: 18px;
  }
  .world.is-episode .copy {
    opacity: 1;
    transform: none;
  }
  .world-actions {
    margin-top: 22px;
  }
  .chip {
    height: 42px;
    padding: 0 17px 0 14px;
    font-size: 14px;
  }
  .chip svg {
    width: 17px;
    height: 17px;
  }

  /* The lyrics take the words' column, from under the bar to the card's foot. */
  .lyrics {
    inset: calc(96px + env(safe-area-inset-top, 0px)) auto 7vh 8vw;
    width: min(520px, 38vw);
    height: auto;
    border-radius: 30px;
    border: 1px solid var(--glass-edge);
    background: var(--glass-fill), rgba(8, 6, 8, 0.74);
    box-shadow: var(--glass-lift);
  }
  .lyrics-head {
    padding: 24px 20px 14px 28px;
  }
  .lyrics-head h2 {
    font-size: 36px;
  }
  .lyrics-text {
    padding: 8px 28px 36px;
  }
  .lyrics-text .l {
    font-size: 25px;
  }
  .lyrics-text .tr {
    font-size: 16px;
  }

  .card {
    left: 8vw;
    right: auto;
    bottom: 7vh;
    width: min(460px, 34vw);
    margin: 0;
    /* Here the card sits on the world's ambient light, already blurred at export, so a live blur adds
       nothing but GPU seams: hovering a button inside redrew only its patch of the blur and left bars
       across the glass. The same glass fill over a light tint of ink looks the same and stays clean. */
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    background: var(--glass-fill), rgba(10, 4, 6, 0.3);
    /* A breath of the world's own colour gives back the glow the blur's saturation used to add. */
    background: var(--glass-fill), color-mix(in srgb, var(--accent) 14%, rgba(10, 4, 6, 0.3));
  }
  /* On a wide screen, past the worlds, the bar's own Listen pill takes over from the card. */
  .is-after .card {
    display: none;
  }

  .after {
    padding: 150px 8vw 120px;
  }
  /* One screen below the bar: the frame and the text sit whole where the scroll comes to rest. */
  .making {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 8vw;
    min-height: calc(100vh - 64px);
    padding-top: 48px;
    padding-bottom: 48px;
  }
  .making .peek {
    display: none;
  }
  .making-film {
    margin-top: 0;
  }
  .making-film .portal {
    width: auto;
    height: min(calc(100vh - 160px), 780px);
  }

  .about {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
    align-items: center;
    gap: 7vw;
  }
  .about-text {
    margin-top: 0;
  }
  .page-bio .about {
    min-height: 100vh;
    padding-top: calc(120px + env(safe-area-inset-top, 0px));
  }

  /* "Follow me" closes the page. */
  .closing {
    padding: 150px 8vw 140px;
  }
  .closing .after {
    padding: 0;
  }

  .foot {
    padding: 32px 8vw 40px;
  }

  /* Bar on a wide screen: pages beside the name, language and Listen on the right. */
  .top {
    gap: 16px;
  }
  .pages {
    display: flex;
    gap: 4px;
    margin-left: 18px;
  }
  .pages a {
    display: inline-flex;
    align-items: center;
    height: 36px;
    padding: 0 14px;
    border-radius: 999px;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-2);
    text-decoration: none;
    text-shadow: 0 1px 10px rgba(0, 0, 0, 0.5);
    transition: color 0.3s var(--ease-out), background 0.3s var(--ease-out);
  }
  .pages a:hover {
    color: #fff;
  }
  .pages a[aria-current="page"] {
    color: #fff;
    background: rgba(255, 255, 255, 0.12);
  }
  .actions > .lang {
    display: flex;
  }
  .menu-btn,
  .top .menu,
  .top.is-open .menu {
    display: none;
  }
  /* Past the worlds (and on the discography) the bar offers every platform: a white pill that opens a
     short list, drawn like the phone menu. */
  .is-after .pick,
  .page-plain .pick {
    display: block;
  }
  .pick {
    position: relative;
  }
  .pick-btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 40px;
    padding: 0 12px;
    border-radius: 999px;
    background: #fff;
    color: var(--ink);
    font-size: 14px;
    font-weight: 700;
    letter-spacing: -0.01em;
    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
    transition: transform 0.35s var(--ease-out);
  }
  .pick-btn svg {
    width: 16px;
    height: 16px;
  }
  .pick-btn:hover {
    transform: translateY(-1px);
  }
  .pick-chev {
    transition: rotate 0.35s var(--ease-out);
  }
  .pick.is-open .pick-chev {
    rotate: 180deg;
  }
  .pick-list {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    width: max(100%, 236px);
    padding: 6px;
    border-radius: 20px;
    background: rgba(18, 18, 20, 0.97);
    border: 1px solid rgba(255, 255, 255, 0.12);
    box-shadow: 0 18px 44px rgba(0, 0, 0, 0.55);
    display: none;
    flex-direction: column;
    gap: 2px;
  }
  .pick.is-open .pick-list {
    display: flex;
  }
  .pick-list a {
    display: flex;
    align-items: center;
    gap: 12px;
    height: 44px;
    padding: 0 12px;
    border-radius: 14px;
    font-size: 15px;
    font-weight: 500;
    color: var(--text-2);
    text-decoration: none;
    transition: background 0.25s var(--ease-out), color 0.25s var(--ease-out);
  }
  .pick-list a:hover,
  .pick-list a:focus-visible {
    color: #fff;
    background: rgba(255, 255, 255, 0.08);
  }
  .pick-list svg {
    width: 18px;
    height: 18px;
  }


  .disco {
    padding: 150px 8vw 0;
  }
  .feature {
    grid-template-columns: minmax(0, 440px) minmax(0, 1fr);
    align-items: center;
    gap: 6vw;
    margin-top: 56px;
  }
  .releases {
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 44px 28px;
  }
  .singles {
    margin-top: 140px;
    padding-bottom: 120px;
  }
}

@media (min-width: 1024px) and (max-height: 760px) {
  .world h1,
  .world h2 {
    font-size: clamp(54px, 5.4vw, 78px);
  }
  .line {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*),
  .halo-mix,
  .halo-velvet,
  .mote {
    animation: none !important;
  }
  /* Still, the dust stays where it was scattered. */
  .mote {
    top: var(--y);
  }
}
