/* One sheet of the player's design system (tickets/T19-room-for-two.md).
   style.css keeps the fonts, the tokens and the reset; every area has its
   own file from there, linked in index.html in this order — the cascade is
   the order of the <link>s, not of a bundler. A ticket works in its sheet.
   No @import: it serialises the requests, which is the cost being avoided. */

/* ---------- Cover backdrop: the sleeve on air, blurred behind the page ----------
   One band, across the middle, at its strongest where the headline and the
   sleeve itself sit — and faded out, by the mask alone, before it reaches the
   bar or the keys. No per-theme opacity to tune: var(--bg) already carries the
   theme, so one wash reads right in both. The band sits above true centre
   (the bar above it reads fine with more of the sleeve behind it) and closes
   out well before the volume rail, but with room to breathe either side. */
.cover-bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  opacity: 0;
  transition: opacity 0.8s ease;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to bottom,
    transparent 0%, transparent 4%,
    rgba(0, 0, 0, 0.08) 10%, rgba(0, 0, 0, 0.4) 18%, rgba(0, 0, 0, 0.8) 26%,
    #000 36%, #000 48%,
    rgba(0, 0, 0, 0.75) 58%, rgba(0, 0, 0, 0.35) 68%, rgba(0, 0, 0, 0.08) 78%,
    transparent 85%, transparent 100%);
  mask-image: linear-gradient(to bottom,
    transparent 0%, transparent 4%,
    rgba(0, 0, 0, 0.08) 10%, rgba(0, 0, 0, 0.4) 18%, rgba(0, 0, 0, 0.8) 26%,
    #000 36%, #000 48%,
    rgba(0, 0, 0, 0.75) 58%, rgba(0, 0, 0, 0.35) 68%, rgba(0, 0, 0, 0.08) 78%,
    transparent 85%, transparent 100%);
}
.cover-bg.is-on { opacity: 1; }
.cover-bg__img {
  position: absolute;
  inset: -20vw;   /* overscan past the viewport so the blur has image to eat into, not a hard edge */
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  /* ponytail: clamped — a bare 20vw is 1000px+ of blur on an ultrawide, all GPU
     cost for a wash that's already flat past ~360px. Same read, bounded work.
     saturate() runs before brightness/the veil, on the still-unlit pixels —
     that's where the chroma is. Amplified after, there's little left to
     recover: a blurred sleeve averages toward grey fast, and most of the
     catalogue reads near-monochrome once it does. --cover-sat is set per
     track by app.js (a canvas read of the sleeve's own average saturation,
     applySaturationCap()): a near-grey cover gets pushed toward the fallback
     max below, an already-vivid one is left alone instead of pushed past it
     into neon. The fallback is that max, for the moment before JS measures. */
  filter: blur(clamp(24px, 20vw, 360px)) saturate(var(--cover-sat, 1.9));
}
/* Same lift, two different amounts of it. Light reads the sleeve against a
   near-white page: a dark cover came through as a flat grey smear right
   where the page is palest, so brightness() pushes hard and the veil (below)
   is cut back to 0.2 so that doesn't get re-flattened toward --bg. Dark
   already sits close to the sleeve's own tone, but the same near-grey-once-
   blurred problem still drains it of colour against a near-black page — a
   smaller lift (it should stay dark) plus a lighter veil (0.22) is enough to
   keep the hue readable instead of crushed to black. */
:root[data-theme="light"] .cover-bg__img { filter: blur(clamp(24px, 20vw, 360px)) saturate(var(--cover-sat, 1.9)) brightness(1.85); }
:root[data-theme="dark"]  .cover-bg__img { filter: blur(clamp(24px, 20vw, 360px)) saturate(var(--cover-sat, 2.3)) brightness(1.35); }
.cover-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--bg);
  opacity: 0.45;
}
:root[data-theme="light"] .cover-bg::after { opacity: 0.2; }
:root[data-theme="dark"]  .cover-bg::after { opacity: 0.22; }
@media (prefers-reduced-motion: reduce) { .cover-bg { transition: none; } }
