/* ============================================================
   STREAMVAULT — projector-chrome palette on deep indigo.
   Amber reads as marquee light and rating gold; cyan is
   reserved strictly for "this is live / selected".
   ============================================================ */
:root {
  --void:   #0B0E1A;
  --sink:   #070914;
  --panel:  #141929;
  --raised: #1B2135;
  --edge:   #232A40;
  --mist:   #8C96B4;
  --paper:  #EDF0FA;
  --amber:  #F5A524;
  --cyan:   #35D6E8;
  --reel:   #E5484D;
  /* Text *on* amber. Three selectors have asked for this since the chips were written, and an
     undefined custom property makes `color` fall back to inherit — which put --paper on --amber
     at about 1.7:1 on every active genre chip. Naming it is the fix. */
  --ink:    #0B0E1A;

  --display: 'Archivo Black', 'Arial Black', system-ui, sans-serif;
  --body: 'Archivo', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono: 'JetBrains Mono', ui-monospace, 'Cascadia Mono', Consolas, monospace;

  --gut: clamp(16px, 3.2vw, 56px);
  --bar-h: 66px;
  /* The distance between the bar's own children. Named because the sections menu was lifted out of
     `.bar__nav` to sit beside the wordmark, and the nav has to give exactly this much of its left
     margin back so the desktop row is spaced as it was. */
  --bar-gap: clamp(12px, 2vw, 32px);
  --r: 3px;
  --ease: cubic-bezier(.22, .61, .36, 1);

  /* The parts of the page the browser draws rather than we do: scrollbars, the caret, the option list
     a <select> opens, and the focus ring's default. Without this they come back in the OS's light
     palette on a page that is #0B0E1A everywhere else — which on the four <select>s the site now has
     means white-on-white options. One declaration, no tokens spent. */
  color-scheme: dark;
}

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

/* Author `display` beats the hidden attribute's UA rule, and .sheet/.player both set
   display: grid — without this they paint as full-screen overlays while "hidden". */
[hidden] { display: none !important; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--void);
  color: var(--paper);
  font: 400 15px/1.6 var(--body);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3 { margin: 0; font-family: var(--display); font-weight: 400; letter-spacing: -.015em; }
p { margin: 0; }
img { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
input { font: inherit; }

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

.vh {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.skip {
  position: absolute; top: 8px; left: 8px; z-index: 200;
  padding: 10px 14px; background: var(--cyan); color: var(--sink);
  font: 700 12px var(--mono); text-transform: uppercase;
  text-decoration: none; transform: translateY(-160%);
  transition: transform .18s var(--ease);
}
.skip:focus { transform: none; }

/* ---------------- top bar ---------------- */
.bar {
  position: sticky; top: 0; z-index: 90;
  display: flex; align-items: center; gap: var(--bar-gap);
  height: var(--bar-h); padding: 0 var(--gut);
  background: linear-gradient(180deg, rgba(7,9,20,.96), rgba(7,9,20,.72) 70%, transparent);
  backdrop-filter: blur(14px);
  transition: background .3s var(--ease), border-color .3s var(--ease);
  border-bottom: 1px solid transparent;
}
.bar.is-stuck {
  background: rgba(7,9,20,.97);
  border-bottom-color: var(--edge);
}

.brand { display: flex; align-items: center; gap: 10px; text-decoration: none; flex: 0 0 auto; }
.brand__mark {
  width: 15px; height: 22px;
  background: var(--amber);
  clip-path: polygon(0 0, 100% 12%, 100% 88%, 0 100%);
  box-shadow: 0 0 22px -4px var(--amber);
}
.brand__word {
  font: 400 17px var(--display); letter-spacing: .06em; color: var(--paper);
}
.brand__word em { font-style: normal; color: var(--amber); }

/* The menu now sits between the brand and the nav as a child of the bar, so the nav pulls back the
   bar's own gap and adds its original 4px in its place � the desktop row stays exactly as wide and
   as spaced as it was when the button lived inside the nav. */
.bar__nav { display: flex; gap: 4px; flex: 0 0 auto; margin-left: calc(4px - var(--bar-gap)); }
.bar__link {
  padding: 7px 13px; border-radius: var(--r);
  font-size: 13.5px; font-weight: 500; color: var(--mist);
  transition: color .16s, background .16s;
}
.bar__link:hover { color: var(--paper); background: var(--panel); }
.bar__link.is-active { color: var(--sink); background: var(--paper); font-weight: 600; }

/* The search instrument. Three controls — a scope, a box, a sparkle — drawn as one pill, because
   they are one question: *what* to look for and *where*. The border belongs to the pill and lights on
   `:focus-within`, so aiming the scope and typing the words are visibly the same act; three separately
   bordered widgets in a row would read as three things to remember to set. */
/* On desktop the instrument is a magnifier at the right of the bar. Pressing it hides the section
   links — they already live in the ☰ menu — and slides the full pill in from the right to sit
   centred in the row: the search becomes the bar's one subject while it is being used, then gives
   the row back. `.search` holds only the glyph in flow; the pill is taken out of flow and centred
   on the bar itself, so it is symmetric regardless of how wide the hidden nav was. On a phone none
   of this applies — the pill is simply always there on its own row (see the ≤700 block). */
.search { position: static; margin-left: auto; flex: 0 0 auto; display: flex; align-items: center; }
.find__toggle {
  width: 40px; height: 40px; display: grid; place-items: center;
  background: rgba(20,25,41,.86); color: var(--mist);
  border: 1px solid var(--edge); border-radius: var(--r);
  cursor: pointer;
  transition: color .16s, background .16s, border-color .16s, opacity .18s var(--ease), visibility 0s;
}
.find__toggle svg { width: 18px; height: 18px; display: block; }
.find__toggle:hover { color: var(--paper); background: var(--panel); }
.find__toggle:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
/* While the pill is open the glyph steps aside — the centred pill is the control now, and the two
   sharing the row would read as two search boxes. It fades where it stands rather than moving, so
   the eye follows the pill sliding in, not two things crossing. */
.bar.is-searching .find__toggle {
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: color .16s, background .16s, border-color .16s, opacity .18s var(--ease), visibility 0s .18s;
}

.find {
  display: flex; align-items: stretch;
  height: 40px;
  background: rgba(20,25,41,.86);
  border: 1px solid var(--edge); border-radius: var(--r);
  transition: border-color .18s, background .18s;
}
.find:focus-within { border-color: var(--cyan); background: var(--panel); }

/* Desktop: the pill is centred on the bar, hidden and nudged right until summoned, then it slides
   left into the centre. `visibility` rather than `display` so both the open and the close animate,
   and so a closed pill is out of the tab order. Anchored to `.bar` (a sticky, hence positioned,
   ancestor) via `.search { position: static }`, so `left: 50%` centres on the whole row. */
@media (min-width: 701px) {
  .search .find {
    position: absolute; top: 50%; left: 50%; z-index: 120;
    width: min(620px, calc(100vw - 520px));
    transform: translate(-50%, -50%) translateX(64px);
    opacity: 0; visibility: hidden; pointer-events: none;
    border-color: var(--cyan); background: var(--panel);
    box-shadow: 0 18px 44px -14px rgba(4,5,11,.72);
    transition: opacity .22s var(--ease), transform .24s var(--ease), visibility 0s .24s;
  }
  .bar.is-searching .search .find {
    opacity: 1; visibility: visible; pointer-events: auto;
    transform: translate(-50%, -50%) translateX(0);
    transition: opacity .22s var(--ease), transform .24s var(--ease);
  }
  /* The links clear the stage for the search and come back when it closes. Kept in the layout while
     they fade so nothing on either side of them shifts; the centred pill covers the gap they leave. */
  .bar__nav { transition: opacity .2s var(--ease); }
  .bar.is-searching .bar__nav { opacity: 0; pointer-events: none; }
}

/* The scope reads as a label on the box rather than as a dropdown competing with it: mono, uppercase,
   the same weight as `.net__txt`, with a hairline where the box begins. */
.find__in {
  flex: 0 0 auto; max-width: 124px;
  padding: 0 9px 0 11px; margin: 0;
  background: transparent; color: var(--mist);
  border: 0; border-right: 1px solid var(--edge);
  font: 500 11px var(--mono); text-transform: uppercase; letter-spacing: .07em;
  cursor: pointer; transition: color .16s, border-color .18s;
}
.find__in:hover { color: var(--paper); }
.find__in:focus-visible { outline: 2px solid var(--cyan); outline-offset: -2px; }
/* The list itself is drawn by the OS, which does not inherit the page: without this the options come
   back white-on-white in a dark browser chrome. */
.find__in option { background: var(--panel); color: var(--paper); font: 400 13px var(--body); }

.find__box { position: relative; flex: 1 1 auto; min-width: 0; display: flex; }
.find input {
  width: 100%; padding: 0 34px 0 12px;
  background: transparent; color: var(--paper);
  border: 0; border-radius: 0;
}
.find input::placeholder { color: #66708C; }
.find input:focus { outline: none; }
.find__hint {
  position: absolute; right: 9px; top: 50%; transform: translateY(-50%);
  padding: 2px 6px; font: 500 11px var(--mono); color: var(--mist);
  border: 1px solid var(--edge); border-radius: 2px; pointer-events: none;
  transition: opacity .16s;
}
.find input:focus + .find__hint { opacity: 0; }

/* The sparkle. Off by default and quiet about it — a dim glyph, not a labelled button — because it is
   the *second* thing you try, after the catalogue answered plainly and did not have it. On, it holds
   amber so the mode it puts the box into is visible without being read. */
.find__ai {
  flex: 0 0 auto; width: 34px;
  display: grid; place-items: center;
  border-left: 1px solid var(--edge);
  color: var(--mist); font-size: 14px; line-height: 1;
  cursor: pointer; transition: color .16s, background .16s;
}
.find__ai:hover { color: var(--amber); background: rgba(245,165,36,.08); }
.find__ai:focus-visible { outline: 2px solid var(--cyan); outline-offset: -2px; }
.find__ai.is-on { color: var(--ink); background: var(--amber); }
.find__ai.is-on:hover { background: #ffb733; }

.net { display: flex; align-items: center; gap: 7px; flex: 0 0 auto; }
.net__dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--mist); box-shadow: 0 0 0 3px rgba(140,150,180,.14);
}
.net__txt { font: 500 10.5px var(--mono); text-transform: uppercase; letter-spacing: .1em; color: var(--mist); }
.net.is-up .net__dot { background: var(--cyan); box-shadow: 0 0 0 3px rgba(53,214,232,.16); }
.net.is-up .net__txt { color: var(--cyan); }
.net.is-down .net__dot { background: var(--reel); box-shadow: 0 0 0 3px rgba(229,72,77,.18); }
.net.is-down .net__txt { color: var(--reel); }

/* ---------------- hero spotlight ---------------- */
.hero {
  position: relative;
  min-height: clamp(460px, 76vh, 760px);
  margin-top: calc(var(--bar-h) * -1);
  display: flex; align-items: flex-end;
  overflow: hidden;
}
.hero__stack { position: absolute; inset: 0; }
.hero__shot {
  position: absolute; inset: 0;
  background-size: cover; background-position: center 18%;
  opacity: 0; transform: scale(1.06);
  transition: opacity 1s var(--ease), transform 9s linear;
}
.hero__shot.is-on { opacity: 1; transform: scale(1); }
.hero__scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(96deg, var(--void) 6%, rgba(11,14,26,.86) 34%, rgba(11,14,26,.12) 66%, rgba(11,14,26,.5) 100%),
    linear-gradient(0deg, var(--void) 2%, transparent 46%);
}
.hero__body {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  max-width: 620px;
  padding: 0 var(--gut) clamp(40px, 6vh, 76px);
}

.eyebrow {
  display: flex; align-items: center; gap: 9px;
  font: 500 10.5px var(--mono); text-transform: uppercase; letter-spacing: .18em;
  color: var(--amber); margin-bottom: 14px;
}
.eyebrow::before {
  content: ''; width: 26px; height: 1px; background: var(--amber); opacity: .7;
}

.hero__title {
  font-size: clamp(38px, 6.4vw, 78px);
  line-height: .93; text-transform: uppercase;
  margin-bottom: 16px;
  text-shadow: 0 4px 40px rgba(7,9,20,.7);
}
.hero__facts {
  display: flex; flex-wrap: wrap; align-items: center; gap: 9px;
  font: 500 12px var(--mono); color: var(--mist);
  margin-bottom: 14px;
}
.hero__facts .sep { opacity: .4; }
.hero__blurb {
  color: #C3CADF; margin-bottom: 26px;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.hero__acts { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }

.hero__dots {
  position: absolute; right: var(--gut); bottom: clamp(40px, 6vh, 76px);
  display: flex; gap: 7px;
}
.dot {
  width: 26px; height: 3px; background: rgba(237,240,250,.24);
  transition: background .2s, transform .2s;
}
.dot:hover { background: rgba(237,240,250,.5); }
.dot.is-on { background: var(--amber); transform: scaleY(2); }

/* ---------------- buttons ---------------- */
.btn {
  display: inline-flex; align-items: center; gap: 8px;
  height: 42px; padding: 0 20px; border-radius: var(--r);
  font-weight: 600; font-size: 14px; white-space: nowrap;
  transition: transform .14s var(--ease), background .16s, border-color .16s, color .16s;
}
.btn:active { transform: translateY(1px); }
.btn--go { background: var(--paper); color: var(--sink); }
.btn--go:hover { background: var(--amber); }
.btn--ghost {
  background: rgba(20,25,41,.7); color: var(--paper);
  border: 1px solid var(--edge); backdrop-filter: blur(6px);
}
.btn--ghost:hover { border-color: var(--mist); background: var(--raised); }
.btn svg { width: 15px; height: 15px; fill: currentColor; flex: 0 0 auto; }

/* A toggle that reports a fact about the title rather than a live selection — so it takes the
   amber the genre chips and the id switch use for "this is the chosen one", not the cyan held
   back for "this is playing". */
.act.is-on { color: var(--amber); border-color: var(--amber); background: rgba(245,165,36,.1); }
.act.is-on:hover { border-color: var(--amber); background: rgba(245,165,36,.17); }

.tag {
  padding: 3px 7px; border: 1px solid currentColor; border-radius: 2px;
  font: 700 9.5px var(--mono); letter-spacing: .1em; text-transform: uppercase;
}
.tag--hd { color: var(--amber); }
.tag--rate { color: var(--paper); border-color: var(--edge); background: var(--panel); }
.tag--rate b { color: var(--amber); }
/* Whose score it is, and where it places. Both sit in `--mist` so the number keeps the emphasis
   — the label is there to disambiguate two stars, not to compete with them. `<i>`/`<em>` are
   the tag's own semantics rather than italics, so the slant is cancelled. */
.tag--rate i, .tag--rate em { font-style: normal; color: var(--mist); }
.tag--rate i { margin-right: 5px; }
.tag--rate em { margin-left: 5px; }
/* MyAnimeList's figure reads in `--cyan` where AniList's reads in `--amber`, which is the same
   distinction the key manifest already draws between a label and an id: one glance separates
   them without a second legend. */
.tag--mal b { color: var(--cyan); }

/* ---------------- carousel rows ---------------- */
.rows { padding: clamp(24px, 4vh, 44px) 0 10px; }

.row { margin-bottom: clamp(28px, 4.4vh, 52px); }
.row__head {
  display: flex; align-items: baseline; gap: 14px;
  padding: 0 var(--gut); margin-bottom: 14px;
}
.row__title {
  font-size: clamp(15px, 1.5vw, 19px); text-transform: uppercase; letter-spacing: .02em;
}
.row__rule { flex: 1; height: 1px; background: var(--edge); }
.row__nav { display: flex; gap: 5px; }
.row__btn {
  width: 28px; height: 28px; display: grid; place-items: center;
  border: 1px solid var(--edge); border-radius: var(--r); color: var(--mist);
  transition: color .16s, border-color .16s, background .16s;
}
.row__btn:hover { color: var(--sink); background: var(--paper); border-color: var(--paper); }
.row__btn svg { width: 12px; height: 12px; fill: currentColor; }

.reel {
  display: flex; gap: 12px;
  padding: 4px var(--gut) 14px;
  overflow-x: auto; scroll-snap-type: x proximity;
  scrollbar-width: none;
}
.reel::-webkit-scrollbar { display: none; }

/* ---------------- poster card ---------------- */
.card {
  position: relative; flex: 0 0 auto;
  width: clamp(132px, 13.4vw, 176px);
  scroll-snap-align: start;
  text-align: left;
  transition: transform .22s var(--ease);
}
.card:hover, .card:focus-visible { transform: translateY(-5px); }

.card__art {
  position: relative; aspect-ratio: 2 / 3;
  background: var(--panel) center/cover;
  border: 1px solid var(--edge); border-radius: var(--r);
  overflow: hidden;
  transition: border-color .2s, box-shadow .22s;
}
.card:hover .card__art {
  border-color: var(--amber);
  box-shadow: 0 14px 34px -12px rgba(7,9,20,.9), 0 0 0 1px var(--amber);
}
.card__art img { width: 100%; height: 100%; object-fit: cover; }
/* No cover, or a cover that 404s after the fact. Books were already emitting this class with nothing
   styling it, and AniList advertises cover URLs its own CDN has since dropped — a failed `<img>` with
   `alt=""` collapses silently, leaving a plate that reads as still-loading rather than empty. Two
   letters of the title instead, the same answer `.lv__blank` gives a channel with no logo. */
.card__blank {
  position: absolute; inset: 0; display: grid; place-items: center;
  font: 700 clamp(19px, 2.4vw, 30px) var(--display); color: var(--mist);
  text-transform: uppercase; letter-spacing: .06em;
}

/* The same plate, for a picture that *did* have a URL and failed to load. `card__blank` above is the
   fallback the markup chooses when there is no cover at all; this one is swapped in after the fact by
   the document's `error` listener, and it lands in whatever box the image was in � a poster frame, a
   centred channel plate, a cast face, an album tile.

   That is why it is sized to `100%` of its holder rather than pinned with `inset: 0` like its
   sibling: not every one of those holders is positioned, and an absolutely-placed plate inside a
   static one would escape to some further ancestor and cover the wrong thing entirely. Filling the
   box works in all of them without having to know which it is in. */
.art-blank {
  width: 100%; height: 100%;
  display: grid; place-items: center;
  background: var(--panel);
  font: 700 clamp(19px, 2.4vw, 30px) var(--display); color: var(--mist);
  text-transform: uppercase; letter-spacing: .06em;
}
.art-blank--wide { font-size: 26px; }

.card__badge {
  position: absolute; top: 6px; left: 6px;
  padding: 2px 5px; background: rgba(7,9,20,.86); color: var(--amber);
  font: 700 9px var(--mono); letter-spacing: .08em; border-radius: 2px;
  backdrop-filter: blur(4px);
}
.card__score {
  position: absolute; top: 6px; right: 6px;
  padding: 2px 5px; background: rgba(7,9,20,.86); color: var(--paper);
  font: 700 9.5px var(--mono); border-radius: 2px; backdrop-filter: blur(4px);
}

/* The ID readout is the point of the demo, so it earns a slot rather than a tooltip. */
.card__id {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 16px 7px 5px;
  background: linear-gradient(0deg, rgba(7,9,20,.95), transparent);
  font: 500 9.5px var(--mono); color: var(--cyan); letter-spacing: .05em;
  opacity: 0; transform: translateY(6px);
  transition: opacity .2s, transform .2s var(--ease);
}
.card:hover .card__id, .card:focus-visible .card__id { opacity: 1; transform: none; }

.card__meta { padding: 9px 2px 0; }
.card__name {
  font-size: 13px; font-weight: 600; line-height: 1.3; color: var(--paper);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.card__sub { font: 400 11px var(--mono); color: var(--mist); margin-top: 3px; }

.card--skel .card__art { background: linear-gradient(100deg, var(--panel) 30%, var(--raised) 50%, var(--panel) 70%) 0/300% 100%; animation: shim 1.3s infinite linear; }
@keyframes shim { to { background-position: -300% 0; } }

/* ---------------- cold-start boot loader ---------------- */
/* The placeholder shimmer above is the right answer for a filter change that returns in under a
   second. It is the wrong answer for the first paint on a sleeping free-tier host, where /api/home
   can take half a minute to wake and reply: three motionless grey rows for thirty seconds read as a
   dead page, not a loading one. This is what fills #catalogue on a cold load instead — the amber
   vault shard repeated as film frames that light up left to right, so the wait keeps a pulse even
   when the network has gone quiet. */
.boot { min-height: 46vh; display: grid; place-items: center; gap: 20px; padding: 40px 20px; }
.boot__strip { display: flex; gap: 9px; }
.boot__frame {
  width: 17px; height: 27px; background: var(--amber);
  clip-path: polygon(0 0, 100% 12%, 100% 88%, 0 100%);
  opacity: .16; animation: bootFill 1.4s var(--ease) infinite;
}
.boot__frame:nth-child(2) { animation-delay: .12s; }
.boot__frame:nth-child(3) { animation-delay: .24s; }
.boot__frame:nth-child(4) { animation-delay: .36s; }
.boot__frame:nth-child(5) { animation-delay: .48s; }
@keyframes bootFill {
  0%, 70%, 100% { opacity: .16; filter: drop-shadow(0 0 0 transparent); }
  25% { opacity: 1; filter: drop-shadow(0 0 16px rgba(245,165,36,.7)); }
}
.boot__hint { font: 400 12px var(--mono); color: var(--mist); letter-spacing: .05em; transition: opacity .26s var(--ease); }
.boot__hint b { color: var(--paper); font-weight: 600; }
.boot__hint.is-fading { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .boot__frame { animation: none; opacity: .6; }
  .boot__hint { transition: none; }
}

/* ---------------- the visitor's own shelves ---------------- */
/* Continue watching and My List sit between the spotlight and the catalogue — above what
   TMDB thinks is popular, because what you already chose outranks it. With nothing on the
   shelf the host collapses entirely rather than printing an empty state: home is six rows
   deep already, and a panel saying "nothing here yet" on a working page is furniture. */
.mine { padding-bottom: 0; }
.mine:empty { display: none; }
.mine:not(:empty) + .rows { padding-top: 0; }

/* A card cannot contain the button that removes it, so the two are siblings and the wrapper
   carries the card's own width. */
.slot { position: relative; flex: 0 0 auto; width: clamp(132px, 13.4vw, 176px); }
.slot .card { width: 100%; }
.grid .slot { width: auto; }

.slot__x {
  position: absolute; top: 6px; right: 6px; z-index: 2;
  width: 22px; height: 22px; display: grid; place-items: center;
  background: rgba(7,9,20,.9); border: 1px solid var(--edge); border-radius: 2px;
  color: var(--mist); backdrop-filter: blur(4px);
  opacity: 0; transform: scale(.85);
  transition: opacity .16s, transform .16s var(--ease), color .16s, border-color .16s;
}
.slot__x svg { width: 12px; height: 12px; fill: currentColor; }
.slot__x:hover { color: var(--reel); border-color: var(--reel); }
/* Revealed on approach on a pointer, and always present where there is no hover to have. */
.slot:hover .slot__x, .slot__x:focus-visible { opacity: 1; transform: none; }

/* Progress reads as a line under the poster rather than a number on it: the shelf is scanned,
   not read, and the length of the bar is the whole message. Cyan, because this is the one
   place on a card that says "this is where you are". */
.card__bar {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 1;
  height: 3px; background: rgba(7,9,20,.72);
}
.card__bar i { display: block; height: 100%; background: var(--cyan); }

/* ---------------- browse grid ---------------- */
.grid-wrap { padding: clamp(20px, 3vh, 34px) var(--gut) 40px; }
.grid-head {
  display: flex; align-items: baseline; gap: 12px;
  padding-bottom: 12px; margin-bottom: 20px; border-bottom: 1px solid var(--edge);
}
.grid-title { font-size: clamp(19px, 2.4vw, 27px); text-transform: uppercase; }
.grid-count { font: 500 11px var(--mono); color: var(--mist); letter-spacing: .08em; }
.grid {
  display: grid; gap: clamp(12px, 1.6vw, 20px);
  grid-template-columns: repeat(auto-fill, minmax(clamp(126px, 13vw, 168px), 1fr));
}
.grid .card { width: auto; }

/* Genre chips. The catalogue is 900,000 titles deep and "Films" is not a way in, so the
   filter sits directly under the head where the count is — close enough that changing a
   chip and watching the count move reads as one action. A segmented bar would be wrong at
   nineteen options: these wrap. */
.chips {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin: -8px 0 22px;
}
.chip {
  padding: 6px 11px; border-radius: 999px;
  background: var(--panel); border: 1px solid var(--edge); color: var(--mist);
  font: 500 11px var(--mono); letter-spacing: .04em; white-space: nowrap;
  transition: color .16s, border-color .16s, background .16s;
}
.chip:hover { color: var(--paper); border-color: var(--mist); }
.chip:focus-visible { outline: 2px solid var(--cyan); outline-offset: 1px; }
.chip.is-on { background: var(--amber); border-color: var(--amber); color: var(--ink); }
.empty {
  padding: 56px 0; text-align: center; color: var(--mist);
  font: 400 14px var(--body);
}
.empty b { display: block; margin-bottom: 6px; font-family: var(--display); font-size: 17px; color: var(--paper); text-transform: uppercase; }
/* The way out of an empty state. An empty state that only reports emptiness makes the visitor hunt
   the control that caused it � with a genre, a language and a platform stacked they may not find it
   at all � so wherever there is something to clear, or something to retry, the state offers it. */
.empty__act {
  display: block; margin: 16px auto 0; padding: 9px 18px;
  background: var(--raised); border: 1px solid var(--edge); border-radius: var(--r);
  font: 500 11px var(--mono); letter-spacing: .1em; text-transform: uppercase;
  color: var(--paper); cursor: pointer;
  transition: color .16s, border-color .16s, background-color .16s;
}
.empty__act:hover { color: var(--cyan); border-color: var(--cyan); }
.empty__act:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

/* ---------------- detail sheet ---------------- */
.sheet {
  position: fixed; inset: 0; z-index: 120;
  display: grid; place-items: start center;
  padding: clamp(12px, 4vh, 52px) clamp(10px, 3vw, 28px);
  background: rgba(4,6,12,.82); backdrop-filter: blur(9px);
  overflow-y: auto;
  animation: fade .22s var(--ease);
}
@keyframes fade { from { opacity: 0; } }

.sheet__card {
  position: relative; width: min(1080px, 100%);
  background: var(--panel); border: 1px solid var(--edge); border-radius: 5px;
  overflow: hidden;
  animation: rise .3s var(--ease);
}
@keyframes rise { from { opacity: 0; transform: translateY(22px); } }

.sheet__shot {
  position: relative; aspect-ratio: 16 / 7;
  background: var(--raised) center 20%/cover;
}
.sheet__shot::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(0deg, var(--panel) 1%, rgba(20,25,41,.35) 55%, rgba(20,25,41,.55));
}
.sheet__x {
  position: absolute; top: 12px; right: 12px; z-index: 3;
  width: 34px; height: 34px; display: grid; place-items: center;
  background: rgba(7,9,20,.8); border: 1px solid var(--edge); border-radius: var(--r);
  color: var(--paper); backdrop-filter: blur(6px);
  transition: background .16s, border-color .16s;
}
.sheet__x:hover { background: var(--reel); border-color: var(--reel); }
.sheet__x svg { width: 13px; height: 13px; fill: currentColor; }

.sheet__lede {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  padding: 0 clamp(18px, 3vw, 34px) clamp(14px, 2vw, 22px);
}
.sheet__title {
  font-size: clamp(24px, 4vw, 46px); line-height: .96; text-transform: uppercase;
  margin-bottom: 10px;
}
.sheet__tagline {
  font: italic 400 13.5px var(--body); color: var(--amber); margin-bottom: 10px;
}
.sheet__facts {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  font: 500 11.5px var(--mono); color: var(--mist);
}
.sheet__facts .sep { opacity: .4; }

.sheet__grid {
  display: grid; gap: clamp(20px, 3vw, 38px);
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  padding: clamp(20px, 3vw, 32px) clamp(18px, 3vw, 34px) clamp(24px, 3vw, 36px);
}
.sheet__blurb { color: #C8CEE2; margin-bottom: 22px; }

.sub {
  font: 500 10.5px var(--mono); text-transform: uppercase; letter-spacing: .16em;
  color: var(--mist); margin-bottom: 11px;
  padding-bottom: 7px; border-bottom: 1px solid var(--edge);
}

.cast { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
.cast__p { text-align: left; }
.cast__face {
  aspect-ratio: 1; border-radius: 50%; overflow: hidden; margin-bottom: 7px;
  background: var(--raised) center/cover; border: 1px solid var(--edge);
}
.cast__face img { width: 100%; height: 100%; object-fit: cover; }
.cast__n { font-size: 12px; font-weight: 600; line-height: 1.25; }
.cast__c { font: 400 10.5px var(--mono); color: var(--mist); margin-top: 2px; }

/* More like this. A scrolling strip rather than a grid, so the block stays one line tall
   however many TMDB returns — the sheet is already a long column and recommendations are
   the last thing on it, not the reason you opened it. */
.cast + .sub { margin-top: 26px; }
.like {
  display: flex; gap: 10px;
  overflow-x: auto; scroll-snap-type: x proximity;
  padding-bottom: 6px; scrollbar-width: thin; scrollbar-color: var(--edge) transparent;
}
.like::-webkit-scrollbar { height: 4px; }
.like::-webkit-scrollbar-thumb { background: var(--edge); border-radius: 2px; }
.like .card { width: clamp(94px, 8.4vw, 116px); }
.like .card__name { font-size: 12px; }
.like .card__id { display: none; }   /* too narrow to read; the sub line carries the year */

/* ---- the signature element: source rail + key manifest ---- */
.rail { display: flex; flex-direction: column; gap: 7px; margin-bottom: 24px; }
.src {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 11px;
  padding: 11px 13px; text-align: left;
  background: var(--raised); border: 1px solid var(--edge); border-radius: var(--r);
  transition: border-color .16s, background .16s, transform .14s var(--ease);
}
.src:hover { border-color: var(--mist); transform: translateX(3px); }
.src.is-live { border-color: var(--cyan); background: rgba(53,214,232,.07); }
.src__n {
  width: 22px; height: 22px; display: grid; place-items: center;
  background: var(--void); border-radius: 2px;
  font: 700 10px var(--mono); color: var(--mist);
}
.src.is-live .src__n { background: var(--cyan); color: var(--sink); }
.src__label { display: block; font-size: 13px; font-weight: 600; line-height: 1.25; }
.src__ep:not(:empty) {
  margin-left: 7px;
  font: 400 9.5px var(--mono); font-style: normal; letter-spacing: .08em;
  text-transform: uppercase; color: var(--amber);
}
.src__note {
  display: block; font: 400 10.5px var(--mono); color: var(--mist); margin-top: 2px;
  overflow-wrap: anywhere;
}
.src__q {
  padding: 3px 6px; border: 1px solid var(--edge); border-radius: 2px;
  font: 700 9px var(--mono); letter-spacing: .08em; color: var(--amber);
}
.src.is-live .src__q { border-color: var(--cyan); color: var(--cyan); }
/* Slot whose id scheme is unavailable on this title: greyed and inert. */
.src.is-off { opacity: .45; cursor: not-allowed; }
.src.is-off:hover { border-color: var(--edge); transform: none; }
.src.is-off .src__note { color: var(--amber); }

/* A band inside the rail rather than a heading above it. The rail carries three or four different
   kinds of thing — a dozen checked hosts, then the routes of one operator's API, then the Vault
   file, then the trailers — and as one unbroken run of numbered rows it read as one list of
   interchangeable sources. `.sub`'s own vocabulary at rail scale, and the rule runs out to the
   right so the label opens a section instead of floating over it. */
.rail__grp {
  display: flex; align-items: center; gap: 9px; margin: 12px 0 3px;
  font: 500 9.5px var(--mono); text-transform: uppercase; letter-spacing: .16em;
  color: var(--mist); white-space: nowrap;
}
.rail__grp:first-child { margin-top: 0; }
.rail__grp::after { content: ''; flex: 1 1 auto; height: 1px; background: var(--edge); }

/* The download surface, under the rail and deliberately not in it. Same row anatomy as a source —
   label, note, chip — so it is recognisably one of the same family of things, but the number is
   replaced by the act, and the whole row is drawn on `--sink` rather than `--raised` so it reads as
   the floor of the column rather than another slot on the wall. Its own hover moves it down instead
   of across: the rail's rows step towards the player, and this one does not go there. */
.dl {
  width: 100%;
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 11px;
  padding: 12px 13px; text-align: left; margin-bottom: 24px;
  background: var(--sink); border: 1px dashed var(--edge); border-radius: var(--r);
  transition: border-color .16s, background .16s, transform .14s var(--ease);
}
/* `minmax(0, 1fr)` and a shrinkable body, for the same reason `.src` has them: a `1fr` track keeps
   an automatic minimum, so the note � which the watch page sets to one ellipsized line � would hold
   the row wider than the column it sits in and push the whole page sideways on a phone. Each child
   then has to be allowed to shrink too, or the track has nothing to give. */
.dl__b { min-width: 0; }
.dl__b > .src__label, .dl__b > .src__note { display: block; min-width: 0; }
.dl:hover { border-color: var(--amber); background: var(--raised); transform: translateY(2px); }
.dl__i {
  width: 26px; height: 26px; display: grid; place-items: center;
  background: var(--void); border-radius: 2px; color: var(--amber);
}
.dl__i svg { width: 15px; height: 15px; fill: currentColor; }
.dl.is-off { opacity: .45; cursor: not-allowed; }
.dl.is-off:hover { border-color: var(--edge); background: var(--sink); transform: none; }
.dl.is-off .src__note { color: var(--amber); }

.manifest {
  background: var(--sink); border: 1px solid var(--edge); border-radius: var(--r);
  padding: 13px;
  font: 400 11.5px/1.75 var(--mono);
}
.manifest__row { display: grid; grid-template-columns: 88px 1fr; gap: 10px; }
.manifest__k { color: var(--mist); }
.manifest__v { color: var(--paper); word-break: break-all; }
.manifest__v.is-key { color: var(--cyan); }
.manifest__v.is-none { color: #5A6280; }
.manifest__list { margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--edge); }
.manifest__vid {
  display: flex; justify-content: space-between; gap: 10px;
  color: var(--mist);
}
.manifest__vid b { color: var(--amber); font-weight: 500; }
.copy {
  margin-top: 10px; padding: 6px 9px; width: 100%;
  border: 1px solid var(--edge); border-radius: 2px;
  font: 500 10px var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--mist);
  transition: color .16s, border-color .16s;
}
.copy:hover { color: var(--cyan); border-color: var(--cyan); }
/* The MyAnimeList block borrows the manifest's rows wholesale — same grid, same mono, same
   "not on file" — because it is answering the same kind of question about the same title. It
   only needs the trailing gap the rail above it has, since something follows it. */
.mal { margin-bottom: 24px; }
/* `.copy` was written for the manifest's <button>; here it is the link out to MAL's own page,
   so it takes the block layout a button gets for free. */
a.copy { display: block; text-align: center; text-decoration: none; }
.mal__why { margin-top: 9px; color: var(--amber); font: 400 11px/1.6 var(--mono); }

.sheet__acts { display: flex; flex-wrap: wrap; gap: 9px; margin-bottom: 24px; }

/* ---------------- id scheme switch ---------------- */
.ids {
  background: var(--panel); border: 1px solid var(--edge); border-radius: var(--r);
  padding: 12px; margin-bottom: 18px;
}
.ids__seg {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  border: 1px solid var(--edge); border-radius: var(--r); overflow: hidden;
}
.ids__b {
  padding: 8px 6px;
  font: 500 10px var(--mono); letter-spacing: .14em; text-transform: uppercase;
  color: var(--mist); background: var(--raised); border: 0; cursor: pointer;
  transition: color .16s, background-color .16s;
}
.ids__b + .ids__b { border-left: 1px solid var(--edge); }
.ids__b:hover { color: var(--paper); }
.ids__b:focus-visible { outline: 2px solid var(--cyan); outline-offset: -2px; }
.ids__b.is-on { color: var(--ink); background: var(--amber); }
/* Offered but empty: dimmed, still clickable so the gap is inspectable. */
.ids__b.is-empty { color: #5A6280; text-decoration: line-through; }
.ids__b.is-empty.is-on { color: var(--ink); background: #7A6420; text-decoration: none; }
.ids__val {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  margin-top: 10px; padding-top: 9px; border-top: 1px solid var(--edge);
}
.ids__val code { font: 400 13px var(--mono); color: var(--cyan); word-break: break-all; }
.ids__val code.is-none { color: var(--mist); }
.ids__miss { font: 400 10px var(--mono); color: var(--amber); white-space: nowrap; }

/* Which version of the stream: the id scheme, and on anime the audio track. One decision, so one
   block � a row of two controls where the column can hold them, stacked where it cannot. In the
   sheet's own narrow column they stay stacked; the watch page is the full width of the page, so there
   they pair up. The track is capped rather than `1fr`: a two-button segmented control stretched across
   a 1160px column is mostly empty plate with an amber slab in the middle of it, and the pair reads
   better at the size of the controls it holds. */
.opts { display: grid; gap: 14px; margin-bottom: 20px; }
.opt { min-width: 0; }
.opt .sub { margin-bottom: 9px; }
/* The last `.ids` in the block owns the gap, so pairing them does not leave a doubled margin. */
.opts .ids { margin-bottom: 0; }
@media (min-width: 701px) {
  body.watch .opts {
    grid-template-columns: repeat(auto-fit, minmax(190px, 280px));
    justify-content: start;
  }
}

/* ---------------- season / episode picker ---------------- */
.picker {
  background: var(--panel); border: 1px solid var(--edge); border-radius: var(--r);
  padding: 12px; margin-bottom: 18px;
}
.picker__f { display: grid; gap: 5px; min-width: 0; }
.picker__season { margin-bottom: 10px; }
.picker__k {
  font: 400 9px var(--mono); letter-spacing: .16em; text-transform: uppercase;
  color: var(--mist);
}
/* The reader's chapter switcher is the same control on a different surface, so it is the same
   rule rather than a copy of it — one place to change what a select looks like on this site. */
.picker__sel, .rd__sel {
  width: 100%; appearance: none;
  padding: 8px 26px 8px 10px;
  font: 400 12px var(--body); color: var(--paper);
  background: var(--raised) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%238C96B4'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E") no-repeat right 6px center / 14px;
  border: 1px solid var(--edge); border-radius: var(--r); cursor: pointer;
  text-overflow: ellipsis;
  transition: border-color .16s, background-color .16s;
}
.picker__sel:hover, .rd__sel:hover { border-color: var(--mist); }
.picker__sel:focus-visible, .rd__sel:focus-visible { outline: 2px solid var(--cyan); outline-offset: 1px; }
.picker__sel option, .rd__sel option { background: var(--raised); color: var(--paper); }

/* The episode picker is a grid of numbered tiles. On a long-runner it pages in blocks of a
   hundred — the range select names the block, the jump box goes straight to a number — so the
   thousand-episode scroll never happens. */
.picker__pagehead { display: flex; gap: 8px; margin-bottom: 9px; }
.picker__range { flex: 1 1 auto; }
.picker__jump { flex: 0 0 92px; background-image: none; padding-right: 10px; cursor: text; }
/* The jump and find boxes wear the select's skin minus its arrow, so the hint inside them needs the
   same muted colour the header search's placeholder uses rather than the UA default. */
.picker__jump::placeholder { color: #66708C; }
.picker__cells {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(46px, 1fr));
  gap: 6px; max-height: 320px; overflow-y: auto;
}
/* A tile reads its own three states off its classes: idle, watched (cyan, the app's "accounted
   for"), and the one selected now (the ring). Square and monospaced so the numbers line up. */
.cell {
  aspect-ratio: 1 / 1; display: grid; place-items: center;
  background: var(--void); border: 1px solid var(--edge); border-radius: var(--r);
  color: var(--mist); font: 500 12px var(--mono);
  cursor: pointer; transition: border-color .14s, background-color .14s, color .14s;
}
.cell:hover { border-color: var(--mist); color: var(--paper); }
.cell.is-on { background: var(--raised); color: var(--cyan); }
.cell.is-cur { color: var(--paper); border-color: var(--cyan); box-shadow: inset 0 0 0 1px var(--cyan); }
.cell.is-on.is-cur { color: var(--cyan); }

/* The roster: the same choice as a tile, drawn with enough of each episode to choose from. A still
   at the size a thumbnail is actually read at, the number over it, then the name and one line of
   metadata � which is the difference between picking a number and picking an episode. Where the
   tile grid scrolls inside its own 320px box, the roster does not: a list of episodes is the thing
   the page is for, so the page scrolls it rather than a box within the page. */
.picker__cells.is-rows {
  display: flex; flex-direction: column; gap: 6px;
  max-height: none; overflow: visible;
}
/* Expanded, the roster earns back its own scroll box: the whole run is reachable without the sheet
   growing past the viewport. Collapsed, its ten rows are short enough to need no cap. The tile grid
   keeps the 320px box it always had, so only the roster needs this. */
.picker__cells.is-rows.is-open { max-height: 420px; overflow-y: auto; padding-right: 2px; }
/* The reveal beneath the block. Full-width and quiet — it is a control, not a tile — carrying the
   count so the visitor knows what "more" is worth before opening it. */
.picker__more {
  width: 100%; margin-top: 8px; padding: 8px;
  background: var(--void); border: 1px solid var(--edge); border-radius: var(--r);
  font: 500 10px var(--mono); letter-spacing: .12em; text-transform: uppercase; color: var(--mist);
  cursor: pointer; transition: border-color .16s, color .16s, background-color .16s;
}
.picker__more:hover { border-color: var(--mist); color: var(--paper); background: var(--raised); }
.picker__more:focus-visible { outline: 2px solid var(--cyan); outline-offset: 1px; }
.ep {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 6px; align-items: center;
  padding: 7px;
  background: var(--void); border: 1px solid var(--edge); border-radius: var(--r);
  transition: border-color .14s, background-color .14s;
}
/* The open action fills the row; the still and body keep the two-column layout the row used to own. */
.ep__open {
  display: grid; grid-template-columns: 104px minmax(0, 1fr); gap: 11px; align-items: start;
  min-width: 0; text-align: left; color: inherit; cursor: pointer;
}
/* The same watched control the foot and the manga rows use, sat at the end of each episode. */
.ep__tick { align-self: center; }
.ep:hover { border-color: var(--mist); }
.ep.is-on { background: var(--raised); }
.ep.is-cur { border-color: var(--cyan); }
.ep__still {
  position: relative; display: block; aspect-ratio: 16 / 9;
  border-radius: 2px; overflow: hidden; background: var(--raised);
}
.ep__still img { display: block; width: 100%; height: 100%; object-fit: cover; }
.ep__no {
  position: absolute; left: 4px; bottom: 4px; padding: 1px 4px; border-radius: 2px;
  background: rgba(6, 8, 16, .78); font: 500 10px var(--mono); color: var(--paper);
}
/* An episode with no still is still an episode: the number sits on the plate it would have been
   printed over rather than over a broken image. */
.ep__still--none { display: grid; place-items: center; }
.ep__still--none .ep__no { position: static; background: none; }
.ep__body { display: block; min-width: 0; padding: 1px 2px 0 0; }
.ep__name { display: block; font-size: 12.5px; font-weight: 600; line-height: 1.3; color: var(--paper); }
.ep__meta {
  display: block; margin-top: 3px;
  font: 400 10px var(--mono); color: var(--mist); letter-spacing: .03em;
}
/* Two lines is a glance; the whole synopsis is on the title sheet, not in a chooser. */
.ep__ov {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
  margin-top: 5px; font-size: 11.5px; line-height: 1.45; color: #A9B1C9;
}
/* Watched reads the same way on a row as it does on a tile � the app's cyan, the same "accounted
   for" the shelf and the tick use. */
.ep.is-on .ep__name { color: var(--cyan); }
/* Filler classification, the same two tints the tiles carry. */
.picker__cells.is-rows .ep.is-filler { border-color: rgba(255, 170, 40, .5); }
.picker__cells.is-rows .ep.is-mixed { border-color: rgba(170, 120, 255, .5); }
.picker__foot {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  margin-top: 10px; padding-top: 9px; border-top: 1px solid var(--edge);
}
.picker__tag { font: 400 13px var(--mono); color: var(--amber); letter-spacing: .04em; }
.picker__of { font: 400 10px var(--mono); color: var(--mist); margin-left: auto; }

/* Same control the Marvel spine uses for the same claim, so a watched mark reads identically
   wherever it is made. Cyan is the app's "accounted for". */
.picker__tick {
  flex: 0 0 auto; width: 30px; height: 30px; display: grid; place-items: center;
  background: var(--void); border: 1px solid var(--edge); border-radius: var(--r);
  color: #5A6280;
  transition: color .16s, background-color .16s, border-color .16s;
}
.picker__tick svg { width: 13px; height: 13px; fill: currentColor; }
.picker__tick:hover { color: var(--paper); border-color: var(--mist); }
.picker__tick.is-on { background: var(--cyan); border-color: var(--cyan); color: var(--sink); }

/* How much of the series is accounted for. Hidden until there is a mark to report — a bar at
   zero on a show nobody has started is decoration. */
.picker__run { display: flex; align-items: center; gap: 8px; margin-top: 9px; }
.picker__bar {
  flex: 1 1 auto; height: 3px; min-width: 0;
  background: var(--void); border-radius: 2px; overflow: hidden;
}
.picker__bar i { display: block; height: 100%; background: var(--cyan); }
.picker__runtxt { flex: 0 0 auto; font: 400 10px var(--mono); color: var(--mist); }

/* ---------------- player ---------------- */
.player {
  position: fixed; inset: 0; z-index: 160;
  background: #000;
  display: grid; grid-template-rows: auto 1fr auto;
  animation: fade .2s var(--ease);
}
.player__top {
  display: flex; align-items: center; gap: 14px;
  padding: 13px clamp(14px, 3vw, 26px);
  background: linear-gradient(180deg, rgba(7,9,20,.95), transparent);
  position: absolute; top: 0; left: 0; right: 0; z-index: 4;
  transition: opacity .3s var(--ease);
}
.player.is-idle .player__top { opacity: 0; pointer-events: none; }
/* A framed source (embed, trailer, game) hides the overlay bar on an idle fullscreen so it stops
   covering the frame's own controls. `pointer-events: none` is the half that matters — a faded but
   still-clickable bar would go on eating the taps meant for the video underneath it. */
.player.is-framehide .player__top { opacity: 0; pointer-events: none; }
/* The reveal strip: a transparent band along the very top edge, above the frame, that wakes the
   chrome when the pointer crosses it. Present only while fullscreen (JS adds and removes it), and it
   drops out of the way the instant the bar is up so it never sits between the cursor and a control. */
.player__reveal {
  position: absolute; top: 0; left: 0; right: 0; height: 64px; z-index: 3;
}
.player:not(.is-framehide) .player__reveal { pointer-events: none; }
.player__back {
  width: 34px; height: 34px; display: grid; place-items: center; flex: 0 0 auto;
  border: 1px solid var(--edge); border-radius: var(--r); color: var(--paper);
  background: rgba(7,9,20,.7);
  transition: background .16s, border-color .16s;
}
.player__back:hover { background: var(--paper); color: var(--sink); border-color: var(--paper); }
.player__back svg { width: 14px; height: 14px; fill: currentColor; }
.player__meta { min-width: 0; }
.player__name {
  font: 400 15px var(--display); text-transform: uppercase; letter-spacing: .02em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.player__srcname { font: 400 10.5px var(--mono); color: var(--cyan); letter-spacing: .08em; }
.player__ep { color: var(--amber); font-weight: 400; }

/* Episode steps and the way out of a dead source, at the far end of the top bar — the opposite
   corner from Close, because these move you along inside the player where that one leaves it. */
.player__nav { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.pnav {
  height: 34px; min-width: 34px; padding: 0; display: grid; place-items: center;
  border: 1px solid var(--edge); border-radius: var(--r); color: var(--paper);
  background: rgba(7,9,20,.7);
  transition: background .16s, border-color .16s, color .16s, opacity .16s;
}
.pnav svg { width: 14px; height: 14px; fill: currentColor; }
.pnav--wide {
  padding: 0 12px;
  font: 400 10px var(--mono); letter-spacing: .12em; text-transform: uppercase;
  color: var(--cyan);
}
.pnav:hover:not(:disabled) { background: var(--paper); color: var(--sink); border-color: var(--paper); }
/* A text tab in the top bar (Spotify's "Lyrics"): quiet by default, a bright pill when it is on.
   Declared after the generic hover so it wins for a `.pnav.pnav--tab`. */
.pnav--tab {
  padding: 0 16px; border: 0; background: none; border-radius: 999px;
  font: 600 14px var(--body); letter-spacing: .01em; color: var(--mist);
}
.pnav--tab:hover:not(:disabled) { background: none; color: var(--paper); border-color: transparent; }
.pnav--tab.is-on, .pnav--tab.is-on:hover:not(:disabled) { background: var(--paper); color: var(--ink); }
/* The ad-blocker shield. Icon only — a square button the size of the episode-step controls beside
   it, so it takes no more room than a glyph needs. It inherits the base `.pnav` layout (a centred
   34px square); only the colour and the shield's own fill/stroke are set here. Armed it goes amber
   with a filled shield; off it drops to the mist outline with the slashed glyph. */
.pnav--shield.is-on { color: var(--amber); border-color: color-mix(in srgb, var(--amber) 42%, var(--edge)); }
.pnav--shield.is-on svg { fill: currentColor; stroke: none; }
.pnav--shield:not(.is-on) svg { fill: none; stroke: currentColor; }
.pnav--shield:hover:not(:disabled),
.pnav--shield.is-on:hover:not(:disabled) {
  background: var(--paper); color: var(--sink); border-color: var(--paper);
}
.pnav--shield:hover:not(:disabled) svg { fill: none; stroke: currentColor; }
.pnav--shield.is-on:hover:not(:disabled) svg { fill: currentColor; stroke: none; }
.pnav:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
/* Kept in place rather than hidden at the edges of a season, so the control does not move under
   the thumb between episode one and episode two. */
.pnav:disabled { opacity: .3; cursor: default; }

.player__stage {
  grid-row: 1 / -1; position: relative;
  display: grid; place-items: center; background: #000; overflow: hidden;
}
.player__stage video, .player__stage iframe {
  width: 100%; height: 100%; border: 0; display: block; object-fit: contain;
}
.player.has-anime-controls { grid-template-rows: minmax(0, 1fr) auto; }
.player.has-anime-controls .player__stage { grid-row: 1; min-height: 0; }
.player.has-anime-controls:fullscreen { grid-template-rows: minmax(0, 1fr); }
.player:fullscreen .anime-controls { display: none; }
.anime-controls {
  position: relative; z-index: 6;
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px 14px;
  padding: 7px clamp(10px, 2vw, 22px);
  background: var(--panel); border-top: 1px solid var(--edge);
}
.anime-controls__toggle {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 44px; padding: 8px 5px; border-radius: var(--r);
  font: 400 13px var(--body); color: var(--paper); white-space: nowrap;
}
.anime-controls__toggle:hover { background: var(--raised); }
.anime-controls__toggle:focus-visible { outline: 2px solid var(--cyan); outline-offset: 1px; }
.anime-controls__state { color: var(--mist); font-weight: 600; }
.anime-controls__toggle[aria-pressed="true"] .anime-controls__state { color: var(--cyan); }
.anime-controls__status { margin-inline-start: auto; max-width: 38ch; font: 400 11px var(--body); line-height: 1.4; color: var(--mist); }
.player__spin {
  position: absolute; width: 34px; height: 34px;
  border: 2px solid rgba(237,240,250,.2); border-top-color: var(--cyan);
  border-radius: 50%; animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Shown over an embed slot whose host is still the placeholder, so an empty cross-origin
   frame reads as "not wired up yet" instead of "the player is broken". */
.embednote {
  position: absolute; max-width: 520px; margin: 0 auto;
  padding: 22px clamp(18px, 4vw, 30px);
  background: rgba(20,25,41,.94); border: 1px solid var(--edge); border-radius: var(--r);
  text-align: center;
}
.embednote__tag {
  font: 400 9.5px var(--mono); letter-spacing: .16em; text-transform: uppercase;
  color: var(--mist); margin-bottom: 10px;
}
.embednote__url {
  display: block; font: 400 13px var(--mono); color: var(--cyan);
  word-break: break-all; margin-bottom: 14px;
}
.embednote__body { font-size: 12.5px; line-height: 1.6; color: var(--mist); margin: 0; }
.embednote__body b, .embednote__body code {
  font: 400 12px var(--mono); color: var(--amber);
}

.player__ctl {
  position: absolute; bottom: 0; left: 0; right: 0; z-index: 4;
  padding: 0 clamp(14px, 3vw, 26px) 16px;
  background: linear-gradient(0deg, rgba(7,9,20,.96), rgba(7,9,20,.55) 60%, transparent);
  transition: opacity .3s var(--ease);
}
.player.is-idle .player__ctl { opacity: 0; pointer-events: none; }

.scrub {
  position: relative; height: 16px; display: flex; align-items: center; cursor: pointer;
  margin-bottom: 4px;
}
.scrub__track { position: relative; width: 100%; height: 3px; background: rgba(237,240,250,.22); }
.scrub__buf { position: absolute; inset: 0 auto 0 0; background: rgba(237,240,250,.32); width: 0; }
.scrub__fill { position: absolute; inset: 0 auto 0 0; background: var(--amber); width: 0; }
.scrub__knob {
  position: absolute; top: 50%; left: 0; width: 11px; height: 11px;
  background: var(--amber); border-radius: 50%;
  transform: translate(-50%, -50%) scale(0);
  transition: transform .16s var(--ease);
}
.scrub:hover .scrub__knob, .scrub:focus-visible .scrub__knob { transform: translate(-50%, -50%) scale(1); }

.player__bar { display: flex; align-items: center; gap: 6px; }
.pbtn {
  width: 36px; height: 36px; display: grid; place-items: center;
  border-radius: var(--r); color: var(--paper);
  transition: background .16s, color .16s;
}
.pbtn:hover { background: rgba(237,240,250,.13); }
.pbtn svg { width: 17px; height: 17px; fill: currentColor; }
.pbtn--big svg { width: 21px; height: 21px; }
.player__time {
  font: 500 12px var(--mono); color: var(--paper); letter-spacing: .04em;
  margin: 0 8px;
}
.player__time span { color: var(--mist); }
.player__spacer { flex: 1; }
.vol { width: 74px; accent-color: var(--amber); }

.player__note {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: min(430px, 84vw); text-align: center; color: var(--mist);
  font-size: 13.5px; line-height: 1.65;
}
.player__note b {
  display: block; margin-bottom: 8px;
  font: 400 17px var(--display); text-transform: uppercase; color: var(--paper);
}
/* The auto-advance countdown line, between the message and its buttons. Amber so it reads as the
   thing about to happen, not just more copy. */
.player__count {
  margin-top: 12px; color: var(--amber);
  font: 500 11px var(--mono); letter-spacing: .1em; text-transform: uppercase;
}
/* The recovery offer under a failure. A dead source that only reports itself is half a message —
   this is the other half, so nobody has to reopen the sheet to get out of a black frame. */
.player__noteb {
  display: inline-block; margin-top: 16px; padding: 9px 15px; border-radius: 4px;
  font: 500 10px var(--mono); letter-spacing: .12em; text-transform: uppercase;
  color: var(--sink); background: var(--paper);
  transition: background .16s;
}
.player__noteb:hover { background: var(--amber); }
.player__noteb:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
.player__noteb + .player__noteb { margin-left: 8px; }

/* The second button on a note is always the lesser choice — it dismisses a guess rather than
   acting on it — so it reads as an outline against the filled primary next to it. */
.player__noteb--ghost {
  color: var(--mist); background: none;
  box-shadow: inset 0 0 0 1px var(--edge);
  transition: color .16s, box-shadow .16s;
}
.player__noteb--ghost:hover { color: var(--paper); background: none; box-shadow: inset 0 0 0 1px var(--mist); }

/* The ad-blocker disclaimer. A corner toast, not a stage-owning notice like `.player__note` — the
   video is playing (or trying to) behind it, so it sits below the top bar, out of the way, and
   removes itself after a few seconds or on the ×. It clears the 60px header the same way the resume
   toast does, since `.player__stage` spans behind the bar. */
.adnote {
  position: absolute; top: 72px; left: 18px; z-index: 6;
  max-width: min(360px, 78vw); padding: 12px 34px 12px 14px;
  background: rgba(11,14,26,.94); border: 1px solid var(--edge); border-radius: 10px;
  box-shadow: 0 14px 34px -12px rgba(4,5,11,.7); backdrop-filter: blur(8px);
  font-size: 12.5px; line-height: 1.55; color: var(--mist);
  animation: adnoteIn .18s var(--ease);
}
@keyframes adnoteIn { from { opacity: 0; transform: translateY(-6px); } }
.adnote b { color: var(--paper); font-weight: 600; }
.adnote > b:first-child { display: block; margin-bottom: 3px; font-size: 13px; }
.adnote__x {
  position: absolute; top: 8px; right: 8px; width: 20px; height: 20px;
  display: grid; place-items: center; padding: 0;
  background: none; border: 0; border-radius: 5px; color: var(--mist); cursor: pointer;
  transition: color .14s, background-color .14s;
}
.adnote__x svg { width: 14px; height: 14px; fill: currentColor; }
.adnote__x:hover { color: var(--paper); background: var(--raised); }
.adnote__x:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

/* The resume notice. Corner-anchored rather than centred like `.player__note`, because that
   one reports a failure and owns the stage while this one sits beside a film that is already
   playing — and it removes itself after a few seconds, so it must never have been in the way.

   `.player__stage` spans every grid row, so its own top-left corner is *behind* `.player__top`
   — a toast at `top: 18px` lands under the title, which is where the first attempt put it. So
   it clears the 60px header instead and takes the header's own left inset, which lines it up
   with the back button rather than with the edge of the frame. */
.player__toast {
  position: absolute; z-index: 3;
  left: clamp(14px, 3vw, 26px); top: 72px;
  display: flex; align-items: center; gap: 12px;
  max-width: calc(100% - 2 * clamp(14px, 3vw, 26px));
  padding: 9px 9px 9px 14px; border-radius: var(--r);
  background: rgba(11,14,26,.86); border: 1px solid var(--edge);
  font: 400 11px var(--mono); letter-spacing: .07em; text-transform: uppercase;
  color: var(--mist);
}
.player__toast b { color: var(--paper); font-weight: 500; }
.player__toastb {
  flex: 0 0 auto; padding: 6px 11px; border-radius: 4px;
  font: 500 10px var(--mono); letter-spacing: .11em; text-transform: uppercase;
  color: var(--sink); background: var(--paper);
  transition: background .16s;
}
.player__toastb:hover { background: var(--amber); }
.player__toastb:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
/* Anchored to the foot instead, for the one toast that is not a passing notice: the reader escape
   hatch stays for as long as the frame is open, and every book reader puts its own toolbar exactly
   where `.player__toast` sits. */
.player__toast--foot { top: auto; bottom: clamp(14px, 3vw, 26px); }

/* ---------------- footer ---------------- */
.foot {
  padding: 34px var(--gut) 46px; border-top: 1px solid var(--edge);
  color: var(--mist); font-size: 12.5px; line-height: 1.7;
}
.foot strong { color: var(--paper); font-weight: 600; }
.foot__note { margin-top: 6px; color: #616B88; font-size: 11.5px; }

/* ============================================================
   MARVEL — watch-order timelines.

   Films and Series already have a poster grid, and a grid cannot
   express sequence. So this section is a numbered spine: a
   hairline rule down the left with a numbered node pinned to it
   per title. Order is the layout, not a caption on it.

   Colour follows the house rules rather than inventing any.
   Selected collection borrows the nav's own paper-on-ink chip.
   Amber is the marquee light — it marks the one title you are up
   to. Cyan stays "on / done": the watched tick and the progress
   it fills. Reel only ever means a gap.
   ============================================================ */
.tl { padding: clamp(20px, 3vh, 34px) var(--gut) 64px; }

.tl__head { padding-bottom: 16px; border-bottom: 1px solid var(--edge); }
.tl__title {
  font-size: clamp(26px, 4vw, 44px); line-height: .96; text-transform: uppercase;
}
.tl__lede {
  font: 400 11.5px var(--mono); color: var(--mist); letter-spacing: .05em; margin-top: 11px;
}

/* ---- collection + order switches ---- */
/* Shared with the anime section's format and order switches (`.an__seg` / `.an__b`): they are
   the same control answering the same kind of question, and two sections that look alike should
   not be two rules that have to be kept alike. */
.tl__controls {
  display: flex; flex-wrap: wrap; align-items: flex-start;
  justify-content: space-between; gap: 10px;
  margin: 18px 0 20px;
}
/* Sized by its buttons, not by the row: universes have different numbers of collections —
   Marvel has seven, Star Wars five — and a switcher that stretched to fill would draw an
   empty stretch of frame after the last button on every one of them but the widest.

   It scrolls rather than clips when the buttons outrun the row, which Marvel's seven do on a
   narrow laptop as surely as on a phone. Clipping would hide "Legacy" behind a frame edge
   that looks deliberate; scrolling keeps it reachable. */
.tl__seg, .an__seg {
  display: flex; flex: 0 1 auto; min-width: 0; max-width: 100%;
  border: 1px solid var(--edge); border-radius: var(--r);
  overflow-x: auto; scrollbar-width: none;
}
.tl__seg::-webkit-scrollbar, .an__seg::-webkit-scrollbar { display: none; }
.tl__seg--order { flex: 0 0 auto; }
.tl__b, .an__b {
  flex: 0 0 auto;
  padding: 9px 13px;
  font: 500 10px var(--mono); letter-spacing: .13em; text-transform: uppercase;
  color: var(--mist); background: var(--raised);
  transition: color .16s, background-color .16s;
}
.tl__b + .tl__b, .an__b + .an__b { border-left: 1px solid var(--edge); }
.tl__b:hover, .an__b:not(:disabled):hover { color: var(--paper); }
.tl__b:focus-visible, .an__b:focus-visible { outline: 2px solid var(--cyan); outline-offset: -2px; }
/* The same paper chip the top bar uses for the section you are in — because that is
   exactly what these are. */
.tl__b.is-on, .an__b.is-on { color: var(--sink); background: var(--paper); font-weight: 700; }
.tl__b em { font-style: normal; margin-left: 7px; opacity: .62; }

/* A search is ranked by match, so the order switch has nothing to say while there is one. It is
   shown spent rather than removed: a control that vanished as you started typing would take the
   row's width with it and shift everything beside it. */
.an__seg.is-idle { opacity: .42; }
.an__b:disabled { cursor: default; }

/* ---- progress ---- */
.tl__prog { margin-bottom: 22px; }
.tl__track {
  position: relative; height: 3px; overflow: hidden;
  background: var(--edge); border-radius: 2px;
}
/* Scaled rather than resized: the fill spans the track and is squashed from the left, so
   the transition runs on the compositor instead of relaying out the bar on every frame. */
.tl__fill {
  position: absolute; inset: 0 auto 0 0; width: 100%;
  transform: scaleX(0); transform-origin: left center;
  background: var(--cyan); box-shadow: 0 0 14px -2px var(--cyan);
  transition: transform .45s var(--ease);
}
.tl__stat {
  display: flex; align-items: baseline; justify-content: space-between;
  flex-wrap: wrap; gap: 8px 14px; margin-top: 9px;
  font: 400 11px var(--mono); letter-spacing: .05em; color: var(--mist);
}
.tl__stat b { color: var(--cyan); font-weight: 500; }
.tl__statend { display: flex; align-items: baseline; gap: 14px; }
.tl__reset {
  font: 500 10px var(--mono); letter-spacing: .12em; text-transform: uppercase;
  color: var(--mist); border-bottom: 1px solid transparent;
  transition: color .16s, border-color .16s;
}
.tl__reset:hover, .tl__reset.is-armed { color: var(--reel); border-bottom-color: var(--reel); }

/* ---- next up: the surface answers its own question ---- */
/* Marked the way the source rail marks its live slot — a hairline in the state colour with
   a wash of the same behind it — so "the one you are on" looks identical whether it is a
   player slot, a spine row, or this strip. Scale carries the hierarchy, not border weight. */
.tl__next {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: center; gap: 18px;
  margin-bottom: 26px; padding: 15px 18px;
  background: rgba(245, 165, 36, .07);
  border: 1px solid var(--amber);
  border-radius: var(--r);
}
.tl__next.is-soon { background: var(--panel); border-color: var(--mist); }
.tl__next.is-clear {
  grid-template-columns: 1fr;
  background: rgba(53, 214, 232, .07); border-color: var(--cyan);
}
.tl__nextopen {
  display: grid; grid-template-columns: 62px minmax(0, 1fr);
  align-items: center; gap: 16px; min-width: 0; text-align: left;
}
.tl__nextart {
  aspect-ratio: 2 / 3; overflow: hidden;
  background: var(--raised); border: 1px solid var(--edge); border-radius: 2px;
}
.tl__nextart img { width: 100%; height: 100%; object-fit: cover; }
.tl__nextbody { min-width: 0; }
.tl__nexttag {
  display: block; font: 500 9.5px var(--mono); letter-spacing: .16em;
  text-transform: uppercase; color: var(--amber);
}
.tl__next.is-soon .tl__nexttag { color: var(--mist); }
.tl__next.is-clear .tl__nexttag { color: var(--cyan); }
.tl__nextname {
  display: block; margin-top: 7px;
  font: 400 clamp(19px, 2.2vw, 27px)/1.05 var(--display);
  text-transform: uppercase; letter-spacing: -.01em;
  transition: color .16s;
}
.tl__nextopen:hover .tl__nextname { color: var(--amber); }
.tl__nextsub {
  display: block; margin-top: 8px;
  font: 400 11px var(--mono); letter-spacing: .04em; color: var(--mist);
}
.tl__nextacts { display: flex; flex-wrap: wrap; gap: 9px; }
/* Unscoped on purpose. A disabled button has to look disabled wherever it is, and the sheet's
   primary action can now be one: a manga MangaDex has no record for has nothing to open. The two
   hover rules are put back by hand, because `:hover` still matches a disabled button — without
   these the primary would light amber under a pointer that cannot press it. */
.btn:disabled { opacity: .42; cursor: not-allowed; transform: none; }
.btn--go:disabled:hover { background: var(--paper); }
.btn--ghost:disabled:hover { border-color: var(--edge); background: rgba(20,25,41,.7); }

/* ---- the spine ---- */
.tl__spine { display: grid; gap: 7px; margin: 0; padding: 0; list-style: none; position: relative; }
/* The rule every title hangs off. Nodes are opaque, so they read as pinned to it. */
.tl__spine::before {
  content: ''; position: absolute; top: 8px; bottom: 8px; left: 23px;
  width: 1px; background: var(--edge);
}

/* Strand or year headings, sticky under the bar so you always know where you are. */
.tl__mark {
  position: sticky; top: var(--bar-h); z-index: 3;
  display: flex; align-items: center; gap: 11px;
  padding: 14px 0 9px; background: var(--void);
  font: 500 10.5px var(--mono); letter-spacing: .18em; text-transform: uppercase;
  color: var(--amber);
}
.tl__mark::after { content: ''; flex: 1; height: 1px; background: var(--edge); opacity: .8; }

.tl__row {
  position: relative;
  display: grid; grid-template-columns: 46px minmax(0, 1fr) auto; align-items: center;
}
/* One surface behind the band and its tick, so the number stays out in the gutter on the
   rule rather than boxed in with the title. */
.tl__row::after {
  content: ''; position: absolute; z-index: 0; inset: 0 0 0 46px;
  background: var(--raised); border: 1px solid var(--edge); border-radius: var(--r);
  transition: border-color .16s, background-color .16s;
}
.tl__row:hover::after { border-color: var(--mist); }

.tl__num {
  position: relative; z-index: 1; justify-self: center;
  width: 26px; height: 26px; display: grid; place-items: center;
  background: var(--void); border: 1px solid var(--edge); border-radius: 2px;
  font: 700 10px var(--mono); color: var(--mist);
  transition: color .16s, background-color .16s, border-color .16s;
}

.tl__band {
  position: relative; z-index: 1; min-width: 0;
  display: grid; grid-template-columns: 34px minmax(0, 1fr) auto;
  align-items: center; gap: 13px;
  padding: 9px 6px 9px 13px; text-align: left;
}
.tl__art {
  aspect-ratio: 2 / 3; overflow: hidden;
  background: var(--void); border-radius: 2px;
}
.tl__art img { width: 100%; height: 100%; object-fit: cover; }
.tl__meta { min-width: 0; }
.tl__name { display: block; font-size: 13.5px; font-weight: 600; line-height: 1.3; }
.tl__band:hover .tl__name { color: var(--amber); }
.tl__sub {
  display: block; margin-top: 3px;
  font: 400 10.5px var(--mono); letter-spacing: .04em; color: var(--mist);
}
.tl__flags { display: flex; align-items: center; gap: 6px; }
.tag--soon { color: var(--amber); }
.tag--gap { color: var(--reel); }

.tl__tick {
  position: relative; z-index: 1;
  width: 34px; height: 34px; margin: 0 12px 0 6px;
  display: grid; place-items: center;
  background: var(--void); border: 1px solid var(--edge); border-radius: var(--r);
  color: #5A6280;
  transition: color .16s, background-color .16s, border-color .16s;
}
.tl__tick svg { width: 14px; height: 14px; fill: currentColor; }
.tl__tick:hover { color: var(--paper); border-color: var(--mist); }
.tl__tick:disabled { opacity: .32; cursor: not-allowed; }
.tl__tick:disabled:hover { color: #5A6280; border-color: var(--edge); }

/* Watched: the node fills and the tick lights, the row recedes without vanishing. */
.tl__row.is-done::after { background: var(--panel); }
.tl__row.is-done .tl__num { background: var(--cyan); border-color: var(--cyan); color: var(--sink); }
.tl__row.is-done .tl__name { color: var(--mist); font-weight: 500; }
.tl__row.is-done .tl__art { opacity: .48; }
.tl__row.is-done .tl__tick { background: var(--cyan); border-color: var(--cyan); color: var(--sink); }

/* Where you are up to — the same title the strip above is offering. */
.tl__row.is-next::after { border-color: var(--amber); background: rgba(245,165,36,.07); }
.tl__row.is-next .tl__num { background: var(--amber); border-color: var(--amber); color: var(--sink); }

/* No TMDB match: the slot is kept, dashed and dimmed, because the order is the point. */
.tl__row.is-gap::after { border-style: dashed; }
.tl__row.is-gap .tl__art, .tl__row.is-gap .tl__meta { opacity: .58; }

/* Resolving a gap on demand. */
.tl__row.is-seeking::after {
  border-color: var(--cyan);
  animation: mvseek .9s var(--ease) infinite alternate;
}
@keyframes mvseek { to { background: rgba(53,214,232,.12); } }

@media (max-width: 700px) {
  /* The gutter narrows but the spine survives — it is the whole idea. */
  .tl__row { grid-template-columns: 34px minmax(0, 1fr) auto; }
  .tl__row::after { inset: 0 0 0 34px; }
  .tl__spine::before { left: 17px; }
  .tl__num { width: 22px; height: 22px; font-size: 9px; }

  .tl__controls { flex-direction: column; align-items: stretch; }
  /* The switcher scrolls at every width (see .tl__seg) and `align-items: stretch` above already
     gives it the column's full width, so it needs nothing here — and specifically must NOT be
     handed `max-width: none`. A scroll container whose cap is lifted is sized by its content
     instead of its frame: Star Wars' five collections measure 484px, which stretched the section
     past a 375px viewport and gave the whole page a horizontal scroll. The cap is what turns
     "wider than the row" into "scrolls inside the row". */
  /* Two buttons, though, would leave the rest of a stretched frame empty. */
  .tl__seg--order { align-self: start; }

  .tl__next { grid-template-columns: 1fr; gap: 14px; }
  .tl__nextopen { grid-template-columns: 52px minmax(0, 1fr); gap: 13px; }

  .tl__band { grid-template-columns: 30px minmax(0, 1fr); gap: 11px; padding-left: 10px; }
  .tl__flags { grid-column: 2; margin-top: 7px; }
  .tl__tick { width: 30px; height: 30px; margin: 0 8px 0 4px; }

  /* The bar wraps to two rows here and has no fixed height, so a sticky offset would
     either overlap it or float below it. Let the headings scroll away instead. */
  .tl__mark { position: static; }
}

/* ============================================================
   ANIME — the AniList catalogue.

   Two surfaces under one header: curated rows when nothing is
   filtered, a results grid the moment anything is. Both are the
   same catalogue asked differently, so the chrome above them —
   title, search, format, order, genres — never moves when they
   swap. Only the area below the chips changes.

   The spotlight is one still banner, not the home page's rotating
   stack: two surfaces moving on their own would compete for the
   same glance, and this one already has a search box asking for
   attention. The dots keep all six reachable at the visitor's own
   pace.

   Colour follows the house rules. Amber is the marquee light on
   the eyebrow, cyan is focus and the live rating, and everything
   structural is edge on panel — the same vocabulary as the
   timelines, because this is the same kind of surface.
   ============================================================ */
.an { padding: clamp(20px, 3vh, 34px) var(--gut) 64px; }

.an__head { padding-bottom: 16px; border-bottom: 1px solid var(--edge); }
.an__title {
  font-size: clamp(26px, 4vw, 44px); line-height: .96; text-transform: uppercase;
}
.an__lede {
  font: 400 11.5px var(--mono); color: var(--mist); letter-spacing: .05em; margin-top: 11px;
}

/* ---- controls ---- */
/* The search box leads and the switches trail, because a query overrides both of them: read
   left to right, the row says what it does in the order it does it. */
.an__controls {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: 12px;
  margin: 18px 0 16px;
}
.an__find { flex: 1 1 300px; max-width: 420px; min-width: 0; }
.an__find input {
  width: 100%; height: 38px; padding: 0 13px;
  background: rgba(20,25,41,.86); color: var(--paper);
  border: 1px solid var(--edge); border-radius: var(--r);
  transition: border-color .18s, background .18s;
}
.an__find input::placeholder { color: #66708C; }
.an__find input:focus { border-color: var(--cyan); background: var(--panel); outline: none; }
.an__segs { display: flex; flex-wrap: wrap; gap: 10px; }

.an .chips { margin-bottom: 22px; }

/* ---- spotlight ---- */
.an__spot {
  position: relative; display: flex; align-items: flex-end;
  min-height: clamp(300px, 46vh, 460px);
  margin-bottom: 34px; overflow: hidden;
  border: 1px solid var(--edge); border-radius: var(--r);
}
.an__spotshot {
  position: absolute; inset: 0;
  background-size: cover; background-position: center 20%;
}
.an__spotscrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(96deg, var(--void) 4%, rgba(11,14,26,.84) 38%, rgba(11,14,26,.16) 74%),
    linear-gradient(0deg, var(--void) 1%, transparent 52%);
}
.an__spotbody {
  position: relative; flex: 1 1 auto; min-width: 0; max-width: 560px;
  padding: 26px clamp(20px, 3vw, 34px) 28px;
}
.an__spotname {
  /* Capped below `.an__title`, and well below the home hero: anime titles run long — season
     numbers, subtitles and light-novel sentences — and at the hero's scale a real one wraps to
     four uppercase lines that bury the buttons under them. */
  font-size: clamp(23px, 3.2vw, 36px); line-height: .98; text-transform: uppercase;
  overflow-wrap: break-word;
  margin-bottom: 14px; text-shadow: 0 4px 40px rgba(7,9,20,.7);
}
.an__spotfacts {
  display: flex; flex-wrap: wrap; align-items: center; gap: 9px;
  font: 500 12px var(--mono); color: var(--mist); margin-bottom: 13px;
}
.an__spotfacts .sep { opacity: .4; }
.an__spotblurb {
  color: #C3CADF; margin-bottom: 22px;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.an__spotacts { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.an__spotdots {
  position: absolute; right: clamp(20px, 3vw, 34px); bottom: 30px;
  display: flex; gap: 7px;
}

/* ---- results ---- */
/* Paging is a deliberate act here, not a scroll that fires requests on its own: AniList allows
   thirty a minute, and an infinite list would spend them on titles nobody looked at. */
.an__more { display: flex; justify-content: center; margin-top: 30px; }

@media (max-width: 700px) {
  .an__controls { flex-direction: column; align-items: stretch; }
  /* `flex-basis` measures the main axis, and the main axis is now vertical — left as it is on
     the wide layout, the search box's 300px basis would become a 300px-tall input. */
  .an__find { flex: 0 0 auto; max-width: none; }
  /* Both switches stretch and scroll from the same edge as the search box above them —
     `align-items: stretch` does that on its own, and the base `max-width: 100%` has to survive:
     lifting the cap on a scroll container sizes it by its content instead of its frame, which is
     how a nine-genre chip row ends up wider than the phone it is on. */
  .an__segs { flex-direction: column; align-items: stretch; }

  /* Stack body above dots — as flex siblings they would fight for the width. */
  .an__spot {
    flex-direction: column; align-items: stretch; justify-content: flex-end;
    min-height: 0;
  }
  .an__spotbody { max-width: none; padding-bottom: 18px; }
  .an__spotdots { position: static; padding: 0 clamp(20px, 3vw, 34px) 22px; }
  .an__spotblurb { -webkit-line-clamp: 4; }
}

/* ---------------- responsive ---------------- */
@media (max-width: 900px) {
  /* minmax(0,…), not 1fr: a bare 1fr keeps an automatic minimum, so one wide child — the
     recommendations strip, a long embed URL — would stretch the track past the sheet
     instead of scrolling inside it. The two-column rule above guards this the same way. */
  .sheet__grid { grid-template-columns: minmax(0, 1fr); }
  .net { display: none; }
}
@media (max-width: 700px) {
  :root { --bar-h: 58px; }
  .bar { flex-wrap: wrap; height: auto; padding: 10px var(--gut); gap: 10px; }
  /* Nine sections — Home, Films, Series, Anime, Manga, Live TV, Books, Comics, Collections —
     measure wider than a 375px phone, so the row has to give somewhere. It scrolls rather than
     wraps or clips, the same choice `.tl__seg` documents: wrapping would push a sticky bar to
     three rows and shove the hero down, and clipping would put the last section behind an edge
     that looks deliberate. `min-width: 0` is what lets a `flex: 0 0 auto` row shrink to the
     frame at all. */
  .bar__nav {
    order: 3; width: 100%; min-width: 0;
    overflow-x: auto; scrollbar-width: none;
    /* The bar wraps here, so the nav is on its own full-width row and the desktop compensation
       above (a negative left margin) would pull it off the row's edge. */
    margin-left: 0;
  }
  .bar__nav::-webkit-scrollbar { display: none; }
  .bar__link { flex: 0 0 auto; }
  /* The sections menu leaves the scrolling row and rides the wordmark's line, pushed to the far
     right by the free space � the one thing a thumb reaches for and the nav is a swipe. */
  .bar__menu { order: 0; margin-left: auto; }
  /* Stack the body above the dots — as flex siblings they'd otherwise fight for width. */
  .hero {
    flex-direction: column; align-items: stretch; justify-content: flex-end;
    margin-top: 0; min-height: 0; padding-top: 20px;
  }
  .hero__body { max-width: none; padding-bottom: 20px; }
  .hero__dots { position: static; padding: 0 var(--gut) 22px; }
  .hero__blurb { -webkit-line-clamp: 4; }
  .btn { height: 40px; padding: 0 15px; font-size: 13px; }
  .player__time { margin: 0 3px; font-size: 11px; }
  .vol { display: none; }
  /* The top bar has four things competing for a phone's width, and the title is the one that can
     give: the nav keeps its full size because it is what fingers aim at. "Next source" loses its
     words and keeps its position, since the failure note it belongs to spells the offer out. */
  .player__top { gap: 9px; padding: 10px 12px; }
  .player__name { font-size: 13px; }
  .player__nav { gap: 5px; }
  .pnav--wide {
    min-width: 34px; padding: 0; font-size: 0; letter-spacing: 0;
  }
  .pnav--wide::before { font: 400 15px var(--mono); letter-spacing: 0; content: '⇄'; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .hero__shot { transform: none; }
}

/* ---------------- manga ---------------- */
/* The section root is `class="an mg"`, so every rule in the anime block above already applies to
   it — the two surfaces are the same composition down to the spotlight dots, and there is no
   second copy of any of it to keep in step.

   One thing genuinely differs. AniList files a banner on far fewer manga than anime, so the
   spotlight is the one place the two come apart: an empty `background-image` there reads as a hole
   in the page rather than as a photograph, so the shot sits on a panel wash and the scrim goes on
   doing its job over the top of it. */
.mg .an__spotshot { background-color: var(--panel); background-position: center 16%; }

/* ---------------- the reader ---------------- */
/* Read rather than Operate: the artifact leads and the interface recedes. One centred column of
   pages on black, a hairline saying how far down it you are, and a bar that leaves after two and a
   half seconds. Everything below is either a page or something that fades.

   The stage centres and clips its one child for a video or a frame. A column of pages is taller
   than the viewport by definition, so a reading player asks it to scroll instead — and to align its
   child to the top, because `place-items: center` on a tall column would open chapter one halfway
   down. */
.player.is-reading .player__stage {
  overflow-y: auto; overflow-x: hidden;
  place-items: start center;
  overscroll-behavior-y: contain;
  scrollbar-width: thin; scrollbar-color: var(--edge) transparent;
}

/* Paged mode moves the scrolling one level in, onto the track itself, so the stage stops being a
   scroller and starts being a frame: one page, screen-sized, nothing above or below it to reach.
   The explicit row is what makes that true. Left to size itself the stage's grid row takes the
   column's own height, and every `height: 100%` below it then resolves against the content it was
   supposed to be constraining — a "one screen tall" page 300px taller than the screen. */
.player.is-paged .player__stage { overflow: hidden; place-items: stretch; grid-template-rows: 100%; }

.rd { width: 100%; padding-bottom: clamp(56px, 11vh, 96px); }

/* `margin-inline: auto` and not the stage's `place-items` — `.rd` is full-width by design (the
   rail rides its left edge, the bar its bottom), so centring it centres nothing. The column is
   the thing with a width, and so the column is the thing that has to be centred: without this a
   860px page hangs off the left of a 2560px monitor. */
.rd__col { display: flex; flex-direction: column; align-items: center; margin-inline: auto; }

/* Fit width caps at a page's own natural width rather than the viewport's: a 900px scan blown up
   across a 2560px monitor is softer, not bigger. Fit height gives one page per screen, which is
   what a double-page spread was drawn for. */
.rd--width .rd__col { width: min(100%, 860px); }
.rd--width .rd__page img { width: 100%; height: auto; }
.rd--height .rd__page { width: auto; height: 100vh; }
.rd--height .rd__page img { height: 100%; width: auto; max-width: 100vw; }

/* Paged: the column becomes a track of screen-wide cells and the browser's own scroll-snap does the
   turning. That is the whole reason this is snap and not a touch handler — a swipe, a trackpad
   flick, a shift-wheel and a dragged scrollbar all already mean "next page" to the platform, and
   none of them mean it to code we would have written.
   Right to left is the `direction` property and nothing more: flex lays the cells out from the
   right, `scrollLeft` runs negative away from the origin, and page one is where a manga's page one
   belongs. `scroll-snap-stop: always` is what stops a hard flick from skipping four pages. */
.rd--paged { height: 100%; padding-bottom: 0; }
.rd--paged .rd__col {
  flex-direction: row; align-items: center;
  width: 100%; height: 100%;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.rd--paged .rd__col::-webkit-scrollbar { display: none; }
.rd--rtl .rd__col { direction: rtl; }
.rd--paged .rd__page,
.rd--paged .rd__end {
  flex: 0 0 100%; width: 100%; height: 100%;
  aspect-ratio: auto; background: none;
  scroll-snap-align: center; scroll-snap-stop: always;
}
/* The cell is a grid, and its one row is content-sized unless told otherwise — which is what makes
   `height: 100%` on the image inside it resolve against the image's own height and hand back exactly
   the overflow the cell was sized to prevent. */
.rd--paged .rd__page { grid-template-rows: 100%; }
/* Contained rather than filled, because one page per screen is what paged means — a page taller
   than the screen would put a vertical scroll inside a horizontal one, which is a reader that
   fights the hand using it. The fit toggle stays with the scrolling mode, where it means something.
   `object-fit` and not `max-height: 100%`: the cell is a grid whose single row is content-sized, so
   a percentage max-height would resolve against the image it was meant to be limiting. Letterboxing
   the image inside a cell-sized box asks no percentage question at all. */
.rd--paged .rd__page img {
  width: 100%; height: 100%;
  object-fit: contain; object-position: center;
}
/* The band is prose. It sits in an RTL track and reads left to right regardless, and in a cell it
   now owns outright it is centred in the screen rather than pinned to the top of one — the border
   and the gradient go, because there is nothing above it any more to be divided from. */
.rd--paged .rd__end {
  direction: ltr; justify-content: center;
  border-top: 0; background: none;
  padding-block: clamp(38px, 9vh, 74px);
}
@media (prefers-reduced-motion: no-preference) {
  .rd--paged .rd__col { scroll-behavior: smooth; }
}

/* A page holds its shape before its bytes arrive. Without the reserved ratio a late image would
   shove the column down under someone reading the page above it, which is the one thing a
   scrolling reader must never do. 1:1.45 is close enough to a B5 tankŝbon page that the correction
   on load is invisible. */
.rd__page {
  position: relative; width: 100%;
  aspect-ratio: 1 / 1.45;
  background: #05060d;
  display: grid; place-items: center;
}
.rd__page img { display: block; opacity: 0; transition: opacity .3s var(--ease); }
.rd__page.is-in { aspect-ratio: auto; background: none; }
.rd__page.is-in img { opacity: 1; }

/* A page that will not load keeps its reserved box rather than collapsing, so the column still
   reads as "page 14 is missing" instead of quietly renumbering everything after it. While a page is
   quietly re-asking its source, the box holds a pulse instead of prose � the page either arrives or
   graduates to the error state, and a reader mid-chapter does not need a paragraph about a second
   chance. The error state offers a Retry button, because "the CDN hiccuped" is a fixable thing and
   a dead box would say nothing can be done. */
.rd__page.is-flaky { background: rgba(237, 240, 250, .04); }
.rd__page.is-flaky::after {
  content: ''; width: 4px; height: 4px; border-radius: 50%;
  background: var(--mist); animation: rd-pulse 1.1s ease-in-out infinite;
}
@keyframes rd-pulse { 50% { opacity: .25; } }
.rd__page.is-bad { aspect-ratio: 1 / 1.45; border: 1px dashed var(--edge); }
.rd__bad {
  margin: 0; display: flex; flex-direction: column; align-items: center; gap: 10px;
  font: 400 12px var(--mono); color: var(--amber); letter-spacing: .04em;
}
.rd__retry {
  font: 600 11px var(--mono); letter-spacing: .07em; color: var(--paper);
  background: none; border: 1px solid var(--edge); border-radius: var(--r);
  padding: 7px 14px; cursor: pointer; transition: color .15s, border-color .15s;
}
.rd__retry:hover { color: var(--cyan); border-color: var(--cyan); }
.rd__note .rd__again { margin-top: 18px; }

.rd__num {
  position: absolute; right: 8px; bottom: 8px;
  padding: 2px 6px; border-radius: var(--r);
  font: 400 10px var(--mono); color: var(--mist); letter-spacing: .06em;
  background: rgba(7,9,20,.72);
  transition: opacity .3s var(--ease);
}
.player.is-idle .rd__num { opacity: 0; }

/* The one piece of chrome that is never in the way, so it is the one piece that never leaves: a
   hairline on the left edge saying how far down the chapter you are. Same idiom and the same cyan
   as `.picker__bar`, because it is the same claim measured continuously. */
.rd__rail {
  position: absolute; left: 0; top: 0; bottom: 0; width: 2px; z-index: 5;
  background: rgba(237,240,250,.08); pointer-events: none;
}
/* Scaled rather than grown. The fill is written on every scroll event of a page column that can
   be forty screens tall, so it is the one piece of chrome that must not cost a layout to move:
   `transform` on a full-height bar with a top origin says the same thing on the compositor. */
.rd__rail i {
  display: block; width: 100%; height: 100%;
  transform: scaleY(0); transform-origin: top center;
  background: var(--cyan); transition: transform .12s linear;
}

/* The rail turns with the reading axis, because it answers the same question in whichever direction
   the answer now runs: a vertical hairline beside pages moving sideways would be measuring an axis
   nobody is travelling. It keeps `z-index: 5` against the bar's 4, so the last two pixels of the
   screen are the progress line and not the bar's gradient. */
.player.is-paged .rd__rail { top: auto; bottom: 0; left: 0; right: 0; width: auto; height: 2px; }
.player.is-paged .rd__rail i {
  width: 100%; height: 100%;
  transform: scaleX(0); transform-origin: left center;
  transition: transform .12s linear;
}
.player.is-paged.is-rtl .rd__rail i { transform-origin: right center; }

/* Modelled on `.player__ctl` and fading with the same `is-idle` class the top bar uses, so the
   interface arrives and leaves as one thing rather than two halves disagreeing about whether it is
   present. */
.rd__bar {
  position: absolute; bottom: 0; left: 0; right: 0; z-index: 4;
  display: flex; align-items: center; gap: 6px;
  padding: 14px clamp(14px, 3vw, 26px) 16px;
  background: linear-gradient(0deg, rgba(7,9,20,.96), rgba(7,9,20,.55) 60%, transparent);
  transition: opacity .3s var(--ease);
}
.player.is-idle .rd__bar { opacity: 0; pointer-events: none; }

/* The select is `width: 100%` in the sheet, where it fills a column. Here it is one item in a full
   bar, so it takes a share instead — wide enough for `v3 Ch. 24 · Title`, never wide enough to push
   the next-chapter button off a phone. */
.rd__sel { width: auto; flex: 0 1 300px; min-width: 96px; }
.rd__facts {
  min-width: 0;
  font: 400 10.5px var(--mono); color: var(--mist); letter-spacing: .05em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* The one number in the bar that moves while you read, so it is the one thing in the bar printed in
   `--paper` rather than `--mist` — a readout, not a label. Tabular figures because a counter whose
   digits change width is a counter that twitches sideways from 9 to 10, and the eye goes to the
   movement instead of the number. `:empty` rather than a hidden flag: a chapter whose pages MangaDex
   will not serve has no page to be on, and the element says so by having nothing in it. */
.rd__pos {
  flex: 0 0 auto;
  font: 500 11px var(--mono); color: var(--paper); letter-spacing: .06em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.rd__pos:empty { display: none; }
.rd__bar .picker__tick { background: rgba(7,9,20,.7); }

/* A word rather than a glyph. Three states is one too many for an icon to carry honestly — an arrow
   alone cannot say "and this one is continuous" — so the button prints the direction it advances in
   and the sentence lives on the hover. Mono, because it is a readout as much as a control. */
.rd__mode {
  width: auto; min-width: 0; padding: 0 11px; gap: 0;
  font: 500 11px var(--mono); color: var(--paper);
  letter-spacing: .07em; white-space: nowrap;
}
.rd__mode:hover { color: var(--cyan); }

/* Both notices in the column — "opening chapter…" and "chapter unavailable" — read as
   `.player__note` does, because they say the same kind of thing. They are in the flow rather than
   centred over the stage, since the stage is a scroll container here and an absolutely centred
   notice would scroll away from centre the moment anything else arrived. */
.rd__note {
  width: min(430px, 84vw); margin: clamp(56px, 20vh, 150px) auto;
  text-align: center; color: var(--mist); font-size: 13.5px; line-height: 1.65;
}
.rd__note b {
  display: block; margin-bottom: 8px;
  font: 400 17px var(--display); text-transform: uppercase; color: var(--paper);
}

/* Finishing a chapter is a moment, so the column ends in a surface rather than in the next
   chapter's first page arriving unannounced. The scanlation credit lands here as well as on every
   row of the sheet's list, because this is where a reader has just finished reading it. */
.rd__end {
  width: 100%;
  padding: clamp(38px, 9vh, 74px) clamp(18px, 4vw, 34px) clamp(46px, 10vh, 84px);
  display: flex; flex-direction: column; align-items: center; gap: 11px;
  text-align: center;
  border-top: 1px solid var(--edge);
  background: linear-gradient(180deg, transparent, rgba(20,25,41,.55));
}
.rd__endk {
  margin: 0; font: 400 clamp(19px, 3vw, 27px) var(--display);
  text-transform: uppercase; letter-spacing: .02em; color: var(--paper);
}
.rd__endg {
  margin: 0; max-width: 46ch;
  font: 400 11px var(--mono); color: var(--mist); letter-spacing: .04em; line-height: 1.7;
}
.rd__end .btn { margin-top: 7px; }

/* ---------------- the chapter list ---------------- */
/* It stands where `.picker` stands on a series and shares its panel, its select, its tick and its
   progress bar — the same controls, because a chapter and an episode are the same kind of thing to
   mark off. What differs is that the roster is the control: MangaDex publishes what it publishes,
   with gaps, decimals and several groups on one number, so there is nothing here to reduce to two
   dropdowns. */
.chs {
  background: var(--panel); border: 1px solid var(--edge); border-radius: var(--r);
  padding: 12px; margin-bottom: 18px;
}
.chs__head { display: flex; align-items: flex-end; gap: 10px; }
.chs__head .picker__f { flex: 0 1 220px; min-width: 0; }
.chs__of {
  flex: 0 0 auto; margin-left: auto; white-space: nowrap;
  font: 400 10px var(--mono); color: var(--mist); letter-spacing: .04em;
}

/* Four hundred chapters is a normal number, so the list scrolls inside the panel rather than
   pushing the rail below it off the bottom of the sheet. */
.chs__list {
  max-height: min(46vh, 420px); overflow-y: auto;
  margin-top: 11px; padding-top: 10px; border-top: 1px solid var(--edge);
  display: flex; flex-direction: column; gap: 5px;
  scrollbar-width: thin; scrollbar-color: var(--edge) transparent;
  overscroll-behavior-y: contain;
}
.chs__row {
  display: flex; align-items: center; gap: 8px;
  background: var(--void); border: 1px solid transparent; border-radius: var(--r);
  transition: border-color .16s, background-color .16s;
}
.chs__row:hover { border-color: var(--edge); }
.chs__go {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; gap: 10px;
  padding: 8px 4px 8px 9px; text-align: left; color: var(--paper);
}

/* The number is the thing being looked for in a list of four hundred, so it is fixed-width mono and
   the first thing on the row — a column to scan down rather than text to read across. */
.chs__n {
  flex: 0 0 auto; min-width: 62px;
  font: 500 12px var(--mono); color: var(--amber); letter-spacing: .03em;
}
.chs__b { min-width: 0; display: grid; gap: 2px; }
.chs__t {
  font-size: 12.5px; line-height: 1.3; color: var(--paper);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The scanlation credit MangaDex's terms ask for, at the granularity it is actually owed at: the
   group that translated this chapter, beside the page count that says whether it is readable. */
.chs__grp {
  font: 400 10px var(--mono); color: var(--mist); letter-spacing: .04em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chs__tick { margin-right: 5px; }

/* Read is stated once, on the tick, and echoed by draining the row rather than by a second badge.
   Where the reader is *now* is the louder mark, because it is the one thing on this list that the
   visitor did not already know. */
.chs__row.is-read .chs__n { color: var(--mist); }
.chs__row.is-read .chs__t { color: #A6AFC8; }
.chs__row.is-at { border-color: var(--cyan); background: rgba(53,214,232,.07); }
.chs__row.is-at .chs__n { color: var(--cyan); }

.chs__why {
  margin: 0; padding: 18px 4px 12px; text-align: center;
  font-size: 12.5px; line-height: 1.65; color: var(--mist);
}
.chs__why b {
  display: block; margin-bottom: 6px;
  font: 400 14px var(--display); text-transform: uppercase; color: var(--paper);
}

/* Licensed chapters. MangaDex lists them and serves no pages for them, so they are links to the
   publisher — and they are drawn as links, in amber rather than cyan, because presenting them as
   reader slots with nothing behind them would be the one dishonest thing this panel could do. */
.chs__ext { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--edge); }
.chs__k {
  margin: 0 0 7px;
  font: 400 9px var(--mono); letter-spacing: .16em; text-transform: uppercase; color: var(--mist);
}
.chs__row--out {
  padding: 8px 9px; margin-bottom: 5px; text-decoration: none;
  border-color: var(--edge); background: none;
}
.chs__row--out:hover { border-color: var(--amber); background: rgba(245,165,36,.06); }
.chs__row--out .chs__n { color: var(--mist); }
.chs__row--out:hover .chs__n { color: var(--amber); }
.chs__row--out .chs__b { flex: 1 1 auto; }
.chs__out { flex: 0 0 auto; display: grid; place-items: center; color: var(--mist); }
.chs__out svg { width: 13px; height: 13px; fill: currentColor; }
.chs__row--out:hover .chs__out { color: var(--amber); }

@media (max-width: 700px) {
  /* Edge to edge. On a phone the page *is* the viewport, and 860px of cap would only mean letterbox
     bars either side of something already narrower than the screen. */
  .rd--width .rd__col { width: 100%; }

  /* Eight slots do not cross a phone in one line, and left to arithmetic the one that wraps is the
     next-chapter button — which lands alone under prev and reads as a broken bar rather than a
     wrapped one. So the three rows are chosen: the chapter row keeps its two arrows flanking the
     select and the page counter the way the wide bar does, the facts line takes the full width it
     needs to print the scanlation credit rather than ellipsing it away, and the reading controls sit
     last, at the edge a thumb reaches. Ordering rather than reflowing, because source order is what
     the wide bar needs.
     The spacer that holds the right-hand group out on a wide bar is exactly what a wrapping one
     cannot afford: given a flex line to fill it takes a whole one. */
  .rd__bar { flex-wrap: wrap; justify-content: center; row-gap: 8px; padding-bottom: 12px; }
  .rd__bar .player__spacer { display: none; }
  .rd__sel { flex: 1 1 96px; }
  .rd__bar [data-r="next"] { order: 1; }
  .rd__facts { order: 9; flex: 1 1 100%; white-space: normal; text-align: center; }
  .rd__mode, .rd__bar .picker__tick, .rd__bar [data-r="fit"] { order: 9; }

  /* The sheet is a single column here, so the list can afford more of the screen — and it needs it,
     because the tick and the row it belongs to are now the whole width apart. */
  .chs__list { max-height: 52vh; }
  .chs__head { flex-wrap: wrap; }
  .chs__head .picker__f { flex: 1 1 100%; }
  /* The count keeps its own short line and stays right-aligned there; it is smaller than a control
     and must not sit under the thumb's target the way the fields do. */
  .chs__of { margin-left: auto; }
}

/* ---------------- not found ---------------- */
/* A dead end is the one screen nobody chose to visit, so it earns none of the site's noise: no
   poster wall, no shelves, nothing to scroll. Left-aligned rather than centred, because the two
   things a person reads here — the address that failed and the way out — are a sentence and a
   row of buttons, and centring makes both harder to scan than the grid they came from.

   The status code is an eyebrow rather than the giant outlined numeral the genre expects. That
   numeral was tried and cut: at any size subtle enough not to shout it disappears into --void,
   and at any weight loud enough to be legible it makes a typo feel like a fault. This is the
   idiom the timelines already use — a mono line naming the kind of thing, a display heading
   naming the thing — and --reel is already the site's colour for a failure, worn by the offline
   dot and the timeline's gap tags. */
.lost {
  padding: clamp(44px, 11vh, 116px) var(--gut) clamp(64px, 14vh, 148px);
  max-width: 720px;
}
.lost__code {
  font: 500 11px var(--mono); letter-spacing: .14em; text-transform: uppercase;
  color: var(--reel);
}
.lost__h {
  margin-top: 10px;
  font-size: clamp(26px, 6vw, 44px); line-height: 1.04; text-transform: uppercase;
}
.lost__path {
  margin-top: 16px;
  display: flex; gap: 8px; align-items: baseline;
  color: var(--mist); font: 400 12px var(--mono);
}
.lost__path code {
  /* An address is arbitrary length and arbitrary content, so it wraps rather than pushing the
     page sideways, and it keeps the panel behind it so it reads as quoted input. */
  min-width: 0; padding: 3px 7px; border-radius: var(--r);
  background: var(--panel); border: 1px solid var(--edge); color: var(--paper);
  font: inherit; overflow-wrap: anywhere;
}
.lost__say { margin-top: 18px; max-width: 46ch; color: var(--mist); font-size: 14px; }
.lost__try {
  margin-top: 22px; padding: 9px 15px; border-radius: var(--r);
  background: var(--amber); color: var(--ink);
  font: 600 12px var(--mono); letter-spacing: .04em;
  transition: filter .16s var(--ease);
}
.lost__try:hover { filter: brightness(1.08); }
.lost__exits { margin-top: 28px; padding-top: 22px; border-top: 1px solid var(--edge); }


/* ---------------- browse by platform ---------------- */
/* A platform tile is a poster's sibling, not a poster: it rides the same .reel, keeps the same
   lift-on-hover. TMDB serves provider logos as full-bleed app icons that already carry their own
   brand colour, so the plate is dark (--panel) and the logo covers it edge to edge rather than
   floating on a bright --paper square — a wall of white tiles reads as noise in a dark UI. Squarer
   than a poster and narrower, because the name lives under it and the logo does the identifying.
   Where TMDB has no logo, two mono letters stand in on the same dark plate so the row never shows
   a hole. */
.ott {
  flex: 0 0 auto; width: clamp(104px, 11vw, 132px);
  scroll-snap-align: start; text-align: center;
  transition: transform .22s var(--ease);
}
.ott:hover, .ott:focus-visible { transform: translateY(-5px); }
.ott:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; }
.ott__plate {
  display: grid; place-items: center; aspect-ratio: 1 / 1;
  background: var(--panel);
  border: 1px solid var(--edge); border-radius: var(--r); overflow: hidden;
  transition: border-color .2s, box-shadow .22s;
}
.ott:hover .ott__plate, .ott:focus-visible .ott__plate {
  border-color: var(--amber);
  box-shadow: 0 14px 34px -12px rgba(7,9,20,.9), 0 0 0 1px var(--amber);
}
.ott__plate img { width: 100%; height: 100%; object-fit: cover; }
.ott__mono {
  font: 700 26px var(--mono); letter-spacing: .04em; color: var(--mist);
  text-transform: uppercase;
}
.ott__name {
  display: block; margin-top: 9px;
  font-size: 12px; font-weight: 600; line-height: 1.3; color: var(--paper);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* The active-platform banner sits above the grid it filters. It reads as one line — a removable
   platform pill on the left, a Films / Series toggle on the right — because a service carries both
   and the visitor should not be stranded on films alone. The pill clears the filter; the toggle
   swaps which half of the catalogue the grid shows. */
.gp {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-bottom: 18px;
}
.gp__on { font: 500 11px var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--mist); }
.gp__pill {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 5px 10px 5px 6px; cursor: pointer;
  background: var(--panel); border: 1px solid var(--edge); border-radius: var(--r);
  transition: border-color .16s, background .16s;
}
.gp__pill:hover { border-color: var(--reel); background: var(--raised); }
.gp__pill:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
.gp__logo {
  width: 26px; height: 26px; object-fit: cover;
  border-radius: 5px;
}
.gp__name { font-size: 13px; font-weight: 600; color: var(--paper); }
.gp__x { display: inline-flex; color: var(--mist); transition: color .16s; }
.gp__x svg { width: 12px; height: 12px; fill: currentColor; }
.gp__pill:hover .gp__x { color: var(--reel); }
.gp__seg { display: inline-flex; gap: 6px; margin-left: auto; }

/* ================= Live TV · Books · Comics ================= */
/* All three reuse the anime section's frame (`.an` on the host), so only what differs is here:
   the channel tile — a wide logo plate, not a 2:3 poster — the country <select>, the comics
   browse-only note, and the comic reference modal that opens in the sheet shell. */

/* ---- Live TV: the country picker + the channel tile ---- */
.lv__select {
  height: 38px; padding: 0 30px 0 12px; min-width: 0; max-width: 260px;
  background: rgba(20,25,41,.86); color: var(--paper);
  border: 1px solid var(--edge); border-radius: var(--r);
  font: inherit; transition: border-color .18s, background .18s;
}
.lv__select:focus { border-color: var(--cyan); background: var(--panel); outline: none; }
/* Below 700px `.an__controls` becomes a column, and the 260px cap above — there to stop one control
   eating a wide row — then leaves the select 68px short of the search box directly over it, which
   reads as a misaligned edge rather than a chosen size. In a stacked column the frame is the cap.
   Written here rather than beside the other ≤700 stacking rules because those come earlier in the
   file, and a later flat rule of equal specificity would win. */
@media (max-width: 700px) {
  .lv__select { max-width: none; }
}

/* Wider tracks than the poster grid: a logo plate is landscape, so a 2:3 column would strand it
   in a tall box of empty plate. */
.lv__grid {
  grid-template-columns: repeat(auto-fill, minmax(clamp(150px, 15vw, 202px), 1fr));
}

.lv__tile {
  position: relative; flex: 0 0 auto;
  width: clamp(160px, 16vw, 210px);
  scroll-snap-align: start; text-align: left;
  transition: transform .22s var(--ease);
}
.lv__grid .lv__tile { width: auto; }
.lv__tile:hover, .lv__tile:focus-visible { transform: translateY(-5px); }

.lv__plate {
  position: relative; aspect-ratio: 16 / 10;
  display: grid; place-items: center;
  background: var(--panel); border: 1px solid var(--edge); border-radius: var(--r);
  overflow: hidden; transition: border-color .2s, box-shadow .22s;
}
.lv__tile:hover .lv__plate, .lv__tile:focus-visible .lv__plate {
  border-color: var(--amber);
  box-shadow: 0 14px 34px -12px rgba(7,9,20,.9), 0 0 0 1px var(--amber);
}
.lv__plate img { max-width: 78%; max-height: 64%; object-fit: contain; }
.lv__blank { font: 700 26px var(--display); color: var(--mist); text-transform: uppercase; }
.lv__live {
  position: absolute; top: 6px; right: 6px;
  padding: 2px 6px; background: var(--reel); color: #fff;
  font: 700 8.5px var(--mono); letter-spacing: .1em; border-radius: 2px;
}

.lv__meta { padding: 9px 2px 0; }
.lv__name {
  font-size: 13px; font-weight: 600; line-height: 1.3; color: var(--paper);
  display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden;
}
.lv__sub {
  font: 400 11px var(--mono); color: var(--mist); margin-top: 3px;
  display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden;
}

/* ---- The escape hatch, shared by Live TV and Books ----
   Both sections are somebody else's catalogue and neither is complete, so each carries an offer of
   somewhere else to look — Live TV's is a *list* of directories (a stream directory moves and dies,
   so a second mirror is a hedge), Books' is one. It sits under the section's own controls — the
   search box and the filter chips — rather than at the foot, which is the whole point of it: the gap
   is why most visitors leave these two surfaces, and an offer they only meet after 4000 tiles is an
   offer made too late. Below the chips rather than above them because the question is one a visitor
   only has after trying both ways of looking, and because splitting the box from its own filters to
   make room for a notice is a worse trade than following them with it.

   So it reads as a notice, not a feature — one line of the section's own display type, one line of
   prose, and one action group on the right, on the same `--panel`/`--edge` plate the Comics scope
   note uses. Sentence left, actions right; it takes a single row of height and never competes with
   the shelves under it. A section with one directory shows one plain "Watch there"; a section with
   two shows one button per directory, named so the two offers read as distinct rather than as a
   broken sentence. There is no input and no address, deliberately: the destinations are configured
   on the server, and an address printed on the page is an address that gets typed into another tab —
   which is the one thing routing it through our own player exists to prevent. */
.ask {
  display: flex; align-items: center; justify-content: space-between; gap: 12px 22px;
  margin: 2px 0 20px; padding: 13px 16px;
  background: var(--panel); border: 1px solid var(--edge); border-radius: var(--r);
}
.ask__q {
  font: 400 clamp(15px, 1.5vw, 17px)/1.2 var(--display); text-transform: uppercase;
  letter-spacing: .01em; color: var(--paper);
}
.ask__d {
  max-width: 66ch; margin-top: 5px;
  color: var(--mist); font-size: 13px; line-height: 1.6;
}
.ask__acts {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: flex-end;
  flex-wrap: wrap; gap: 8px;
}
/* The bar sits under the filter chips, so what follows it is the shelf wall itself — and `.rows` opens
   with clamp(24px, 4vh, 44px) of its own padding, which stacked on the bar's 20px reads as a dropped
   section rather than as a spacing choice. `:not([hidden])` is the load-bearing part: with no fallback
   configured the bar is never revealed, and the chips-to-shelves rhythm then has to be exactly the one
   every other section keeps. */
.ask:not([hidden]) + .rows { padding-top: clamp(10px, 1.6vh, 18px); }

@media (max-width: 620px) {
  .ask { flex-direction: column; align-items: stretch; }
  .ask__acts { width: 100%; flex-direction: column; }
  .ask__go { width: 100%; justify-content: center; }
}

/* ---- Comics: the browse-only note, and the reference modal ---- */
/* The scope statement, and the one place on the site that has to pre-empt a question rather than
   answer one: a visitor arriving from Films or Manga has every reason to expect a reader here.
   It takes the same shape as `.player__note` — mist prose with the load-bearing clause in
   `--paper` — rather than an accent bar, because the sentence is what carries this, and the
   section eyebrow directly above it is already amber. */
.cx__note {
  margin: 4px 0 22px; padding: 12px 15px;
  background: var(--panel); border: 1px solid var(--edge);
  border-radius: var(--r); color: var(--mist); font-size: 13px; line-height: 1.6;
}
.cx__note b { font-weight: 600; color: var(--paper); }

.cx__card { width: min(760px, 100%); }
.cx__hd {
  display: grid; grid-template-columns: 150px minmax(0, 1fr); gap: clamp(16px, 2.4vw, 26px);
  padding: clamp(20px, 3vw, 32px);
}
.cx__cover {
  aspect-ratio: 2 / 3; display: grid; place-items: center; overflow: hidden;
  background: var(--raised); border: 1px solid var(--edge); border-radius: var(--r);
}
.cx__cover img { width: 100%; height: 100%; object-fit: cover; }
.cx__title {
  font-size: clamp(21px, 3vw, 30px); line-height: 1; text-transform: uppercase;
  margin: 8px 0 10px;
}
.cx__run { font: 500 12px var(--mono); color: var(--mist); letter-spacing: .04em; margin-bottom: 14px; }
.cx__desc {
  color: #C3CADF; font-size: 13.5px; line-height: 1.66; margin-bottom: 12px;
  display: -webkit-box; -webkit-line-clamp: 6; -webkit-box-orient: vertical; overflow: hidden;
}
.cx__desc a { color: var(--cyan); }
.cx__legal { font: 400 11px var(--mono); color: var(--mist); letter-spacing: .03em; }

.cx__h {
  padding: 0 clamp(20px, 3vw, 32px); margin: 6px 0 12px;
  font: 400 13px var(--display); text-transform: uppercase; letter-spacing: .05em; color: var(--paper);
}
/* Says the list below is a window onto a longer run, so 100 rows under a 1,287-issue headline
   reads as a cap rather than as a contradiction. */
.cx__cap {
  margin-left: 8px; font: 500 11px var(--mono); letter-spacing: .04em;
  text-transform: none; color: var(--mist);
}
.cx__chars {
  list-style: none; padding: 0 clamp(20px, 3vw, 32px); margin: 0 0 20px;
  display: flex; flex-wrap: wrap; gap: 6px;
}
.cx__chars li {
  padding: 5px 10px; border-radius: 999px;
  background: var(--raised); border: 1px solid var(--edge);
  font: 500 11px var(--mono); color: var(--mist);
}
.cx__issues {
  display: grid; gap: 12px; padding: 0 clamp(20px, 3vw, 32px) clamp(20px, 3vw, 30px);
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
}
.cx__issue { display: flex; flex-direction: column; text-align: left; }
.cx__issue-a { display: block; text-align: left; color: inherit; text-decoration: none; }
.cx__issue-art {
  aspect-ratio: 2 / 3; overflow: hidden; margin-bottom: 6px;
  background: var(--raised); border: 1px solid var(--edge); border-radius: var(--r);
  transition: border-color .18s;
}
.cx__issue-a:hover .cx__issue-art { border-color: var(--amber); }
.cx__issue-art img { width: 100%; height: 100%; object-fit: cover; }
.cx__issue-n { font: 700 11px var(--mono); color: var(--cyan); }
.cx__issue-t {
  font-size: 11px; color: var(--mist); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* "Read" � the button that turns this reference tile into a readable comic when the same issue
   exists in the public-domain archive. */
.cx__read {
  margin-top: 8px; align-self: flex-start;
  font: 700 10px var(--mono); letter-spacing: .12em; text-transform: uppercase;
  color: var(--cyan); background: none; border: 1px solid var(--edge);
  border-radius: var(--r); padding: 5px 10px; cursor: pointer;
  transition: color .16s, border-color .16s, opacity .16s;
}
.cx__read:hover { border-color: var(--cyan); }
.cx__read:disabled { opacity: .45; cursor: default; }

@media (max-width: 560px) {
  .cx__hd { grid-template-columns: 108px minmax(0, 1fr); gap: 14px; }
}

/* ================= federated search ================= */
/* The header box now answers for all seven catalogues, which needs two things this stylesheet did not
   have: a way to say *where* the answer came from, and a way to say *how the question was read*. */

/* The count-and-through on a grouped row. It sits between the rule and the arrows — after the fact
   ("214 matches") and before the controls — because it is a label first and a control second. */
.row__more {
  flex: 0 0 auto;
  padding: 4px 9px; border: 1px solid var(--edge); border-radius: var(--r);
  font: 500 10.5px var(--mono); text-transform: uppercase; letter-spacing: .08em;
  color: var(--mist);
  transition: color .16s, border-color .16s, background .16s;
}
.row__more:hover { color: var(--ink); background: var(--amber); border-color: var(--amber); }
.row__more:focus-visible { outline: 2px solid var(--cyan); outline-offset: 1px; }

/* Grouped results sit in the grid container, so they need the grid's own bottom air rather than the
   home page's — `.rows` is padded for a full page and this is a panel inside one. */
#gridRows { padding: 4px 0 0; }
#gridRows .row { margin-bottom: clamp(18px, 2.4vw, 30px); }

/* What the model did to the query, stated in the visitor's own words and reversible in one press.
   Amber-tinted rather than amber-filled: it is an annotation on the results, not a result. The tint,
   the hairline and the amber ✦ AI tag are the whole signal — there is no accent tab down the left
   edge, which was the first thing written here and the one thing that did not belong: every other
   side border in this file is a 1px `--edge` divider between adjacent controls, so a coloured one
   would have been the only accent tab on the site as well as the tiredest shape in the vocabulary. */
.aim {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px;
  margin: 0 0 14px; padding: 9px 12px;
  background: rgba(245,165,36,.07);
  border: 1px solid rgba(245,165,36,.28);
  border-radius: var(--r);
}
.aim__tag {
  flex: 0 0 auto;
  font: 700 10px var(--mono); text-transform: uppercase; letter-spacing: .12em;
  color: var(--amber);
}
.aim__say { flex: 1 1 240px; min-width: 0; font-size: 13px; color: var(--mist); }
.aim__undo {
  flex: 0 0 auto;
  padding: 5px 10px; border: 1px solid var(--edge); border-radius: var(--r);
  font: 500 11px var(--mono); color: var(--mist);
  transition: color .16s, border-color .16s;
}
.aim__undo:hover { color: var(--paper); border-color: var(--mist); }

/* ================= paint cost ================= */
/* Live TV's grid can put four hundred tiles on screen from one keystroke, and a search grid a hundred
   cards. `content-visibility: auto` lets the browser skip layout, paint and style for the ones scrolled
   past; `contain-intrinsic-size` beside it is what keeps the scrollbar honest while it does, since a
   skipped element with no declared size collapses and makes the page jump.
   Two places it is deliberately NOT used, both learned the hard way:
     · `.reel` — it scrolls horizontally, and a skipped reel cannot report its own scroll width, which
       is exactly what the arrow buttons measure;
     · `.row` — a row *contains* a reel, so skipping the row takes the reel with it, and the placeholder
       height can only ever be one number while the shelves are 371px (posters), 357px (Continue
       watching) and 209px (the platform strip). Measured on Home: the four rows below the fold each
       reported 340px against a real 371px, so approaching them shifted the document by ~120px. Seven
       rows of twelve cards is not the cost worth paying that for — four hundred tiles is. */
.grid .card, .lv__grid .lv__tile { content-visibility: auto; contain-intrinsic-size: auto 300px; }

/* ================= responsive: the search instrument ================= */
/* The pill holds three children now, so the widths it was given when it held one are wrong at every
   breakpoint. Each step below drops the least useful part rather than squeezing all three: the hint
   first (a keyboard shortcut on a device with no keyboard), then the scope's words. */

/* Before any of that, the honest cause. There are ten sections in the bar plus a status badge, and at
   1440px the nav alone measures 717px of a 1333px row — which left the pill 271px, a box too narrow to
   show its own placeholder. The nav gives up padding rather than the search giving up words: a 13.5px
   label with 10px either side is still a comfortable target, and the alternative (scrolling the nav)
   hides whole sections to save a search box. */
@media (max-width: 1560px) {
  .bar__link { padding-left: 10px; padding-right: 10px; }
}
@media (max-width: 1340px) {
  .bar__link { padding-left: 8px; padding-right: 8px; font-size: 13px; }
  .bar__nav { gap: 2px; }
  .find__hint { display: none; }
  .find input { padding-right: 12px; }
}
/* 1180px is a laptop, not an edge case, and at 1180 the box measured 26px wide — a search field that
   cannot show one word. Three things give up something rather than the box giving up everything: the
   status badge keeps its dot and drops its word (the colour was always the signal; "TMDB UP" was the
   caption), the nav tightens once more, and the scope loses its letter-spacing. */
@media (max-width: 1240px) {
  .bar__link { padding-left: 6px; padding-right: 6px; }
  .net__txt {
    /* Hidden from the eye, not from a screen reader: the word is the only place the state is written
       down in text, and `#net` carries `title="TMDB connection"` but not the reading itself. */
    position: absolute; width: 1px; height: 1px;
    margin: -1px; padding: 0; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap;
  }
  .find__in { max-width: 96px; padding-left: 9px; letter-spacing: .03em; }
}
@media (max-width: 1080px) {
  /* The wordmark goes before the search box does. Ten section labels and a box you can type a title
     into are the two things in this bar anybody uses; the name of the site is the one thing the
     visitor already knows, and the amber mark still says it. Safe to remove outright rather than clip
     — the link carries `aria-label="Streamvault home"`, so its accessible name survives. */
  .brand__word { display: none; }
  .find__hint { display: none; }
  .find input { padding-right: 12px; }

  /* From here down the nav is the thing that gives, not the box. Ten labels measure 545px and no
     amount of tightening makes them and a typeable field share 1000px — so the nav becomes a scroller
     (the same choice it already makes below 700px, and `.tl__seg` makes at every width: scroll a row
     wider than its frame rather than clip it, wrap it, or squeeze its neighbour to nothing), and the
     box gets a floor it cannot be pushed under. `min-width: 0` is what lets a `flex: 0 0 auto` row
     shrink to its frame in the first place; the 240px floor on `.find` is what stops the shrink from
     landing on the box, which is where every earlier attempt at this put it — 26px of input at 1180px.
     Both keep `flex-shrink: 1`, so the two of them give up width together and proportionally rather
     than one of them absorbing all of it. */
  .bar__nav {
    flex: 0 1 auto; min-width: 0;
    overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none;
  }
  .bar__nav::-webkit-scrollbar { display: none; }
  .bar__link { flex: 0 0 auto; }
  .find { min-width: 300px; }
}

/* A scrolling row that ends mid-word reads as broken; the same row ending in a fade reads as
   scrollable. So the nav gets a right-edge mask — but only across the widths where it is actually
   scrolling, because fading the last label of a row that fits is a lie in the other direction.
   Measured overflow, ten labels: 70px at 1024, 116px at 900, 181px at 768 — then nothing between 620
   and 700, where the bar wraps and the nav has the whole row to itself — then 20px at 560, 216px at
   360. Hence the two ranges rather than one. No arrows to go with it: on everything below 1080 the
   nav is a thumb-drag or a trackpad swipe, and a pair of 28px buttons would cost more of the row than
   the two labels they would reveal. */
@media (max-width: 1080px) and (min-width: 701px) {
  .bar__nav {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
  }
}
@media (max-width: 600px) {
  .bar__nav {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
  }
}
@media (max-width: 900px) {
  .find__in { max-width: 92px; padding-left: 9px; letter-spacing: .04em; }
}
@media (max-width: 700px) {
  /* On a phone there is no icon and no reveal — the search is back to what it was: a full-width pill
     on its own row, always there. A tucked-away search only earns its keep where the row is fighting
     for width against ten section links, and here the bar has already wrapped, so the box has the
     room to simply stay open. The toggle is dropped, and the pill is pinned back into the flow
     (static, visible, full width) out of the centred-overlay state the desktop rules put it in. */
  .search { flex: 1 1 100%; order: 2; margin-left: 0; min-width: 0; display: block; }
  .find__toggle { display: none; }
  .search .find {
    position: static; width: 100%; min-width: 0;
    transform: none; opacity: 1; visibility: visible; pointer-events: auto;
    border-color: var(--edge); background: rgba(20,25,41,.86); box-shadow: none;
  }
  .find__in { max-width: 116px; }
  /* And the wordmark comes back, because the reason it left is gone: the bar wraps here, so the brand
     shares its row with nothing but the status dot. Hiding it would leave a lone amber glyph in 375px
     of empty row — the crop was to buy the search box width, not because the name stopped mattering. */
  .brand__word { display: inline; }
}
@media (max-width: 420px) {
  /* Below this the scope's longest label ("Everything") and a typeable amount of box do not both fit.
     The select keeps its width and the box keeps the rest: the chips over the results are the other
     way to change scope, and they are one scroll away. */
  .find__in { max-width: 88px; font-size: 10px; }
}








/* ---------------- the watch page: a title as the document ---------------- */
/* Step 1 of the watch-page design. Films, series and anime open into the document instead of the
   fixed overlay: `body.watch` restyles the #sheet shell into a normal page flow � the backdrop,
   the lock, the animation and the card plate all go � while every rule that styles the *contents*
   (shot, grid, rail, cast) keeps working untouched. The footer and the dead section floor are
   hidden, because the page is the main content now; manga and comics never set `watch`, so their
   overlay behaviour is unchanged. */
body.watch { overflow-y: scroll; }
body.watch .foot { display: none; }
body.watch #sheet {
  position: static; inset: auto; z-index: 0;
  display: block; padding: 0;
  background: transparent; backdrop-filter: none;
  overflow: visible; animation: none;
}
body.watch .sheet__card {
  width: 100%; max-width: 1200px; margin: 0 auto;
  background: transparent; border: 0; border-radius: 0;
  overflow: visible; animation: none;
}
body.watch .sheet__grid {
  max-width: 1160px; margin: 0 auto;
  padding: clamp(20px, 3vw, 32px) clamp(10px, 2vw, 20px) 64px;
}

/* ---------------- the inline player: the stage on the watch page ---------------- */
/* Step 2. While `body.watch` is open, the player mounts where the hero shot was � inside the
   sheet's card at the top of the document � instead of over everything. The overlay rules still
   apply whenever it is used from a non-watch surface (Live TV, books, a comic, a manga reader):
   this block only overrides the box and the stage sizing. */
body.watch .player.is-inline {
  position: relative; display: block;
  width: 100%; max-width: 1200px; margin: 0 auto;
  aspect-ratio: 16 / 9; background: #000; overflow: hidden;
  border-radius: 0 0 6px 6px;
}
body.watch .player.is-inline .player__stage {
  position: absolute; inset: 0; grid-row: auto;
}
body.watch .player.is-inline.has-anime-controls { display: grid; grid-template-rows: auto auto; aspect-ratio: auto; }
body.watch .player.is-inline.has-anime-controls .player__stage { position: relative; inset: auto; grid-row: 1; width: 100%; aspect-ratio: 16 / 9; }
body.watch .player.is-inline.has-anime-controls:fullscreen { max-width: none; margin: 0; grid-template-rows: minmax(0, 1fr); }
body.watch .player.is-inline.has-anime-controls:fullscreen .player__stage { aspect-ratio: auto; height: 100%; }
body.watch .player.is-inline .player__top { z-index: 5; }
/* The watch page reflows the two sheet columns into one reading order under the stage: players,
   episodes, extras, metadata, cast. */
body.watch .sheet__grid { grid-template-columns: minmax(0, 1fr); }

/* The watch page compacts the player rail: fourteen full-width rows are a wall between the stage
   and the episodes, so on watch pages the rail becomes a grid of cards � a source per tile, group
   headings spanning the row. Long notes ellipsize with the full text on hover; the manga/overlay
   sheet keeps the original list because it never carries a bank of fourteen. */
body.watch .rail {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(215px, 1fr));
  gap: 8px;
}
body.watch .rail__grp { grid-column: 1 / -1; }
body.watch .src {
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 8px; padding: 9px 10px;
}
body.watch .src:hover { transform: translateY(-1px); }
body.watch .src__note {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
body.watch .src__q { padding: 2px 5px; }

/* A phone fits one 215px tile to a row, so fourteen alternate hosts became fourteen rows � most of
   a page of scrolling for a choice that is a fallback in the first place. At two tiles a row the
   rail reads as a block you can see past, and the label ellipsizes to keep the rows one line tall
   rather than letting a long host name wrap them to two. */
@media (max-width: 520px) {
  body.watch .rail { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
  body.watch .src__label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
}

/* Transparent chrome must not eat clicks. The gradient top bar covers the whole width of the stage,
   so taps near the top used to land on the bar � or on the title text in the middle � instead of on
   the control underneath or beside. The bar still *looks* identical; only buttons are interactive. */
body.watch .player.is-inline .player__top { pointer-events: none; }
body.watch .player.is-inline .player__top button { pointer-events: auto; }

/* ============================================================
   COLLECTIONS � the doorway to the watch orders.

   The nav carries one button for every franchise, and this landing
   is the choice between them: a card per universe from
   data/universes.js, styled with the house parts � panel card,
   paper-on-ink eyebrow, the same cyan hover the rest of the
   chrome uses. No franchise artwork is imported here, so each
   card leads with a monogram tile instead of a poster.
   ============================================================ */
.co { padding: clamp(24px, 3.5vh, 40px) var(--gut) 64px; }

.co__head { padding-bottom: 18px; border-bottom: 1px solid var(--edge); }
.co__title {
  font-size: clamp(26px, 4vw, 44px); line-height: .96;
  text-transform: uppercase; color: var(--paper);
}
.co__lede {
  font: 400 11.5px var(--mono); color: var(--mist); letter-spacing: .05em;
  margin-top: 11px; max-width: 62ch; line-height: 1.7;
}

.co__grid {
  display: grid; gap: 14px; margin-top: 22px;
  grid-template-columns: 1fr;
}
@media (min-width: 860px) {
  /* Many collections today, two columns; the grid stretches rather than breaks as the list
     grows, since the card is built to fill whatever column it lands in. */
  .co__grid { grid-template-columns: repeat(auto-fit, minmax(min(360px, 100%), 1fr)); }
}

.co__card {
  appearance: none; cursor: pointer; text-align: left;
  display: grid; grid-template-columns: 84px minmax(0, 1fr) auto;
  align-items: center; gap: 18px;
  padding: 18px;
  background: var(--panel);
  border: 1px solid var(--edge); border-radius: var(--r);
  color: inherit; font: inherit;
  transition: border-color .18s var(--ease), transform .18s var(--ease), background-color .18s;
}
.co__card:hover { border-color: var(--cyan); transform: translateY(-2px); }
.co__card:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

/* The art slot is a 2:3 poster (a film collection or a franchise's stand-in poster). When a
   poster is not on file the slot falls back to a monogram on raised glass � the same language
   the card blanks on posters speak. */
.co__art {
  position: relative; width: 84px; aspect-ratio: 2 / 3;
  display: grid; place-items: center;
  background: var(--raised);
  border: 1px solid var(--edge); border-radius: 3px;
  overflow: hidden;
}
.co__art img { width: 100%; height: 100%; object-fit: cover; display: block; }
.co__art i {
  font: 500 clamp(18px, 2vw, 24px) var(--display);
  font-style: normal; letter-spacing: .02em; color: var(--mist);
  transition: color .18s;
}
.co__card:hover .co__art i { color: var(--cyan); }
.co__card:hover .co__art { border-color: var(--cyan); }

.co__body { min-width: 0; }
.co__name {
  display: block; margin-top: 5px;
  font: 400 clamp(20px, 2.4vw, 28px) var(--display);
  text-transform: uppercase; letter-spacing: .02em; color: var(--paper);
}
.co__deck {
  display: block; margin-top: 9px;
  color: var(--mist); font-size: 13.5px; line-height: 1.6;
  /* A collection's overview runs to a paragraph or more; the card keeps its own height by
     clamping the deck at four lines, with the collection's own page holding the whole thing. */
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden;
}
.co__facts {
  display: block; margin-top: 12px;
  font: 400 11px var(--mono); color: var(--mist); letter-spacing: .05em;
}
.co__facts b { color: var(--cyan); font-weight: 500; }

/* The line between the header and the grid while the resolved data is on its way in. */
.co__status {
  margin: 18px 0 0; padding-bottom: 4px;
  font: 400 11px var(--mono); color: var(--mist); letter-spacing: .05em;
}
.co__status b { display: block; color: var(--paper); font: 400 15px var(--display); }
.co__status.is-error { color: var(--amber); }
.co__status.is-error b { color: var(--amber); }

.co__go {
  width: 36px; height: 36px; flex: 0 0 auto;
  display: grid; place-items: center;
  color: var(--mist);
  border: 1px solid var(--edge); border-radius: var(--r);
  transition: color .18s, border-color .18s;
}
.co__go svg { width: 15px; height: 15px; fill: currentColor; }
.co__card:hover .co__go { color: var(--cyan); border-color: var(--cyan); }

/* ============================================================
   COMICS — the public-domain archive (Comic Book Plus).

   Comic Vine stays a browse-only index; this half of the Comics
   section actually reads. Rows are text-only because the archive
   lists hundreds of series per genre; the reader is a single-page
   strip with keyboard arrows, styled from the house sheet chrome.
   ============================================================ */
.cx__srcs { margin: 0 0 16px; align-self: flex-start; }

.cxp__bar { margin: 0 0 14px; }
.cxp__back {
  display: inline-flex; align-items: center; gap: 7px;
  font: 500 10px var(--mono); letter-spacing: .12em; text-transform: uppercase;
  color: var(--mist); border: 1px solid var(--edge); border-radius: var(--r);
  padding: 8px 13px; background: var(--raised); cursor: pointer;
  transition: color .16s, border-color .16s;
}
.cxp__back svg { width: 13px; height: 13px; fill: currentColor; }
.cxp__back:hover { color: var(--cyan); border-color: var(--cyan); }

.cxp__crumbs {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px;
  margin: 0 0 14px; font: 400 11px var(--mono); color: var(--mist);
  letter-spacing: .04em;
}
.cxp__sep { opacity: .45; }
.cxp__crumb { color: var(--mist); border-bottom: 1px solid transparent; cursor: pointer;
  transition: color .16s, border-color .16s; }
.cxp__crumb:hover { color: var(--cyan); border-bottom-color: var(--cyan); }
.cxp__crumbs b { color: var(--paper); font-weight: 600; }

.cxp__chips { display: flex; flex-wrap: wrap; gap: 8px; }

.cxp__row {
  width: 100%; display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: 12px; align-items: center;
  padding: 13px 4px; text-align: left; cursor: pointer;
  background: none; border: 0; border-top: 1px solid var(--edge);
  color: inherit; font: inherit;
  transition: padding-left .18s var(--ease);
}
.cxp__row:hover { padding-left: 10px; }
.cxp__row:focus-visible { outline: 2px solid var(--cyan); outline-offset: -2px; }
.cxp__rowmain { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.cxp__t { color: var(--paper); font-size: 15px; line-height: 1.4; }
.cxp__s { font: 400 11px var(--mono); color: var(--mist); letter-spacing: .05em; }
.cxp__go { color: var(--mist); transition: color .16s; }
.cxp__row:hover .cxp__go { color: var(--cyan); }

.cxp__more {
  margin-top: 14px; font: 500 10px var(--mono); letter-spacing: .12em;
  text-transform: uppercase; color: var(--mist);
  border: 1px solid var(--edge); border-radius: var(--r);
  padding: 9px 15px; background: var(--raised); cursor: pointer;
  transition: color .16s, border-color .16s;
}
.cxp__more:hover { color: var(--cyan); border-color: var(--cyan); }

.cxp__retry {
  color: var(--cyan); text-decoration: underline; text-underline-offset: 3px;
  background: none; border: 0; padding: 0; cursor: pointer;
}

/* ---- the issue reader ---- */
.cbp {
  position: fixed; inset: 0; z-index: 220;
  background: rgba(4, 6, 11, .94); backdrop-filter: blur(5px);
  display: flex; flex-direction: column; align-items: center;
  padding: 0 clamp(8px, 2vw, 26px);
}
.cbp__top {
  width: 100%; max-width: 940px;
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center; gap: 14px; padding: 13px 0;
}
.cbp__x {
  width: 34px; height: 34px; display: grid; place-items: center;
  color: var(--mist); background: none; border: 1px solid var(--edge);
  border-radius: var(--r); cursor: pointer;
  transition: color .16s, border-color .16s;
}
.cbp__x:hover { color: var(--cyan); border-color: var(--cyan); }
.cbp__x svg { width: 14px; height: 14px; fill: currentColor; }
.cbp__name {
  font: 400 15px var(--display); color: var(--paper);
  text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cbp__count { font: 400 11px var(--mono); color: var(--mist); justify-self: end; white-space: nowrap; }

.cbp__stage {
  flex: 1 1 auto; width: 100%; max-width: 940px; min-height: 0;
  display: grid; grid-template-columns: 54px minmax(0, 1fr) 54px;
  align-items: center; gap: 8px;
}
.cbp__page {
  display: flex; justify-content: center; min-width: 0;
  max-height: calc(100vh - 150px);
}
.cbp__page img {
  max-width: 100%; max-height: calc(100vh - 150px);
  object-fit: contain;
  box-shadow: 0 14px 44px rgba(0, 0, 0, .55);
}
.cbp__nav {
  width: 44px; height: 44px; display: grid; place-items: center;
  color: var(--mist); background: var(--raised);
  border: 1px solid var(--edge); border-radius: 50%; cursor: pointer;
  transition: color .16s, border-color .16s, opacity .16s;
}
.cbp__nav svg { width: 17px; height: 17px; fill: currentColor; }
.cbp__nav:hover { color: var(--cyan); border-color: var(--cyan); }
.cbp__nav[disabled] { opacity: .3; cursor: default; }

.cbp__note {
  min-height: 20px; margin: 10px 0 16px;
  font: 400 11px var(--mono); color: var(--mist); letter-spacing: .05em;
  text-align: center;
}
.cbp__note.is-err { color: var(--amber); }
.cbp__note b { display: block; color: var(--amber); font: 500 13px var(--display); margin-bottom: 2px; }

/* ---- publisher catalogue cards (archive genre pages are walls of publisher logos) ---- */
.cxp__pcards {
  display: grid; gap: 18px 14px;
  grid-template-columns: repeat(auto-fill, minmax(118px, 1fr));
}
.cxp__pcard {
  padding: 0; background: none; border: 0; cursor: pointer;
  text-align: left; color: inherit; font: inherit;
}
.cxp__cover {
  position: relative; display: grid; place-items: center;
  width: 100%; aspect-ratio: 175 / 245;
  overflow: hidden; background: var(--raised);
  border: 1px solid var(--edge); border-radius: 4px;
  transition: border-color .18s var(--ease), transform .18s var(--ease);
}
.cxp__cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cxp__cover i { font: 600 20px var(--display); font-style: normal; color: var(--mist); }
.cxp__pcard:hover .cxp__cover { border-color: var(--cyan); transform: translateY(-2px); }
.cxp__pcard:focus-visible .cxp__cover { outline: 2px solid var(--cyan); outline-offset: 2px; }
.cxp__pmeta { margin-top: 8px; }
.cxp__pmeta b { display: block; color: var(--paper); font-size: 13px; line-height: 1.3; }
.cxp__pmeta i { display: block; margin-top: 3px; font-style: normal;
  font: 400 10.5px var(--mono); color: var(--mist); letter-spacing: .04em; }

/* ---- archive landing: Comic Vine-style shelves, then all-genre chips ---- */
.cxp__chips--all {
  margin-top: 28px; padding-top: 18px;
  border-top: 1px solid var(--edge);
}

/* ---- issue rows with real cover thumbnails ---- */
.cxp__row--art { grid-template-columns: 46px minmax(0, 1fr) auto; gap: 14px; }
.cxp__art {
  width: 46px; aspect-ratio: 2 / 3; overflow: hidden;
  border-radius: 3px; background: var(--raised);
  border: 1px solid var(--edge);
}
.cxp__art img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cxp__row--art:hover .cxp__art { border-color: var(--cyan); }


/* ---------------- Apps & Games ---------------- */
/* The kind switch sits where the anime section's order switch does; the shelf wall below it is the
   same .rows/.grid every catalogue uses. */
.gm .an__seg { margin: 18px 0 0; align-self: flex-start; }
.gm .an__controls { margin-top: 14px; }

/* Icons and transparent covers sit better contained than cropped into a poster frame. */
.card__art--contain img { object-fit: contain; background: var(--panel); }

/* ---------------- the comics web reader (embedded site) ---------------- */
.cx__web { margin-top: 2px; }
.cx__web .cxp__bar { margin-bottom: 0; }
.cx__frame {
  margin-top: 14px;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  overflow: hidden;
  background: #05070f;
  height: clamp(420px, 62vh, 780px);
}
.cx__frame iframe { width: 100%; height: 100%; border: 0; display: block; }
.cx__webfoot {
  margin-top: 10px;
  font: 400 11px var(--mono); color: var(--mist); letter-spacing: .03em;
}

/* ---------------- frame players (games & apps) ---------------- */
/* A game or an app is interactive right up to its top edge, so the player's floating bar � which
   normally overlays the stage and fades over a playing video � must not float over a frame. When
   `is-frame` is set, the bar becomes the grid's own first row: solid, in-flow, with the stage
   (and therefore the game) starting below it. Nothing the game draws is ever covered. */
.player.is-frame { grid-template-rows: auto 1fr; }
.player.is-frame .player__top {
  position: relative; inset: auto;
  background: linear-gradient(180deg, #0b0e1d, #0e1226);
  box-shadow: 0 1px 0 var(--edge);
}
/* The stage normally spans every grid row (`grid-row: 1 / -1`) because it sits *behind* the
   floating bar. With the bar in-flow that would put the game underneath the bar again, so a frame
   player pins the stage to the second row � the space between the two is real. */
.player.is-frame .player__stage { grid-row: 2; }

/* Game fullscreen: fullscreening the whole player would carry the title bar into it. Fullscreening
   just the stage keeps the screen all game; a brief hint names the one way out, then fades. */
.player__fsnote {
  position: absolute; top: clamp(10px, 2vh, 18px); left: 50%;
  transform: translateX(-50%);
  padding: 7px 12px;
  background: rgba(7, 9, 20, .82);
  border: 1px solid var(--edge); border-radius: 999px;
  color: var(--mist); font: 400 11px var(--mono); letter-spacing: .05em;
  white-space: nowrap;
  opacity: 0; transition: opacity .4s var(--ease);
  pointer-events: none; z-index: 6;
}
.player__fsnote.is-on { opacity: 1; }




/* ---------------- game/app open choice ---------------- */
.gm__menu {
  position: fixed; z-index: 220; width: 216px; padding: 6px;
  background: var(--raised);
  border: 1px solid var(--edge); border-radius: 10px;
  box-shadow: 0 18px 54px rgba(0, 0, 0, .55);
  animation: fade .14s var(--ease);
}
.gm__opt {
  display: flex; flex-direction: column; gap: 3px; width: 100%;
  padding: 10px 11px; text-align: left;
  background: none; border: 0; border-radius: 7px;
  cursor: pointer; color: var(--paper);
  font: inherit; font-family: var(--mono);
  transition: background .14s;
}
.gm__opt b { font: 600 12px var(--display); letter-spacing: .03em; text-transform: uppercase; }
.gm__opt small { font: 400 10px var(--mono); color: var(--mist); letter-spacing: .03em; }
.gm__opt:hover, .gm__opt:focus-visible { background: var(--panel); outline: none; }
.gm__opt--go { background: rgba(255, 255, 255, .05); }
.gm__opt--go b { color: var(--amber); }

/* ---------------- filler tint in the anime episode picker ---------------- */
/* The tile's nature (filler / mixed canon/filler) vs its progress (watched = cyan) vs the
   selection (the ring) are three different facts, so the tint is text + border only and the
   watched/current states keep their own colours on top. */
.picker__cells .cell.is-filler { color: var(--amber); border-color: rgba(255, 170, 40, .5); }
.picker__cells .cell.is-mixed { color: #cfa9ff; border-color: rgba(170, 120, 255, .5); }
.picker__cells .cell.is-filler.is-on, .picker__cells .cell.is-mixed.is-on { color: var(--cyan); }
.picker__cells .cell.is-filler.is-cur,
.picker__cells .cell.is-mixed.is-cur { color: var(--paper); border-color: var(--cyan); }

.filler__note {
  margin-top: 9px; padding-top: 8px; border-top: 1px solid var(--edge);
  font: 400 10px var(--mono); color: var(--mist); letter-spacing: .03em;
}
.filler__note .filler__k { font-weight: 600; }
.filler__note .is-fk { color: var(--amber); }
.filler__note .is-mk { color: #cfa9ff; }

/* ================= Music (JioSaavn) ================= */
/* The section wears a small player world of its own inside the site: a masthead that greets like
   a listening room, square covers with a round amber ?, sentence-case shelf titles, and a banded
   record page with a marquee play pill. The palette is the house palette (amber is the play
   accent, cyan stays reserved for live/selected) and the chrome is the site's chrome � mono
   eyebrows and metadata, display titles, edge-on-panel structure. Only the composition is the
   music app's. */

/* ---- the TIDAL-style shell: a left rail beside the scrolling content ---- */
#music.mu { padding: 0; }
.mu__shell { display: flex; align-items: flex-start; }
.mu__side {
  flex: 0 0 250px; align-self: flex-start;
  position: sticky; top: var(--bar-h, 66px);
  max-height: calc(100vh - var(--bar-h, 66px));
  display: flex; flex-direction: column;
  padding: clamp(16px, 3vh, 26px) 12px clamp(16px, 3vh, 26px);
  background: rgba(6, 8, 17, .5);
  border-right: 1px solid var(--edge);
  overflow: hidden;
}
.mu__main { flex: 1 1 auto; min-width: 0; padding: clamp(20px, 3vh, 34px) var(--gut) 90px; }
.mu__brand {
  display: flex; align-items: center; gap: 10px; padding: 4px 10px 16px;
  font: 400 19px var(--display); letter-spacing: .04em; color: var(--paper);
}
.mu__brand svg { width: 24px; height: 24px; fill: none; stroke: var(--amber); }
.mu__nav { display: flex; flex-direction: column; gap: 2px; }
.mu__navitem {
  display: flex; align-items: center; gap: 14px; width: 100%;
  padding: 10px 12px; border: 0; border-radius: 8px; background: none;
  color: var(--mist); font: 600 14px var(--body); text-align: left; cursor: pointer;
  transition: color .16s var(--ease), background .16s var(--ease);
}
.mu__navitem svg { width: 20px; height: 20px; flex: 0 0 auto; fill: none; stroke: currentColor; }
.mu__navitem .mu__heart, .mu__navitem svg[fill] { fill: currentColor; }
.mu__navitem:hover { color: var(--paper); background: rgba(237, 240, 250, .06); }
.mu__navitem.is-on { color: var(--paper); background: rgba(237, 240, 250, .05); }
.mu__navitem.is-on svg { stroke: var(--amber); }
.mu__navhead {
  display: flex; align-items: center; justify-content: space-between;
  padding: 22px 12px 8px; font: 500 11px var(--mono); letter-spacing: .12em;
  text-transform: uppercase; color: var(--mist);
}
.mu__navadd { display: grid; place-items: center; width: 26px; height: 26px; border: 0; border-radius: 6px; background: none; color: var(--mist); cursor: pointer; transition: color .16s, background .16s; }
.mu__navadd svg { width: 18px; height: 18px; fill: currentColor; }
.mu__navadd:hover { color: var(--amber); background: rgba(237, 240, 250, .06); }
.mu__navpls { display: flex; flex-direction: column; gap: 1px; overflow-y: auto; min-height: 0; scrollbar-width: thin; }
.mu__plitem {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 7px 12px; border: 0; border-radius: 8px; background: none;
  color: var(--mist); text-align: left; cursor: pointer;
  transition: color .16s var(--ease), background .16s var(--ease);
}
.mu__plitem:hover { color: var(--paper); background: rgba(237, 240, 250, .06); }
.mu__plitem.is-on { color: var(--paper); background: rgba(237, 240, 250, .05); }
.mu__plart { flex: 0 0 auto; width: 36px; height: 36px; border-radius: 5px; overflow: hidden; display: grid; place-items: center; background: var(--panel); border: 1px solid var(--edge); }
.mu__plart img { width: 100%; height: 100%; object-fit: cover; }
.mu__plart svg { width: 16px; height: 16px; fill: none; stroke: var(--mist); }
.mu__plname { min-width: 0; font: 500 13.5px var(--body); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mu__plempty { padding: 8px 12px; margin: 0; font: 400 12px var(--mono); color: var(--mist); }
@media (max-width: 860px) {
  .mu__side { display: none; }
}

/* ---- the greeting masthead ---- */
.mu__mast {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 18px;
  padding-bottom: 18px; border-bottom: 1px solid var(--edge);
}
.mu__mastmain { min-width: 260px; }
.mu__mast .eyebrow { margin-bottom: 10px; }
.mu__masttitle { font-size: clamp(26px, 3.6vw, 42px); line-height: .98; }
.mu__lede {
  font: 400 11.5px var(--mono); color: var(--mist); letter-spacing: .05em; margin-top: 10px;
}
.mu__find { flex: 1 1 300px; max-width: 480px; min-width: 240px; margin-left: auto; }
.mu__find input {
  width: 100%; height: 44px; padding: 0 18px;
  background: var(--panel); color: var(--paper);
  border: 1px solid var(--edge); border-radius: 999px;
  transition: border-color .18s, background .18s, box-shadow .18s;
}
.mu__find input::placeholder { color: #66708C; }
.mu__find input:focus {
  border-color: var(--cyan); background: var(--raised); outline: none;
  box-shadow: 0 0 0 3px rgba(53, 214, 232, .16);
}

/* Rows keep the shared anatomy but shed the poster chrome: no rule line, no arrows, sentence-case
   titles at a listener's scale, and the shelf shares the masthead's left edge. */
.mu .rows { padding: clamp(22px, 3.2vh, 34px) 0 6px; }
.mu .row { margin-bottom: clamp(26px, 3.6vh, 46px); }
.mu .row__head { padding: 0; margin-bottom: 14px; }
.mu .row__rule, .mu .row__nav { display: none; }
.mu .row__title {
  font: 700 clamp(16px, 1.9vw, 21px) var(--body);
  letter-spacing: -.01em; text-transform: none; color: var(--paper);
}
.mu .reel { gap: clamp(14px, 1.7vw, 22px); padding: 2px 0 10px; }

/* ---- square record tile ---- */
.mu__tile {
  position: relative; flex: 0 0 auto; width: clamp(136px, 15vw, 184px);
  scroll-snap-align: start;
  transition: transform .26s var(--ease);
}
.mu__tile:hover { transform: translateY(-5px); }
.mu__tile:active { transform: translateY(-2px) scale(.99); }
.mu__artwrap {
  position: relative; aspect-ratio: 1 / 1;
  border-radius: 10px; overflow: hidden;
  background: linear-gradient(160deg, var(--raised), var(--panel));
  border: 1px solid var(--edge);
  transition: border-color .26s var(--ease), box-shadow .26s var(--ease);
}
.mu__tile:hover .mu__artwrap {
  border-color: rgba(245, 165, 36, .55);
  box-shadow: 0 22px 46px -18px rgba(0, 0, 0, .85);
}
.mu__face { position: absolute; inset: 0; z-index: 1; display: block; padding: 0; }
.mu__face img { width: 100%; height: 100%; object-fit: cover; transition: transform .35s var(--ease); }
.mu__tile:hover .mu__face img { transform: scale(1.04); }
.mu__plate {
  position: absolute; inset: 0; display: grid; place-items: center;
  font: 700 clamp(18px, 2vw, 26px) var(--display); color: var(--mist);
  text-transform: uppercase; letter-spacing: .06em;
}
/* A plate can hold a glyph instead of initials (an empty playlist's icon). Without this the SVG has
   no intrinsic size and balloons to fill — the giant note bug. */
.mu__plate svg { width: 40%; height: 40%; fill: none; stroke: currentColor; stroke-width: 2; }
.mu__face:focus-visible { outline: none; }
.mu__tile:focus-within .mu__artwrap { border-color: var(--cyan); }
.mu__tmeta { padding: 9px 2px 0; min-width: 0; }
.mu__tname {
  font-size: 13.5px; font-weight: 600; color: var(--paper); line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.mu__tsub {
  font: 400 11px var(--mono); color: var(--mist); margin-top: 3px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Gradient "mix" tiles — a big name set into a colour wash, TIDAL's Custom-mixes look. */
.mu__mixart { position: relative; }
.mu__mixart .mu__face { z-index: 2; background: none; }
.mu__mixname {
  position: absolute; left: 14px; right: 14px; bottom: 14px; z-index: 1;
  font: 800 clamp(20px, 2vw, 28px)/1.05 var(--body); letter-spacing: -.02em; color: #fff;
  text-shadow: 0 2px 16px rgba(0, 0, 0, .4); pointer-events: none;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.mu__mixart::after {
  content: ''; position: absolute; inset: 0; z-index: 0;
  background: linear-gradient(180deg, transparent 40%, rgba(0, 0, 0, .35));
}
.mu__mix .mu__play { z-index: 3; }

/* The round ? � the shelf's whole promise. Sits over the cover's corner until the tile is
   approached, then it is the one thing there is to press. */
.mu__play {
  position: absolute; right: 10px; bottom: 10px; z-index: 2;
  width: 42px; height: 42px; display: grid; place-items: center;
  background: var(--amber); color: var(--ink);
  border-radius: 50%;
  box-shadow: 0 10px 26px -8px rgba(0, 0, 0, .75);
  opacity: 0; transform: translateY(7px) scale(.9);
  transition: opacity .18s var(--ease), transform .18s var(--ease), background .16s;
  pointer-events: none;
}
.mu__play svg { width: 17px; height: 17px; fill: currentColor; transform: translateX(1px); }
.mu__tile:hover .mu__play,
.mu__tile:focus-within .mu__play,
.mu__play:focus-visible {
  opacity: 1; transform: none; pointer-events: auto;
}
.mu__play:hover { background: #ffb847; }
.mu__play:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

/* ---- search head ---- */
.mu__search, .mu__detail { min-width: 0; }
.mu__search, .mu__detail { padding-top: 6px; }
.mu .grid-head { border: 0; padding: 0 0 10px; margin: 0 0 4px; }
.mu .grid-title {
  font: 700 clamp(18px, 2.2vw, 26px) var(--body);
  text-transform: none; letter-spacing: -.01em;
}
.mu .grid-count { margin-left: auto; }

/* ---- the record band ---- */
.mu__band {
  position: relative; display: flex; align-items: flex-end; gap: clamp(18px, 3vw, 34px);
  padding: clamp(18px, 3vw, 30px);
  border-radius: 14px; overflow: hidden;
  background:
    radial-gradient(90% 130% at 0% 0%, rgba(245, 165, 36, .13), transparent 52%),
    linear-gradient(140deg, #1D2440 0%, var(--sink) 74%);
  border: 1px solid var(--edge);
  box-shadow: 0 26px 70px -32px rgba(0, 0, 0, .85);
}
.mu__bandart {
  position: relative; flex: 0 0 auto;
  width: clamp(150px, 21vw, 232px); aspect-ratio: 1 / 1;
  border-radius: 10px; overflow: hidden;
  background: var(--sink); border: 1px solid rgba(237, 240, 250, .1);
  box-shadow: 0 22px 54px -20px rgba(0, 0, 0, .9);
}
.mu__bandart img { width: 100%; height: 100%; object-fit: cover; }
.mu__bandart .mu__plate { font-size: clamp(26px, 5vw, 44px); }
.mu__bandbody { min-width: 0; padding-top: 4px; }
.mu__bandkind {
  display: flex; align-items: center; gap: 9px;
  font: 500 10.5px var(--mono); text-transform: uppercase; letter-spacing: .18em;
  color: var(--amber); margin-bottom: 10px;
}
.mu__bandkind::before { content: ''; width: 26px; height: 1px; background: var(--amber); opacity: .7; }
.mu__bandtitle {
  font-size: clamp(26px, 3.6vw, 46px); line-height: .98;
  overflow-wrap: break-word; margin-bottom: 8px;
  text-shadow: 0 4px 34px rgba(7, 9, 20, .55);
}
.mu__bandwho { color: var(--paper); font-weight: 600; margin-bottom: 2px; }
.mu__bandmeta {
  font: 400 11px var(--mono); color: var(--mist); letter-spacing: .06em;
  margin-bottom: 12px;
}
.mu__bandbio {
  color: #B9C1D8; font-size: 13px; line-height: 1.55; max-width: 64ch;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
  margin-bottom: 16px;
}
.mu__bandacts { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }

/* The one round pill the band needs. */
.btn--play {
  height: 46px; padding: 0 28px; border-radius: 999px;
  background: var(--amber); color: var(--ink); font-weight: 700;
  box-shadow: 0 12px 30px -12px rgba(245, 165, 36, .8);
  transition: background .16s, transform .14s var(--ease);
}
.btn--play:hover { background: #ffb847; transform: translateY(-1px); }
.btn--play:active { transform: none; }
.btn--play svg { width: 16px; height: 16px; fill: currentColor; }

/* ---- the queue tracklist ---- */
.mu__songs {
  display: flex; flex-direction: column; gap: 1px;
  margin-top: clamp(14px, 2.2vh, 22px);
  padding: 6px;
  background: rgba(13, 16, 30, .55);
  border: 1px solid var(--edge); border-radius: 14px;
}
.mu__trackrow {
  position: relative; display: flex; align-items: center; gap: 2px;
  border-radius: 9px; transition: background .15s;
}
.mu__trackrow:hover, .mu__trackrow:focus-within { background: var(--raised); }
.mu__track {
  flex: 1 1 auto; min-width: 0;
  display: grid;
  grid-template-columns: 30px 44px minmax(0, 1fr) auto;
  align-items: center; gap: 12px;
  padding: 7px 10px; text-align: left;
  border: 0; background: none; border-radius: 9px;
  transition: background .15s;
}
.mu__track:focus-visible { outline: none; }
/* The row's trailing controls: hidden until the row is hovered or focused, so the list stays calm
   and only the row you are on shows its verbs — the Spotify pattern, in our own colours. */
.mu__rowlike, .mu__more {
  flex: 0 0 auto; display: grid; place-items: center;
  width: 34px; height: 34px; margin-right: 2px;
  border: 0; border-radius: 999px; background: none; cursor: pointer;
  color: var(--mist); opacity: 0;
  transition: opacity .15s, color .15s, background .15s, transform .1s;
}
.mu__more { margin-right: 6px; }
.mu__rowlike svg, .mu__more svg { width: 17px; height: 17px; fill: currentColor; }
.mu__trackrow:hover .mu__rowlike, .mu__trackrow:hover .mu__more,
.mu__trackrow:focus-within .mu__rowlike, .mu__trackrow:focus-within .mu__more,
.mu__rowlike.is-liked { opacity: 1; }
.mu__rowlike:hover, .mu__more:hover { color: var(--paper); background: rgba(237, 240, 250, .1); }
.mu__rowlike:active, .mu__more:active { transform: scale(.9); }
.mu__rowlike.is-liked { color: var(--amber); }
.mu__rowlike.is-liked svg { fill: var(--amber); }
.mu__rowlike:focus-visible, .mu__more:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; opacity: 1; }
@media (hover: none) {
  .mu__rowlike, .mu__more { opacity: 1; }
}
.mu__idx {
  position: relative; display: grid; place-items: center;
  font: 400 11px var(--mono); color: var(--mist);
  user-select: none;
}
.mu__num { transition: opacity .15s; }
.mu__go {
  position: absolute; inset: 0; display: grid; place-items: center;
  color: var(--amber); opacity: 0; transition: opacity .15s;
}
.mu__go svg { width: 15px; height: 15px; fill: currentColor; }
.mu__trackrow:hover .mu__num, .mu__track:focus-visible .mu__num { opacity: 0; }
.mu__trackrow:hover .mu__go, .mu__track:focus-visible .mu__go { opacity: 1; }
.mu__cover {
  width: 44px; height: 44px; flex: 0 0 auto;
  display: grid; place-items: center;
  background: var(--sink); border: 1px solid var(--edge);
  border-radius: 6px; overflow: hidden;
}
.mu__cover img { width: 100%; height: 100%; object-fit: cover; }
.mu__noart { color: var(--mist); font-size: 15px; }
.mu__meta { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.mu__t {
  font-size: 14px; font-weight: 600; color: var(--paper); line-height: 1.3;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mu__ex {
  display: inline-block; font: 700 8px var(--mono); font-style: normal;
  color: var(--sink); background: var(--amber);
  padding: 1px 4px; border-radius: 2px; vertical-align: 2px; margin-left: 6px;
}
.mu__w {
  font: 400 11px var(--mono); color: var(--mist);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mu__dur { font: 400 11px var(--mono); color: var(--mist); }
.mu__search .row, .mu__detail .row { margin-top: 22px; }
.mu .empty { padding: 40px 0; }

/* ---------------- library tiles ---------------- */
.mu__lib { margin-top: 4px; }
.mu__recent { margin-top: 4px; }

/* ---- TIDAL-style quick-access / recently-played grid ---- */
.mu__sec { margin: clamp(16px, 3vh, 30px) 0 0; }
.mu__sectitle {
  font: 700 clamp(18px, 2.2vw, 24px) var(--body); letter-spacing: -.01em;
  color: var(--paper); margin: 0 0 14px;
}
.mu__hgrid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 8px clamp(16px, 2vw, 28px);
}
.mu__hcard {
  position: relative; display: flex; align-items: center; gap: 12px;
  padding: 8px; border-radius: 8px; cursor: pointer;
  background: rgba(237, 240, 250, .03);
  transition: background .2s var(--ease), transform .12s var(--ease);
}
.mu__hcard:hover { background: rgba(237, 240, 250, .09); }
.mu__hcard:active { transform: scale(.99); }
.mu__hcard:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
.mu__hart {
  position: relative;   /* contains an absolute .mu__plate glyph instead of letting it escape */
  flex: 0 0 auto; width: 56px; height: 56px; border-radius: 6px; overflow: hidden;
  display: grid; place-items: center; background: var(--panel); border: 1px solid var(--edge);
}
.mu__hart img { width: 100%; height: 100%; object-fit: cover; }
.mu__hart .mu__mosaic { width: 100%; height: 100%; }
.mu__hgrad { width: 100%; height: 100%; display: grid; place-items: center; }
.mu__hgrad--liked { background: linear-gradient(150deg, var(--amber), #d98a1a 45%, var(--cyan) 135%); color: #1a1205; }
.mu__hgrad--liked svg { width: 44%; height: 44%; fill: currentColor; stroke: currentColor; }
.mu__hgrad--new { border: 1.5px dashed var(--edge); color: var(--mist); background: var(--panel); transition: color .2s, border-color .2s; }
.mu__hcard:hover .mu__hgrad--new { color: var(--amber); border-color: var(--amber); }
.mu__hgrad--new svg { width: 40%; height: 40%; fill: currentColor; }
.mu__hplate { font: 700 18px var(--display); color: var(--mist); }
.mu__hmeta { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.mu__hname { font: 600 14px var(--body); color: var(--paper); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mu__hsub { font: 400 12px var(--mono); color: var(--mist); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mu__hplay {
  flex: 0 0 auto; width: 40px; height: 40px; margin-right: 6px;
  display: grid; place-items: center; border-radius: 50%;
  background: var(--amber); color: var(--ink); border: 0; cursor: pointer;
  opacity: 0; transform: scale(.82) translateX(4px);
  transition: opacity .18s var(--ease), transform .18s var(--ease), background .16s;
}
.mu__hplay svg { width: 17px; height: 17px; fill: currentColor; transform: translateX(1px); }
.mu__hplay:hover { background: #ffb847; }
.mu__hcard:hover .mu__hplay, .mu__hcard:focus-within .mu__hplay { opacity: 1; transform: scale(1) translateX(0); }
.mu__hplay:focus-visible { opacity: 1; outline: 2px solid var(--cyan); outline-offset: 2px; }
@media (hover: none) { .mu__hplay { opacity: 1; transform: none; } }
@media (max-width: 640px) { .mu__hgrid { grid-template-columns: 1fr; } }

.mu__tile--lib .mu__face, .mu__tile--new .mu__face { display: grid; place-items: center; }
/* Liked Songs wears a signature amber-to-cyan wash with a filled heart — its own face, the way a
   real music app gives the liked collection one. */
.mu__face--liked {
  background: linear-gradient(150deg, var(--amber) 0%, #d98a1a 42%, var(--cyan) 130%);
}
.mu__likedart { display: grid; place-items: center; color: #1a1205; }
.mu__likedart svg { width: 42%; height: 42%; fill: currentColor; stroke: currentColor; }
.mu__mosaic { display: grid; grid-template-columns: 1fr 1fr; width: 100%; height: 100%; }
.mu__mosaic img { width: 100%; height: 100%; object-fit: cover; }
.mu__face--new {
  background: var(--panel);
  border: 1.5px dashed var(--edge); color: var(--mist);
  transition: color .2s, border-color .2s;
}
.mu__tile--new:hover .mu__face--new { color: var(--amber); border-color: var(--amber); }
.mu__face--new svg { width: 34px; height: 34px; fill: currentColor; }

/* ---------------- a library detail hero ---------------- */
.mu__band--lib .mu__bandart--liked {
  display: grid; place-items: center;
  background: linear-gradient(150deg, var(--amber) 0%, #d98a1a 42%, var(--cyan) 130%);
}
.mu__likedart--big { color: #1a1205; }
.mu__likedart--big svg { width: 46%; height: 46%; fill: currentColor; stroke: currentColor; }
.mu__band--lib .mu__bandart .mu__mosaic { display: grid; grid-template-columns: 1fr 1fr; width: 100%; height: 100%; }
.mu__band--lib .mu__bandart .mu__plate { display: grid; place-items: center; }
.mu__band--lib .mu__bandart .mu__plate svg { width: 40%; height: 40%; fill: none; stroke: currentColor; stroke-width: 2; }
.mu__iconbtn {
  display: grid; place-items: center; width: 40px; height: 40px;
  border: 1px solid var(--edge); border-radius: 999px; background: var(--panel);
  color: var(--mist); cursor: pointer; transition: color .15s, border-color .15s, background .15s, transform .1s;
}
.mu__iconbtn svg { width: 18px; height: 18px; fill: none; stroke: currentColor; }
.mu__iconbtn:hover { color: var(--paper); border-color: var(--mist); background: var(--raised); }
.mu__iconbtn--danger:hover { color: var(--reel); border-color: var(--reel); background: rgba(229, 72, 77, .1); }
.mu__iconbtn:active { transform: scale(.92); }
.mu__iconbtn:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
.mu__plrename { display: block; }
.mu__plrenamein {
  width: min(460px, 80vw);
  font: 400 clamp(24px, 3.2vw, 40px) var(--display); letter-spacing: -.015em;
  color: var(--paper); background: var(--panel);
  border: 1px solid var(--cyan); border-radius: 8px; padding: 4px 12px;
}
.mu__plrenamein:focus { outline: 2px solid var(--cyan); outline-offset: 1px; }

/* ---------------- the track ⋯ menu ---------------- */
.tmenu {
  position: fixed; z-index: 120; width: 264px;
  padding: 6px; background: rgba(20, 25, 41, .98);
  border: 1px solid var(--edge); border-radius: 12px;
  box-shadow: 0 18px 44px rgba(0, 0, 0, .55);
  backdrop-filter: blur(10px);
  animation: tmenuIn .13s var(--ease);
}
@keyframes tmenuIn { from { opacity: 0; transform: translateY(-4px); } }

/* The header card: cover, title and artists for the track the ⋯ belongs to. */
.tmenu__card {
  display: flex; align-items: center; gap: 10px; padding: 6px 8px 8px; min-width: 0;
}
.tmenu__thumb {
  flex: 0 0 auto; width: 42px; height: 42px; border-radius: 7px; overflow: hidden;
  display: grid; place-items: center; background: var(--raised); color: var(--mist);
}
.tmenu__thumb img { width: 100%; height: 100%; object-fit: cover; }
.tmenu__meta { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.tmenu__ctitle {
  font: 600 13.5px var(--body); color: var(--paper);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tmenu__cwho {
  font: 400 12px var(--body); color: var(--mist);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.tmenu__list { display: flex; flex-direction: column; }
.tmenu__opt {
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 9px 11px; border: 0; border-radius: 8px; background: none;
  color: var(--paper); font: 500 13px var(--body); text-align: left; cursor: pointer;
  transition: background .12s, color .12s;
}
.tmenu__opt svg { width: 17px; height: 17px; flex: 0 0 auto; fill: currentColor; color: var(--mist); }
.tmenu__opt svg[stroke], .tmenu__opt .tmenu__caret svg { fill: none; stroke: currentColor; }
.tmenu__opt span { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tmenu__opt:hover, .tmenu__opt:focus-visible { background: var(--raised); outline: none; }
.tmenu__opt:hover svg { color: var(--amber); }
.tmenu__opt.is-liked svg { color: var(--amber); }
.tmenu__caret { display: grid; place-items: center; margin-left: auto; }
.tmenu__caret svg { width: 16px; height: 16px; transition: transform .14s var(--ease); }
.tmenu__rule { height: 1px; margin: 5px 8px; background: var(--edge); }

/* A row that owns a flyout. The submenu is absolutely placed to the side of the row and slides in
   when the row (or the submenu itself) is hovered or focused — TIDAL's "slide left and open". */
.tmenu__sub { position: relative; }
.tmenu__sub > .tmenu__opt[aria-haspopup]:hover .tmenu__caret svg { transform: translateX(2px); }
.tmenu__flyout {
  position: absolute; top: -6px; z-index: 2; width: 240px;
  padding: 6px; background: rgba(24, 30, 48, .99);
  border: 1px solid var(--edge); border-radius: 12px;
  box-shadow: 0 18px 44px rgba(0, 0, 0, .55);
  opacity: 0; visibility: hidden; pointer-events: none;
  transform: translateX(8px);
  transition: opacity .14s var(--ease), transform .16s var(--ease), visibility .14s;
}
/* Default: open to the right of the card. */
.tmenu__flyout { left: calc(100% + 4px); }
/* When the card sits in the right half of the screen, flyouts slide out to the LEFT instead. */
.tmenu.is-left .tmenu__flyout { left: auto; right: calc(100% + 4px); transform: translateX(-8px); }
.tmenu__sub:hover > .tmenu__flyout,
.tmenu__sub:focus-within > .tmenu__flyout {
  opacity: 1; visibility: visible; pointer-events: auto; transform: translateX(0);
}

.tmenu__scroll { max-height: 240px; overflow-y: auto; overscroll-behavior: contain; }
.tmenu__pl { justify-content: space-between; }
.tmenu__pl svg { margin-left: auto; }
.tmenu__plname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1 1 auto; }
.tmenu__plmark { flex: 0 0 auto; display: grid; place-items: center; }
.tmenu__plmark svg { width: 15px; height: 15px; color: var(--amber); }
.tmenu__pl[data-has] .tmenu__plname { color: var(--mist); }
.tmenu__none { padding: 10px 12px; margin: 0; font: 400 12px var(--mono); color: var(--mist); }
.tmenu__new { display: flex; gap: 6px; padding: 2px; }
.tmenu__newin {
  flex: 1 1 auto; min-width: 0; padding: 8px 10px;
  font: 400 12.5px var(--body); color: var(--paper);
  background: var(--sink); border: 1px solid var(--edge); border-radius: 8px;
}
.tmenu__newin:focus { outline: none; border-color: var(--cyan); }
.tmenu__newgo {
  flex: 0 0 auto; display: grid; place-items: center; width: 36px;
  border: 0; border-radius: 8px; background: var(--amber); color: var(--ink); cursor: pointer;
  transition: background .12s, transform .1s;
}
.tmenu__newgo svg { width: 18px; height: 18px; fill: currentColor; }
.tmenu__newgo:hover { background: #ffb847; }
.tmenu__newgo:active { transform: scale(.92); }

/* On touch, there is no hover — tapping a flyout row opens it, and it stacks in flow instead of
   flying off the side of a narrow screen. */
@media (hover: none) {
  .tmenu { width: min(280px, calc(100vw - 24px)); }
  .tmenu__flyout {
    position: static; width: auto; opacity: 1; visibility: visible; pointer-events: auto;
    transform: none; box-shadow: none; border: 0; border-radius: 0;
    background: none; padding: 2px 0 2px 22px; max-height: 200px; overflow-y: auto;
  }
}

/* ---------------- import a playlist ---------------- */
.mu__navheadacts { display: flex; align-items: center; gap: 2px; }
.mu__navimport {
  display: grid; place-items: center; width: 28px; height: 28px;
  border: 0; border-radius: 7px; background: none; color: var(--mist); cursor: pointer;
  transition: color .14s, background .14s, transform .1s;
}
.mu__navimport:hover { color: var(--amber); background: var(--raised); }
.mu__navimport:active { transform: scale(.9); }

.mu__import {
  position: fixed; inset: 0; z-index: 240; display: grid; place-items: center;
  padding: 20px; background: rgba(7, 9, 20, .72); backdrop-filter: blur(6px);
  animation: muImpFade .16s var(--ease);
}
@keyframes muImpFade { from { opacity: 0; } }
.mu__importcard {
  width: min(520px, 100%); max-height: 88vh; overflow-y: auto;
  background: var(--panel); border: 1px solid var(--edge); border-radius: 16px;
  box-shadow: 0 30px 80px rgba(0, 0, 0, .6);
  animation: muImpRise .2s var(--ease);
}
@keyframes muImpRise { from { opacity: 0; transform: translateY(10px); } }
.mu__importhead {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 20px 12px;
}
.mu__importhead h2 { margin: 0; font: 400 20px var(--display); color: var(--paper); }
.mu__importx {
  display: grid; place-items: center; width: 34px; height: 34px;
  border: 0; border-radius: 999px; background: none; color: var(--mist); cursor: pointer;
  transition: color .14s, background .14s;
}
.mu__importx svg { width: 18px; height: 18px; fill: currentColor; }
.mu__importx:hover { color: var(--paper); background: var(--raised); }
.mu__importbody { padding: 0 20px 20px; display: flex; flex-direction: column; gap: 12px; }
.mu__importlbl { margin: 0; font: 500 13px var(--body); color: var(--mist); }
.mu__importlbl b { color: var(--paper); font-weight: 600; }
.mu__importrow { display: flex; gap: 8px; align-items: center; }
.mu__importrow--paste { display: block; }
.mu__importin {
  flex: 1 1 auto; min-width: 0; padding: 11px 13px;
  font: 400 13.5px var(--body); color: var(--paper);
  background: var(--sink); border: 1px solid var(--edge); border-radius: 10px;
}
.mu__importin:focus { outline: none; border-color: var(--cyan); }
.mu__importta {
  width: 100%; resize: vertical; min-height: 100px; padding: 11px 13px;
  font: 400 13px var(--mono); color: var(--paper); line-height: 1.6;
  background: var(--sink); border: 1px solid var(--edge); border-radius: 10px;
}
.mu__importta:focus { outline: none; border-color: var(--cyan); }
.mu__importor {
  margin: 2px 0; font: 500 11px var(--mono); text-transform: uppercase; letter-spacing: .1em;
  color: var(--mist); text-align: center; position: relative;
}
.mu__importor::before, .mu__importor::after {
  content: ''; position: absolute; top: 50%; width: 38%; height: 1px; background: var(--edge);
}
.mu__importor::before { left: 0; }
.mu__importor::after { right: 0; }
.mu__importnote { font: 400 12.5px var(--body); color: var(--mist); }

.mu__importresult { border-top: 1px solid var(--edge); padding-top: 12px; display: flex; flex-direction: column; gap: 10px; }
.mu__importsum { font: 500 13px var(--body); color: var(--mist); }
.mu__importsum b { color: var(--amber); font: 700 15px var(--body); }
.mu__importsum span { color: var(--paper); }
.mu__importlist {
  max-height: 300px; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: 1px;
  border: 1px solid var(--edge); border-radius: 10px; padding: 4px;
}
.mu__importrowitem {
  display: flex; align-items: center; gap: 10px; padding: 7px 8px; border-radius: 7px;
}
.mu__importrowitem:hover { background: var(--raised); }
.mu__importmatch { flex: 0 0 auto; display: grid; place-items: center; width: 20px; height: 20px; color: var(--amber); }
.mu__importmatch svg { width: 16px; height: 16px; fill: currentColor; }
.mu__importrowitem.is-miss .mu__importmatch { color: var(--mist); }
.mu__importmeta { min-width: 0; display: flex; flex-direction: column; }
.mu__importt {
  font: 500 13px var(--body); color: var(--paper);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mu__importrowitem.is-miss .mu__importt { color: var(--mist); }
.mu__importw {
  font: 400 11.5px var(--body); color: var(--mist);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mu__importsave { display: flex; gap: 8px; align-items: center; }
.mu__importsave .btn { white-space: nowrap; }

/* ---------------- toast ---------------- */
.toast {
  position: fixed; left: 50%; bottom: calc(var(--bar-h) + 24px); z-index: 200;
  transform: translate(-50%, 12px);
  padding: 11px 20px; border-radius: 999px;
  background: var(--paper); color: var(--ink);
  font: 600 12.5px var(--body); letter-spacing: .01em;
  box-shadow: 0 12px 34px rgba(0, 0, 0, .5);
  opacity: 0; pointer-events: none;
  transition: opacity .2s var(--ease), transform .2s var(--ease);
}
.toast.is-on { opacity: 1; transform: translate(-50%, 0); }
body.has-now .toast { bottom: calc(var(--bar-h) + 84px); }

@media (max-width: 700px) {
  .mu__mast { flex-direction: column; align-items: stretch; gap: 14px; }
  .mu__find { max-width: none; margin-left: 0; }
}
@media (max-width: 620px) {
  .mu__band { flex-direction: column; align-items: center; text-align: center; }
  .mu__bandart { width: min(58vw, 240px); }
  .mu__bandbody { display: flex; flex-direction: column; align-items: center; }
  .mu__bandkind { justify-content: center; }
  .mu__bandacts { justify-content: center; }
  .mu__bandbio { margin-left: auto; margin-right: auto; }
  .mu__track { grid-template-columns: 20px 40px minmax(0, 1fr); }
  .mu__cover { width: 40px; height: 40px; }
  .mu__dur { display: none; }
}

/* A music hit wears the site's poster card everywhere outside the section � the all-catalogue
   search and the collections/library shelves � and square covers need square art in that 2:3 slot. */
.card--mu .card__art { background: var(--sink); }
.card--mu .card__art img { aspect-ratio: 1 / 1; object-fit: cover; }

/* ---- the music player surface (audio in the player shell) ---- */
.player__art {
  position: absolute; inset: 0; z-index: 1;
  display: grid; place-items: center;
  padding: clamp(20px, 4vw, 40px) clamp(16px, 3vw, 30px) 130px;
}
.player__cover {
  width: min(46vh, 40vw); aspect-ratio: 1 / 1;
  background: var(--sink); border: 1px solid var(--edge);
  border-radius: 6px; overflow: hidden;
  box-shadow: 0 30px 90px -30px rgba(0, 0, 0, .9);
}
.player__cover img { width: 100%; height: 100%; object-fit: cover; }
.player__ids { margin-top: 16px; text-align: center; max-width: min(64vw, 560px); }
.player__track {
  font: 400 clamp(18px, 2.4vw, 26px) var(--display);
  text-transform: uppercase; letter-spacing: .01em; line-height: 1.15;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.player__who {
  color: var(--mist); font: 400 12.5px var(--mono);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  margin-top: 4px;
}
.player__art + .player__ctl { z-index: 3; }

/* Lyrics panel skin: one big scrolling column on the player's colour wash — the line being sung
   bright and large, the rest dimmed, each timed line tappable to seek. The 40/60 split that places
   it beside the cover lives with the music-player layout further down. */
@keyframes lyrIn { from { opacity: 0; transform: translateX(16px); } }
.player__panel--lyrics { justify-content: stretch; }
.player__lyrscroll {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: none;
  -webkit-mask-image: linear-gradient(180deg, transparent, #000 8%, #000 90%, transparent);
          mask-image: linear-gradient(180deg, transparent, #000 8%, #000 90%, transparent);
}
.player__lyrscroll::-webkit-scrollbar { width: 0; }
.player__lyrbody {
  padding: 10vh clamp(8px, 1.5vw, 24px);
  display: flex; flex-direction: column; gap: 4px;
}
/* Words straight on the player's colour wash — no card. The line being sung is bright white and
   large; the rest sit dimmed above and below it. */
.player__lyrline {
  display: block; width: 100%; text-align: left;
  border: 0; background: none; padding: 8px 2px;
  font: 800 clamp(24px, 2.9vw, 40px)/1.22 var(--body); letter-spacing: -.015em;
  color: rgba(237, 240, 250, .34); cursor: pointer;
  transition: color .3s var(--ease), transform .3s var(--ease), text-shadow .3s;
}
.player__lyrline:hover { color: rgba(237, 240, 250, .62); }
.player__lyrline.is-at {
  color: #fff; transform: translateX(4px);
  text-shadow: 0 2px 30px rgba(0, 0, 0, .5);
}
/* An untimed block never lights a line — no timestamps to follow — so it reads calm and evenly. */
.player__lyrline--flat { cursor: default; color: rgba(237, 240, 250, .62); font-weight: 700; }
.player__lyrline--flat:hover { color: rgba(237, 240, 250, .62); }
.player__lyrline:focus-visible { outline: none; color: #fff; }
.player__lyrstate {
  font: 500 13px var(--mono); color: var(--mist); text-align: left; padding-top: 2vh;
}

/* ---------------- the docked now-playing bar ---------------- */
/* Music keeps one <audio> session at body level, shown in this floating dock on every section of
   the site, so playback survives Back and navigation the way a listening session should. It sits
   between the content and the overlay surfaces: under the sheet and the full-screen player, over
   everything that scrolls. */
:root { --dock-h: 70px; }
.nowbar {
  position: fixed; left: 14px; right: 14px; bottom: 14px; z-index: 110;
  height: var(--dock-h);
  display: flex; align-items: center; gap: clamp(8px, 1.4vw, 14px);
  padding: 0 clamp(10px, 1.6vw, 16px) 0 12px;
  border-radius: 18px; overflow: hidden;
  background: linear-gradient(180deg, rgba(29, 35, 60, .94), rgba(12, 15, 28, .97));
  border: 1px solid var(--edge);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  box-shadow: 0 28px 80px -24px rgba(0, 0, 0, .92), 0 10px 34px -16px rgba(0, 0, 0, .8);
  animation: dockup .3s var(--ease);
}
@keyframes dockup { from { opacity: 0; transform: translateY(26px) scale(.985); } }
/* The page clears the dock with padding rather than the dock shrinking the viewport, so it never
   covers the last row of a shelf or the footer. */
body.has-now { padding-bottom: calc(var(--dock-h) + 44px); }

/* A hairline of progress across the dock's whole top edge � the album's time, at a glance, and
   the seek target too. */
.nowbar__rail {
  position: absolute; top: 0; left: 0; right: 0; height: 14px; cursor: pointer;
}
.nowbar__rail::before {
  content: ''; position: absolute; left: 0; right: 0; top: 50%;
  height: 3px; transform: translateY(-50%);
  background: rgba(237, 240, 250, .14); border-radius: 99px;
}
.nowbar__fill {
  position: absolute; left: 0; top: 50%; height: 3px; width: 0;
  transform: translateY(-50%);
  background: linear-gradient(90deg, var(--amber), #ffc46b);
  border-radius: 99px;
  box-shadow: 0 0 12px rgba(245, 165, 36, .55);
}
.nowbar__knob {
  position: absolute; top: 50%; left: 0; width: 12px; height: 12px;
  background: var(--paper); border-radius: 50%;
  box-shadow: 0 0 0 4px rgba(245, 165, 36, .18);
  transform: translate(-50%, -50%) scale(0);
  transition: transform .15s var(--ease);
}
.nowbar__rail:hover .nowbar__knob,
.nowbar__rail:focus-visible .nowbar__knob { transform: translate(-50%, -50%) scale(1); }
.nowbar__rail:focus-visible { outline: none; }

/* The "now" half: cover, a live equaliser, and the track's names. */
.nowbar__now {
  display: flex; align-items: center; gap: 12px; flex: 1 1 auto; min-width: 0;
  max-width: min(46%, 460px); text-align: left;
  padding: 0; background: none; border: 0; color: inherit; cursor: pointer;
  border-radius: 12px;
}
.nowbar__now:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
.nowbar__art {
  position: relative; flex: 0 0 46px; width: 46px; height: 46px;
  display: grid; place-items: center;
  background: linear-gradient(160deg, var(--raised), var(--panel));
  border: 1px solid rgba(237, 240, 250, .14); border-radius: 11px; overflow: hidden;
  transition: box-shadow .3s var(--ease), border-color .3s var(--ease);
}
.nowbar__art::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(135deg, rgba(255, 255, 255, .18), transparent 46%);
  pointer-events: none;
}
.nowbar__art img { width: 100%; height: 100%; object-fit: cover; }
.nowbar.is-playing .nowbar__art {
  border-color: rgba(245, 165, 36, .5);
  box-shadow: 0 0 0 1px rgba(245, 165, 36, .4), 0 6px 26px -8px rgba(245, 165, 36, .6);
}
.nowbar.is-playing .nowbar__art img { animation: artbreathe 3.4s ease-in-out infinite; }
@keyframes artbreathe {
  50% { filter: saturate(1.18) brightness(1.07); transform: scale(1.04); }
}
.nowbar__ids { display: flex; align-items: center; gap: 10px; min-width: 0; }
.nowbar__text { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
/* The equaliser only talks while the session is actually playing. */
.nowbar__eq {
  display: none; align-items: flex-end; gap: 2px; height: 15px; flex: 0 0 auto;
}
.nowbar.is-playing .nowbar__eq { display: flex; }
.nowbar__eq i {
  width: 2.5px; background: var(--amber); border-radius: 2px;
  transform-origin: bottom;
  animation: eqb .95s ease-in-out infinite;
}
.nowbar__eq i:nth-child(1) { height: 38%; animation-delay: -.2s; }
.nowbar__eq i:nth-child(2) { height: 100%; animation-delay: -.55s; }
.nowbar__eq i:nth-child(3) { height: 62%; animation-delay: -.75s; }
@keyframes eqb { 0%, 100% { transform: scaleY(.35); } 50% { transform: scaleY(1); } }
.nowbar__title {
  font-size: 13px; font-weight: 600; color: var(--paper); line-height: 1.25;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nowbar__who {
  font: 400 10.5px var(--mono); color: var(--mist); letter-spacing: .04em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Transport: quiet ghosts that raise on approach, with the amber play pill at the heart. */
.nowbar__transport { display: flex; align-items: center; gap: 3px; flex: 0 0 auto; }
.nowbar__btn {
  width: 36px; height: 36px; display: grid; place-items: center;
  color: #C6CCE0; border-radius: 50%;
  transition: background .16s, color .16s, transform .14s var(--ease);
}
.nowbar__btn:hover { background: rgba(237, 240, 250, .12); color: var(--paper); }
.nowbar__btn:active { transform: scale(.92); }
.nowbar__btn svg { width: 15px; height: 15px; fill: currentColor; }
.nowbar__btn--big {
  width: 40px; height: 40px;
  background: linear-gradient(160deg, #ffbe4d, var(--amber));
  color: var(--ink);
  box-shadow: 0 10px 28px -10px rgba(245, 165, 36, .95), inset 0 1px 0 rgba(255, 255, 255, .35);
}
.nowbar__btn--big:hover { background: linear-gradient(160deg, #ffc96a, #ffb847); color: var(--ink); transform: scale(1.08); }
.nowbar__btn--big svg { width: 19px; height: 19px; }
.nowbar__btn:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
.nowbar__clock {
  flex: 0 0 auto; font: 500 11px var(--mono); color: var(--mist); letter-spacing: .03em;
}
.nowbar__sep { color: #4B5470; font: 500 11px var(--mono); }
.nowbar__close {
  flex: 0 0 auto; width: 34px; height: 34px; display: grid; place-items: center;
  color: var(--mist); border-radius: 50%;
  transition: background .16s, color .16s, transform .14s var(--ease);
}
.nowbar__close:hover { background: rgba(237, 240, 250, .13); color: var(--paper); transform: rotate(90deg); }
.nowbar__close svg { width: 15px; height: 15px; fill: currentColor; }
.nowbar__close:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

/* ---------------- the full-screen music face ---------------- */
/* The same session, blown up: an aurora breathes behind the cover and the cover itself floats,
   shines and keeps its square only while audio is actually playing � pausing settles the room. */
/* A three-row shell: the tab bar, the art/panel region, then the controls. The backdrop is a deep
   gradient tinted per song by the --mu-bg vars the player sets from the cover's dominant colour. */
.player[data-mu="1"] {
  --mu-bg1: #1b2138; --mu-bg2: #05060d;
  background: linear-gradient(180deg, var(--mu-bg1) 0%, var(--mu-bg2) 76%);
  display: grid; grid-template-rows: auto minmax(0, 1fr) auto;
  transition: background .6s var(--ease);
}
/* No video stage in music — keep it only as a centred overlay for the buffering spinner. */
.player[data-mu="1"] .player__stage {
  position: absolute; inset: 0; display: grid; place-items: center;
  pointer-events: none; z-index: 7; background: none;
}
/* Art region: cover centred by default; a split with the panel on the right when a tab is on. */
.player[data-mu="1"] .player__art {
  position: relative; display: grid; place-items: center;
  padding: clamp(18px, 4vh, 52px) clamp(24px, 6vw, 96px);
  min-height: 0;
}
.player[data-mu="1"] .player__bg {
  position: absolute; inset: 0; z-index: 0;
  background-size: cover; background-position: center;
  filter: blur(130px) brightness(.5) saturate(1.25); opacity: .28;
  pointer-events: none;
}
.player[data-mu="1"] .player__cover {
  position: relative; z-index: 1; grid-area: 1 / 1;
  height: min(100%, 60vh); width: auto; max-width: min(100%, 60vh); aspect-ratio: 1 / 1;
  margin: 0; border-radius: 8px; overflow: hidden;
  border: 1px solid rgba(237, 240, 250, .12);
  box-shadow: 0 40px 120px -40px rgba(0, 0, 0, .92);
}
.player[data-mu="1"] .player__cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.player[data-mu="1"] .scrub:hover .scrub__fill { filter: drop-shadow(0 0 6px rgba(245, 165, 36, .55)); }

/* ---- a tab is open: cover left, panel right ---- */
.player[data-mu="1"].is-panel .player__art {
  grid-template-columns: minmax(0, 40%) minmax(0, 1fr);
  place-items: center stretch;
  gap: clamp(22px, 3.5vw, 64px);
  padding: clamp(18px, 4vh, 52px) clamp(28px, 5vw, 84px);
}
.player[data-mu="1"].is-panel .player__cover {
  grid-area: auto; grid-column: 1; justify-self: center; align-self: center;
  height: auto; width: 100%; max-width: min(52vh, 460px);
}
.player[data-mu="1"].is-panel .player__panel { grid-column: 2; }
.player[data-mu="1"] .player__panel { position: relative; z-index: 1; min-width: 0; height: 100%; }

/* The music face rises out of the dock and, when minimised, slides back down under it � the down
   button is a gesture, so the surface answers with the motion that gesture describes. */
.player[data-mu="1"] {
  will-change: transform;
  animation: muUp .34s var(--ease);
}
@keyframes muUp { from { transform: translateY(7vh); opacity: 0; } }
.player[data-mu="1"].is-minimizing {
  pointer-events: none;
  animation: muDown .34s cubic-bezier(.6, 0, .7, .2) forwards;
}
@keyframes muDown {
  to { transform: translateY(108vh); opacity: .15; }
}

@media (max-width: 700px) {
  :root { --dock-h: 62px; }
  .nowbar { left: 8px; right: 8px; bottom: 8px; border-radius: 14px; gap: 8px; padding: 0 10px 0 10px; }
  body.has-now { padding-bottom: calc(var(--dock-h) + 34px); }
  .nowbar__now { max-width: none; }
  .nowbar__who { display: none; }
  .nowbar__clock { font-size: 10px; }
}
@media (max-width: 520px) {
  .nowbar__clock, .nowbar__sep { display: none; }
  .nowbar__eq { display: none !important; }
  .nowbar__art { flex-basis: 42px; width: 42px; height: 42px; border-radius: 10px; }
  .nowbar__btn { width: 34px; height: 34px; }
  .nowbar__btn--big { width: 38px; height: 38px; }
  .nowbar__close { width: 32px; height: 32px; }
}

@media (prefers-reduced-motion: reduce) {
  .nowbar, .nowbar__art img, .nowbar__eq i,
  .player[data-mu="1"],
  .player[data-mu="1"].is-minimizing,
  .player[data-mu="1"] .player__art::before,
  .player[data-mu="1"] .player__cover,
  .player[data-mu="1"] .player__cover::after {
    animation: none !important;
  }
  .nowbar__eq { display: none; }
  .nowbar.is-playing .nowbar__eq { display: none; }
}

/* ---------------- music transport extras (shuffle, repeat, favourites, up next) ---------------- */

/* Line-drawn controls keep the ghost feel; amber marks whichever mode is live. */
.pbtn--ln svg,
.nowbar__like svg {
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
.pbtn--ln { color: #C6CCE0; }
.pbtn--ln:hover { background: rgba(237, 240, 250, .12); color: var(--paper); }
.pbtn--ln.is-on { color: var(--amber); }
.pbtn--ln.is-on:hover { color: var(--amber); }
.pbtn--ln.is-liked { color: var(--amber); }
.pbtn--ln.is-liked svg { fill: currentColor; }
.pbtn--ln.is-liked { animation: likePop .32s var(--ease); }
@keyframes likePop { 0% { transform: scale(1); } 42% { transform: scale(1.32); } 100% { transform: scale(1); } }

.pbtn[data-p="repeat"] { position: relative; }
.pbtn[data-p="repeat"].is-one::after {
  content: '1'; position: absolute; top: 7px; right: 8px;
  font: 700 8px var(--mono); line-height: 1;
  color: var(--ink); background: var(--amber);
  border-radius: 2px; padding: 1px 2px;
}

/* Favourites heart on the dock. */
.nowbar__like svg { width: 16px; height: 16px; }
.nowbar__like { color: var(--mist); transition: color .16s, transform .16s var(--ease); }
.nowbar__like:hover { color: var(--paper); }
.nowbar__like.is-liked { color: var(--amber); }
.nowbar__like.is-liked svg { fill: currentColor; }

/* The up-next drawer � a queue list that mirrors the transport, with the playing track live. */
.player__up {
  position: absolute; top: 64px; right: clamp(12px, 2vw, 26px); bottom: 118px;
  width: clamp(300px, 34vw, 430px); z-index: 6;
  display: flex; flex-direction: column; overflow: hidden;
  background: rgba(7, 9, 20, .9);
  border: 1px solid var(--edge); border-radius: var(--r);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  box-shadow: 0 26px 70px -30px rgba(0, 0, 0, .9);
  animation: dockup .22s var(--ease);
}
.player__uphead {
  display: flex; align-items: center; gap: 8px; flex: 0 0 auto;
  padding: 9px 10px 9px 14px;
  border-bottom: 1px solid rgba(237, 240, 250, .09);
}
.player__uphead b {
  flex: 1 1 auto; font: 500 10px var(--mono); letter-spacing: .16em;
  text-transform: uppercase; color: var(--mist);
}
.player__uphead .pnav--wide.is-on {
  color: var(--amber); border-color: rgba(245, 165, 36, .55);
  background: rgba(245, 165, 36, .08);
}
.player__upbody {
  flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: 1px; padding: 6px;
}
/* A queue row is a flex shell: the wide play target plus a remove ✕ that surfaces on hover. The
   recommended rows below reuse the class but stay single buttons, so they override back to a grid. */
.player__uprow {
  position: relative; display: flex; align-items: center; width: 100%;
  border-radius: 9px; transition: background .15s;
}
.player__uprow:hover, .player__uprow:focus-within { background: var(--raised); }
.player__uprow.is-at { background: rgba(245, 165, 36, .07); }
.player__upmain {
  flex: 1 1 auto; min-width: 0;
  display: grid; grid-template-columns: 30px minmax(0, 1fr) auto;
  align-items: center; gap: 10px;
  padding: 7px 9px; text-align: left; border: 0; background: none; border-radius: 9px;
}
.player__upmain:focus-visible { outline: none; }
.player__uprm {
  flex: 0 0 auto; display: grid; place-items: center;
  width: 30px; height: 30px; margin-right: 5px;
  border: 0; border-radius: 999px; background: none; color: var(--mist); cursor: pointer;
  opacity: 0; transition: opacity .15s, color .15s, background .15s, transform .1s;
}
.player__uprm svg { width: 15px; height: 15px; fill: currentColor; }
.player__uprow:hover .player__uprm, .player__uprow:focus-within .player__uprm { opacity: 1; }
.player__uprm:hover { color: var(--reel); background: rgba(229, 72, 77, .12); }
.player__uprm:active { transform: scale(.9); }
.player__uprm:focus-visible { outline: 2px solid var(--cyan); outline-offset: 1px; opacity: 1; }
.player__uprow--rec:hover, .player__uprow--rec:focus-visible { background: var(--raised); outline: none; }
.player__upnum {
  width: 16px; text-align: center; color: var(--mist);
  font: 400 11px var(--mono);
}
.player__upnum.is-live {
  display: inline-flex; align-items: flex-end; justify-content: center; gap: 2px; height: 12px;
}
.player__upnum.is-live i {
  width: 2.5px; background: var(--amber); border-radius: 2px;
  transform-origin: bottom; animation: eqb .9s ease-in-out infinite;
}
.player__upnum.is-live i:nth-child(1) { height: 45%; animation-delay: -.2s; }
.player__upnum.is-live i:nth-child(2) { height: 100%; animation-delay: -.5s; }
.player__upnum.is-live i:nth-child(3) { height: 70%; animation-delay: -.75s; }
.player__upmeta { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.player__upname {
  font-size: 13px; font-weight: 600; color: var(--paper); line-height: 1.3;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.player__uprow.is-at .player__upname { color: #FFD98A; }
.player__upwho {
  font: 400 10.5px var(--mono); color: var(--mist);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.player__updur { font: 400 10.5px var(--mono); color: var(--mist); }

/* The transport row has more company now � on narrower screens the volume is still reachable by
   the mute button and keyboard (m), and the slider steps aside. */
@media (max-width: 1000px) {
  .player[data-mu="1"] .player__bar .vol { display: none; }
}
@media (max-width: 860px) {
  .player__up {
    left: 0; right: 0; width: auto; max-height: 46%;
    border-left: 0; border-right: 0; bottom: 116px;
  }
}
@media (max-width: 700px) {
  .player[data-mu="1"] .player__time { display: none; }
}
@media (max-width: 480px) {
  .nowbar__like { display: none; }
  .player__bar .pbtn[data-p="lyr"] { font-size: 0; width: 34px; height: 34px; display: grid; place-items: center; }
  .player__bar .pbtn[data-p="lyr"] { padding: 0; }
  .player__bar .pbtn[data-p="lyr"]::after { content: '?'; font-size: 15px; }
}

/* ---- transport hygiene: nothing in the deck shrinks or collides, times stay on one line and
   the track's names always sit clear of the control bar. ---- */
.player[data-mu="1"] .player__bar { gap: clamp(4px, .8vw, 10px); }
.player[data-mu="1"] .player__bar .pbtn,
.player[data-mu="1"] .player__bar .player__time,
.player[data-mu="1"] .player__bar .vol { flex: 0 0 auto; }
.player[data-mu="1"] .player__time {
  display: inline-flex; align-items: baseline; gap: 4px;
  white-space: nowrap; margin: 0 6px; letter-spacing: .03em;
}
.player[data-mu="1"] .player__ids { position: relative; z-index: 2; margin-top: clamp(14px, 2.2vh, 22px); }
.player[data-mu="1"] .player__who, .player[data-mu="1"] .player__track {
  overflow-wrap: anywhere; white-space: normal;
}
.player[data-mu="1"] .player__track { line-height: 1.15; }
.player[data-mu="1"] .player__who { line-height: 1.4; margin-top: 6px; }

/* ---- dock restructure: one clean info row over its own full-width scrub row, so no text can
   ever sit on the seek line or crowd the clock. ---- */
:root { --dock-h: 96px; }
.nowbar {
  height: auto;
  display: flex; flex-direction: column; align-items: stretch; justify-content: center;
  padding: 8px clamp(10px, 1.6vw, 16px) 6px;
}
body.has-now { padding-bottom: calc(var(--dock-h) + 40px); }
.nowbar__row {
  display: flex; align-items: center;
  justify-content: flex-start;
  gap: clamp(10px, 2vw, 26px); min-height: 48px;
}
/* Equal flexible zones on either side of the transport keep the play button dead-centre no matter
   how long the title or how many right-side controls are present. */
.nowbar__now {
  flex: 1 1 0;
  max-width: none;
}
.nowbar__transport { flex: 0 0 auto; }
.nowbar__side {
  display: flex; align-items: center; justify-content: flex-end;
  gap: clamp(4px, .8vw, 10px);
  flex: 1 1 0; min-width: 0;
}
.nowbar__scrubrow {
  position: relative; display: flex; align-items: center;
  height: 16px; margin: 2px 0 0;
}
.nowbar__rail {
  position: relative; top: auto; left: 0; right: 0;
  flex: 1 1 auto; width: auto; height: 16px;
}
@media (max-width: 700px) {
  :root { --dock-h: 86px; }
  .nowbar { padding: 6px 10px 4px; }
}
@media (max-width: 520px) {
  :root { --dock-h: 80px; }
  .nowbar__row { gap: 4px; min-height: 44px; }
}

/* Narrow screens: shed the non-essential deck so the core transport always fits and nothing ever
   spills past the right edge. The dropped controls stay reachable (m = mute, like lives on the
   dock, download is in the full view only on roomy screens). */
@media (max-width: 700px) {
  .player[data-mu="1"] .player__time { display: none; }
}
@media (max-width: 560px) {
  .player[data-mu="1"] .player__bar .pbtn[data-p="like"],
  .player[data-mu="1"] .player__bar .pbtn[data-p="mute"],
  .player[data-mu="1"] .player__bar .pbtn[data-p="dl"] { display: none; }
}

/* The dock's up-next button is a line icon, same stroke treatment as the heart. */
.nowbar__up { color: var(--mist); transition: color .16s, transform .16s var(--ease); }
.nowbar__up:hover { color: var(--paper); }
.nowbar__up svg {
  width: 16px; height: 16px;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}

/* ---------------- Tidal-style surfaces ---------------- */
/* The dock becomes Tidal's bar: an edge-to-edge strip with a hairline of progress along its very
   top, one calm row � track left, transport centred, like/up-next/close right � and the seek
   hairline is the only ornament. */
:root { --dock-h: 88px; }
body.has-now { padding-bottom: calc(var(--dock-h) + 12px); }
.nowbar {
  left: 0; right: 0; bottom: 0;
  height: var(--dock-h);
  border-radius: 0;
  padding: 0;
  overflow: hidden;
  background: linear-gradient(180deg, rgba(22, 27, 46, .98), rgba(10, 13, 24, .99));
  border-top: 1px solid rgba(237, 240, 250, .12);
}
.nowbar__row {
  flex: 1 1 auto; min-height: 0;
  padding: 0 clamp(10px, 1.6vw, 18px);
  gap: clamp(10px, 2vw, 30px);
}
.nowbar__scrubrow {
  position: absolute; top: 4px; left: 0; right: 0;
  height: 5px; margin: 0; z-index: 3;
}
.nowbar__rail { height: 5px; }
.nowbar__rail::before { height: 2.5px; }
.nowbar__fill { height: 2.5px; }
.nowbar__knob { top: 50%; }
.nowbar__clock, .nowbar__sep { display: none; }
.nowbar__btn { color: #DDE2F0; }
.nowbar__btn:hover { background: rgba(237, 240, 250, .1); color: var(--paper); }
.nowbar__btn--big {
  width: 42px; height: 42px;
  background: var(--paper); color: var(--sink);
  box-shadow: none; border-radius: 50%;
}
.nowbar__btn--big:hover { background: #fff; color: var(--sink); }
.nowbar__btn--big svg { width: 20px; height: 20px; fill: currentColor; }
.nowbar__now { gap: 12px; }
.nowbar__art { width: 52px; height: 52px; flex-basis: 52px; border-radius: 6px; }
.nowbar__title { font-size: 14px; }
.nowbar__who { font-size: 11.5px; }
.nowbar__side { gap: 2px; }
.nowbar__like, .nowbar__up, .nowbar__close { color: #B9C1D8; }
.nowbar__like:hover, .nowbar__up:hover, .nowbar__close:hover { color: var(--paper); }
.nowbar__like.is-liked { color: var(--amber); }
.nowbar__like.is-liked svg { fill: currentColor; stroke: currentColor; }
.nowbar__close:hover { transform: rotate(0); background: rgba(237, 240, 250, .1); }

/* The expanded face becomes Tidal's now-playing: the room is washed in a blurred, dimmed copy of
   the cover art, and the art itself sits left with the track, artist and transport to its right �
   quiet, one thing at a time. */
/* ---- the top tab bar (Similar tracks · Credits · Lyrics) + window controls ---- */
.player[data-mu="1"] .player__top {
  position: relative; z-index: 6;
  display: flex; align-items: center; justify-content: flex-end;
  padding: clamp(14px, 2vh, 22px) clamp(18px, 4vw, 40px);
  background: none; border: 0;
}
.player__tabs { display: flex; align-items: center; gap: clamp(4px, 1vw, 14px); }
.ptab {
  padding: 8px 16px; border: 0; background: rgba(237, 240, 250, 0); border-radius: 999px;
  font: 600 15px var(--body); letter-spacing: .01em; color: rgba(237, 240, 250, .58);
  cursor: pointer; will-change: transform, background-color;
  transition: color .24s var(--ease), background-color .24s var(--ease), transform .14s var(--ease);
}
.ptab:hover { color: var(--paper); background: rgba(237, 240, 250, .12); }
.ptab:active { transform: scale(.96); }
.ptab.is-on { background: var(--paper); color: var(--ink); }
.ptab.is-on:hover { background: #fff; }
.ptab:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
.picon {
  display: grid; place-items: center; width: 38px; height: 38px; margin-left: 4px;
  border: 0; border-radius: 999px; background: none; color: rgba(237, 240, 250, .72); cursor: pointer;
  transition: color .16s, background .16s, transform .1s;
}
.picon svg { width: 19px; height: 19px; fill: currentColor; }
.picon:hover { color: var(--paper); background: rgba(237, 240, 250, .1); }
.picon:active { transform: scale(.92); }
.picon:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

/* ---- the bottom controls: identity · transport · extras, then a full-width scrubber ---- */
.player[data-mu="1"] .player__ctl {
  position: relative; z-index: 6;
  display: flex; flex-direction: column; gap: clamp(8px, 1.4vh, 16px);
  padding: clamp(10px, 1.6vh, 18px) clamp(18px, 4vw, 40px) clamp(14px, 2vh, 24px);
  background: linear-gradient(180deg, transparent, rgba(0, 0, 0, .3));
}
.player__foot { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 16px; }
.player__nowid { display: flex; align-items: center; gap: 10px; min-width: 0; }
.player__idtext { min-width: 0; }
.player__idline { display: flex; align-items: center; gap: 8px; min-width: 0; }
.player[data-mu="1"] .player__track {
  font: 600 15px var(--body); color: var(--paper); letter-spacing: 0; line-height: 1.25;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-shadow: none; text-align: left;
}
.player__exbadge {
  flex: 0 0 auto; display: grid; place-items: center; min-width: 16px; height: 16px; padding: 0 3px;
  font: 700 9px var(--mono); font-style: normal; color: var(--ink); background: rgba(237, 240, 250, .72);
  border-radius: 3px;
}
.player[data-mu="1"] .player__who {
  font: 400 13px var(--body); color: var(--mist); margin: 2px 0 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-shadow: none; text-align: left;
}
.player__lang {
  display: flex; align-items: center; gap: 5px; margin-top: 3px;
  font: 500 11px var(--mono); color: var(--mist);
}
.player__lang::before { content: '♫'; opacity: .8; }
.player__nowid .pbtn { flex: 0 0 auto; }
.player__transport { display: flex; align-items: center; gap: clamp(6px, 1vw, 14px); justify-content: center; }
.player__extra { display: flex; align-items: center; gap: 6px; justify-content: flex-end; }
/* Every transport, extra and identity button is one size, so the row reads as a set. */
.player[data-mu="1"] .player__ctl .pbtn { width: 40px; height: 40px; }
.player[data-mu="1"] .player__ctl .pbtn svg { width: 19px; height: 19px; }
.player[data-mu="1"] .player__ctl .pbtn--big {
  width: 50px; height: 50px; border-radius: 50%;
  background: var(--paper); color: var(--sink); box-shadow: none;
}
.player[data-mu="1"] .player__ctl .pbtn--big:hover { background: #fff; color: var(--sink); transform: scale(1.05); }
.player[data-mu="1"] .player__ctl .pbtn--big svg { width: 22px; height: 22px; }

/* Volume: a slim icon that grows a slider on hover (desktop) or on tap (touch, via .is-open). */
.player__vol { display: flex; align-items: center; }
.player[data-mu="1"] .player__vol .vol {
  width: 0; opacity: 0; margin-left: 0; accent-color: var(--amber); cursor: pointer;
  transition: width .2s var(--ease), opacity .2s, margin-left .2s;
}
.player__vol:hover .vol,
.player__vol:focus-within .vol,
.player__vol.is-open .vol { width: 96px; opacity: 1; margin-left: 8px; }
.player__scrubrow { display: flex; align-items: center; gap: 12px; }
.player__scrubrow .scrub { flex: 1 1 auto; }
.player__clock { flex: 0 0 auto; font: 400 11px var(--mono); color: var(--mist); min-width: 34px; text-align: center; }
.player[data-mu="1"] .player__up { bottom: 132px; }

/* ---- the panels (right column when a tab is open) ---- */
.player__panel { display: flex; flex-direction: column; overflow: hidden; animation: lyrIn .24s var(--ease); }

/* Credits — label / value pairs. */
.player__panel--credits { justify-content: center; }
.player__creds { overflow-y: auto; overscroll-behavior: contain; max-width: 640px; padding: 2vh 0; scrollbar-width: none; }
.player__creds::-webkit-scrollbar { width: 0; }
.player__cred { margin-bottom: clamp(16px, 2.4vh, 26px); }
.player__credk { font: 700 15px var(--body); color: var(--paper); }
.player__credv { font: 400 15px var(--body); color: var(--mist); margin-top: 3px; }

/* Similar tracks — radio cards over a suggested-tracks list. */
.player__simscroll { overflow-y: auto; overscroll-behavior: contain; height: 100%; padding-right: 6px; scrollbar-width: thin; }
.player__simhead { font: 400 18px var(--display); color: var(--paper); margin: 6px 0 12px; }
.player__radios { display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: 12px; margin-bottom: 24px; }
.player__radio { display: flex; flex-direction: column; text-align: left; padding: 0; border: 0; background: none; color: var(--paper); cursor: pointer; }
.player__radioart { aspect-ratio: 1 / 1; border-radius: 8px; overflow: hidden; background: var(--panel); border: 1px solid var(--edge); }
.player__radioart img { width: 100%; height: 100%; object-fit: cover; transition: transform .3s var(--ease); }
.player__radio:hover .player__radioart img { transform: scale(1.05); }
.player__radiokind { font: 500 12px var(--body); color: var(--mist); margin-top: 8px; }
.player__radioname { font: 600 14px var(--body); color: var(--paper); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.player__simlist { display: flex; flex-direction: column; gap: 2px; padding-bottom: 4vh; }
.player__simrow { position: relative; display: flex; align-items: center; border-radius: 8px; transition: background .15s; }
.player__simrow:hover, .player__simrow:focus-within { background: var(--raised); }
.player__simmain { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 12px; padding: 7px 8px; border: 0; background: none; text-align: left; }
.player__simart { flex: 0 0 auto; width: 44px; height: 44px; border-radius: 6px; overflow: hidden; display: grid; place-items: center; background: var(--sink); border: 1px solid var(--edge); color: var(--mist); }
.player__simart img { width: 100%; height: 100%; object-fit: cover; }
.player__simmeta { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.player__simname { font: 600 14px var(--body); color: var(--paper); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.player__simwho { font: 400 12px var(--mono); color: var(--mist); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.player__simadd, .player__simmore {
  flex: 0 0 auto; display: grid; place-items: center; width: 34px; height: 34px; margin-right: 4px;
  border: 0; border-radius: 999px; background: none; color: var(--mist); opacity: 0; cursor: pointer;
  transition: opacity .15s, color .15s, background .15s;
}
.player__simadd svg, .player__simmore svg { width: 17px; height: 17px; fill: currentColor; }
.player__simrow:hover .player__simadd, .player__simrow:hover .player__simmore,
.player__simrow:focus-within .player__simadd, .player__simrow:focus-within .player__simmore { opacity: 1; }
.player__simadd:hover, .player__simmore:hover { color: var(--paper); background: rgba(237, 240, 250, .1); }
.player__simadd.is-in { opacity: 1; color: var(--amber); }
@media (hover: none) { .player__simadd, .player__simmore { opacity: 1; } }

/* Tablet / small-desktop: keep the split but tighter. */
@media (max-width: 860px) {
  .player[data-mu="1"].is-panel .player__art {
    grid-template-columns: minmax(0, 34%) minmax(0, 1fr);
    gap: 16px; padding: clamp(14px, 3vh, 32px) clamp(14px, 4vw, 26px);
  }
  .player[data-mu="1"].is-panel .player__cover { max-width: min(30vh, 240px); }
  .player__foot { gap: 10px; }
  .player__extra [data-p="dl"] { display: none; }   /* download drops first; queue + volume stay */
  .ptab { font-size: 13px; padding: 7px 11px; }
}

/* Phone: one calm column. The cover-beside-panel split can't fit, so a tab shows its content
   full-width; the controls stack (identity → transport → queue/volume); the lyrics shrink to a
   readable phone size instead of the desktop scale. */
@media (max-width: 700px) {
  .player[data-mu="1"] .player__top { padding: 12px 14px; }
  .player__tabs { gap: 2px; flex: 1 1 auto; justify-content: flex-end; min-width: 0; }
  .ptab { font-size: 13px; padding: 6px 10px; white-space: nowrap; }
  .picon { width: 34px; height: 34px; margin-left: 2px; }

  .player[data-mu="1"] .player__art { padding: 14px clamp(12px, 4vw, 22px); }
  .player[data-mu="1"]:not(.is-panel) .player__cover { height: min(100%, 44vh); max-width: min(100%, 44vh); }
  /* Any open tab takes the whole width on a phone — no side cover. */
  .player[data-mu="1"].is-panel .player__art { grid-template-columns: minmax(0, 1fr); padding: 8px clamp(14px, 5vw, 22px) 8px; }
  .player[data-mu="1"].is-panel .player__cover { display: none; }

  .player[data-mu="1"] .player__ctl { padding: 10px 14px 16px; gap: 12px; }
  .player__foot { grid-template-columns: 1fr; row-gap: 14px; justify-items: stretch; }
  .player__nowid { order: 1; justify-content: flex-start; }
  .player__transport { order: 2; gap: clamp(10px, 5vw, 26px); }
  .player__extra { order: 3; justify-content: center; gap: 10px; }
  /* One consistent touch size for every control; the play button is the one that's bigger. */
  .player[data-mu="1"] .player__ctl .pbtn { width: 44px; height: 44px; }
  .player[data-mu="1"] .player__ctl .pbtn svg { width: 20px; height: 20px; }
  .player[data-mu="1"] .player__ctl .pbtn--big { width: 60px; height: 60px; }
  .player[data-mu="1"] .player__ctl .pbtn--big svg { width: 26px; height: 26px; }
  .player__vol.is-open .vol { width: 120px; }
  .player__scrubrow .player__clock { min-width: 30px; font-size: 10px; }

  /* Lyrics at a phone scale — not the desktop zoom. */
  .player__lyrbody { padding: 4vh clamp(6px, 2vw, 16px); }
  .player__lyrline { font: 800 clamp(20px, 5.4vw, 26px)/1.3 var(--body); padding: 6px 2px; }
  .player__creds { max-width: none; }
  .player__credk, .player__credv { font-size: 14px; }
  .player__radios { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 380px) {
  .ptab { font-size: 12px; padding: 6px 8px; }
  .picon { width: 32px; height: 32px; }
  .player[data-mu="1"] .player__ctl .pbtn--big { width: 56px; height: 56px; }
}
@media (max-width: 700px) {
  .nowbar { --dock-h: 78px; }
}
@media (max-width: 560px) {
  .nowbar__art { width: 44px; height: 44px; flex-basis: 44px; }
}
@media (max-width: 480px) {
  .nowbar__like { display: none; }
  .nowbar__now { max-width: none; flex: 1 1 0; min-width: 0; }
}

/* ---- the recommended-for-you half of the up-next drawer ---- */
.player__recs {
  display: flex; flex-direction: column;
  flex: 1 1 0; min-height: 0;
  border-top: 1px solid rgba(237, 240, 250, .09);
}
.player__upbody { flex: 1 1 0; min-height: 0; }
.player__rechead { padding: 8px 14px 6px; }
.player__rechead b { font: 500 10px var(--mono); letter-spacing: .16em; text-transform: uppercase; color: #B9C1D8; }
.player__recsbody {
  overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: 1px;
  padding: 0 6px 6px;
}
.player__uprow--rec {
  display: grid; grid-template-columns: 36px minmax(0, 1fr) auto;
  align-items: center; gap: 10px;
  padding: 7px 9px; text-align: left; border: 0; background: none;
}
.player__uprow--rec .player__upnum {
  width: 36px; height: 36px;
  display: grid; place-items: center; overflow: hidden;
  border-radius: 6px;
  background: var(--sink); border: 1px solid var(--edge);
  color: var(--amber); font-size: 12px;
}
.player__uprow--rec .player__upnum img { width: 100%; height: 100%; object-fit: cover; }
.player__recempty { padding: 14px 6px !important; }

/* ---------------- TV / keyboard navigation ---------------- */
/* The ? menu and the focus treatment a remote needs. The spatial walk itself lives in app.js;
   this is what it looks like: one grid to reach any section, and a focus ring that reads from a
   sofa. With a mouse nothing here is in the way. */
.bar__menu {
  flex: 0 0 auto; width: 40px; height: 34px; display: grid; place-items: center;
  background: var(--panel); border: 1px solid var(--edge); border-radius: var(--r);
  color: var(--paper); transition: background .15s, border-color .15s;
}
.bar__menu:hover { background: var(--raised); }
.bar__menu:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
.bar__menuicon {
  display: block; width: 16px; height: 2px; border-radius: 2px;
  background: currentColor; box-shadow: 0 -5px 0 currentColor, 0 5px 0 currentColor;
}

.tvmenu {
  position: fixed; inset: 0; z-index: 130;
  display: grid; place-items: center;
  padding: clamp(14px, 4vh, 44px);
  background: rgba(4, 5, 11, .88);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  animation: fade .18s var(--ease);
}
.tvmenu__card {
  width: min(1100px, 100%); max-height: 100%; overflow: auto;
  padding: clamp(16px, 3vw, 30px);
  background: linear-gradient(180deg, rgba(29, 35, 60, .97), rgba(12, 15, 28, .99));
  border: 1px solid var(--edge); border-radius: 14px;
  box-shadow: 0 40px 120px -40px rgba(0, 0, 0, .95);
}
.tvmenu__head { display: flex; align-items: center; gap: 16px; margin-bottom: clamp(14px, 2.4vh, 22px); }
.tvmenu__title {
  flex: 1 1 auto; margin: 0;
  font: 400 clamp(20px, 2.4vw, 30px) var(--display);
  text-transform: uppercase; letter-spacing: -.01em;
}
.tvmenu__x {
  height: 40px; padding: 0 20px; border-radius: 999px;
  background: var(--paper); color: var(--sink); font-weight: 700;
}
.tvmenu__grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: clamp(10px, 1.4vw, 16px);
}
.tvmenu__b {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  min-height: 66px; padding: 0 clamp(16px, 2vw, 22px);
  text-align: left; color: var(--paper);
  background: var(--panel); border: 1px solid var(--edge); border-radius: 10px;
  font: 600 clamp(15px, 1.5vw, 19px) var(--body);
  transition: background .15s, border-color .15s, transform .15s var(--ease);
}
.tvmenu__b:hover { background: var(--raised); }
.tvmenu__b:focus-visible {
  outline: none; border-color: var(--cyan);
  box-shadow: 0 0 0 3px rgba(53, 214, 232, .35);
  transform: translateY(-2px);
}
.tvmenu__bh { color: var(--mist); font: 400 14px var(--mono); }

/* ---------------- settings ---------------- */
/* The gear rides the utility cluster at the right of the bar, sized to match the ☰ menu button so
   the two read as one control family. */
.gear {
  flex: 0 0 auto; width: 40px; height: 40px; display: grid; place-items: center;
  background: rgba(20,25,41,.86); color: var(--mist);
  border: 1px solid var(--edge); border-radius: var(--r);
  transition: color .16s, background .16s, border-color .16s, transform .5s var(--ease);
}
.gear svg { width: 18px; height: 18px; display: block; }
.gear:hover { color: var(--paper); background: var(--panel); transform: rotate(30deg); }
.gear:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
.gear[aria-expanded="true"] { color: var(--amber); border-color: var(--amber); }

/* The overlay mirrors the Browse dialog exactly — same scrim, blur and entry — so the app has one
   modal grammar rather than two. */
.settings {
  position: fixed; inset: 0; z-index: 130;
  display: grid; place-items: center;
  padding: clamp(14px, 4vh, 44px);
  background: rgba(4, 5, 11, .88);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  animation: fade .18s var(--ease);
}
.settings__card {
  width: min(560px, 100%); max-height: 100%; overflow: auto;
  padding: clamp(18px, 3vw, 30px);
  background: linear-gradient(180deg, rgba(29, 35, 60, .97), rgba(12, 15, 28, .99));
  border: 1px solid var(--edge); border-radius: 14px;
  box-shadow: 0 40px 120px -40px rgba(0, 0, 0, .95);
}
.settings__head { display: flex; align-items: center; gap: 16px; margin-bottom: 22px; }
.settings__title {
  flex: 1 1 auto; margin: 0;
  font: 400 clamp(20px, 2.4vw, 28px) var(--display);
  text-transform: uppercase; letter-spacing: -.01em;
}
.settings__x {
  height: 38px; padding: 0 18px; border-radius: 999px;
  background: var(--paper); color: var(--sink); font-weight: 700; cursor: pointer;
}
.settings__x:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

.set-group { margin-top: clamp(18px, 3vh, 26px); }
.set-group:first-of-type { margin-top: 0; }
.set-group__title {
  margin: 0 0 12px; font: 700 11px var(--mono);
  text-transform: uppercase; letter-spacing: .16em; color: var(--mist);
}
.set-row {
  display: flex; align-items: center; gap: 16px;
  padding: 13px 0; border-top: 1px solid var(--edge);
}
.set-row:first-child { border-top: 0; }
.set-row__label { flex: 1 1 auto; min-width: 0; }
.set-row__name { display: block; font-size: 14px; font-weight: 500; color: var(--paper); }
.set-row__hint { display: block; margin-top: 3px; font-size: 12px; color: var(--mist); line-height: 1.4; }

/* On/off switch — the pill the AI and ad-blocker toggles share. */
.set-switch {
  flex: 0 0 auto; position: relative; width: 46px; height: 26px; border-radius: 999px;
  background: var(--raised); border: 1px solid var(--edge); cursor: pointer;
  transition: background .18s var(--ease), border-color .18s var(--ease);
}
.set-switch::after {
  content: ''; position: absolute; top: 2px; left: 2px; width: 20px; height: 20px;
  border-radius: 50%; background: var(--mist);
  transition: transform .18s var(--ease), background .18s var(--ease);
}
.set-switch[aria-pressed="true"] { background: var(--amber); border-color: var(--amber); }
.set-switch[aria-pressed="true"]::after { transform: translateX(20px); background: var(--sink); }
.set-switch:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

/* Two-choice segment — the sub/dub control, matching the sheet's own audio segment. */
.set-seg { flex: 0 0 auto; display: inline-flex; background: var(--raised); border: 1px solid var(--edge); border-radius: 999px; padding: 2px; }
.set-seg button {
  padding: 6px 14px; border-radius: 999px; font: 600 11px var(--mono);
  letter-spacing: .08em; color: var(--mist); cursor: pointer; transition: color .15s, background .15s;
}
.set-seg button.is-on { background: var(--paper); color: var(--sink); }
.set-seg button:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

/* A plain action row (clear remembered sources). */
.set-action {
  flex: 0 0 auto; height: 34px; padding: 0 15px; border-radius: 999px;
  background: transparent; border: 1px solid var(--edge); color: var(--paper);
  font: 600 12px var(--mono); cursor: pointer; transition: background .15s, border-color .15s, color .15s;
}
.set-action:hover { background: var(--panel); }
.set-action:disabled { color: var(--mist); cursor: default; opacity: .6; }
.set-action.is-done { border-color: var(--cyan); color: var(--cyan); }
.set-action:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

/* The keyboard-shortcut reference — a definition grid, key on the right like the rest of the app. */
.set-keys { display: grid; gap: 9px; }
.set-keys__row { display: flex; align-items: center; gap: 14px; }
.set-keys__what { flex: 1 1 auto; font-size: 13px; color: var(--paper); }
.set-keys kbd {
  flex: 0 0 auto; min-width: 22px; padding: 3px 8px; text-align: center;
  background: var(--sink); border: 1px solid var(--edge); border-radius: 5px;
  font: 500 11px var(--mono); color: var(--mist); box-shadow: 0 2px 0 rgba(0,0,0,.4);
}
.set-note { margin: 10px 0 0; font-size: 12px; color: var(--mist); line-height: 1.5; }

@media (prefers-reduced-motion: reduce) {
  .settings { animation: none; }
  .gear:hover { transform: none; }
}

/* A living-room focus ring: thick, offset and glowing, on every control. */
body.tv-ready :focus-visible {
  outline: 3px solid var(--cyan) !important;
  outline-offset: 3px;
  box-shadow: 0 0 0 6px rgba(53, 214, 232, .22);
}
body.tv-ready .card:focus-visible { transform: translateY(-5px) scale(1.03); }
body.tv-ready .bar__link,
body.tv-ready .tvmenu__b { min-height: 40px; }

@media (prefers-reduced-motion: reduce) {
  .tvmenu { animation: none; }
  .tvmenu__b:focus-visible, body.tv-ready .card:focus-visible { transform: none; }
}

/* ---------------- pointer: coarse / no hover ---------------- */
/* Two controls exist only as a hover reveal, which on a touch screen means they do not exist at all:
   the music tile's play button (`.mu__play` � `opacity: 0` with `pointer-events: none`, so an album
   could only be played by opening it first) and the id badge on every poster (`.card__id` � the
   `TMDB movie/123` line, which is the whole point of the catalogue and was invisible on a phone).

   They are revealed here rather than redesigned, because the reveal is the right behaviour on a
   pointer and there is simply no hover to wait for on glass. `pointer: coarse` is the test rather
   than a width, because a narrow desktop window is still a mouse.

   The same query raises the small controls to a 44px touch target. This is deliberately scoped to
   coarse pointers: the desktop design is a dense one, and the sizes that are wrong under a thumb are
   perfectly readable under a cursor. Nothing here reflows a layout � only what can be hit. */
@media (hover: none), (pointer: coarse) {
  .mu__play { opacity: 1; transform: none; pointer-events: auto; }
  .card__id { opacity: 1; transform: none; }
  .slot__x { opacity: 1; transform: none; }

  .row__btn { width: 44px; height: 44px; }
  .slot__x { width: 32px; height: 32px; }
  .picker__tick { width: 44px; height: 44px; }
  .ids__b { padding: 12px 6px; }
  .empty__act { padding: 12px 20px; }

  /* The dots are the only way to move the hero and each spotlight by hand, and 26x3px is a target
     for a stylus rather than a finger. The mark stays a thin line � that is what it is for � but the
     box it sits in becomes reachable. */
  .dot { position: relative; height: 3px; }
  .dot::after {
    content: ''; position: absolute; left: 0; right: 0;
    top: -10px; bottom: -10px;
  }
  .dot { margin-top: 10px; margin-bottom: 10px; }
}
