/* ==========================================================================
   Homeflix — design system
   Dark, content-first, material-based. Built on an 8pt rhythm with hairline
   separators, translucent surfaces and spring motion.
   ========================================================================== */

:root {
  /* Surfaces — true black base so artwork appears to float on OLED panels */
  --black: #000;
  --s1: #0a0a0b;
  --s2: #141416;
  --s3: #1c1c1f;
  --s4: #26262b;

  /* Text: Apple's off-white reads softer than pure #fff at large sizes */
  --t1: #f5f5f7;
  --t2: rgba(235, 235, 245, .62);
  --t3: rgba(235, 235, 245, .32);

  --hairline: rgba(255, 255, 255, .09);
  --hairline-strong: rgba(255, 255, 255, .16);

  /* Brand: a warm gradient, used sparingly — never as a flat fill */
  --brand-1: #ff6a3d;
  --brand-2: #ff2d55;
  --brand: #ff375f;
  --brand-soft: rgba(255, 55, 95, .16);

  /* Radii — larger than typical web, closer to a native app */
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 18px;
  --r-xl: 24px;
  --r-full: 999px;

  /* Motion — the standard spring-like curve; everything uses it */
  --ease: cubic-bezier(.32, .72, 0, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --d-fast: .22s;
  --d: .42s;
  --d-slow: .7s;

  /* Materials */
  --blur: saturate(180%) blur(24px);
  --blur-heavy: saturate(180%) blur(40px);

  --shadow-sm: 0 2px 8px rgba(0, 0, 0, .4);
  --shadow-md: 0 12px 32px rgba(0, 0, 0, .55);
  --shadow-lg: 0 32px 80px rgba(0, 0, 0, .7);

  --pad: clamp(20px, 5vw, 64px);
  --nav-h: 60px;
}

* { box-sizing: border-box; }

/* The UA's [hidden] rule loses to any author `display` below, so state it here
   and win. Every overlay in this app is toggled via the hidden attribute. */
[hidden] { display: none !important; }

html {
  background: var(--black);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--black);
  color: var(--t1);
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text",
    "Inter", "Segoe UI", Roboto, system-ui, sans-serif;
  font-size: 15px;
  line-height: 1.47;
  letter-spacing: -.01em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

body.no-scroll { overflow: hidden; }

button, input, a { font: inherit; color: inherit; letter-spacing: inherit; }
button { background: none; border: 0; cursor: pointer; padding: 0; }
a { text-decoration: none; }
img { max-width: 100%; display: block; }

/* ------------------------------------------------------------------ focus
   Focus has to read from three metres away on a TV, so it's a ring plus a
   glow rather than a hairline outline. `:focus-visible` keeps it off the
   screen for mouse users. */

/* A thin light ring plus depth, not a saturated outline. Colour shouts;
   a hairline and a lift read as "selected" without fighting the artwork. */

:focus-visible {
  outline: 1px solid rgba(255, 255, 255, .55);
  outline-offset: 3px;
  border-radius: 6px;
}

.btn:focus-visible,
.tool-btn:focus-visible,
.icon-btn:focus-visible,
.nav-links button:focus-visible,
.menu button:focus-visible,
.url-chip button:focus-visible,
.folder-row .fx:focus-visible,
.modal-close:focus-visible,
.tool-x:focus-visible {
  outline: none;
  background: rgba(255, 255, 255, .16);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, .5),
    0 6px 20px rgba(0, 0, 0, .5);
}

.btn-play:focus-visible {
  background: #fff;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, .25), 0 8px 26px rgba(0, 0, 0, .55);
}

.folder-add input:focus-visible,
.field select:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .55);
}

/* The input is borderless inside the pill, so highlight the pill itself —
   a ring drawn around the bare text field reads as a stray box. */
#search:focus-visible { outline: none; box-shadow: none; }
.search:focus-within {
  background: rgba(255, 255, 255, .16);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .6);
}
.search:focus-within .ico { color: var(--t1); }

summary:focus-visible { outline-offset: 0; }

/* Cards: hairline ring on the artwork plus a deeper shadow — the scale-up
   already signals focus, so the ring only needs to define the edge. */
.card:focus-visible { outline: none; }
.card:focus-visible .card-poster {
  box-shadow:
    inset 0 0 0 2px rgba(255, 255, 255, .9),
    0 22px 50px rgba(0, 0, 0, .7);
}

/* Leave room when a rail scrolls a focused card into view */
.card { scroll-margin-inline: var(--pad); scroll-margin-block: 96px; }

/* In the TV app the viewer is far away and using a remote — push the
   affordances up a notch. */
/* ==========================================================================
   TV layout
   A bottom sheet is a touch idiom — on a remote, at three metres, the detail
   screen becomes a full-bleed page: poster left, everything else right, and
   one obvious Back target.
   ========================================================================== */

.tv-back { display: none; }

body.tv { font-size: 16px; }

body.tv .modal { padding: 0; display: block; height: 100vh; }
body.tv .modal-backdrop { display: none; }

body.tv .modal-card {
  position: fixed;
  inset: 0;
  width: 100%;
  max-width: none;
  max-height: none;
  border-radius: 0;
  background: var(--black);
  box-shadow: none;
  animation: fadeIn var(--d) var(--ease) both;
}

/* The stock round × is a mouse affordance; hide it and use the labelled one. */
body.tv .modal-close { display: none; }

body.tv .tv-back {
  position: absolute;
  top: clamp(24px, 3vh, 44px);
  left: clamp(32px, 4vw, 68px);
  z-index: 8;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  height: 52px;
  padding: 0 24px 0 18px;
  border-radius: var(--r-full);
  /* Clear by default — it only needs to read as a control once focused. */
  background: transparent;
  box-shadow: none;
  font-size: 16px;
  font-weight: 600;
  color: var(--t2);
  text-shadow: 0 1px 8px rgba(0, 0, 0, .8);
  transition: background var(--d-fast) var(--ease), color var(--d-fast) var(--ease);
}
body.tv .tv-back:hover { color: var(--t1); }
body.tv .tv-back:focus-visible {
  outline: none;
  color: var(--t1);
  background: rgba(255, 255, 255, .14);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .5);
}

/* Ambient wash fills the whole screen instead of a banner strip */
body.tv .modal-art {
  position: absolute;
  inset: 0;
  aspect-ratio: auto;
  z-index: 0;
}
body.tv .modal-art-fade {
  background:
    linear-gradient(90deg, rgba(0, 0, 0, .96) 30%, rgba(0, 0, 0, .7) 62%, rgba(0, 0, 0, .5) 100%),
    linear-gradient(0deg, var(--black) 2%, transparent 60%);
}

body.tv .modal-scroll {
  position: relative;
  z-index: 2;
  height: 100vh;
  overflow-y: auto;
  scrollbar-width: none;
}
body.tv .modal-scroll::-webkit-scrollbar { display: none; }

body.tv .modal-body {
  /* Must be positioned with a z-index above .modal-art. Non-positioned block
     content paints *below* a positioned sibling even at z-index 0, which
     otherwise hides the poster, title and Play button behind the wash. */
  position: relative;
  z-index: 1;
  min-height: 100vh;
  /* Plain block flow, not grid. A grid whose first column holds a row-spanning
     poster inflates the other rows to match its height, which opens a dead gap
     under the title. Floating the poster out of flow avoids the problem
     entirely and the content simply stacks. */
  display: block;
  padding: clamp(80px, 10vh, 120px) clamp(40px, 6vw, 100px) clamp(48px, 6vh, 80px);
  padding-left: calc(clamp(40px, 6vw, 100px) + clamp(200px, 20vw, 300px) + clamp(32px, 4vw, 60px));
}

/* Poster becomes its own column, spanning the full content height */
body.tv .detail-head {
  display: contents;
}
body.tv .detail-poster {
  position: absolute;
  left: clamp(40px, 6vw, 100px);
  top: clamp(80px, 10vh, 120px);
  width: clamp(200px, 20vw, 300px);
  margin: 0;
  border-radius: var(--r-lg);
  box-shadow: inset 0 0 0 1px var(--hairline-strong), 0 30px 70px rgba(0, 0, 0, .8);
}
body.tv .modal-body h2 { font-size: clamp(34px, 3.6vw, 52px); margin-bottom: 14px; }
body.tv .badges { margin-bottom: 0; }
body.tv .badge { font-size: 14px; padding: 6px 13px; }


body.tv .modal-actions { margin: 22px 0 0; gap: 12px; }
body.tv .modal-actions .btn { height: 58px; padding: 0 32px; font-size: 17px; }
body.tv .modal-actions .btn .ico { width: 22px; height: 22px; }

body.tv .overview-wrap summary { font-size: 16px; padding: 16px 0; }
body.tv .overview { font-size: 17px; line-height: 1.65; max-width: 62ch; }

body.tv .tool-btn { height: 50px; padding: 0 20px; font-size: 15px; }

body.tv .facts { font-size: 16px; max-width: 720px; }
body.tv .facts dt, body.tv .facts dd { padding: 14px 0; }
body.tv .tech summary { font-size: 15px; padding: 14px 0; }

/* One column on smaller TV panels and 720p output */
@media (max-height: 620px) {
  body.tv .modal-body { padding-top: clamp(80px, 10vh, 110px); }
  body.tv .detail-poster { max-width: 200px; }
}

body.tv .card:focus-visible .card-poster {
  box-shadow:
    inset 0 0 0 2px rgba(255, 255, 255, .95),
    0 28px 60px rgba(0, 0, 0, .78);
}
body.tv .card:focus-visible { transform: translateY(-10px) scale(1.06); }
body.tv .card-hover { opacity: 0; }
body.tv .card:focus-visible .card-hover { opacity: 1; }

.ico { width: 22px; height: 22px; fill: currentColor; display: block; flex: none; }
.ico.flip { transform: scaleX(-1); }
.muted { color: var(--t2); }

::selection { background: var(--brand-soft); }

/* ============================================================ navigation */

#topbar {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 60;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 0 var(--pad);
  background: linear-gradient(180deg, rgba(0, 0, 0, .7), transparent);
  transition: background var(--d) var(--ease), backdrop-filter var(--d) var(--ease);
}

#topbar.solid {
  background: rgba(10, 10, 11, .72);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  box-shadow: inset 0 -1px 0 var(--hairline);
}

.bar-left { display: flex; align-items: center; gap: clamp(16px, 3vw, 36px); min-width: 0; }
.bar-right { display: flex; align-items: center; gap: 6px; }

/* Logo: gradient squircle mark carries the colour, wordmark stays neutral */
.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  white-space: nowrap;
  transition: opacity var(--d-fast) var(--ease);
}
.brand:hover { opacity: .82; }
/* The wordmark duplicates the Home tab, so it stays out of the focus order
   rather than sitting there as an unhighlighted dead stop. */
.brand:focus-visible { outline: none; box-shadow: none; }
.brand:active { transform: scale(.97); }

/* No container, no fill-box — the monogram stands on its own. The H's stems
   frame a play triangle; the whole glyph carries the brand gradient. */
.brand-mark {
  display: block;
  width: 26px;
  height: 26px;
  filter: drop-shadow(0 2px 10px rgba(255, 55, 95, .38));
}
.brand-mark svg { width: 100%; height: 100%; display: block; }
.brand-mark.lg { width: 46px; height: 46px; }

.brand-text {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: .17em;
  color: var(--t1);
  padding-left: 1px;
}

/* Poster card floating over the hero backdrop */
.hero-poster {
  position: absolute;
  right: clamp(40px, 8vw, 130px);
  bottom: clamp(60px, 12vh, 130px);
  z-index: 2;
  width: clamp(150px, 15vw, 232px);
  margin: 0;
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, .22),
    0 34px 76px rgba(0, 0, 0, .8),
    0 6px 18px rgba(0, 0, 0, .5);
  animation: posterIn 1s var(--ease-out) both .3s;
}
.hero-poster img { width: 100%; aspect-ratio: 2 / 3; object-fit: cover; }
@keyframes posterIn {
  from { opacity: 0; transform: translateY(30px) scale(.94); }
  to { opacity: 1; transform: none; }
}
@media (max-width: 980px) { .hero-poster { display: none; } }

/* Segmented control, not a row of links */
.nav-links {
  display: flex;
  gap: 2px;
  padding: 3px;
  border-radius: var(--r-full);
  background: rgba(255, 255, 255, .06);
  box-shadow: inset 0 0 0 1px var(--hairline);
  /* A flex item won't shrink below its content unless told to. Without this
     the pill overflows its parent and runs under the search box whenever the
     bar narrows — which the docked queue sidebar does at any window width. */
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}
.nav-links::-webkit-scrollbar { display: none; }
.nav-links button {
  flex: none;
  padding: 6px 15px;
  border-radius: var(--r-full);
  font-size: 13.5px;
  font-weight: 500;
  color: var(--t2);
  white-space: nowrap;
  transition: color var(--d-fast) var(--ease), background var(--d-fast) var(--ease);
}
.nav-links button:hover { color: var(--t1); }
.nav-links button.active {
  color: var(--t1);
  font-weight: 600;
  background: rgba(255, 255, 255, .13);
  box-shadow: var(--shadow-sm);
}

.search {
  display: flex;
  align-items: center;
  gap: 7px;
  height: 34px;
  padding: 0 13px;
  border-radius: var(--r-full);
  background: rgba(255, 255, 255, .07);
  box-shadow: inset 0 0 0 1px var(--hairline);
  transition: box-shadow var(--d-fast) var(--ease), background var(--d-fast) var(--ease);
}
/* The whole pill lights up — see the focus section; the input inside carries
   no ring of its own. */
.search:focus-within {
  background: rgba(255, 255, 255, .16);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .6);
}
.search:focus-within .ico { color: var(--t1); }
.search .ico { width: 16px; height: 16px; color: var(--t3); }
#search {
  background: none;
  border: 0;
  outline: none;
  width: clamp(110px, 17vw, 230px);
  font-size: 13.5px;
}
#search::placeholder { color: var(--t3); }
#search::-webkit-search-cancel-button { filter: invert(.6); }

.icon-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  width: 36px;
  height: 36px;
  border-radius: var(--r-full);
  color: var(--t1);
  transition: background var(--d-fast) var(--ease), transform var(--d-fast) var(--ease),
    color var(--d-fast) var(--ease);
}
.icon-btn:hover { background: rgba(255, 255, 255, .11); }
.icon-btn:active { transform: scale(.9); }
.icon-btn.big { width: 44px; height: 44px; }
.icon-btn.big .ico { width: 28px; height: 28px; }
.icon-btn b {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 9px;
  font-weight: 700;
  pointer-events: none;
}
.icon-btn b.rate { position: static; font-size: 13px; font-weight: 600; min-width: 24px; }
.icon-btn.on { color: var(--brand); }

/* ================================================================== hero */

.hero {
  position: relative;
  min-height: min(92vh, 860px);
  display: flex;
  align-items: flex-end;
  padding: 0 var(--pad) clamp(72px, 13vh, 150px);
  overflow: hidden;
}

.hero-art {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center 20%;
  transform: scale(1.06);
  animation: heroIn 1.6s var(--ease-out) both;
}

/* A 2:3 poster can't fill a widescreen hero without distortion, so blow it up
   and blur it into an ambient wash. The sharp poster card sits over the top. */
/* Pushed well down in brightness: the sharp poster sits on top of a blurred
   copy of itself, so without real separation the two merge into mush. */
.hero-art.ambient {
  background-position: center center;
  filter: blur(44px) saturate(150%) brightness(.38);
  transform: scale(1.3);
}
@keyframes heroIn {
  from { opacity: 0; transform: scale(1.12); }
  to { opacity: 1; transform: scale(1.06); }
}

/* Two scrims: one for text legibility, one to melt into the page below */
.hero-fade {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(90deg, rgba(0, 0, 0, .95) 0%, rgba(0, 0, 0, .75) 32%, rgba(0, 0, 0, .1) 68%, transparent 100%),
    linear-gradient(0deg, var(--black) 0%, rgba(0, 0, 0, .35) 38%, transparent 72%);
}

.hero-body {
  position: relative;
  max-width: min(660px, 94%);
  animation: riseIn .9s var(--ease-out) both .15s;
}
@keyframes riseIn {
  from { opacity: 0; transform: translateY(28px); }
  to { opacity: 1; transform: none; }
}

.hero-kicker {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 14px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--t2);
}
.hero-kicker .dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: linear-gradient(120deg, var(--brand-1), var(--brand-2));
  box-shadow: 0 0 12px var(--brand);
}

#hero-title {
  margin: 0 0 16px;
  font-size: clamp(34px, 5.4vw, 66px);
  line-height: 1.04;
  font-weight: 700;
  letter-spacing: -.035em;
  text-wrap: balance;
}

.hero-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 9px;
  margin: 0 0 14px;
  font-size: 14px;
  color: var(--t2);
}
.hero-meta .pill {
  padding: 3px 9px;
  border-radius: var(--r-sm);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .02em;
  color: var(--t1);
  background: rgba(255, 255, 255, .1);
  box-shadow: inset 0 0 0 1px var(--hairline);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
}

.hero-synopsis {
  margin: 0 0 22px;
  font-size: 16px;
  line-height: 1.55;
  color: var(--t2);
  max-width: 56ch;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.hero-file {
  margin: 0 0 24px;
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  color: var(--t3);
  overflow-wrap: anywhere;
}

.hero-actions { display: flex; gap: 10px; flex-wrap: wrap; }

/* =============================================================== buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 46px;
  padding: 0 26px;
  border-radius: var(--r-full);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -.01em;
  white-space: nowrap;
  transition: transform var(--d-fast) var(--ease), background var(--d-fast) var(--ease),
    opacity var(--d-fast) var(--ease), box-shadow var(--d-fast) var(--ease);
}
.btn:active { transform: scale(.96); }
.btn .ico { width: 19px; height: 19px; }

.btn-play {
  background: var(--t1);
  color: #000;
  box-shadow: 0 4px 20px rgba(255, 255, 255, .14);
}
.btn-play:hover { background: #fff; box-shadow: 0 6px 26px rgba(255, 255, 255, .22); }

.btn-ghost {
  background: rgba(255, 255, 255, .12);
  color: var(--t1);
  box-shadow: inset 0 0 0 1px var(--hairline);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
}
.btn-ghost:hover { background: rgba(255, 255, 255, .19); }

.btn-sm { height: 34px; padding: 0 16px; font-size: 13px; }

/* ================================================================== rows */

#rows { position: relative; z-index: 5; padding-bottom: 72px; margin-top: -3vh; }
body.no-hero #rows { margin-top: calc(var(--nav-h) + 40px); }

.row { margin-bottom: clamp(30px, 4vw, 52px); }

.row-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 var(--pad) 16px;
}
/* The count sits on the heading's own line. Centred against the heading, not
   baseline-aligned to it: the pill's padding makes its box taller than the
   digits, so sharing a baseline drops it visibly below the title's centre. */
.row-title-line {
  display: flex;
  align-items: center;
  gap: 12px;
}
.row-title h2 {
  margin: 0;
  font-size: clamp(20px, 2vw, 27px);
  font-weight: 650;
  letter-spacing: -.028em;
  line-height: 1.15;
}
.row-title p {
  margin: 3px 0 0;
  font-size: 13.5px;
  font-weight: 400;
  letter-spacing: -.005em;
  color: var(--t3);
}
.row-head .count {
  flex: none;
  padding: 4px 11px;
  border-radius: var(--r-full);
  background: rgba(255, 255, 255, .06);
  box-shadow: inset 0 0 0 1px var(--hairline);
  font-size: 12px;
  font-weight: 600;
  color: var(--t2);
  font-variant-numeric: tabular-nums;
}

.row-scroller { position: relative; }

.rail {
  display: flex;
  gap: 12px;
  overflow-x: auto;
  scroll-behavior: smooth;
  scroll-snap-type: x proximity;
  scroll-padding-left: var(--pad);
  padding: 30px var(--pad) 34px;
  margin: -30px 0 -34px;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
}
.rail::-webkit-scrollbar { display: none; }

.arrow {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 12;
  width: clamp(40px, 4.5vw, 64px);
  display: grid;
  place-items: center;
  color: var(--t1);
  opacity: 0;
  transition: opacity var(--d) var(--ease);
}
.arrow::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(0, 0, 0, .8), transparent);
}
.arrow.right::before { transform: scaleX(-1); }
.arrow .ico { position: relative; width: 28px; height: 28px; }
.arrow.left { left: 0; }
.arrow.right { right: 0; }
.row-scroller:hover .arrow:not([disabled]) { opacity: 1; }
.arrow:hover .ico { transform: scale(1.15); }
.arrow[disabled] { pointer-events: none; }

/* ================================================================= cards */

.card {
  position: relative;
  flex: 0 0 auto;
  width: clamp(146px, 15.5vw, 210px);
  scroll-snap-align: start;
  border-radius: var(--r-md);
  cursor: pointer;
  transition: transform var(--d) var(--ease);
  will-change: transform;
}
.card:hover, .card:focus-visible { transform: translateY(-8px) scale(1.025); z-index: 20; }
.card:active { transform: translateY(-4px) scale(.99); }

.card-poster {
  position: relative;
  aspect-ratio: 2 / 3;
  border-radius: var(--r-md);
  overflow: hidden;
  background-color: var(--s2);
  background-size: cover;
  background-position: center;
  display: grid;
  place-items: center;
  padding: 16px;
  box-shadow: inset 0 0 0 1px var(--hairline), var(--shadow-sm);
  transition: box-shadow var(--d) var(--ease);
}
.card:hover .card-poster {
  box-shadow: inset 0 0 0 1px var(--hairline-strong), var(--shadow-md);
}

.gen-title {
  position: relative;
  z-index: 2;
  text-align: center;
  font-size: clamp(14px, 1.2vw, 17px);
  font-weight: 650;
  line-height: 1.24;
  letter-spacing: -.02em;
  text-wrap: balance;
  overflow-wrap: anywhere;
}
.gen-year {
  position: relative;
  z-index: 2;
  margin-top: 7px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  color: var(--t2);
}

.card-badge, .card-new {
  position: absolute;
  top: 9px;
  z-index: 3;
  padding: 3px 7px;
  border-radius: 6px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .04em;
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
}
.card-badge {
  left: 9px;
  background: rgba(0, 0, 0, .55);
  box-shadow: inset 0 0 0 1px var(--hairline-strong);
}
.card-badge.q4k {
  background: linear-gradient(135deg, rgba(255, 215, 130, .9), rgba(196, 145, 40, .9));
  color: #241a04;
}
.card-new {
  right: 9px;
  background: linear-gradient(120deg, var(--brand-1), var(--brand-2));
  color: #fff;
}

.card-hover {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 4;
  /* Bottom padding has to clear the progress bar and its "time left" label,
     otherwise the Play pill lands on top of them. */
  padding: 34px 12px 26px;
  border-radius: 0 0 var(--r-md) var(--r-md);
  background: linear-gradient(0deg, rgba(0, 0, 0, .94) 26%, transparent);
  opacity: 0;
  transform: translateY(6px);
  transition: opacity var(--d) var(--ease), transform var(--d) var(--ease);
}
.card:hover .card-hover, .card:focus-visible .card-hover { opacity: 1; transform: none; }
.card-hover .t {
  font-size: 13px;
  font-weight: 600;
  line-height: 1.28;
  letter-spacing: -.015em;
  margin-bottom: 4px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.card-hover .m {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  font-size: 11px;
  color: var(--t2);
}
.card-hover .play {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 9px;
  padding: 6px 12px;
  border-radius: var(--r-full);
  background: var(--t1);
  color: #000;
  font-size: 11.5px;
  font-weight: 650;
}
.card-hover .play .ico { width: 13px; height: 13px; }

.card-left {
  position: absolute;
  left: 10px;
  bottom: 18px;
  z-index: 5;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .02em;
  color: var(--t1);
  text-shadow: 0 1px 6px rgba(0, 0, 0, .9);
  pointer-events: none;
}
.card:hover .card-left, .card:focus-visible .card-left { opacity: 0; }

.card-progress {
  position: absolute;
  left: 10px;
  right: 10px;
  bottom: 9px;
  z-index: 5;
  height: 3px;
  border-radius: 2px;
  background: rgba(255, 255, 255, .26);
  overflow: hidden;
}
.card-progress i {
  display: block;
  height: 100%;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--brand-1), var(--brand-2));
}

@media (hover: none) {
  .card:hover { transform: none; }
  .card-hover { opacity: 1; transform: none; }
  .arrow { display: none; }
}

/* ================================================================= empty */

/* Same card language as the pairing gate, so a first-run library and a
   first-run device read as one app rather than two. */
.empty { min-height: 74vh; display: grid; place-items: center; padding: calc(var(--nav-h) + 60px) var(--pad) 60px; }
.empty-card {
  width: min(520px, 100%);
  display: grid;
  justify-items: center;
  text-align: center;
  padding: clamp(32px, 5vw, 46px) clamp(24px, 5vw, 40px) clamp(26px, 4vw, 36px);
  border-radius: var(--r-xl);
  background: rgba(255, 255, 255, .04);
  box-shadow: inset 0 0 0 1px var(--hairline-strong), var(--shadow-lg);
}
.empty-mark {
  display: grid;
  place-items: center;
  width: 68px;
  height: 68px;
  margin-bottom: 16px;
  border-radius: 22px;
  background: radial-gradient(120% 120% at 50% 0%, rgba(255, 106, 61, .22), rgba(255, 45, 85, .10) 70%, transparent);
  box-shadow: inset 0 0 0 1px var(--hairline-strong);
}
.empty-mark svg { width: 34px; height: 34px; fill: var(--brand); }
.empty-mark.brand svg { width: 40px; height: 40px; }
.empty h2 { margin: 0; font-size: clamp(22px, 3vw, 27px); font-weight: 700; letter-spacing: -.03em; }
.empty-lead {
  margin: 10px 0 0;
  max-width: 42ch;
  color: var(--t2);
  font-size: 14.5px;
  line-height: 1.6;
}

/* Two plain sentences about how films get here — no paths, no jargon. */
.empty-steps {
  width: 100%;
  display: grid;
  gap: 8px;
  margin: 24px 0 4px;
  text-align: left;
}
.empty-step {
  display: flex;
  align-items: flex-start;
  gap: 13px;
  padding: 14px 16px;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .045);
  box-shadow: inset 0 0 0 1px var(--hairline);
}
.empty-step-ico {
  flex: none;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 11px;
  background: rgba(255, 255, 255, .06);
  color: var(--t1);
}
.empty-step-ico .ico { width: 19px; height: 19px; }
.empty-step-text { min-width: 0; }
.empty-step-text b { display: block; font-size: 14px; font-weight: 620; letter-spacing: -.01em; }
.empty-step-text span { display: block; margin-top: 3px; font-size: 12.5px; line-height: 1.55; color: var(--t2); }

.empty-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin-top: 20px; }
.empty-actions .btn { height: 46px; padding: 0 22px; }
.empty-foot { margin: 18px 0 0; font-size: 11.5px; color: var(--t3); }
.empty-foot code { font-size: 11px; }

@media (max-width: 560px) {
  .empty-actions { width: 100%; }
  .empty-actions .btn { flex: 1 1 100%; }
}

/* The TV app shares this page but can't open a file picker. */
.tv-only { display: none; }
body.tv .tv-only { display: flex; }
body.tv .no-tv { display: none !important; }
body.tv .empty-card { width: min(680px, 100%); }
body.tv .empty h2 { font-size: 34px; }
body.tv .empty-lead { font-size: 18px; max-width: 46ch; }
body.tv .empty-step-text b { font-size: 17px; }
body.tv .empty-step-text span { font-size: 15px; }
body.tv .empty-actions .btn { height: 56px; padding: 0 30px; font-size: 17px; }

code {
  padding: 2px 7px;
  border-radius: 5px;
  background: rgba(255, 255, 255, .09);
  font-variant-numeric: tabular-nums;
  font-size: .9em;
}

.footer {
  padding: 32px var(--pad) 52px;
  color: var(--t3);
  font-size: 12px;
  box-shadow: inset 0 1px 0 var(--hairline);
}

/* ================================================================ sheets */

.modal {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: auto;
  /* lvh = viewport with browser chrome retracted. The sheet therefore reaches
     the physical bottom edge and paints *behind* iOS's floating address bar,
     instead of stopping short and leaving a transparent strip. Content is kept
     clear of the bar with safe-area padding on the card, not by shrinking it. */
  height: 100vh;
  height: 100lvh;
  z-index: 90;
  display: grid;
  place-items: start center;
  padding: 6vh 16px 48px;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.modal-backdrop {
  position: fixed;
  /* Overhang generously: iOS paints the strip behind the address bar and the
     rubber-band overscroll region from whatever sits underneath, so a backdrop
     sized to the visible area leaves the library showing through there. */
  top: -30vh;
  bottom: -30vh;
  left: 0;
  right: 0;
  background: rgba(0, 0, 0, .6);
  -webkit-backdrop-filter: var(--blur-heavy);
  backdrop-filter: var(--blur-heavy);
  animation: fadeIn var(--d) var(--ease) both;
}
.modal-card {
  position: relative;
  width: min(860px, 100%);
  border-radius: var(--r-xl);
  overflow: hidden;
  background: var(--s2);
  box-shadow: inset 0 0 0 1px var(--hairline), var(--shadow-lg);
  animation: sheetIn var(--d) var(--ease) both;
}
.modal-card.narrow { width: min(580px, 100%); }

@keyframes fadeIn { from { opacity: 0 } }
@keyframes sheetIn {
  from { opacity: 0; transform: translateY(36px) scale(.98); }
  to { opacity: 1; transform: none; }
}

.modal-close {
  position: absolute;
  top: 16px;
  right: 16px;
  z-index: 5;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: var(--r-full);
  color: var(--t1);
  background: rgba(0, 0, 0, .5);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  transition: background var(--d-fast) var(--ease), transform var(--d-fast) var(--ease);
}
.modal-close .ico { width: 16px; height: 16px; }
.modal-close:hover { background: rgba(0, 0, 0, .8); transform: scale(1.08); }

.modal-art {
  position: relative;
  aspect-ratio: 16 / 6;
  background-color: var(--s3);
  background-size: cover;
  background-position: center 22%;
  overflow: hidden;
}
/* The wash lives on its own layer — blurring the parent would smear the
   bottom fade too and leave a visible seam against the sheet. */
.modal-art-bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center center;
}
.modal-art-bg.ambient { filter: blur(30px) saturate(130%) brightness(.6); transform: scale(1.2); }
.modal-art-fade {
  position: absolute;
  inset: 0;
  background: linear-gradient(0deg, var(--s2) 2%, rgba(20, 20, 22, .15) 62%, transparent 100%);
}

.modal-body { padding: 0 clamp(22px, 4vw, 40px) 36px; }
.modal-body h2 {
  margin: 0 0 12px;
  font-size: clamp(22px, 2.8vw, 34px);
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1.08;
  text-wrap: balance;
}

/* The poster is the anchor: a sharp card lifted over the blurred wash, with
   the title set beside it. */
.detail-head {
  position: relative;
  z-index: 3;
  display: flex;
  align-items: flex-end;
  gap: clamp(16px, 2.4vw, 26px);
  margin-top: clamp(-96px, -9vw, -70px);
  margin-bottom: 22px;
}
.detail-poster {
  flex: none;
  width: clamp(104px, 13vw, 156px);
  margin: 0;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--s3);
  box-shadow: inset 0 0 0 1px var(--hairline-strong), var(--shadow-lg);
}
.detail-poster img { width: 100%; aspect-ratio: 2 / 3; object-fit: cover; display: block; }
.detail-headline { flex: 1; min-width: 0; padding-bottom: 2px; }
.detail-headline .badges { margin-bottom: 0; }
.sub-h {
  margin: 28px 0 10px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--t3);
}

/* Collapsed by default — the actions matter more than the plot summary */
.overview-wrap { margin-bottom: 20px; }
.overview-wrap summary {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 11px 0;
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--t2);
  cursor: pointer;
  list-style: none;
  transition: color var(--d-fast) var(--ease);
}
.overview-wrap summary::-webkit-details-marker { display: none; }
.overview-wrap summary:hover, .overview-wrap[open] summary { color: var(--t1); }
.overview-wrap summary .chev {
  width: 16px;
  height: 16px;
  transition: transform var(--d-fast) var(--ease);
}
.overview-wrap[open] summary .chev { transform: rotate(90deg); }

.overview {
  margin: 2px 0 6px;
  font-size: 15px;
  line-height: 1.62;
  color: var(--t2);
  max-width: 68ch;
  animation: fadeIn var(--d-fast) var(--ease) both;
}

.badges { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 20px; }
.badge {
  padding: 4px 10px;
  border-radius: var(--r-sm);
  font-size: 11.5px;
  font-weight: 600;
  color: var(--t2);
  background: rgba(255, 255, 255, .07);
  box-shadow: inset 0 0 0 1px var(--hairline);
}
.badge.hot { color: #ffb4be; background: var(--brand-soft); box-shadow: inset 0 0 0 1px rgba(255, 55, 95, .3); }
.badge.gold { color: #f5d491; background: rgba(196, 145, 40, .18); box-shadow: inset 0 0 0 1px rgba(255, 215, 130, .3); }

.modal-actions { display: flex; gap: 9px; flex-wrap: wrap; margin-bottom: 22px; }

/* Phones get icon-only circular actions; desktop keeps icon + label */
@media (max-width: 560px) {
  .detail-head { gap: 14px; margin-top: -58px; margin-bottom: 18px; }
  .detail-poster { width: 96px; }
  .modal-body h2 { font-size: 21px; }
  .modal-actions { justify-content: flex-start; }
  .modal-actions .btn-label { display: none; }
  .modal-actions .btn {
    width: 52px;
    height: 52px;
    padding: 0;
    border-radius: var(--r-full);
  }
  .modal-actions .btn .ico { width: 22px; height: 22px; }
}

.facts {
  display: grid;
  grid-template-columns: minmax(88px, auto) 1fr;
  gap: 0;
  margin: 0;
  font-size: 14px;
}
.facts dt {
  padding: 11px 20px 11px 0;
  color: var(--t3);
  box-shadow: inset 0 -1px 0 var(--hairline);
}
.facts dd {
  margin: 0;
  padding: 11px 0;
  overflow-wrap: anywhere;
  box-shadow: inset 0 -1px 0 var(--hairline);
}

/* ============================================== settings / about sheets */

.modal-card.sheet { background: var(--s2); }

.sheet-head {
  position: sticky;
  top: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 20px clamp(20px, 4vw, 32px);
  background: rgba(20, 20, 22, .82);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  box-shadow: inset 0 -1px 0 var(--hairline);
}
.sheet-head h2 { margin: 0; font-size: 21px; font-weight: 650; letter-spacing: -.028em; }

.modal-close.static { position: static; }

/* Enough top padding that the first row clears the sticky header instead of
   scrolling underneath it. */
.sheet-body { padding: 16px clamp(20px, 4vw, 32px) 32px; }
/* Settings opens straight into a .group, which brings its own top padding. */
.sheet-body > .group:first-child { padding-top: 8px; }

.group { padding: 24px 0; box-shadow: inset 0 -1px 0 var(--hairline); }
.group:last-child { box-shadow: none; padding-bottom: 4px; }
.group-title {
  margin: 0 0 14px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--t3);
}
.small { font-size: 13px; margin: -6px 0 12px; }
.group-help {
  margin: 0 0 14px;
  font-size: 13px;
  line-height: 1.55;
  color: var(--t2);
  max-width: 56ch;
}
/* Tightened only against a heading it explains. As a blanket rule the negative
   margin dragged help text up into whatever preceded it — under the sticky
   sheet header at the top of a dialog, and into the buttons at the bottom. */
.group-title + .group-help { margin-top: -6px; }

/* Folded-away technical detail */
.tech { margin-top: 18px; }
.tech summary {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 0;
  font-size: 13px;
  font-weight: 500;
  color: var(--t2);
  cursor: pointer;
  list-style: none;
  transition: color var(--d-fast) var(--ease);
}
.tech summary::-webkit-details-marker { display: none; }
.tech summary:hover { color: var(--t1); }
.tech summary .chev {
  width: 16px;
  height: 16px;
  transition: transform var(--d-fast) var(--ease);
}
.tech[open] summary .chev { transform: rotate(90deg); }
.tech[open] summary { color: var(--t1); }
.tech .facts { font-size: 13px; }
.tech .facts dt, .tech .facts dd { padding: 9px 0; }
.tech .facts dt { padding-right: 20px; }

/* Capability rows with a status dot */
.cap-list { display: flex; flex-direction: column; gap: 2px; }
.cap {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 2px;
  box-shadow: inset 0 -1px 0 var(--hairline);
}
.cap:last-child { box-shadow: none; }
.cap-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
  background: var(--t3);
}
.cap.on .cap-dot { background: #32d74b; box-shadow: 0 0 10px rgba(50, 215, 75, .6); }
.cap.off .cap-dot { background: rgba(255, 159, 10, .9); box-shadow: 0 0 10px rgba(255, 159, 10, .45); }
.cap-name { font-size: 14px; font-weight: 500; flex: none; min-width: 116px; }
.cap-detail { font-size: 12.5px; color: var(--t3); text-align: right; flex: 1; overflow-wrap: anywhere; }

/* Storage tiles */
.stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(108px, 1fr)); gap: 8px; margin-bottom: 12px; }
.stat {
  padding: 14px;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .05);
  box-shadow: inset 0 0 0 1px var(--hairline);
}
.stat b {
  display: block;
  font-size: 20px;
  font-weight: 650;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
}
.stat span { font-size: 11.5px; color: var(--t3); }
.storage-actions { display: flex; flex-wrap: wrap; gap: 8px; }

/* Form controls inside tool panels */
.stt-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
.field { display: flex; flex-direction: column; gap: 6px; }
.field > span { font-size: 11.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--t3); }
.field select {
  height: 38px;
  padding: 0 12px;
  border: 0;
  border-radius: var(--r-sm);
  background: rgba(0, 0, 0, .5);
  box-shadow: inset 0 0 0 1px var(--hairline);
  color: var(--t1);
  font-size: 13.5px;
  outline: none;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%238e8e93'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
  background-size: 18px;
}
.field select:focus { box-shadow: inset 0 0 0 1px var(--hairline-strong); }

.about-hero { display: flex; align-items: center; gap: 16px; padding: 22px 0 4px; }
.about-name { display: block; font-size: 22px; font-weight: 650; letter-spacing: -.03em; }
.about-sub { font-size: 13px; color: var(--t2); }

/* ======================================================== library folders */

.folders { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; }
.folder-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .05);
  box-shadow: inset 0 0 0 1px var(--hairline);
  font-size: 13px;
}
.folder-row .fp { flex: 1; min-width: 0; }
.folder-row .fp b {
  display: block;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -.015em;
}
.folder-row .fp > span {
  display: block;
  margin-top: 2px;
  font-variant-numeric: tabular-nums;
  font-size: 11.5px;
  color: var(--t3);
  overflow-wrap: anywhere;
}
.folder-row .fc { color: var(--t3); font-size: 11.5px; white-space: nowrap; }
.folder-row .fx {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: var(--r-full);
  color: var(--t3);
  transition: color var(--d-fast) var(--ease), background var(--d-fast) var(--ease);
}
.folder-row .fx:hover { color: #ff6b6b; background: rgba(255, 107, 107, .14); }
.folder-row.missing { box-shadow: inset 0 0 0 1px rgba(255, 107, 107, .35); }
.folder-row.missing .fp { color: #ff9a9f; }

.folder-add { display: flex; gap: 8px; }
.folder-add input {
  flex: 1;
  min-width: 0;
  height: 40px;
  padding: 0 14px;
  border: 0;
  border-radius: var(--r-md);
  background: rgba(0, 0, 0, .45);
  box-shadow: inset 0 0 0 1px var(--hairline);
  outline: none;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  transition: box-shadow var(--d-fast) var(--ease);
}
.folder-add input:focus { box-shadow: inset 0 0 0 1px var(--hairline-strong); }
.folder-msg { margin: 10px 0 0; font-size: 12.5px; line-height: 1.55; color: var(--t2); }
.folder-msg.err { color: #ff9a9f; }
.folder-msg.ok { color: #7ee08a; }
.folder-msg.confirm {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 13px 15px;
  border-radius: var(--r-md);
  background: rgba(255, 159, 10, .1);
  box-shadow: inset 0 0 0 1px rgba(255, 159, 10, .3);
  color: var(--t1);
}
.folder-msg.confirm > span:first-child { flex: 1; min-width: 190px; color: var(--t2); }
.confirm-actions { display: flex; gap: 8px; flex: none; }

/* ================================================================= tools */

/* .tool-btn is still the pill used in Settings, the artwork picker and the
   queue sidebar; the film sheet's row of them became the More menu. */
.tool-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 36px;
  padding: 0 14px;
  border-radius: var(--r-full);
  font-size: 13px;
  font-weight: 500;
  color: var(--t1);
  background: rgba(255, 255, 255, .06);
  box-shadow: inset 0 0 0 1px var(--hairline);
  transition: background var(--d-fast) var(--ease), box-shadow var(--d-fast) var(--ease),
    opacity var(--d-fast) var(--ease), transform var(--d-fast) var(--ease);
}
.tool-btn:hover:not(:disabled) {
  background: rgba(255, 255, 255, .12);
  box-shadow: inset 0 0 0 1px var(--hairline-strong);
}
.tool-btn:active:not(:disabled) { transform: scale(.96); }
.tool-btn:disabled { opacity: .35; cursor: not-allowed; }
.tool-btn .ico { width: 16px; height: 16px; }

.tool-panel {
  margin-bottom: 24px;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: rgba(0, 0, 0, .35);
  box-shadow: inset 0 0 0 1px var(--hairline);
  animation: sheetIn var(--d-fast) var(--ease) both;
}
.tool-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 13px 16px;
  font-size: 13px;
  font-weight: 600;
  box-shadow: inset 0 -1px 0 var(--hairline);
}
.tool-x {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: var(--r-full);
  color: var(--t3);
  font-size: 12px;
}
.tool-x:hover { color: var(--t1); background: rgba(255, 255, 255, .1); }
.tool-body { padding: 14px 16px; max-height: 360px; overflow-y: auto; font-size: 13.5px; }
.tool-body .muted { line-height: 1.6; }

.res-row {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 10px 8px;
  border-radius: var(--r-sm);
  box-shadow: inset 0 -1px 0 var(--hairline);
  transition: background var(--d-fast) var(--ease);
}
.res-row:last-child { box-shadow: none; }
.res-row:hover { background: rgba(255, 255, 255, .05); }
.res-row .rn { flex: 1; min-width: 0; }
.res-row .rn b { display: block; font-weight: 600; overflow-wrap: anywhere; }
.res-row .rn span { font-size: 11.5px; color: var(--t3); }
.res-row img {
  width: 44px;
  height: 66px;
  object-fit: cover;
  border-radius: 6px;
  background: var(--s3);
  box-shadow: inset 0 0 0 1px var(--hairline);
}
.res-row .btn { height: 32px; padding: 0 15px; font-size: 12.5px; }

.urls { display: flex; flex-direction: column; gap: 8px; }
.url-chip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 15px;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .05);
  box-shadow: inset 0 0 0 1px var(--hairline);
  font-variant-numeric: tabular-nums;
  font-size: 13px;
}
.url-chip button {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .08em;
  color: var(--t3);
}
.url-chip button:hover { color: var(--t1); }

.keys { display: flex; flex-wrap: wrap; gap: 9px 20px; font-size: 12.5px; color: var(--t2); }
kbd {
  display: inline-block;
  min-width: 20px;
  padding: 2px 6px;
  margin-right: 3px;
  border-radius: 5px;
  background: rgba(255, 255, 255, .1);
  box-shadow: inset 0 0 0 1px var(--hairline-strong), 0 1px 0 rgba(0, 0, 0, .4);
  font-family: inherit;
  font-size: 11px;
  font-weight: 600;
  text-align: center;
}

/* ================================================================ player */

.player {
  position: fixed;
  inset: 0;
  z-index: 120;
  background: #000;
  /* dvh tracks the collapsing URL bar on mobile; vh does not, which leaves the
     control bar stranded under the browser chrome. */
  height: 100vh;
  height: 100dvh;
}
#video { width: 100%; height: 100%; object-fit: contain; background: #000; }
video::cue {
  font-size: clamp(17px, 2.3vw, 30px);
  line-height: 1.38;
  background: rgba(0, 0, 0, .58);
  font-family: inherit;
}

.player.hide-ui { cursor: none; }
.player.hide-ui .controls { opacity: 0; pointer-events: none; }

.controls {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  opacity: 1;
  transition: opacity var(--d) var(--ease);
  background:
    linear-gradient(180deg, rgba(0, 0, 0, .6), transparent 16%),
    linear-gradient(0deg, rgba(0, 0, 0, .85), transparent 32%);
}

.ctl-top {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 18px clamp(14px, 3vw, 34px);
  padding-top: max(18px, env(safe-area-inset-top));
  padding-left: max(clamp(14px, 3vw, 34px), env(safe-area-inset-left));
  padding-right: max(clamp(14px, 3vw, 34px), env(safe-area-inset-right));
}
.ctl-title { min-width: 0; }
.ctl-title strong {
  display: block;
  font-size: clamp(16px, 1.9vw, 21px);
  font-weight: 650;
  letter-spacing: -.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ctl-title span { font-size: 12.5px; color: var(--t2); }

.ctl-bottom {
  padding: 0 clamp(14px, 3vw, 34px) clamp(18px, 2.4vw, 30px);
  padding-bottom: max(clamp(18px, 2.4vw, 30px), env(safe-area-inset-bottom));
  padding-left: max(clamp(14px, 3vw, 34px), env(safe-area-inset-left));
  padding-right: max(clamp(14px, 3vw, 34px), env(safe-area-inset-right));
}

.scrub { position: relative; padding: 12px 0; cursor: pointer; touch-action: none; }
.scrub-track {
  position: relative;
  height: 4px;
  border-radius: var(--r-full);
  background: rgba(255, 255, 255, .24);
  transition: height var(--d-fast) var(--ease);
}
.scrub:hover .scrub-track { height: 7px; }
.scrub-buffer {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  border-radius: var(--r-full);
  background: rgba(255, 255, 255, .38);
}
.scrub-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  border-radius: var(--r-full);
  background: linear-gradient(90deg, var(--brand-1), var(--brand-2));
}
.scrub-knob {
  position: absolute;
  right: -7px;
  top: 50%;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .5);
  transform: translateY(-50%) scale(0);
  transition: transform var(--d-fast) var(--ease);
}
.scrub:hover .scrub-knob { transform: translateY(-50%) scale(1); }
.scrub-tip {
  position: absolute;
  bottom: 30px;
  transform: translateX(-50%);
  padding: 5px 10px;
  border-radius: var(--r-sm);
  background: rgba(0, 0, 0, .82);
  box-shadow: inset 0 0 0 1px var(--hairline);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
  white-space: nowrap;
}

.ctl-row { display: flex; align-items: center; gap: clamp(2px, .8vw, 8px); }
.spacer { flex: 1; }
.time {
  padding: 0 8px;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--t2);
  white-space: nowrap;
}

.vol { display: flex; align-items: center; }
.vol-range {
  width: 0;
  opacity: 0;
  height: 4px;
  border-radius: var(--r-full);
  background: rgba(255, 255, 255, .3);
  outline: none;
  -webkit-appearance: none;
  appearance: none;
  transition: width var(--d) var(--ease), opacity var(--d) var(--ease), margin var(--d) var(--ease);
}
.vol:hover .vol-range, .vol-range:focus { width: 86px; opacity: 1; margin: 0 10px 0 4px; }
.vol-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #fff;
  cursor: pointer;
}
.vol-range::-moz-range-thumb {
  width: 12px;
  height: 12px;
  border: 0;
  border-radius: 50%;
  background: #fff;
  cursor: pointer;
}

.menu-wrap { position: relative; }
.menu {
  position: absolute;
  bottom: calc(100% + 14px);
  right: 0;
  min-width: 208px;
  max-height: 48vh;
  overflow-y: auto;
  padding: 6px;
  border-radius: var(--r-lg);
  background: rgba(22, 22, 25, .82);
  box-shadow: inset 0 0 0 1px var(--hairline), var(--shadow-md);
  -webkit-backdrop-filter: var(--blur-heavy);
  backdrop-filter: var(--blur-heavy);
  animation: sheetIn var(--d-fast) var(--ease) both;
}
.menu-title {
  padding: 8px 11px 6px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--t3);
}
.menu button {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 9px 11px;
  border-radius: var(--r-sm);
  text-align: left;
  font-size: 13.5px;
  color: var(--t2);
  transition: background var(--d-fast) var(--ease), color var(--d-fast) var(--ease);
}
.menu button:hover { background: rgba(255, 255, 255, .09); color: var(--t1); }
.menu button.sel { color: var(--t1); font-weight: 600; }
.menu button .tick { width: 13px; color: var(--brand); }

.player-loading { position: absolute; inset: 0; display: grid; place-items: center; pointer-events: none; }
.spinner {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  border: 3px solid rgba(255, 255, 255, .16);
  border-top-color: var(--brand);
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg) } }

.player-error {
  position: absolute;
  inset: 0;
  z-index: 8;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 4px;
  padding: 32px;
  text-align: center;
  background: rgba(0, 0, 0, .8);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
}
.player-error h3 { margin: 0; font-size: clamp(19px, 2.6vw, 26px); font-weight: 650; letter-spacing: -.025em; }
.player-error p { margin: 6px 0 18px; max-width: 520px; line-height: 1.6; color: var(--t2); }
.err-actions { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; }

.toast {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: 34px;
  z-index: 200;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 18px;
  border-radius: var(--r-full);
  background: rgba(22, 22, 25, .84);
  box-shadow: inset 0 0 0 1px var(--hairline), var(--shadow-md);
  -webkit-backdrop-filter: var(--blur-heavy);
  backdrop-filter: var(--blur-heavy);
  font-size: 13.5px;
  animation: toastIn var(--d) var(--ease) both;
}



@keyframes toastIn {
  from { opacity: 0; transform: translate(-50%, 16px); }
  to { opacity: 1; transform: translate(-50%, 0); }
}

/* =========================================================== responsive */

@media (max-width: 820px) {
  /* Films and music are the two halves of the app, so the switch between them
     has to survive down to phone widths. The two secondary film views are
     reachable from Home, so they're what gets dropped instead. */
  .nav-links button[data-nav="continue"] { display: none; }
  .nav-links button { padding: 6px 13px; font-size: 13px; }
  .hero { min-height: 74vh; }
  .hero-synopsis { -webkit-line-clamp: 2; font-size: 15px; }
}

/* Narrow bar: the wordmark, a full-width search and three 44px targets can't
   coexist, so drop to the mark alone and let search take the slack. */
@media (max-width: 700px) {
  #topbar { gap: 10px; }
  .brand-text { display: none; }
  .bar-left { flex: none; }
  .bar-right { flex: 1; justify-content: flex-end; gap: 0; }
  .search { flex: 1; min-width: 0; max-width: 100%; }
  #search { width: 100%; min-width: 0; }
}

/* Phone bar: the Home/Songs switch, a search field and two 44px targets can't
   all be full size at once. Search collapses to its icon and takes the bar over
   only while it's focused — the tabs come back the moment you leave it. */
@media (max-width: 560px) {
  .nav-links { padding: 2px; }
  .nav-links button { padding: 6px 11px; font-size: 12.5px; }

  .search { flex: 0 0 auto; width: 44px; padding: 0 13px; overflow: hidden; cursor: text; }
  .search .ico { flex: none; }
  #search { width: 0; min-width: 0; }

  body.searching .nav-links { display: none; }
  body.searching .search { flex: 1 1 auto; width: auto; }
  body.searching #search { width: 100%; }
}

@media (max-width: 560px) {
  .time { font-size: 11px; padding: 0 4px; }
  .vol, #pl-pip { display: none; }
  .hero-file { display: none; }
  .btn { height: 46px; padding: 0 22px; font-size: 15px; }
  .facts { grid-template-columns: 1fr; }
  .facts dt { padding-bottom: 0; box-shadow: none; }
  .facts dd { padding-top: 2px; }
}

/* ===================================================== touch refinements */

@media (pointer: coarse) {
  /* iOS zooms the whole page whenever a focused control has a font smaller
     than 16px. Every text field and select has to clear that bar. */
  input, select, textarea, #search, .folder-add input, .field select {
    font-size: 16px;
  }
  .search { height: 40px; padding: 0 14px; }
  #search::placeholder { font-size: 15px; }
  .folder-add input, .field select { font-size: 16px; }

  /* Denser backdrop: a 60% scrim still shows the library through it, which
     reads as a glitch on a small screen. */
  .modal-backdrop { background: rgba(0, 0, 0, .86); }

  /* HIG minimum target is 44pt — the desktop 36px buttons are too small to
     hit reliably while holding a phone. */
  .icon-btn { width: 44px; height: 44px; }
  .icon-btn.big { width: 52px; height: 52px; }
  .icon-btn.big .ico { width: 32px; height: 32px; }
  .tool-btn { height: 44px; padding: 0 16px; }
  .folder-add input, .field select { height: 44px; }
  .folder-row .fx { width: 34px; height: 34px; }
  .modal-close { width: 40px; height: 40px; }

  /* A thin bar is hard to grab; enlarge the hit area without changing looks */
  .scrub { padding: 16px 0; }
  .scrub-track { height: 6px; }
  .scrub-knob { transform: translateY(-50%) scale(1); width: 16px; height: 16px; right: -8px; }

  .ctl-row { gap: 4px; }
  .controls { background: linear-gradient(180deg, rgba(0,0,0,.7), transparent 22%), linear-gradient(0deg, rgba(0,0,0,.9), transparent 40%); }

  /* Cards are tap-to-open; the hover overlay would just cover the artwork */
  .card-hover { opacity: 0; }
  .card-progress { bottom: 6px; }
}

/* Phone sheets behave like native bottom sheets */
@media (max-width: 820px) {
  .modal { padding: 0; display: block; overflow: visible; }
  /* Pinned to the true bottom edge. Fixed positioning tracks the visual
     viewport on iOS, so the sheet stays welded to the bottom as the address
     bar shows and hides — no gap, no drift. */
  .modal-card {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    max-height: calc(100dvh - 20px);
    display: flex;
    flex-direction: column;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    animation: sheetUp var(--d) var(--ease) both;
  }
  /* One scroll container for artwork + content, so the poster's negative
     offset isn't clipped by a scrolling body. */
  .modal-scroll {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
  }
  .modal-body { overflow: visible; }
  .modal-card::before {
    content: '';
    position: absolute;
    top: 8px;
    left: 50%;
    transform: translateX(-50%);
    width: 38px;
    height: 4px;
    border-radius: 2px;
    background: rgba(255, 255, 255, .28);
    z-index: 6;
  }
  .sheet-head { padding-top: 26px; }
  .modal-art { flex: none; aspect-ratio: 16 / 7; }
  /* Scrolling happens inside the sheet, never on the page behind it */
  .sheet-body, .modal-scroll {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
  }
  /* Last item clears the home indicator and the floating address bar */
  .modal-body, .sheet-body {
    padding-bottom: calc(env(safe-area-inset-bottom) + 44px);
  }
  @keyframes sheetUp {
    from { transform: translateY(100%); }
    to { transform: none; }
  }
  #topbar {
    padding-top: env(safe-area-inset-top);
    height: calc(var(--nav-h) + env(safe-area-inset-top));
  }
  body.no-hero #rows { margin-top: calc(var(--nav-h) + env(safe-area-inset-top) + 24px); }
  .footer { padding-bottom: max(52px, env(safe-area-inset-bottom)); }
}

/* Honour the OS setting — kill transforms and long transitions, keep opacity */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .card:hover, .card:focus-visible { transform: none; }
  .hero-art { transform: none; }
}

/* ==========================================================================
   Songs
   A second library beside the films. Square artwork instead of 2:3 posters,
   and a docked player that keeps going while you browse — music shouldn't
   take over the screen the way a film does.
   ========================================================================== */

.music { padding-top: clamp(84px, 11vh, 128px); }

.music-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  padding: 0 var(--pad) clamp(26px, 3.4vw, 44px);
}
.music-headline h1 {
  margin: 6px 0 0;
  font-size: clamp(34px, 5vw, 62px);
  font-weight: 700;
  letter-spacing: -.038em;
  line-height: 1.02;
}
.music-kicker {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 12px;
  font-weight: 650;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--t2);
}
.music-kicker .dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--brand-1), var(--brand-2));
}
.music-sub {
  margin: 10px 0 0;
  font-size: 14.5px;
  color: var(--t3);
  letter-spacing: -.01em;
}
.music-head-actions { flex: none; padding-bottom: 4px; }

/* ------------------------------------------------------------- album grid */

.agrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(clamp(132px, 14vw, 178px), 1fr));
  gap: clamp(16px, 1.8vw, 26px);
  padding: 0 var(--pad);
}

.acard {
  cursor: pointer;
  border-radius: var(--r-md);
  transition: transform var(--d) var(--ease);
  will-change: transform;
}
.acard:hover, .acard:focus-visible { transform: translateY(-6px); }
.acard:focus-visible { outline: none; }
.acard:active { transform: translateY(-2px) scale(.99); }

.acard-art {
  position: relative;
  aspect-ratio: 1;
  border-radius: var(--r-md);
  overflow: hidden;
  background-color: var(--s2);
  background-size: cover;
  background-position: center;
  display: grid;
  place-items: center;
  box-shadow: inset 0 0 0 1px var(--hairline), var(--shadow-sm);
  transition: box-shadow var(--d) var(--ease);
}
.acard:hover .acard-art,
.acard:focus-visible .acard-art {
  box-shadow: inset 0 0 0 1px var(--hairline-strong), var(--shadow-md);
}
.acard:focus-visible .acard-art {
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .9), var(--shadow-md);
}

/* Stand-in for a missing cover: the album's own initial over its colour card. */
.acard-initial {
  font-size: clamp(34px, 4vw, 52px);
  font-weight: 700;
  letter-spacing: -.04em;
  color: rgba(255, 255, 255, .3);
}

.acard-play {
  position: absolute;
  right: 10px;
  bottom: 10px;
  width: 42px;
  height: 42px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(20, 20, 22, .72);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  box-shadow: inset 0 0 0 1px var(--hairline-strong), var(--shadow-sm);
  color: var(--t1);
  opacity: 0;
  transform: translateY(8px) scale(.9);
  transition: opacity var(--d-fast) var(--ease), transform var(--d-fast) var(--ease);
}
.acard-play .ico { width: 20px; height: 20px; margin-left: 2px; }
.acard:hover .acard-play,
.acard:focus-visible .acard-play { opacity: 1; transform: none; }
.acard-play:hover { background: var(--brand); }

.acard-name {
  margin-top: 11px;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -.015em;
  line-height: 1.3;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.acard-sub {
  margin-top: 3px;
  font-size: 12.5px;
  color: var(--t3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}


/* ------------------------------------------------------- internal tabs */

.mtabs {
  display: flex;
  gap: 4px;
  margin: 0 var(--pad) clamp(20px, 2.6vw, 34px);
  padding: 4px;
  border-radius: var(--r-full);
  background: rgba(255, 255, 255, .05);
  box-shadow: inset 0 0 0 1px var(--hairline);
  width: fit-content;
  max-width: calc(100% - var(--pad) * 2);
  overflow-x: auto;
  scrollbar-width: none;
}
.mtabs::-webkit-scrollbar { display: none; }
.mtabs button {
  flex: none;
  padding: 8px 18px;
  border-radius: var(--r-full);
  font-size: 13.5px;
  font-weight: 500;
  color: var(--t2);
  white-space: nowrap;
  transition: color var(--d-fast) var(--ease), background var(--d-fast) var(--ease);
}
.mtabs button:hover { color: var(--t1); }
.mtabs button.active {
  color: var(--t1);
  font-weight: 600;
  background: rgba(255, 255, 255, .13);
  box-shadow: var(--shadow-sm);
}
.mtabs button:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .85);
}

/* -------------------------------------------------------------- tracklist */

.tracklist {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* All Songs: four rows deep, flowing into columns and scrolling sideways.
   `--tcols` is how many columns fit the viewport, so a column is always an
   exact fraction of the width and never lands half-visible at the edge. */
.tgrid {
  --tcols: 3;
  --tgap: 20px;
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: repeat(10, auto);
  grid-auto-columns: calc((100% - (var(--tcols) - 1) * var(--tgap)) / var(--tcols));
  gap: 2px var(--tgap);
  overflow-x: auto;
  scroll-behavior: smooth;
  scroll-snap-type: x proximity;
  scroll-padding-left: var(--pad);
  padding: 0 var(--pad);
  scrollbar-width: none;
  overscroll-behavior-x: contain;
}
.tgrid::-webkit-scrollbar { display: none; }
.tgrid > .trow { scroll-snap-align: start; }

/* Columns are separated by the grid gap, so the row hairline would draw a
   line under the last row of every column. Only draw between rows. */
.tgrid > .trow { box-shadow: none; }
.tgrid > .trow:not(:nth-child(10n + 1)) { box-shadow: inset 0 1px 0 var(--hairline); }
.tgrid > .trow:hover { box-shadow: none; }

/* Ten rows deep needs a tighter row than a standalone list, or a single
   column runs past the bottom of the screen and the page scrolls again. */
.tgrid > .trow { padding: 6px 12px; }
.tgrid .trow-title { font-size: 14px; }
.tgrid .trow-sub { margin-top: 1px; font-size: 12px; }
.tgrid .trow-art { width: 38px; height: 38px; }
.tgrid > .trow.has-art { grid-template-columns: 24px 38px 1fr auto; gap: 11px; }

@media (min-width: 1700px) { .tgrid { --tcols: 4; } }
@media (max-width: 1180px) { .tgrid { --tcols: 2; } }
@media (max-width: 760px)  { .tgrid { --tcols: 1.12; --tgap: 14px; } }

.trow {
  display: grid;
  grid-template-columns: 34px 1fr auto;
  align-items: center;
  gap: 14px;
  padding: 10px 12px;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: background var(--d-fast) var(--ease);
}
.trow + .trow { box-shadow: inset 0 1px 0 var(--hairline); }
.trow:hover { background: rgba(255, 255, 255, .06); box-shadow: none; }
.trow:hover + .trow { box-shadow: none; }
.trow:focus-visible {
  outline: none;
  background: rgba(255, 255, 255, .09);
  box-shadow: inset 0 0 0 1px var(--hairline-strong);
}
.trow.on .trow-title { color: var(--brand); }

.trow-n {
  font-size: 13px;
  color: var(--t3);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Cover thumbnail in the song lists. Album rows omit it — the sheet's own
   header already shows that artwork at full size. */
.trow.has-art { grid-template-columns: 26px 40px 1fr auto; gap: 12px; }
.trow-art {
  width: 40px;
  height: 40px;
  border-radius: 9px;
  background-color: var(--s3);
  background-size: cover;
  background-position: center;
  box-shadow: inset 0 0 0 1px var(--hairline);
}
.trow-main { min-width: 0; }
.trow-title {
  display: block;
  font-size: 14.5px;
  font-weight: 550;
  letter-spacing: -.015em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.trow-sub {
  display: block;
  margin-top: 2px;
  font-size: 12.5px;
  color: var(--t3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.trow-dur {
  font-size: 12.5px;
  color: var(--t3);
  font-variant-numeric: tabular-nums;
}

/* Three bars standing in for the track number on whatever is playing. */
.eq {
  display: inline-flex;
  align-items: flex-end;
  gap: 2px;
  height: 13px;
}
.eq i {
  width: 3px;
  border-radius: 1px;
  background: var(--brand);
  animation: eq 1s var(--ease) infinite alternate;
}
.eq i:nth-child(1) { height: 60%; animation-delay: -.4s; }
.eq i:nth-child(2) { height: 100%; animation-delay: -.15s; }
.eq i:nth-child(3) { height: 40%; }
@keyframes eq { from { height: 25%; } to { height: 100%; } }

/* Album sheet reuses the film sheet; only the artwork is square. */
/* ============================================================ album page */

/* The film sheet is sized for a poster and a paragraph of plot. A record is a
   two-column tracklist plus its specs, and 860px squeezed both. */
.modal-card.wide { width: min(1180px, 100%); }

.detail-poster.square { aspect-ratio: 1; border-radius: var(--r-lg); }
.detail-poster.square img { aspect-ratio: 1; object-fit: cover; }

/* The film sheet's 16:6 banner becomes 430px of blurred nothing once the card
   is 1180px wide. Fix the album's header to a height instead of an aspect
   ratio, and let the cover overlap further into it. */
#album .modal-art { aspect-ratio: auto; height: clamp(170px, 21vw, 250px); }
#album .detail-head { margin-top: clamp(-124px, -12vw, -92px); margin-bottom: 20px; }

.album-head { align-items: flex-end; }
.album-head .detail-poster {
  width: clamp(132px, 17vw, 216px);
  box-shadow: inset 0 0 0 1px var(--hairline-strong), var(--shadow-lg);
}
.album-kicker {
  margin: 0 0 8px;
  font-size: 11.5px;
  font-weight: 650;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--t3);
}
.album-artist {
  margin: 0 0 12px;
  font-size: clamp(15px, 1.5vw, 18px);
  font-weight: 550;
  letter-spacing: -.015em;
  color: var(--t2);
}
#album .modal-body h2 { margin-bottom: 8px; }

/* Specs as a quiet grid — present when wanted, never competing with the
   tracklist for attention. */
.album-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(126px, 1fr));
  gap: 2px 24px;
  margin: 0 0 26px;
  padding: 18px 0;
  box-shadow: inset 0 1px 0 var(--hairline), inset 0 -1px 0 var(--hairline);
}
.album-stats dt {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--t3);
}
.album-stats dd {
  margin: 4px 0 0;
  font-size: 14px;
  font-weight: 550;
  letter-spacing: -.01em;
  color: var(--t1);
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Two columns of tracks on a wide sheet: 25 songs in one column is a very
   long scroll for what is really a short list. A column header is deliberately
   absent — it can only line up with one of the two columns. */
.tracklist.cols { columns: 2; column-gap: 34px; }
.tracklist.cols > .trow { break-inside: avoid; }
@media (max-width: 900px) { .tracklist.cols { columns: 1; } }

.trow.has-act { grid-template-columns: 34px 1fr auto 72px; }

.trow-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 2px;
  opacity: 0;
  transition: opacity var(--d-fast) var(--ease);
}
.trow:hover .trow-actions,
.trow:focus-visible .trow-actions,
.trow-actions:focus-within { opacity: 1; }

.trow-act {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: var(--r-full);
  color: var(--t2);
  transition: background var(--d-fast) var(--ease), color var(--d-fast) var(--ease);
}
.trow-act .ico { width: 16px; height: 16px; }
.trow-act:hover { background: rgba(255, 255, 255, .14); color: var(--t1); }
.trow-act:focus-visible {
  outline: none;
  background: rgba(255, 255, 255, .16);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .6);
  opacity: 1;
}

/* Touch has no hover, so the controls stay put rather than never appearing. */
@media (hover: none) {
  .trow-actions { opacity: 1; }
}

@media (max-width: 560px) {
  .album-head .detail-poster { width: 118px; }
  .album-stats { grid-template-columns: repeat(auto-fit, minmax(104px, 1fr)); gap: 2px 16px; }
  .trow.has-act { grid-template-columns: 26px 1fr auto 68px; }
}

/* ================================================================ now playing */

.np {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 80;
  background: rgba(14, 14, 16, .82);
  backdrop-filter: var(--blur-heavy);
  -webkit-backdrop-filter: var(--blur-heavy);
  box-shadow: inset 0 1px 0 var(--hairline-strong), 0 -18px 50px rgba(0, 0, 0, .55);
  padding-bottom: env(safe-area-inset-bottom);
  animation: np-in var(--d) var(--ease-out);
}
@keyframes np-in { from { transform: translateY(100%); } to { transform: none; } }

/* Content has to clear the bar, or the last row sits underneath it. */
body.np-open #footer { padding-bottom: 92px; }
body.np-open #rows, body.np-open #music { padding-bottom: 24px; }

.np-scrub {
  position: relative;
  height: 16px;
  margin-top: -8px;
  cursor: pointer;
  touch-action: none;
}
.np-track {
  position: absolute;
  left: 0;
  right: 0;
  top: 7px;
  height: 3px;
  background: rgba(255, 255, 255, .14);
  overflow: hidden;
  transition: height var(--d-fast) var(--ease), top var(--d-fast) var(--ease);
}
.np-scrub:hover .np-track { height: 6px; top: 5px; }
.np-buffer {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  background: rgba(255, 255, 255, .18);
}
.np-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  background: linear-gradient(90deg, var(--brand-1), var(--brand-2));
}
.np-knob {
  position: absolute;
  right: -5px;
  top: 50%;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #fff;
  transform: translateY(-50%) scale(0);
  transition: transform var(--d-fast) var(--ease);
}
.np-scrub:hover .np-knob { transform: translateY(-50%) scale(1); }

.np-inner {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px var(--pad) 12px;
  min-height: 72px;
}

.np-cover {
  flex: none;
  position: relative;
  width: 50px;
  height: 50px;
  padding: 0;
  border: 0;
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--s2);
  cursor: pointer;
  box-shadow: inset 0 0 0 1px var(--hairline), var(--shadow-sm);
}
.np-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.np-cover-fallback {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: rgba(255, 255, 255, .5);
}
.np-cover-fallback .ico { width: 22px; height: 22px; }
.np-cover:focus-visible { outline: none; box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .9); }

.np-meta { min-width: 0; flex: 1 1 auto; }
.np-meta strong {
  display: block;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -.017em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.np-meta span {
  display: block;
  margin-top: 2px;
  font-size: 12.5px;
  color: var(--t3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.np-transport { display: flex; align-items: center; gap: 4px; flex: none; }
.np-transport .icon-btn[disabled] { opacity: .3; pointer-events: none; }

.np-time {
  flex: none;
  display: flex;
  gap: 5px;
  font-size: 12.5px;
  color: var(--t3);
  font-variant-numeric: tabular-nums;
}
.np-time i { font-style: normal; opacity: .5; }

.np-extras { display: flex; align-items: center; gap: 4px; flex: none; }

/* Film mode: the bar is a "carry on watching" strip, not a transport. Only the
   resume button, the elapsed marker and dismiss make sense. */
.np[data-kind="film"] #np-prev,
.np[data-kind="film"] #np-next,
.np[data-kind="film"] .np-time,
.np[data-kind="film"] #np-shuffle,
.np[data-kind="film"] #np-repeat,
.np[data-kind="film"] .np-vol,
.np[data-kind="film"] #np-queue-btn { display: none; }
.np[data-kind="film"] .np-scrub { pointer-events: none; }
/* A 2:3 poster instead of a square sleeve, sized to fit the bar's content
   box rather than spill past it. */
.np[data-kind="film"] .np-cover { width: 34px; height: 50px; border-radius: 6px; }
.np[data-kind="film"] .np-cover img { object-fit: cover; }
.np[data-kind="film"] #np-play {
  background: #fff;
  color: #000;
  border-radius: var(--r-full);
  width: auto;
  min-width: 44px;
  padding: 0 16px;
  gap: 7px;
}
.np[data-kind="film"] #np-play::after {
  content: 'Resume';
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: -.01em;
}
@media (max-width: 560px) {
  .np[data-kind="film"] #np-play::after { display: none; }
  .np[data-kind="film"] #np-play { padding: 0; min-width: 0; }
}
.np-repeat-wrap { position: relative; }
#np-repeat { position: relative; }
#np-repeat .np-one {
  position: absolute;
  right: 3px;
  bottom: 3px;
  font-size: 9px;
  font-weight: 700;
  line-height: 1;
  color: var(--brand);
}

.np-vol { display: flex; align-items: center; gap: 6px; }
/* The film player reveals its slider on hover; a persistent bar has room to
   just show it, and hunting for a hover target on a docked bar is worse. */
.np-vol .vol-range { width: 78px; opacity: 1; margin: 0 4px 0 2px; }

/* --------------------------------------------------------------- queue */

.np-queue {
  position: absolute;
  right: var(--pad);
  bottom: calc(100% + 10px);
  width: min(360px, calc(100vw - var(--pad) * 2));
  max-height: min(60vh, 460px);
  display: flex;
  flex-direction: column;
  border-radius: var(--r-lg);
  background: rgba(22, 22, 25, .92);
  backdrop-filter: var(--blur-heavy);
  -webkit-backdrop-filter: var(--blur-heavy);
  box-shadow: inset 0 0 0 1px var(--hairline-strong), var(--shadow-lg);
  overflow: hidden;
}
.np-queue-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  box-shadow: inset 0 -1px 0 var(--hairline);
  font-size: 13px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--t2);
}
.np-queue ol {
  list-style: none;
  margin: 0;
  padding: 6px;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.qrow {
  display: grid;
  grid-template-columns: 22px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 9px 10px;
  border-radius: var(--r-sm);
  cursor: pointer;
  font-size: 13.5px;
}
.qrow:hover { background: rgba(255, 255, 255, .07); }
.qrow:focus-visible { outline: none; background: rgba(255, 255, 255, .1); }
.qrow.done { opacity: .45; }
.qrow.on .qrow-title { color: var(--brand); font-weight: 600; }
.qrow-n { font-size: 11.5px; color: var(--t3); text-align: right; font-variant-numeric: tabular-nums; }
.qrow-main { min-width: 0; }
.qrow-title { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qrow-sub { display: block; font-size: 11.5px; color: var(--t3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qrow-dur { font-size: 11.5px; color: var(--t3); font-variant-numeric: tabular-nums; }

/* -------------------------------------------------------------- responsive */

@media (max-width: 860px) {
  /* Title and Shuffle share a line — see the mobile block below. */

  /* On a phone the bar keeps artwork, title and transport — everything else
     is a desktop nicety and would crowd the row. */
  .np-time, .np-vol, #np-queue-btn, #np-shuffle, #np-repeat { display: none; }
  .np-inner { gap: 10px; padding: 8px 14px 10px; min-height: 62px; }
  .np-cover { width: 44px; height: 44px; }
  .np-transport { gap: 0; }
  body.np-open #footer { padding-bottom: 78px; }
  .np-queue { right: 10px; left: 10px; width: auto; }
}

@media (max-width: 520px) {
  .trow { grid-template-columns: 26px 1fr auto; gap: 10px; padding: 9px 6px; }
  .trow.has-art { grid-template-columns: 20px 38px 1fr auto; gap: 10px; }
  .agrid { grid-template-columns: repeat(auto-fill, minmax(126px, 1fr)); gap: 14px; }
}

/* -------------------------------------------------------------------- TV */

body.tv .music { padding-top: clamp(96px, 12vh, 150px); }
body.tv .agrid { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); }
body.tv .acard:focus-visible { transform: translateY(-10px) scale(1.05); }
body.tv .trow { padding: 14px 16px; font-size: 17px; }
body.tv .trow-title { font-size: 17px; }
body.tv .trow:focus-visible { background: rgba(255, 255, 255, .12); }
body.tv .acard { scroll-margin-block: 110px; }
body.tv .trow { scroll-margin-block: 110px; }

body.tv .mtabs { margin-bottom: 30px; }
body.tv .mtabs button { padding: 12px 26px; font-size: 17px; }
/* Fewer, wider columns: a track row has to stay readable from the sofa. */
body.tv .tgrid { --tcols: 2; grid-template-rows: repeat(4, auto); }
@media (min-width: 1500px) { body.tv .tgrid { --tcols: 3; } }
body.tv .np-inner { min-height: 84px; }
body.tv .np-cover { width: 60px; height: 60px; }

/* ==========================================================================
   Playlists, reorderable lists and the Up Next sidebar
   ========================================================================== */

/* Playlist tiles borrow artwork from the records their songs came from. */
.pl-tile { position: absolute; inset: 0; }
.pl-tile.one { background-size: cover; background-position: center; }
.pl-tile.four {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
}
.pl-tile.four i { background-size: cover; background-position: center; }

.acard.newpl .acard-art { background: rgba(255, 255, 255, .05); }
.newpl-art {
  display: grid;
  place-items: center;
  gap: 8px;
  color: var(--t2);
  box-shadow: inset 0 0 0 1px var(--hairline-strong) !important;
}
.newpl-art .ico { width: 34px; height: 34px; }
.newpl-art span { font-size: 12.5px; font-weight: 600; letter-spacing: -.01em; }
.acard.newpl:hover .newpl-art,
.acard.newpl:focus-visible .newpl-art { color: var(--t1); background: rgba(255, 255, 255, .1); }

.btn.danger:hover { background: rgba(255, 69, 92, .18); color: #ff8a9c; }

.reorder-hint {
  margin: 0 0 10px;
  font-size: 12px;
  color: var(--t3);
}

/* --------------------------------------------------------- reorder rows */

.tracklist.reorder, .qlist { display: block; }

.rrow {
  grid-template-columns: 22px 26px 38px 1fr auto auto;
  gap: 11px;
  cursor: pointer;
  background: transparent;
}
.rrow.done { opacity: .5; }
.rrow.dragging { opacity: .35; }

.rgrip {
  display: grid;
  place-items: center;
  color: var(--t3);
  cursor: grab;
}
.rgrip .ico { width: 16px; height: 16px; }
.rrow:active .rgrip { cursor: grabbing; }

/* Where the dragged row would land. */
.rrow.drop-before { box-shadow: inset 0 2px 0 var(--brand); }
.rrow.drop-after { box-shadow: inset 0 -2px 0 var(--brand); }

.rrow .trow-actions { gap: 0; }
.rrow .trow-act[disabled] { opacity: .25; pointer-events: none; }

/* The playlist sheet's rows are wide; the sidebar's are not. The position
   number goes — in a queue the order is the point, and the digits cost more
   width than they earn once titles start truncating. */
.qlist .rrow { grid-template-columns: 18px 36px 1fr auto; gap: 10px; padding: 8px 10px; }
.qlist .rrow .trow-n { display: none; }
.qlist .rrow .trow-art { width: 36px; height: 36px; border-radius: 8px; }
.qlist .rrow .trow-dur { display: none; }

/* ------------------------------------------------------------- sidebar */

.qside {
  --qside-w: 420px;
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 86;
  width: min(var(--qside-w), 100%);
  display: flex;
  flex-direction: column;
  background: rgba(16, 16, 19, .94);
  backdrop-filter: var(--blur-heavy);
  -webkit-backdrop-filter: var(--blur-heavy);
  box-shadow: inset 1px 0 0 var(--hairline-strong), -24px 0 60px rgba(0, 0, 0, .5);
  transform: translateX(100%);
  visibility: hidden;
  transition: transform var(--d) var(--ease), visibility var(--d) var(--ease);
  padding-top: env(safe-area-inset-top);
}
body.queue-open .qside { transform: none; visibility: visible; }
/* Stop above the player bar rather than running underneath it. --np-h is
   measured from the bar itself, which changes height across breakpoints. */
body.np-open .qside { bottom: var(--np-h, 0px); }

.qside-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 18px 16px 14px;
  box-shadow: inset 0 -1px 0 var(--hairline);
}
.qside-title strong {
  display: block;
  font-size: 15px;
  font-weight: 650;
  letter-spacing: -.02em;
}
.qside-title span {
  display: block;
  margin-top: 2px;
  font-size: 12px;
  color: var(--t3);
}
/* --- what's playing, pinned under the header --- */

.qnow {
  flex: none;
  padding: 14px 16px 12px;
  background: linear-gradient(180deg, rgba(255, 106, 61, .12), transparent);
  box-shadow: inset 0 -1px 0 var(--hairline);
}
.qnow-label {
  display: block;
  margin-bottom: 9px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--brand);
}
.qnow-row { display: flex; align-items: center; gap: 12px; }
.qnow-art {
  position: relative;
  flex: none;
  width: 52px;
  height: 52px;
  border-radius: 10px;
  background-size: cover;
  background-position: center;
  background-color: var(--s3);
  box-shadow: inset 0 0 0 1px var(--hairline-strong);
  display: grid;
  place-items: center;
}
/* Bars sit over the artwork, so they need something to read against. */
.qnow-art::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: rgba(0, 0, 0, .45);
}
.qnow-art .eq { position: relative; z-index: 1; }
.eq.lg { height: 18px; gap: 3px; }
.eq.lg i { width: 3.5px; background: #fff; }

.qnow-main { min-width: 0; }
.qnow-main strong {
  display: block;
  font-size: 14.5px;
  font-weight: 650;
  letter-spacing: -.018em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.qnow-main span {
  display: block;
  margin-top: 2px;
  font-size: 12.5px;
  color: var(--t3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.qnow-bar {
  margin-top: 12px;
  height: 3px;
  border-radius: 2px;
  background: rgba(255, 255, 255, .14);
  overflow: hidden;
}
.qnow-bar i {
  display: block;
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, var(--brand-1), var(--brand-2));
}

/* --- section dividers --- */

.qsec {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin: 4px 0 2px;
  padding: 10px 8px 8px;
  background: rgba(16, 16, 19, .96);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--t3);
}
.qsec + .qsec { margin-top: 10px; }
.qsec-n { font-weight: 600; letter-spacing: .04em; opacity: .8; }

.qempty { margin: 14px 10px; font-size: 13px; color: var(--t3); }

.qempty-box {
  display: grid;
  justify-items: center;
  gap: 8px;
  padding: 48px 26px;
  text-align: center;
  color: var(--t3);
}
.qempty-box .ico { width: 30px; height: 30px; opacity: .5; }
.qempty-box strong { font-size: 14.5px; color: var(--t2); font-weight: 600; }
.qempty-box p { margin: 0; font-size: 12.5px; line-height: 1.5; }

.qlist {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 2px 8px 12px;
}

/* --- footer actions --- */

.qside-foot {
  flex: none;
  display: flex;
  gap: 8px;
  padding: 12px 14px calc(12px + env(safe-area-inset-bottom));
  box-shadow: inset 0 1px 0 var(--hairline);
  background: rgba(12, 12, 15, .7);
}
.qside-foot .tool-btn { flex: 1; justify-content: center; height: 38px; font-size: 12.5px; }
.qside-foot .tool-btn[disabled] { opacity: .4; pointer-events: none; }
.qside-foot .ico { width: 15px; height: 15px; }

/* On a wide screen the sidebar docks: the page and the player bar give up the
   space rather than being covered by it. */
@media (min-width: 1100px) {
  body.queue-open #app,
  body.queue-open #footer { padding-right: 420px; }
  body.queue-open .np { right: 420px; }
  /* The bar is fixed full-width, so it has to be inset too — otherwise search
     and settings end up underneath the sidebar and become unreachable. */
  body.queue-open #topbar { right: 420px; }
  body.queue-open .qside { box-shadow: inset 1px 0 0 var(--hairline-strong); }
}

/* Narrower than that there's nothing to give up, so it slides over the top. */
@media (max-width: 1099px) {
  .qside { box-shadow: inset 1px 0 0 var(--hairline-strong), -30px 0 80px rgba(0, 0, 0, .7); }
}

/* --------------------------------------------------- add-to-playlist list */

.pl-picker { display: flex; flex-direction: column; gap: 4px; margin-top: 14px; }

.pl-pick {
  display: grid;
  grid-template-columns: 40px 1fr auto;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 8px 10px;
  border-radius: var(--r-md);
  text-align: left;
  color: var(--t1);
  transition: background var(--d-fast) var(--ease);
}
.pl-pick:hover { background: rgba(255, 255, 255, .07); }
.pl-pick:focus-visible {
  outline: none;
  background: rgba(255, 255, 255, .1);
  box-shadow: inset 0 0 0 1px var(--hairline-strong);
}
.pl-pick-art {
  position: relative;
  width: 40px;
  height: 40px;
  border-radius: 9px;
  overflow: hidden;
  background-size: cover;
  box-shadow: inset 0 0 0 1px var(--hairline);
}
.pl-pick-art i { position: absolute; inset: 0; background-size: cover; background-position: center; }
.pl-pick-main { min-width: 0; }
.pl-pick-main b {
  display: block;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -.015em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pl-pick-main span { display: block; margin-top: 1px; font-size: 12px; color: var(--t3); }
.pl-pick-add {
  flex: none;
  padding: 5px 12px;
  border-radius: var(--r-full);
  background: rgba(255, 255, 255, .1);
  font-size: 12px;
  font-weight: 600;
  color: var(--t2);
}
.pl-pick:hover .pl-pick-add { background: #fff; color: #000; }

/* The playlist sheet's cover is a tile, not an <img>. */
#pl-cover { position: relative; overflow: hidden; aspect-ratio: 1; }

/* Compact song rows carry a single action, so they need one extra column. */
.trow.has-one-act { grid-template-columns: 26px 40px 1fr auto auto; }
.tgrid > .trow.has-one-act { grid-template-columns: 24px 38px 1fr auto auto; }

@media (max-width: 560px) {
  .trow.has-one-act { grid-template-columns: 20px 38px 1fr auto auto; }
  .rrow { grid-template-columns: 20px 22px 34px 1fr auto; gap: 8px; }
  .rrow .trow-dur { display: none; }
}

/* ------------------------------------------------------------------ TV */

body.tv .qside { --qside-w: 480px; }
body.tv .qlist .rrow { padding: 12px; }
body.tv .rrow .trow-actions { opacity: 1; }
body.tv .pl-pick { padding: 12px; }

/* ==========================================================================
   Song options menu, artwork picker, details editor
   ========================================================================== */

.rowmenu {
  position: fixed;
  z-index: 95;
  min-width: 244px;
  padding: 6px;
  border-radius: var(--r-lg);
  background: rgba(26, 26, 30, .96);
  backdrop-filter: var(--blur-heavy);
  -webkit-backdrop-filter: var(--blur-heavy);
  box-shadow: inset 0 0 0 1px var(--hairline-strong), var(--shadow-lg);
  animation: menuIn var(--d-fast) var(--ease-out);
}
@keyframes menuIn { from { opacity: 0; transform: translateY(-6px) scale(.98); } }

.rowmenu-head {
  display: grid;
  grid-template-columns: 34px 1fr;
  align-items: center;
  gap: 10px;
  padding: 8px 10px 10px;
  margin-bottom: 4px;
  box-shadow: inset 0 -1px 0 var(--hairline);
}
.rowmenu-art {
  width: 34px;
  height: 34px;
  border-radius: 7px;
  background-size: cover;
  background-position: center;
  box-shadow: inset 0 0 0 1px var(--hairline);
}
.rowmenu-meta { min-width: 0; }
.rowmenu-meta b {
  display: block;
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rowmenu-meta span {
  display: block;
  font-size: 11.5px;
  color: var(--t3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.rowmenu button {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  padding: 9px 10px;
  border-radius: var(--r-sm);
  text-align: left;
  font-size: 13.5px;
  color: var(--t1);
  transition: background var(--d-fast) var(--ease);
}
.rowmenu button .ico { width: 17px; height: 17px; color: var(--t3); flex: none; }
.rowmenu button:hover { background: rgba(255, 255, 255, .09); }
.rowmenu button:hover .ico { color: var(--t1); }
.rowmenu button:focus-visible {
  outline: none;
  background: rgba(255, 255, 255, .12);
  box-shadow: inset 0 0 0 1px var(--hairline-strong);
}
.rowmenu button[disabled] { opacity: .4; pointer-events: none; }

/* The film menu carries a few actions whose consequence isn't obvious from
   the verb — "takes a while", "needs ffmpeg" — so items can run to two lines. */
.rowmenu-label { min-width: 0; }
.rowmenu-label span {
  display: block;
  margin-top: 2px;
  font-size: 11.5px;
  line-height: 1.4;
  color: var(--t3);
  white-space: normal;
}
.rowmenu button:has(.rowmenu-label span) { align-items: flex-start; }
.rowmenu button:has(.rowmenu-label span) .ico { margin-top: 1px; }

/* Delete sits below a rule rather than in the run of everyday actions.
   (The red treatment itself is further down, with the other destructive bits.) */
.rowmenu-sep { height: 1px; margin: 5px 10px; background: var(--hairline); }

/* On a TV the menu is driven by a remote from across the room. */
body.tv .rowmenu { min-width: 380px; padding: 8px; }
body.tv .rowmenu button { padding: 13px 12px; font-size: 16px; gap: 13px; }
body.tv .rowmenu button .ico { width: 20px; height: 20px; }
body.tv .rowmenu-label span { font-size: 13.5px; }
body.tv .rowmenu-meta b { font-size: 15px; }
.rowmenu-note {
  padding: 8px 10px 4px;
  font-size: 11.5px;
  color: var(--t3);
}

/* ------------------------------------------------------------- artwork */

.art-now {
  display: grid;
  grid-template-columns: 96px 1fr;
  gap: 16px;
  align-items: start;
  margin-bottom: 18px;
}
.art-now img {
  width: 96px;
  height: 96px;
  border-radius: var(--r-md);
  object-fit: cover;
  background: var(--s3);
  box-shadow: inset 0 0 0 1px var(--hairline), var(--shadow-sm);
}
.art-now strong {
  display: block;
  font-size: 15px;
  font-weight: 650;
  letter-spacing: -.02em;
}
.art-now .muted { display: block; margin-top: 3px; font-size: 12.5px; }

.art-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: 12px;
  margin-top: 16px;
}
.art-card {
  display: block;
  text-align: left;
  border-radius: var(--r-md);
  padding: 0;
  color: var(--t1);
  transition: transform var(--d-fast) var(--ease);
}
.art-card img {
  width: 100%;
  aspect-ratio: 1;
  border-radius: var(--r-md);
  object-fit: cover;
  background: var(--s3);
  box-shadow: inset 0 0 0 1px var(--hairline);
}
.art-card:hover { transform: translateY(-3px); }
.art-card:focus-visible { outline: none; }
.art-card:focus-visible img { box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .9); }
.art-card-name {
  display: block;
  margin-top: 7px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.3;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.art-card-sub {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  color: var(--t3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ------------------------------------------------------- details editor */

.edit-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin: 14px 0 16px;
}
.edit-grid .field:nth-child(-n + 3) { grid-column: 1 / -1; }
.edit-grid .field { display: flex; flex-direction: column; gap: 6px; }
.edit-grid .field span {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--t3);
}
.edit-grid input {
  height: 40px;
  padding: 0 12px;
  border: 0;
  border-radius: var(--r-sm);
  background: rgba(255, 255, 255, .07);
  box-shadow: inset 0 0 0 1px var(--hairline);
  color: var(--t1);
  font-size: 14px;
  font-family: inherit;
}
.edit-grid input:focus {
  outline: none;
  background: rgba(255, 255, 255, .12);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .55);
}
.edit-actions { display: flex; gap: 9px; flex-wrap: wrap; margin: 16px 0; }
/* First thing in a body needs no extra gap above it. */
.sheet-body > .sub-h:first-child { margin-top: 0; }

@media (max-width: 520px) {
  .edit-grid { grid-template-columns: 1fr; }
  .edit-grid .field { grid-column: 1 / -1; }
  .art-now { grid-template-columns: 76px 1fr; }
  .art-now img { width: 76px; height: 76px; }
}

body.tv .rowmenu { min-width: 300px; }
body.tv .rowmenu button { padding: 13px 12px; font-size: 16px; }

/* Scope selector in the details editor. */
.escope {
  display: flex;
  gap: 3px;
  padding: 3px;
  margin: 4px 0 14px;
  border-radius: var(--r-full);
  background: rgba(255, 255, 255, .05);
  box-shadow: inset 0 0 0 1px var(--hairline);
  width: fit-content;
  max-width: 100%;
  overflow-x: auto;
  scrollbar-width: none;
}
.escope::-webkit-scrollbar { display: none; }
.escope button {
  flex: none;
  padding: 7px 15px;
  border-radius: var(--r-full);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--t2);
  white-space: nowrap;
  transition: color var(--d-fast) var(--ease), background var(--d-fast) var(--ease);
}
.escope button:hover { color: var(--t1); }
.escope button.active {
  color: var(--t1);
  font-weight: 600;
  background: rgba(255, 255, 255, .13);
  box-shadow: var(--shadow-sm);
}
.escope button:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .85);
}

/* Opt-in to overwriting per-song covers when setting an album's artwork. */
.art-toggle {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: 11px;
  margin: 0 0 16px;
  padding: 12px 14px;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .05);
  box-shadow: inset 0 0 0 1px var(--hairline);
  cursor: pointer;
}
.art-toggle:hover { background: rgba(255, 255, 255, .08); }
.art-toggle input { width: 17px; height: 17px; margin: 1px 0 0; accent-color: var(--brand); cursor: pointer; }
.art-toggle b { display: block; font-size: 13.5px; font-weight: 550; letter-spacing: -.01em; }
.art-toggle i { display: block; margin-top: 3px; font-size: 12px; font-style: normal; color: var(--t3); }

/* A playlist that already holds the whole selection. */
.pl-pick.done { opacity: .62; }
.pl-pick.done .pl-pick-add { background: rgba(255, 255, 255, .06); color: var(--t3); }
.pl-pick.done:hover .pl-pick-add { background: rgba(255, 255, 255, .12); color: var(--t2); }

/* ==========================================================================
   Uploads
   ========================================================================== */

.dropzone {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: grid;
  place-items: center;
  padding: var(--pad);
  background: rgba(6, 6, 8, .74);
  backdrop-filter: var(--blur-heavy);
  -webkit-backdrop-filter: var(--blur-heavy);
  animation: fadeIn var(--d-fast) var(--ease) both;
  pointer-events: none;   /* the drop is handled on the window */
}
.dropzone-card {
  display: grid;
  justify-items: center;
  gap: 12px;
  padding: clamp(34px, 6vw, 60px) clamp(30px, 7vw, 74px);
  text-align: center;
  border-radius: var(--r-xl);
  border: 2px dashed rgba(255, 255, 255, .32);
  background: rgba(255, 255, 255, .04);
}
.dropzone-card .ico { width: 46px; height: 46px; color: var(--brand); }
.dropzone-card strong { font-size: clamp(18px, 2.4vw, 24px); font-weight: 650; letter-spacing: -.02em; }
.dropzone-card p { margin: 0; font-size: 13.5px; color: var(--t3); }

.up-list { display: flex; flex-direction: column; gap: 6px; margin: 14px 0 4px; }

.up-row {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  align-items: center;
  gap: 10px;
  padding: 10px 12px 12px;
  border-radius: var(--r-sm);
  background: rgba(255, 255, 255, .05);
  box-shadow: inset 0 0 0 1px var(--hairline);
  font-size: 13px;
  overflow: hidden;
}
.up-row.ok { box-shadow: inset 0 0 0 1px rgba(60, 210, 130, .45); }
.up-row.err { box-shadow: inset 0 0 0 1px rgba(255, 90, 110, .5); }
/* The one actually moving right now, so a long list still has a "you are here". */
.up-row.now { background: rgba(255, 255, 255, .09); box-shadow: inset 0 0 0 1px var(--hairline-strong); }
.up-row.now .up-state { color: var(--t1); font-weight: 600; }

/* Batch progress inside the sheet — the same numbers as the floating card. */
.up-overall {
  margin: 14px 0 18px;
  padding: 14px 16px 15px;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .05);
  box-shadow: inset 0 0 0 1px var(--hairline-strong);
}
.up-overall-top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.up-overall-top b { font-size: 14px; font-weight: 650; letter-spacing: -.01em; }
.up-overall-top span {
  font-size: 15px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.02em;
}
.up-overall-bar {
  height: 6px;
  margin: 11px 0 9px;
  border-radius: var(--r-full);
  background: rgba(255, 255, 255, .1);
  overflow: hidden;
}
.up-overall-bar i {
  display: block;
  height: 100%;
  width: 0;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--brand-1), var(--brand-2));
  transition: width .3s var(--ease-out);
}
.up-overall-foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  font-size: 12px;
  color: var(--t3);
}
.up-overall-foot span:first-child {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.up-overall-foot span:last-child { flex: none; }

.up-kind {
  flex: none;
  padding: 3px 8px;
  border-radius: var(--r-full);
  background: rgba(255, 255, 255, .09);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--t2);
}
.up-kind.music { color: #ffb26b; }
.up-kind.film { color: #7fb6ff; }

.up-name { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.up-size { font-size: 12px; color: var(--t3); font-variant-numeric: tabular-nums; }
.up-state { font-size: 12px; color: var(--t2); font-variant-numeric: tabular-nums; min-width: 56px; text-align: right; }

.up-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3px;
  background: rgba(255, 255, 255, .08);
}
.up-bar i {
  display: block;
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, var(--brand-1), var(--brand-2));
  transition: width .2s linear;
}

/* The folder row gains a third control, so let it wrap on narrow screens. */
.folder-add { flex-wrap: wrap; }
#folder-upload .ico { width: 15px; height: 15px; }

/* ------------------------------------------------- upload folder picker */

.up-subs {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 210px;
  overflow-y: auto;
  overscroll-behavior: contain;
  margin-bottom: 12px;
}
.up-sub {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  padding: 9px 12px;
  border-radius: var(--r-sm);
  text-align: left;
  color: var(--t1);
  background: rgba(255, 255, 255, .04);
  box-shadow: inset 0 0 0 1px var(--hairline);
  transition: background var(--d-fast) var(--ease);
}
.up-sub:hover { background: rgba(255, 255, 255, .09); }
.up-sub.active { background: rgba(255, 255, 255, .13); box-shadow: inset 0 0 0 1px var(--hairline-strong); }
.up-sub:focus-visible { outline: none; box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .8); }
.up-sub-main b { display: block; font-size: 13.5px; font-weight: 600; letter-spacing: -.01em; }
.up-sub-main span { display: block; margin-top: 2px; font-size: 11.5px; color: var(--t3); }
.up-sub-tick { color: var(--brand); font-weight: 700; }

/* ------------------------------------------------------ match candidates */

.match-list { display: flex; flex-direction: column; gap: 6px; margin-top: 14px; }

.match-row {
  display: grid;
  grid-template-columns: 46px 1fr auto;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 9px 12px 9px 9px;
  border-radius: var(--r-md);
  text-align: left;
  color: var(--t1);
  background: rgba(255, 255, 255, .04);
  box-shadow: inset 0 0 0 1px var(--hairline);
  transition: background var(--d-fast) var(--ease);
}
.match-row.static { grid-template-columns: 1fr auto; cursor: default; }
.match-row:not(.static):hover { background: rgba(255, 255, 255, .09); }
.match-row:focus-visible { outline: none; box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .8); }
.match-row img {
  width: 46px;
  height: 46px;
  border-radius: var(--r-sm);
  object-fit: cover;
  background: var(--s3);
  box-shadow: inset 0 0 0 1px var(--hairline);
}
.match-main { min-width: 0; }
.match-main b {
  display: block;
  font-size: 13.5px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.match-main > span {
  display: block;
  font-size: 12px;
  color: var(--t2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.match-facts { font-size: 11.5px !important; color: var(--t3) !important; margin-top: 1px; }

.match-score {
  flex: none;
  padding: 4px 9px;
  border-radius: var(--r-full);
  font-size: 11.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  background: rgba(255, 255, 255, .08);
  color: var(--t2);
}
.match-score.strong { background: rgba(60, 210, 130, .18); color: #6ee7a8; }
.match-score.fair { background: rgba(255, 178, 107, .18); color: #ffb26b; }
.match-score.weak { background: rgba(255, 255, 255, .07); color: var(--t3); }

/* Destructive actions read as destructive. */
.rowmenu button.danger { color: #ff8a9c; }
.rowmenu button.danger .ico { color: #ff8a9c; }
.rowmenu button.danger:hover { background: rgba(255, 69, 92, .16); }

/* ------------------------------------------- artwork choice when moving */

.mv-art {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-bottom: 8px;
}
.mv-art-opt {
  display: grid;
  justify-items: center;
  gap: 4px;
  padding: 12px 10px 13px;
  border-radius: var(--r-md);
  text-align: center;
  color: var(--t1);
  background: rgba(255, 255, 255, .04);
  box-shadow: inset 0 0 0 1px var(--hairline);
  transition: background var(--d-fast) var(--ease);
}
.mv-art-opt:hover { background: rgba(255, 255, 255, .08); }
.mv-art-opt.active {
  background: rgba(255, 255, 255, .11);
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .55);
}
.mv-art-opt:focus-visible { outline: none; box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .85); }
.mv-art-opt img {
  width: 66px;
  height: 66px;
  border-radius: var(--r-sm);
  object-fit: cover;
  background: var(--s3);
  box-shadow: inset 0 0 0 1px var(--hairline);
  margin-bottom: 4px;
}
.mv-art-opt b { font-size: 12.5px; font-weight: 600; letter-spacing: -.01em; }
.mv-art-opt span { font-size: 11px; color: var(--t3); line-height: 1.35; }

@media (max-width: 520px) {
  .mv-art { grid-template-columns: 1fr; }
  .mv-art-opt { grid-template-columns: 66px 1fr; justify-items: start; text-align: left; align-items: center; gap: 12px; }
  .mv-art-opt img { grid-row: span 2; margin: 0; }
}

/* ------------------------------------------------------ pattern removal */

.pat-list { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; }
.pat-item {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 11px;
  padding: 10px 12px;
  border-radius: var(--r-sm);
  background: rgba(255, 255, 255, .05);
  box-shadow: inset 0 0 0 1px var(--hairline);
  cursor: pointer;
}
.pat-item:hover { background: rgba(255, 255, 255, .09); }
.pat-item input { width: 16px; height: 16px; accent-color: var(--brand); cursor: pointer; }
.pat-item b { display: block; font-size: 13.5px; font-weight: 600; }
.pat-item i { display: block; margin-top: 2px; font-size: 11.5px; font-style: normal; color: var(--t3); }

.pat-preview {
  max-height: 240px;
  overflow-y: auto;
  overscroll-behavior: contain;
  margin-bottom: 4px;
}
.pat-row { padding: 8px 0; box-shadow: inset 0 -1px 0 var(--hairline); }
.pat-row:last-child { box-shadow: none; }
.pat-change {
  display: grid;
  grid-template-columns: 62px 1fr auto 1fr;
  align-items: baseline;
  gap: 8px;
  font-size: 12.5px;
  padding: 2px 0;
}
.pat-field {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--t3);
}
.pat-before { color: var(--t3); text-decoration: line-through; overflow: hidden; text-overflow: ellipsis; }
.pat-arrow { color: var(--t3); }
.pat-after { color: var(--t1); font-weight: 550; overflow: hidden; text-overflow: ellipsis; }

@media (max-width: 520px) {
  .pat-change { grid-template-columns: 1fr; gap: 2px; }
  .pat-arrow { display: none; }
}

/* ------------------------------------------------------- remote access */

.pair-box {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 4px;
}
#pair-code-wrap { display: flex; align-items: baseline; gap: 12px; }
.pair-code {
  font-variant-numeric: tabular-nums;
  font-size: clamp(26px, 4vw, 34px);
  font-weight: 700;
  letter-spacing: .18em;
  color: var(--t1);
  padding: 6px 14px;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .07);
  box-shadow: inset 0 0 0 1px var(--hairline-strong);
}
.pair-hint { font-size: 12px; color: var(--t3); font-variant-numeric: tabular-nums; }
.pair-devices { display: flex; flex-direction: column; gap: 6px; }

/* ==========================================================================
   Pairing gate + Settings tabs
   ========================================================================== */

.gate {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  padding: var(--pad);
  background: radial-gradient(120% 120% at 50% 0%, #1a1420 0%, #0a0a0c 60%, #000 100%);
  animation: fadeIn var(--d) var(--ease) both;
}
.gate-card {
  width: min(420px, 100%);
  display: grid;
  justify-items: center;
  gap: 14px;
  text-align: center;
  padding: clamp(28px, 5vw, 44px) clamp(24px, 5vw, 40px) clamp(24px, 4vw, 34px);
  border-radius: var(--r-xl);
  background: rgba(255, 255, 255, .04);
  box-shadow: inset 0 0 0 1px var(--hairline-strong), var(--shadow-lg);
}
.gate-card .brand-mark.lg svg { width: 52px; height: 52px; }
.gate-card h1 {
  margin: 4px 0 0;
  font-size: clamp(22px, 3vw, 28px);
  font-weight: 700;
  letter-spacing: -.03em;
}
.gate-card p {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--t2);
  max-width: 40ch;
}

#gate-code {
  width: 100%;
  height: 60px;
  margin-top: 6px;
  padding: 0 16px;
  border: 0;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .07);
  box-shadow: inset 0 0 0 1px var(--hairline-strong);
  color: var(--t1);
  font-variant-numeric: tabular-nums;
  font-size: 28px;
  font-weight: 700;
  letter-spacing: .3em;
  text-align: center;
  text-transform: uppercase;
}
#gate-code::placeholder { color: var(--t3); letter-spacing: .3em; }
#gate-code:focus {
  outline: none;
  background: rgba(255, 255, 255, .12);
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .55);
}
#gate-go { width: 100%; height: 48px; }
#gate-go[disabled] { opacity: .5; pointer-events: none; }

.gate-msg { font-size: 13px; margin: 0; }
.gate-msg.err { color: #ff8a9c; }
.gate-msg.ok { color: #6ee7a8; }
.gate-foot { font-size: 12px !important; color: var(--t3) !important; }

/* --------------------------------------------------------- settings tabs */

.settabs {
  position: sticky;
  top: 0;
  z-index: 3;
  display: flex;
  gap: 3px;
  padding: 4px;
  margin: 0 0 4px;
  border-radius: var(--r-full);
  background: rgba(255, 255, 255, .05);
  box-shadow: inset 0 0 0 1px var(--hairline);
  overflow-x: auto;
  scrollbar-width: none;
}
.settabs::-webkit-scrollbar { display: none; }
.settabs button {
  flex: 1 0 auto;
  padding: 8px 16px;
  border-radius: var(--r-full);
  font-size: 13px;
  font-weight: 500;
  color: var(--t2);
  white-space: nowrap;
  transition: color var(--d-fast) var(--ease), background var(--d-fast) var(--ease);
}
.settabs button:hover { color: var(--t1); }
.settabs button.active {
  color: var(--t1);
  font-weight: 600;
  background: rgba(255, 255, 255, .13);
  box-shadow: var(--shadow-sm);
}
.settabs button:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .85);
}

/* With one section on screen the divider under it is just a stray line. */
#settings .group { box-shadow: none; padding-top: 18px; }

/* Nothing of the app is visible until access is confirmed. `checking` covers
   the moment before the status call returns; `locked` is the settled state for
   a device that still has to pair. */
body.checking #topbar,
body.checking #app,
body.checking #footer,
body.checking .np,
body.checking .qside { visibility: hidden; }

body.locked #topbar,
body.locked #app,
body.locked #footer,
body.locked .np,
body.locked .qside { display: none !important; }

/* ------------------------------------------------------------- shortcuts */

.keycards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 12px;
}
.keycard {
  padding: 16px 18px 14px;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .04);
  box-shadow: inset 0 0 0 1px var(--hairline);
}
.keycard h4 {
  margin: 0;
  font-size: 13.5px;
  font-weight: 650;
  letter-spacing: -.015em;
  color: var(--t1);
}
.keycard > p {
  margin: 4px 0 0;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--t3);
}
.keycard dl { margin: 12px 0 0; display: flex; flex-direction: column; }
/* Keys in their own column so the descriptions line up down the card. */
.keycard dl > div {
  display: grid;
  grid-template-columns: 92px 1fr;
  align-items: center;
  gap: 12px;
  padding: 5px 0;
}
.keycard dl > div + div { box-shadow: inset 0 1px 0 var(--hairline); }
.keycard dt { display: flex; gap: 4px; flex-wrap: wrap; }
.keycard dd {
  margin: 0;
  font-size: 12.5px;
  color: var(--t2);
  letter-spacing: -.005em;
}
.keycard kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  height: 24px;
  padding: 0 7px;
  border-radius: 6px;
  background: rgba(255, 255, 255, .1);
  box-shadow: inset 0 0 0 1px var(--hairline-strong), 0 1px 0 rgba(0, 0, 0, .4);
  font-family: inherit;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--t1);
}

@media (max-width: 520px) {
  .keycard dl > div { grid-template-columns: 84px 1fr; gap: 10px; }
}

/* Reusable codes are longer than one-time ones; let the field shrink its
   tracking rather than clipping the last characters. */
#gate-code { font-size: clamp(20px, 5vw, 28px); letter-spacing: .22em; padding: 0 12px; }

/* ------------------------------------------------- phone top bar layout */

/* The overflow menu only exists on a phone; on desktop the two buttons it
   stands in for are shown directly. */
#bar-more { display: none; }

@media (max-width: 820px) {
  #bar-more { display: inline-flex; }
  #rescan, #settings-btn { display: none; }
  /* Reclaimed width goes to the tabs, so Movies stays reachable. */
  .nav-links button { padding: 6px 12px; font-size: 12.5px; }
}

@media (max-width: 560px) {
  .nav-links { padding: 2px; }
  .nav-links button { padding: 6px 10px; font-size: 12px; }
}

/* ==========================================================================
   Mobile precision
   Everything below 820px shares one gutter, one vertical rhythm, and no
   control smaller than 44pt. The aim is that nothing looks approximate:
   the logo lines up with the hero title, which lines up with the artwork.
   ========================================================================== */

@media (max-width: 820px) {
  /* One gutter, everywhere. --pad already drives the hero, rails and rows;
     the bar now uses it too instead of its own inset. */
  #topbar { padding-left: var(--pad); padding-right: var(--pad); height: 64px; }
  body.no-hero #rows { margin-top: calc(64px + 20px); }

  /* 44pt minimum on every control, by width rather than only by pointer type —
     a phone in desktop-site mode still deserves real targets. */
  .icon-btn { width: 44px; height: 44px; }
  .icon-btn .ico { width: 22px; height: 22px; }
  .nav-links { padding: 3px; }
  .nav-links button {
    min-height: 38px;
    padding: 0 14px;
    font-size: 13.5px;
    display: inline-flex;
    align-items: center;
  }
  .search { height: 44px; }
  .tool-btn { min-height: 44px; }
  .qside-foot .tool-btn { height: 44px; }

  /* Vertical rhythm on an 8pt grid, so sections feel deliberate rather than
     wherever the content happened to end. */
  .row { margin-bottom: 32px; }
  .row-head { padding-bottom: 12px; }
  .music-head { padding-bottom: 24px; }
  .mtabs { margin-bottom: 24px; }

  /* Type: one clear step between each level. */
  .row-title h2 { font-size: 21px; letter-spacing: -.03em; }
  .row-title p { font-size: 13px; }
  .card-hover .t { font-size: 13.5px; }
}

@media (max-width: 560px) {
  /* Keep the same gutter rather than shrinking it — a tighter margin here is
     what makes a layout feel improvised. */
  :root { --pad: 20px; }
  .nav-links button { padding: 0 12px; font-size: 13px; }
  .hero-body h1 { font-size: clamp(30px, 8.5vw, 40px); letter-spacing: -.04em; }
  .music-headline h1 { font-size: clamp(30px, 8.5vw, 40px); letter-spacing: -.04em; }
}

@media (max-width: 820px) {
  /* Full 44pt for the tabs, and a real hit area around the logo without
     making the mark itself bigger. */
  .nav-links button { min-height: 44px; }
  .brand { padding: 9px 0; }
}

@media (max-width: 820px) {
  /* Pull the title up: the desktop clamp left a screenful of empty space
     above it on a phone. */
  .music { padding-top: calc(64px + 16px); }

  /* Shuffle sits beside the title rather than under it, aligned to the
     heading's line instead of the block's centre. */
  .music-head {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding-bottom: 20px;
  }
  .music-headline { min-width: 0; }
  .music-head-actions { flex: none; padding-bottom: 0; }
  #music-shuffle { height: 44px; padding: 0 18px; font-size: 14px; }
  #music-shuffle .ico { width: 18px; height: 18px; }
  .music-kicker { font-size: 11px; }
  .music-sub { margin-top: 6px; font-size: 13px; }
}

/* Below this the label crowds the title, so the icon carries it. */
@media (max-width: 430px) {
  #music-shuffle { width: 44px; padding: 0; border-radius: var(--r-full); }
  #music-shuffle .btn-label { display: none; }
}

/* Song rows sit flush — square edges read as a list rather than a stack of
   separate cards, and the hairline between them does the separating. */
.trow, .rrow, .qrow { border-radius: 0; }
.trow:focus-visible, .rrow:focus-visible, .qrow:focus-visible { border-radius: 0; }

/* A header already provides the space above the rails. */
body.with-head #rows { margin-top: 0; }

/* ==================================================== movies grid + tiles */

/* Poster grid, same rhythm as the album grid but 2:3 instead of square. */
.vgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(clamp(132px, 14vw, 178px), 1fr));
  gap: clamp(16px, 1.8vw, 26px);
  padding: 0 var(--pad);
}
.vcard { cursor: pointer; transition: transform var(--d) var(--ease); }
.vcard:hover, .vcard:focus-visible { transform: translateY(-6px); }
.vcard:focus-visible { outline: none; }

.vcard-art {
  position: relative;
  aspect-ratio: 2 / 3;
  border-radius: var(--r-md);
  overflow: hidden;
  background-color: var(--s2);
  background-size: cover;
  background-position: center;
  display: grid;
  place-items: center;
  padding: 14px;
  box-shadow: inset 0 0 0 1px var(--hairline), var(--shadow-sm);
  transition: box-shadow var(--d) var(--ease);
}
.vcard:hover .vcard-art { box-shadow: inset 0 0 0 1px var(--hairline-strong), var(--shadow-md); }
.vcard:focus-visible .vcard-art {
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .9), var(--shadow-md);
}

.vcard-play {
  position: absolute;
  right: 10px;
  bottom: 10px;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(20, 20, 22, .72);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  box-shadow: inset 0 0 0 1px var(--hairline-strong);
  color: var(--t1);
  opacity: 0;
  transform: translateY(6px) scale(.92);
  transition: opacity var(--d-fast) var(--ease), transform var(--d-fast) var(--ease);
}
.vcard-play .ico { width: 19px; height: 19px; margin-left: 2px; }
.vcard:hover .vcard-play, .vcard:focus-visible .vcard-play { opacity: 1; transform: none; }

.vcard-progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3px;
  background: rgba(255, 255, 255, .2);
}
.vcard-progress i { display: block; height: 100%; background: var(--brand); }

.vcard-name {
  margin-top: 11px;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -.015em;
  line-height: 1.3;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.vcard-sub {
  margin-top: 3px;
  font-size: 12.5px;
  color: var(--t3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* --- recently played tiles on Home --- */

.rail.mtiles { scroll-padding-left: var(--pad); }
.mtile {
  flex: 0 0 auto;
  width: clamp(126px, 13vw, 158px);
  scroll-snap-align: start;
  cursor: pointer;
  transition: transform var(--d) var(--ease);
}
.mtile:hover, .mtile:focus-visible { transform: translateY(-6px); }
.mtile:focus-visible { outline: none; }
.mtile-art {
  position: relative;
  aspect-ratio: 1;
  border-radius: var(--r-md);
  overflow: hidden;
  background-color: var(--s2);
  background-size: cover;
  background-position: center;
  box-shadow: inset 0 0 0 1px var(--hairline), var(--shadow-sm);
}
.mtile:focus-visible .mtile-art { box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .9); }
.mtile-play {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgba(0, 0, 0, .42);
  opacity: 0;
  transition: opacity var(--d-fast) var(--ease);
}
.mtile-play .ico { width: 32px; height: 32px; }
.mtile:hover .mtile-play, .mtile:focus-visible .mtile-play { opacity: 1; }
.mtile-name {
  margin-top: 10px;
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: -.015em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mtile-sub {
  margin-top: 2px;
  font-size: 12px;
  color: var(--t3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

@media (max-width: 520px) {
  .vgrid { grid-template-columns: repeat(auto-fill, minmax(126px, 1fr)); gap: 14px; }
}

/* ==================================================== resume / start over */

.resume-card {
  position: absolute;
  left: 50%;
  bottom: clamp(96px, 15vh, 150px);
  transform: translateX(-50%);
  z-index: 9;
  width: min(420px, calc(100vw - 32px));
  padding: 18px;
  border-radius: var(--r-xl);
  background: rgba(20, 20, 23, .88);
  box-shadow: inset 0 0 0 1px var(--hairline-strong), var(--shadow-lg);
  -webkit-backdrop-filter: var(--blur-heavy);
  backdrop-filter: var(--blur-heavy);
  animation: resumeIn var(--d) var(--ease-out) both;
}
@keyframes resumeIn {
  from { opacity: 0; transform: translate(-50%, 16px) scale(.97); }
  to { opacity: 1; transform: translate(-50%, 0) scale(1); }
}

.resume-head { display: flex; align-items: center; gap: 14px; }
.resume-art {
  flex: none;
  width: 46px;
  height: 68px;
  border-radius: var(--r-sm);
  background-color: var(--s3);
  background-size: cover;
  background-position: center;
  box-shadow: inset 0 0 0 1px var(--hairline);
}
.resume-meta { min-width: 0; }
.resume-meta b {
  display: block;
  font-size: 16px;
  font-weight: 650;
  letter-spacing: -.022em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.resume-meta span {
  display: block;
  margin-top: 3px;
  font-size: 13px;
  color: var(--t2);
  font-variant-numeric: tabular-nums;
}

.resume-bar {
  margin: 14px 0 16px;
  height: 3px;
  border-radius: 2px;
  background: rgba(255, 255, 255, .14);
  overflow: hidden;
}
.resume-bar i {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, var(--brand-1), var(--brand-2));
}

.resume-actions { display: flex; gap: 9px; }
.resume-actions .btn { flex: 1; height: 46px; justify-content: center; }

@media (max-width: 620px) {
  /* Anchored to the bottom with the home indicator cleared, and buttons at a
     thumb-friendly height rather than a wrapping pill. */
  .resume-card {
    left: 14px;
    right: 14px;
    width: auto;
    transform: none;
    bottom: calc(104px + env(safe-area-inset-bottom));
    padding: 16px;
  }
  @keyframes resumeIn {
    from { opacity: 0; transform: translateY(16px); }
    to { opacity: 1; transform: none; }
  }
  .resume-actions { flex-direction: column; }
  /* flex:1 governs the vertical axis once the row becomes a column, which
     collapses the buttons to min-content and beats any height. */
  .resume-actions .btn { flex: none; width: 100%; height: 50px; font-size: 15px; }
  .resume-meta b { font-size: 15px; }
}

/* ------------------------------------------------------ background jobs */
/*
   One card for every slow thing: copying files in, building a phone & TV copy,
   writing subtitles. A pill with a 4px sliver of progress couldn't answer "how
   long", so it got room to: what it's working on, share done, and time left.

   They stack upward from the corner, newest at the bottom, because the dock is
   a column-reverse flex — a job that finishes and leaves doesn't shuffle the
   one you were reading.
*/

.taskdock {
  position: fixed;
  right: var(--pad);
  bottom: calc(var(--np-h, 0px) + 20px + env(safe-area-inset-bottom));
  z-index: 110;
  display: flex;
  flex-direction: column-reverse;
  gap: 10px;
  pointer-events: none;
}
.taskdock:empty { display: none; }

.jobcard {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: 4px;
  width: min(348px, calc(100vw - var(--pad) * 2));
  padding: 4px;
  border-radius: var(--r-lg);
  background: rgba(22, 22, 26, .92);
  box-shadow: inset 0 0 0 1px var(--hairline-strong), var(--shadow-lg);
  -webkit-backdrop-filter: var(--blur-heavy);
  backdrop-filter: var(--blur-heavy);
  color: var(--t1);
  animation: resumeIn var(--d) var(--ease-out) both;
}
.jobcard-body {
  flex: 1;
  min-width: 0;
  display: block;
  padding: 12px 12px 11px;
  border-radius: calc(var(--r-lg) - 5px);
  text-align: left;
  color: inherit;
  transition: background var(--d-fast) var(--ease);
}
.jobcard-body:hover { background: rgba(255, 255, 255, .05); }
.jobcard-body:focus-visible { outline: none; box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .7); }

.jobcard-top { display: flex; align-items: center; gap: 10px; }
.jobcard-ico {
  flex: none;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 10px;
  background: rgba(255, 255, 255, .07);
  color: var(--brand);
}
.jobcard-ico .ico { width: 17px; height: 17px; }
/* A moving mark says "still working" during the long quiet stretch of a
   big file, when the percentage barely changes. */
.jobcard:not(.done) .jobcard-ico .ico { animation: upNudge 1.9s var(--ease) infinite; }
@keyframes upNudge {
  0%, 100% { transform: translateY(1px); opacity: .75; }
  50% { transform: translateY(-2px); opacity: 1; }
}

.jobcard-head { min-width: 0; flex: 1; }
.jobcard-head b {
  display: block;
  font-size: 13px;
  font-weight: 650;
  letter-spacing: -.01em;
}
.jobcard-head span {
  display: block;
  margin-top: 2px;
  font-size: 11.5px;
  color: var(--t2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.jobcard-pct {
  flex: none;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.jobcard-bar {
  display: block;
  height: 5px;
  margin: 11px 0 8px;
  border-radius: var(--r-full);
  background: rgba(255, 255, 255, .1);
  overflow: hidden;
}
.jobcard-bar i {
  display: block;
  height: 100%;
  width: 0;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--brand-1), var(--brand-2));
  transition: width .3s var(--ease-out);
}
/* Started, but the job hasn't reported a figure yet. */
.jobcard.indeterminate .jobcard-bar i {
  transition: none;
  animation: jobSlide 1.4s var(--ease) infinite;
}
/* Percentages are relative to the 40%-wide bar, so 260% carries it clear of
   the right edge rather than stopping a third of the way along. */
@keyframes jobSlide {
  0% { transform: translateX(-110%); }
  100% { transform: translateX(260%); }
}
.jobcard.indeterminate .jobcard-pct { display: none; }
.jobcard-foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  font-size: 11.5px;
  color: var(--t3);
  font-variant-numeric: tabular-nums;
}
.jobcard-foot span:first-child { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.jobcard-foot span:last-child { flex: none; }

.jobcard-x {
  flex: none;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  margin: 5px 3px 0 0;
  border-radius: 50%;
  color: var(--t3);
  transition: background var(--d-fast) var(--ease), color var(--d-fast) var(--ease);
}
.jobcard-x:hover { background: rgba(255, 255, 255, .12); color: var(--t1); }
.jobcard-x:focus-visible { outline: none; box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .7); }
.jobcard-x .ico { width: 15px; height: 15px; }

/* Finished: the bar becomes a result, and there is nothing left to cancel. */
.jobcard.done .jobcard-x,
.jobcard.done .jobcard-pct,
.jobcard.done .jobcard-foot,
.jobcard.done .jobcard-head span { display: none; }
.jobcard.done .jobcard-bar { margin-bottom: 2px; }
.jobcard.done .jobcard-head b { font-size: 13.5px; }
.jobcard.ok .jobcard-ico { color: #6ee7a8; }
.jobcard.ok .jobcard-bar i { background: linear-gradient(90deg, #3cd282, #6ee7a8); }
.jobcard.err .jobcard-ico { color: #ff8a9c; }
.jobcard.err .jobcard-bar i { background: linear-gradient(90deg, #ff5a6e, #ff8a9c); }
.jobcard.stop .jobcard-ico { color: var(--t2); }
.jobcard.stop .jobcard-bar i { background: rgba(255, 255, 255, .3); }
/* Still reading the new files: keep the brand bar, but sweep it. */
.jobcard.scan .jobcard-bar i { animation: upSweep 1.3s var(--ease) infinite; }
@keyframes upSweep { 0%, 100% { opacity: .45; } 50% { opacity: 1; } }

/* A remote can reach it, and a sofa can read it. */
body.tv .jobcard { width: min(460px, 42vw); border-radius: var(--r-xl); }
body.tv .jobcard-head b { font-size: 16px; }
body.tv .jobcard-head span { font-size: 14px; }
body.tv .jobcard-pct { font-size: 19px; }
body.tv .jobcard-foot { font-size: 13.5px; }

@media (max-width: 560px) {
  .taskdock { left: var(--pad); }
  .jobcard { width: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .jobcard-ico .ico,
  .jobcard.scan .jobcard-bar i,
  .jobcard.indeterminate .jobcard-bar i { animation: none; }
}
