/* player.css - the web player page. Builds on style.css's theme tokens, so it follows
   the dark/light toggle and the crossfade for free. The stage mimics the desktop
   viewer's two layouts: fill (cover aspect-fit on black) and poster (blurred backdrop,
   centered rounded cover, info box). */

/* --- stage -------------------------------------------------------------------- */
/* Flex-centered, so both layouts and fullscreen all scale from the same rule: the
   cover's size is a percentage of the stage, and the stage can be the embedded box
   or the whole screen without any per-mode positioning. */
.stage {
    --player-tint: var(--text-inverse);
    --backdrop-fade-duration: 1.2s;
    --backdrop-fade-easing: ease;
    --art-text-shadow: 0 1px 8px rgba(0, 0, 0, .8);
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: min(70vh, 640px);
    border-radius: .9rem;
    border: 1px solid var(--line);
    background: var(--media-bg);  /* both layouts sit on black, like the apps */
    overflow: hidden;
    box-shadow: var(--shadow);
    user-select: none;
}
.stage.layout-poster {
    display: grid;
    grid-template-columns: 100%;
    grid-template-rows: 72% 28%;
}
.stage.layout-poster.stage-portrait {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
}
.stage:fullscreen { border-radius: 0; border: 0; height: 100%; }

/* Blurred backdrop - poster layout only. The blur radius is set by JS relative to the
   stage size (mirroring the D2D pass, which blurs at a ~240px working resolution and
   upscales - a fixed pixel blur reads far too mild on a large stage).
   Every backdrop img is one half of a DOUBLE BUFFER: the incoming image preloads into
   the hidden half and crossfades over the visible one (.show), so a new track never
   hard-cuts the background the way a bare src swap does. */
.backdrop {
    position: absolute; inset: 0;
    z-index: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    transform: scale(1.3);         /* hide the blur's soft edges off-canvas */
    filter: blur(var(--poster-blur, 96px)) brightness(.55);
    display: none;
    opacity: 0;
    transition: opacity var(--backdrop-fade-duration) var(--backdrop-fade-easing);
}
.backdrop.show { opacity: 1; }
.layout-poster .backdrop { display: block; }

/* Experimental movie/TV/game backdrop: the pair sits on top of the blurred pair
   (later in the DOM), sharp but dimmed so the cover and info box stay the subject.
   Fades in when a match arrives and out when the next album has none - the blurred
   cover underneath is the ever-present fallback. */
.backdrop-movie {
    transform: none;              /* no oversize: its edges are meant to be seen */
    filter: brightness(.45);
    transition: opacity var(--backdrop-fade-duration) var(--backdrop-fade-easing),
                filter var(--backdrop-fade-duration) var(--backdrop-fade-easing);
}

/* Experimental: hide the cover while a movie backdrop is showing - the backdrop
   becomes the star, so it earns back some brightness. Opacity, not display: the
   coverbox keeps its size, so the info box below it doesn't jump. */
.layout-poster.no-cover .coverbox { opacity: 0; pointer-events: none; }
.no-cover .backdrop-movie { filter: brightness(.7); }

/* --- cover + transitions ------------------------------------------------------- */
/* The covers are square (ASIN art). sizeStage() sets the box's pixel size to the
   largest square that fits the stage (with room for the info box in poster layout) -
   CSS alone can't express "square, capped by BOTH stage dimensions" without breaking
   the aspect ratio on portrait screens. */
.coverbox {
    position: relative;
    z-index: 1;
    aspect-ratio: 1 / 1;   /* pre-JS fallback; JS sets explicit width+height */
    perspective: 1200px;
    transform-origin: center;
    transition: opacity .8s ease, transform .55s ease, width .4s ease, height .4s ease;
}
/* The rigid card holding both buffers: the flip effects rotate THIS, fade/none
   leave it untransformed. preserve-3d keeps the faces glued to it. */
.coverbox .card { position: absolute; inset: 0; transform-style: preserve-3d; }
.coverbox img {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    opacity: 0;
    backface-visibility: hidden;
    transition: filter .5s ease;
}
.layout-poster .coverbox img { border-radius: var(--cover-radius, 0); }
.coverbox.station-outage img {
    filter: grayscale(1) saturate(0) brightness(.68);
}

/* The box's data attributes drive everything - the children are addressed purely
   positionally, never touched by JS: data-front names the visible buffer, data-fx
   the transition. No data-front (before the first cover) = both hidden. */
.coverbox[data-front="a"] img:first-of-type,
.coverbox[data-front="b"] img:last-of-type { opacity: 1; }

/* Crossfade: the incoming image fades in over the outgoing one. */
.coverbox[data-fx="fade"] img {
    transition: opacity var(--fade-ms, 1000ms) ease, filter .5s ease;
}

/* data-warp: one transition-less beat, set by JS while it COMMITS a new effect's
   parked poses - an effect change must teleport into position, not animate there
   (an animating park makes the very next flip a visually static no-op). */
.coverbox[data-warp] img,
.coverbox[data-warp] .card { transition: none !important; }

/* Flips: a real card turn. Buffer "a" is the card's face, buffer "b" is glued to
   its BACK (a fixed 180° pose - the incoming cover literally sticks behind the
   current one), and data-front turns the whole card: front="a" shows the face,
   front="b" the back. Both imgs stay opaque - backface culling does the hiding. */
.coverbox[data-fx="fliph"] { --flip-transform: rotateY(180deg); }
.coverbox[data-fx="flipv"] { --flip-transform: rotateX(180deg); }
.coverbox:is([data-fx="fliph"], [data-fx="flipv"]) img { opacity: 1; }
.coverbox:is([data-fx="fliph"], [data-fx="flipv"]) .card {
    transition: transform var(--fade-ms, 1000ms) ease;
}
.coverbox:is([data-fx="fliph"], [data-fx="flipv"]) img:last-of-type,
.coverbox:is([data-fx="fliph"], [data-fx="flipv"])[data-front="b"] .card {
    transform: var(--flip-transform);
}

/* --- info box (poster only) ------------------------------------------------------ */
/* The lower grid row holds title, artist and countdown. Landscape keeps its center
   fixed; a portrait stage fixes its bottom edge instead, so wrapped titles and extra
   rows can only grow upward and never leave the screen. Fill mode shows no info at
   all; the hairline border keeps the box readable on dark artwork. */
.info {
    position: relative;
    z-index: 2;
    max-width: 86%;
    width: var(--info-panel-width, auto);
    padding: .8rem 1.2rem;
    border: 1px solid rgba(255, 255, 255, .14);
    border-radius: var(--cover-radius, .6rem);
    background: rgba(10, 12, 18, .55);
    backdrop-filter: blur(6px);
    color: var(--player-tint);
    text-align: center;
    opacity: 1;
    visibility: visible;
    transition: opacity var(--backdrop-fade-duration) var(--backdrop-fade-easing),
                visibility 0s linear 0s,
                color var(--backdrop-fade-duration) var(--backdrop-fade-easing),
                margin-bottom .4s ease,
                width var(--backdrop-fade-duration) var(--backdrop-fade-easing);
}
.info.metadata-pending {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition-delay: 0s, var(--backdrop-fade-duration), 0s, 0s;
}
.layout-poster .coverbox {
    grid-column: 1;
    grid-row: 1;
    place-self: center;
    transform: translateY(var(--cover-shift, 0px));
    transition: opacity .8s ease, transform .45s ease, width .4s ease, height .4s ease;
}
.layout-poster.stage-portrait .coverbox {
    flex: 0 0 auto;
    margin-block: auto;
}
.stage.laser-scene .coverbox {
    transform: scale(.8);
}
.stage.layout-poster.laser-scene .coverbox {
    transform: translateY(var(--cover-shift, 0px)) scale(.8);
}
.layout-poster .info {
    grid-column: 1;
    grid-row: 2;
    place-self: center;
}
.layout-poster.stage-portrait .info {
    flex: 0 0 auto;
    margin-bottom: max(.75rem, env(safe-area-inset-bottom));
}
.layout-fill .info { display: none; }
.info .t {
    border-radius: .25rem;
    font-weight: 600;
    font-size: var(--title-size, 1.15rem);
    line-height: 1.25;
    transition: opacity .2s ease, outline-color .2s ease;
}
.info-album, .info-track { display: block; }
.info-album-toggle {
    display: block; width: 100%; margin: 0; padding: 0;
    border: 0; background: transparent; color: inherit; font: inherit;
    text-align: inherit; border-radius: .25rem;
    transition: opacity .2s ease;
}
.info-album-toggle:not(:disabled) { cursor: pointer; }
.info-album-toggle:not(:disabled):hover { opacity: .8; }
.info-album-toggle:focus-visible { outline: 2px solid currentColor; outline-offset: .25rem; }
.info-album-slot {
    display: grid;
    grid-template-rows: 1fr;
    opacity: 1;
    transition: grid-template-rows var(--backdrop-fade-duration) var(--backdrop-fade-easing),
                opacity var(--backdrop-fade-duration) var(--backdrop-fade-easing);
}
.info-album { min-height: 0; overflow: hidden; }
.info.has-media-logo .info-album-slot { grid-template-rows: 0fr; opacity: 0; }
.media-logo {
    display: block;
    position: relative;
    height: 0;
    opacity: 0;
    pointer-events: auto;
    transition: height var(--backdrop-fade-duration) var(--backdrop-fade-easing),
                opacity var(--backdrop-fade-duration) var(--backdrop-fade-easing);
}
.info.has-media-logo .media-logo {
    height: clamp(2.5rem, calc(var(--title-size, 1.15rem) * 1.3), 4.5rem);
    opacity: 1;
}
.media-logo canvas {
    position: absolute;
    bottom: .5rem;
    left: 50%;
    transform: translateX(-50%);
    width: auto;
    height: auto;
    /* Let the artwork grow above the glass; its reserved row stays compact. */
    max-width: min(32rem, 72vw);
    max-height: min(clamp(6rem, calc(var(--title-size, 1.15rem) * 2.8), 10rem), 24vh);
    filter: drop-shadow(0 2px 3px #0009);
    transition: max-height var(--backdrop-fade-duration) var(--backdrop-fade-easing),
                max-width var(--backdrop-fade-duration) var(--backdrop-fade-easing);
}
@media (prefers-reduced-motion: reduce) {
    .info-album-slot, .media-logo, .media-logo canvas, .info-album-toggle { transition: none; }
}
.info-title-separator { display: none; }
.info-track {
    font-size: .88em;
    font-weight: 500;
}
.info-track:not(:empty) { margin-top: .12em; }
.info .a { opacity: .8; font-size: var(--artist-size, .95rem); }
.info .local-backchannel { cursor: pointer; transition: opacity .2s ease; }
.info .local-backchannel:hover { opacity: .78; }
.info .local-backchannel:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: .2rem;
}
.info .t.local-backchannel[aria-busy="true"] {
    cursor: wait;
    opacity: .6;
    pointer-events: none;
}
.backchannel-status {
    max-height: 0;
    margin: 0;
    overflow: hidden;
    font-size: .76rem;
    line-height: 1.25;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-.15rem);
    transition: opacity .2s ease, transform .2s ease, max-height .25s ease,
                margin-top .25s ease, visibility 0s linear .25s;
}
.backchannel-status.show {
    max-height: 2.5rem;
    margin-top: .35rem;
    opacity: .78;
    visibility: visible;
    transform: translateY(0);
    transition-delay: 0s;
}
.backchannel-pairing {
    max-height: 0;
    margin: 0;
    overflow: hidden;
    font-size: .76rem;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateY(-.2rem);
    transition: opacity .2s ease, transform .2s ease, max-height .25s ease,
                margin-top .25s ease, visibility 0s linear .25s;
}
.backchannel-pairing.show {
    max-height: 4rem;
    margin-top: .4rem;
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: translateY(0);
    transition-delay: 0s;
}
.backchannel-pairing label {
    display: block;
    margin-bottom: .25rem;
    opacity: .86;
}
.backchannel-pairing-row {
    display: flex;
    justify-content: center;
    gap: .3rem;
}
.backchannel-pairing input,
.backchannel-pairing button {
    box-sizing: border-box;
    min-height: 1.8rem;
    border: 1px solid rgba(255, 255, 255, .45);
    border-radius: .4rem;
    background: rgba(0, 0, 0, .55);
    color: #fff;
    font: inherit;
}
.backchannel-pairing input {
    width: min(9.5rem, 45vw);
    padding: .25rem .45rem;
    font-variant-numeric: tabular-nums;
    text-transform: uppercase;
}
.backchannel-pairing input::placeholder { color: rgba(255, 255, 255, .6); }
.backchannel-pairing button {
    padding: .25rem .55rem;
    cursor: pointer;
}
.backchannel-pairing input:focus-visible,
.backchannel-pairing button:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 2px;
}

/* --- countdown ------------------------------------------------------------------ */
/* applyLayout() moves the element between its two homes: the info box's last row in
   poster, a corner overlay on the cover in fill - exactly where the apps put it. */
.countdown {
    font-size: var(--cd-size, 18px);
    font-variant-numeric: tabular-nums;
    font-weight: 500;
    color: var(--player-tint);
    opacity: .85;               /* a status row, quieter than the title above it */
    text-shadow: var(--art-text-shadow);
    display: flex;
    justify-content: center;
    margin-top: .4rem;
    height: 1.2em;
    max-height: 1.2em;
    overflow: hidden;
    visibility: visible;
    transition: max-height .45s ease, margin-top .45s ease, opacity .3s ease,
                color .35s ease;
}
.coverbox .countdown { position: absolute; right: 4%; bottom: 4%; z-index: 2; margin-top: 0; }
.countdown.hidden {
    max-height: 0;
    margin-top: 0;
    opacity: 0;
    visibility: hidden;
    transition: max-height .45s ease, margin-top .45s ease, opacity .2s ease,
                visibility 0s linear .45s;
}
/* contain: the once-a-second digit swap (and the roll ghosts) invalidate layout
   INSIDE the cell only - tabular-nums keeps every digit the same width, so the
   cell's own size never changes and the update can never leak a reflow into the
   info box or stage. paint scopes the ghost clipping the same way. */
.cd-cell { position: relative; display: inline-block; overflow: hidden; height: 1.2em; line-height: 1.2em; contain: layout paint; }
.cd-cur { display: block; }
.cd-ghost { position: absolute; inset: 0; } /* the outgoing digit during a roll */

/* Station progress belongs to the cover itself, including in browser fullscreen.
   The regular status line remains the single accessibility announcement source. */
.stage-status {
    display: block;
    position: absolute;
    bottom: 4%;
    left: 50%;
    z-index: 2;
    width: max-content;
    max-width: 92%;
    margin: 0;
    padding: .5rem .85rem;
    transform: translateX(-50%);
    border: 1px solid rgba(255, 255, 255, .16);
    border-radius: .65rem;
    background: rgba(0, 0, 0, .58);
    color: var(--text-inverse);
    font-size: clamp(.8rem, 2vw, 1.15rem);
    font-variant-numeric: tabular-nums;
    text-align: center;
    text-shadow: var(--art-text-shadow);
    white-space: pre-line;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity .2s ease, visibility 0s linear .2s;
}
.stage-status.show {
    opacity: 1;
    visibility: visible;
    transition-delay: 0s;
}

/* --- stage chrome: audio, fullscreen toggle + fullscreen options ----------------- */
.stage-button {
    position: absolute;
    z-index: 3;
    width: 2.4rem; height: 2.4rem;
    border: 0; border-radius: .6rem;
    background: rgba(0, 0, 0, .45);
    color: var(--text-inverse);
    font-size: 1.2rem;
    cursor: pointer;
    opacity: 0;
    transition: opacity .35s ease;
}
.stage-audio { left: .7rem; bottom: .7rem; }
.stage-update {
    position: absolute;
    z-index: 7;
    top: .7rem;
    left: 50%;
    width: max-content;
    max-width: min(24rem, calc(100% - 8rem));
    margin: 0;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translate(-50%, -.25rem);
    transition: opacity .2s ease, transform .2s ease,
                visibility 0s linear .2s;
}
.stage-update.show {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: translate(-50%, 0);
    transition-delay: 0s;
}
.stage-update button {
    appearance: none;
    max-width: 100%;
    padding: .4rem .75rem;
    border: 1px solid rgba(255, 255, 255, .2);
    border-radius: 999px;
    background: rgba(0, 0, 0, .62);
    color: var(--text-inverse);
    box-shadow: 0 .25rem 1rem rgba(0, 0, 0, .3);
    backdrop-filter: blur(8px);
    font: inherit;
    font-size: .82rem;
    font-weight: 600;
    line-height: 1.25;
    cursor: pointer;
}
.stage-update button:hover { background: rgba(20, 20, 20, .78); }
.stage-update button:focus-visible {
    outline: 2px solid var(--text-inverse);
    outline-offset: 2px;
}
.stage-milkdrop,
.stage-lasers,
.stage-lasers-front {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity .5s ease, visibility 0s linear .5s;
}
.stage-milkdrop {
    z-index: 1;
    opacity: 0;
    background: transparent;
    transition: opacity .65s ease, visibility 0s linear .65s;
}
.stage-milkdrop.active {
    opacity: .96;
    visibility: visible;
    transition-delay: 0s;
}
.stage-lasers {
    /* Same layer as the cover, but earlier in the DOM: the black club scene hides
       the backdrop while the later coverbox remains visible above it. */
    z-index: 1;
    background: #000;
}
.stage-lasers-front {
    /* Transparent weaving beams cross in front; info and controls occur later or on
       a higher layer, so the show never makes the player UI unreadable. */
    z-index: 2;
}
.stage-lasers.active,
.stage-lasers-front.active {
    opacity: 1;
    visibility: visible;
    transition-delay: 0s;
}
/* Fade only the expensive blurred background layers out. The album cover stays on
   stage above the black club scene. */
.stage.laser-scene .backdrop {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity .5s ease, visibility 0s linear .5s;
}
.stage.milkdrop-scene .backdrop {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity .65s ease, visibility 0s linear .65s;
}
.stage.milkdrop-scene .coverbox {
    transform: scale(.74);
}
.stage.layout-poster.milkdrop-scene .coverbox {
    transform: translateY(var(--cover-shift, 0px)) scale(.74);
}
.stage-spectrum {
    position: absolute;
    left: .7rem;
    top: .7rem;
    z-index: 3;
    width: var(--cover-side, clamp(8.5rem, 26vw, 15rem));
    height: var(--analyzer-height, clamp(2rem, 7.5vh, 3rem));
    border: 0;
    border-radius: 0;
    background: transparent;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity .4s ease, visibility 0s linear .4s,
                width .4s ease, height .4s ease, top .4s ease;
}
.stage-spectrum.active {
    opacity: .92;
    visibility: visible;
    pointer-events: auto;
    cursor: pointer;
    transition-delay: 0s;
}
.stage.laser-scene .stage-spectrum {
    width: var(--cover-depth-side, var(--cover-side, 12rem));
}
.stage.milkdrop-scene .stage-spectrum {
    width: var(--cover-depth-side, var(--cover-side, 12rem));
}
.layout-poster .stage-spectrum {
    left: 50%;
    top: var(--spectrum-top, 72%);
    transform: translate(-50%, -50%);
}
.layout-poster.no-cover .stage-spectrum {
    width: min(var(--cover-side, 12rem), var(--info-width, var(--cover-side, 12rem)));
}

/* The optional tempo readout occupies the otherwise-clear upper-left corner. Keep
   a steady three-character value column so a changing estimate never
   nudges the BPM label, and retain the last value while the whole badge fades out. */
.stage-bpm {
    position: absolute;
    left: .7rem;
    top: .7rem;
    z-index: 3;
    display: flex;
    align-items: baseline;
    gap: .38rem;
    box-sizing: border-box;
    min-width: 5.6rem;
    margin: 0;
    padding: .45rem .65rem;
    border: 1px solid rgba(255, 255, 255, .16);
    border-radius: .65rem;
    background: rgba(0, 0, 0, .62);
    color: var(--text-inverse);
    font-variant-numeric: tabular-nums;
    line-height: 1;
    text-shadow: var(--art-text-shadow);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateY(-.25rem) scale(.97);
    transition: opacity .3s ease, transform .3s ease, top .4s ease,
                visibility 0s linear .3s;
}
.stage-bpm.active {
    opacity: 1;
    visibility: visible;
    transform: translateY(0) scale(1);
    transition-delay: 0s;
}
.stage-bpm-value {
    min-width: 3ch;
    font-size: clamp(1.05rem, 2.4vw, 1.35rem);
    font-weight: 750;
    text-align: right;
}
.stage-bpm-unit {
    font-size: clamp(.62rem, 1.3vw, .72rem);
    font-weight: 700;
    letter-spacing: .08em;
    opacity: .72;
}
/* Fill layout puts the optional compact analyzer in this same corner. When both
   displays are active, animate the tempo badge just below its rendered height. */
.stage.layout-fill .stage-spectrum.active + .stage-bpm {
    top: calc(1.15rem + var(--analyzer-height, clamp(2rem, 7.5vh, 3rem)));
}

/* Movie/series ratings sit in two retained slots. Each slot is a real double-sided
   card, so a flip turns the already-attached next badge over exactly like the cover. */
.rating-badges {
    --rating-size: clamp(3rem, 8vw, 4.25rem);
    --rating-gap: .7rem;
    position: absolute;
    right: var(--rating-gap);
    bottom: var(--rating-gap);
    z-index: 3;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
    transition: opacity .35s ease, visibility 0s linear .35s;
}
.stage:hover .rating-badges,
.rating-badges.track-intro {
    opacity: 1;
    visibility: visible;
    transition-delay: 0s, 0s;
}
@media (hover: none) {
    .rating-badges { opacity: .6; visibility: visible; transition-delay: 0s, 0s; }
}
.rating-slot {
    position: relative;
    flex: 0 0 auto;
    width: var(--rating-size);
    height: var(--rating-size);
    max-width: 0;
    margin-left: 0;
    overflow: clip;
    perspective: 600px;
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--fade-ms, 1000ms) ease,
                max-width var(--fade-ms, 1000ms) ease,
                margin-left var(--fade-ms, 1000ms) ease,
                visibility 0s linear var(--fade-ms, 1000ms);
}
.rating-slot.show {
    max-width: var(--rating-size);
    overflow: visible;
    opacity: 1;
    visibility: visible;
    transition-delay: 0s;
}
.rating-slot.show + .rating-slot.show { margin-left: var(--rating-gap); }
.rating-card {
    position: absolute;
    top: 0;
    right: 0;
    width: var(--rating-size);
    height: var(--rating-size);
    transform-style: preserve-3d;
}
.rating-face {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    box-sizing: border-box;
    overflow: hidden;
    border: 2px solid #111;
    border-radius: .45rem;
    background: #fff;
    color: #111;
    box-shadow: 0 2px 10px rgba(0, 0, 0, .4);
    opacity: 0;
    backface-visibility: hidden;
}
.rating-face img {
    display: none;
    width: 100%;
    height: 100%;
    object-fit: contain;
}
.rating-face.has-logo { border: 0; background: transparent; }
.rating-face.has-logo img { display: block; }
#rating-us .rating-face {
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
}
#rating-us .rating-face:not(.has-logo) {
    color: #fff;
    text-shadow: 0 1px 3px rgba(0, 0, 0, .9), 0 0 8px rgba(0, 0, 0, .65);
}
.rating-face span {
    padding: .2rem;
    font-size: clamp(.72rem, 2.1vw, 1rem);
    font-weight: 800;
    line-height: 1;
    letter-spacing: -.02em;
    text-align: center;
}
.rating-face.has-logo span { display: none; }
.rating-descriptors {
    position: absolute;
    right: 0;
    bottom: calc(100% + .45rem);
    z-index: 2;
    display: grid;
    gap: .28rem;
    width: min(14.5rem, calc(100vw - 2 * var(--rating-gap)));
    padding: .48rem .62rem;
    border: 1px solid rgba(255, 255, 255, .28);
    border-radius: .4rem;
    background: rgba(12, 12, 15, .92);
    color: #fff;
    box-shadow: 0 2px 10px rgba(0, 0, 0, .45);
    font-size: clamp(.72rem, 1.8vw, .9rem);
    line-height: 1.15;
    text-align: left;
    opacity: 0;
    visibility: hidden;
    transform: translateY(.3rem) scale(.96);
    transform-origin: bottom right;
    transition: opacity .2s ease, transform .2s ease, visibility 0s linear .2s;
}
.rating-descriptor {
    display: grid;
    grid-template-columns: 1.8rem 1fr;
    gap: .4rem;
    align-items: baseline;
}
.rating-descriptor-code {
    font-weight: 800;
    letter-spacing: .08em;
}
.rating-descriptor-label {
    color: rgba(255, 255, 255, .88);
    font-weight: 550;
    letter-spacing: 0;
}
.rating-slot.has-descriptors.show { pointer-events: auto; cursor: help; }
.rating-slot.has-descriptors:is(:hover, :focus) .rating-descriptors {
    opacity: 1;
    visibility: visible;
    transform: translateY(0) scale(1);
    transition-delay: 0s;
}
.rating-slot[data-front="a"] .rating-face:first-child,
.rating-slot[data-front="b"] .rating-face:last-child { opacity: 1; }
.rating-slot[data-fx="fade"] .rating-face {
    transition: opacity var(--fade-ms, 1000ms) ease;
}
.rating-slot[data-warp] .rating-face,
.rating-slot[data-warp] .rating-card { transition: none !important; }
.rating-slot[data-fx="fliph"] { --rating-flip-transform: rotateY(180deg); }
.rating-slot[data-fx="flipv"] { --rating-flip-transform: rotateX(180deg); }
.rating-slot:is([data-fx="fliph"], [data-fx="flipv"]) .rating-face { opacity: 1; }
.rating-slot:is([data-fx="fliph"], [data-fx="flipv"]) .rating-card {
    transition: transform var(--fade-ms, 1000ms) ease;
}
.rating-slot:is([data-fx="fliph"], [data-fx="flipv"]) .rating-face:last-child,
.rating-slot:is([data-fx="fliph"], [data-fx="flipv"])[data-front="b"] .rating-card {
    transform: var(--rating-flip-transform);
}
.rating-slot[data-settled] .rating-card { transform-style: flat; }
.rating-slot[data-settled] .rating-face:first-child { backface-visibility: visible; }
.rating-slot[data-settled] .rating-face:last-child { opacity: 0; }

/* The queued-track announcement owns the upper-right corner beneath the stage
   buttons. Its intrinsic width grows with its single-line content up to half the
   player background, where album and composer ellipsis take over. */
.coming-next {
    position: absolute;
    top: 3.8rem;
    right: .7rem;
    z-index: 3;
    box-sizing: border-box;
    width: max-content;
    max-width: 50%;
    padding: .65rem .85rem;
    overflow: hidden;
    border: 1px solid rgba(255, 255, 255, .16);
    border-radius: .65rem;
    background: rgba(0, 0, 0, .62);
    color: var(--text-inverse);
    text-align: left;
    text-shadow: var(--art-text-shadow);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateX(calc(100% + .7rem));
    transition: opacity .25s ease, transform .25s ease,
                visibility 0s linear .25s, color .35s ease;
}
.coming-next.show {
    opacity: 1;
    visibility: visible;
    transform: translateX(0);
    transition-delay: 0s;
}
.coming-next-content { min-width: 0; overflow: hidden; }
.coming-next-label,
.coming-next-album,
.coming-next-artist {
    display: block;
    min-width: 0;
    white-space: nowrap;
}
.coming-next-album,
.coming-next-artist {
    overflow: hidden;
    text-overflow: ellipsis;
}
.coming-next-label {
    color: color-mix(in srgb, var(--player-tint) 72%, transparent);
    font-size: clamp(.68rem, 1.4vw, .8rem);
    font-weight: 700;
    letter-spacing: .07em;
    line-height: 1.15;
    text-transform: uppercase;
}
.coming-next-album {
    margin-top: .18rem;
    font-size: clamp(.85rem, 2vw, 1.05rem);
    font-weight: 650;
    line-height: 1.25;
}
.coming-next-artist {
    max-height: 1.25em;
    margin-top: .08rem;
    font-size: clamp(.78rem, 1.8vw, .95rem);
    line-height: 1.25;
    opacity: .82;
    transition: max-height .2s ease, margin-top .2s ease, opacity .2s ease;
}
.coming-next-artist:empty {
    max-height: 0;
    margin-top: 0;
    opacity: 0;
}
.options-overlay {
    position: absolute;
    z-index: 4;
    overflow: auto;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: var(--overlay-closed-transform);
    transition: opacity .2s ease, transform .2s ease, visibility 0s linear .2s,
                var(--theme-transition);
}
.options-overlay[data-state="open"] {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: var(--overlay-open-transform);
    transition-delay: 0s;
}
.options-overlay[hidden] { display: none; }
.spectrum-options {
    --overlay-closed-transform: translateY(calc(-50% + .4rem));
    --overlay-open-transform: translateY(-50%);
    max-height: calc(100% - 1rem);
    color: var(--text);
    text-align: left;
    user-select: auto;
}
.spectrum-options fieldset {
    margin: 0;
    padding: .9rem 1rem 1rem;
    border: 1px solid var(--line);
    border-radius: .9rem;
    background: var(--bg);
    box-shadow: var(--shadow);
}
.spectrum-options legend {
    float: left;
    width: 100%;
    margin: 0 0 .75rem;
    padding: 0;
    color: var(--text-dim);
    font-size: .85rem;
}
.spectrum-options legend + * { clear: both; }
.spectrum-options .row + .row { margin-top: .65rem; }
.spectrum-options input[type="range"] { min-width: 0; max-width: none; }
.spectrum-options .note { margin-bottom: 0; }
.stage-fs { top: .7rem; right: .7rem; }
.stage-opts { top: .7rem; right: 3.8rem; display: none; } /* fullscreen only - embedded, the panel is right below */
.stage:fullscreen .stage-opts { display: block; }
.stage:hover .stage-button, .stage-button:focus-visible { opacity: 1; }
@media (hover: none) { .stage-button { opacity: .6; } } /* touch: no hover, keep visible */

/* In fullscreen the stage IS the screen, so :hover is permanently true and useless as
   an idle signal. JS sets .idle after 2s without pointer movement: the chrome fades
   out (these rules out-specify the :hover ones) and the cursor goes with it - a cursor
   parked on the artwork is the same clutter. Any movement lifts .idle again. */
.stage:fullscreen.idle .stage-button { opacity: 0; }
.stage:fullscreen.idle .stage-update {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition-delay: 0s;
}
.stage:fullscreen.idle .rating-badges { opacity: 0; visibility: hidden; }
.stage:fullscreen.idle .rating-badges.track-intro {
    opacity: 1;
    visibility: visible;
}
/* A track change that begins after the badge chrome has idled out keeps the old
   faces concealed while the destination logos load. JS releases this only after
   the retained card has been updated, so pointer movement cannot expose stale art. */
.stage .rating-badges.track-handoff {
    opacity: 0;
    visibility: hidden;
    transition-delay: 0s, 0s;
}
.stage:fullscreen.idle { cursor: none; }

/* The options overlay: hosts the REAL .controls panel while open in fullscreen. */
.fs-options {
    --overlay-closed-transform: translateY(.4rem);
    --overlay-open-transform: translateY(0);
    right: 1rem; bottom: 3.6rem;
    width: min(440px, calc(100% - 2rem));
    max-height: calc(100% - 5.5rem);
    padding: 1rem;
    border: 1px solid var(--line);
    border-radius: .9rem;
    background: var(--bg);
    box-shadow: var(--shadow);
}

/* --- controls ------------------------------------------------------------------- */
.player-section { border-top: 0; }
.controls { display: grid; gap: .85rem; margin-top: 1.5rem; }
/* The station picker block sits ABOVE the stage: its gap belongs below it. */
.controls-top { margin: 0 0 1rem; }
.compact-setting-bar {
    padding: .75rem .9rem;
    border: 1px solid var(--line);
    border-radius: .8rem;
    background: var(--bg-card);
}
.compact-action { padding-top: .15rem; }
.settings-tabs { min-width: 0; }
.settings-tablist {
    display: flex;
    align-items: end;
    gap: .35rem;
    min-width: 0;
    margin-bottom: .9rem;
    border-bottom: 1px solid var(--line);
}
.settings-tab {
    max-width: 12rem;
    overflow: hidden;
    padding: .55rem .9rem;
    border: 0;
    border-bottom: 2px solid transparent;
    background: transparent;
    color: var(--text-dim);
    font: inherit;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    opacity: 1;
    visibility: visible;
    transition: opacity .22s ease, max-width .3s ease, padding-inline .3s ease,
                border-width .3s ease, color .2s ease, border-color .2s ease,
                visibility 0s linear 0s;
}
.settings-tab:hover { color: var(--text); }
.settings-tab[aria-selected="true"] { color: var(--text); border-bottom-color: var(--accent); }
.settings-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.settings-tab.tab-entering,
.settings-tab.tab-exiting {
    max-width: 0;
    padding-inline: 0;
    border-width: 0;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition-delay: 0s, 0s, 0s, 0s, 0s, 0s, .3s;
}
.settings-tab[hidden] { display: none; }
.settings-tab-host {
    position: relative;
    overflow: hidden;
    transition: height .35s ease;
}
.settings-tab-panel {
    box-sizing: border-box;
    width: 100%;
    min-width: 0;
    opacity: 1;
    transition: opacity .25s ease;
}
.settings-tab-panel[hidden] { display: none; }
.settings-tab-host.transitioning .settings-tab-panel {
    position: absolute;
    inset: 0 0 auto;
}
.settings-tab-host.transitioning .tab-panel-incoming { opacity: 0; }
.settings-tab-host.transitioning.swapping .tab-panel-incoming { opacity: 1; }
.settings-tab-host.transitioning.swapping .tab-panel-outgoing { opacity: 0; }
.settings-scope > h3 {
    margin: 0 0 .75rem;
    color: var(--text);
    font-size: 1rem;
}
.settings-scope > h3 span { color: var(--text-dim); font-weight: 600; }
.settings-subheading {
    margin: .2rem 0 .55rem;
    font-size: .95rem;
}
.settings-subheading:not(:first-of-type) { margin-top: 1.15rem; }
.common-settings-grid {
    display: grid;
    gap: 0;
    padding: 0 1rem;
    border: 1px solid var(--line);
    border-radius: .9rem;
    background: var(--bg-card);
}
.station-context-host {
    position: relative;
    overflow: hidden;
    transition: height .35s ease;
}
.station-context-panel {
    box-sizing: border-box;
    width: 100%;
    min-width: 0;
    opacity: 1;
    transition: opacity .25s ease;
}
.station-context-panel[hidden] { display: none; }
.station-context-host.transitioning .station-context-panel {
    position: absolute;
    inset: 0 0 auto;
}
.station-context-host.transitioning .context-incoming { opacity: 0; }
.station-context-host.transitioning.swapping .context-incoming { opacity: 1; }
.station-context-host.transitioning.swapping .context-outgoing { opacity: 0; }
.feature-options {
    display: grid;
    grid-template-rows: 0fr;
    margin-top: 0;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-.2rem);
    transition: grid-template-rows .35s ease, margin-top .35s ease,
                opacity .25s ease, transform .25s ease,
                visibility 0s linear .35s;
}
.feature-options.enabled {
    grid-template-rows: 1fr;
    margin-top: .75rem;
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    transition-delay: 0s;
}
.feature-options-inner { min-height: 0; overflow: hidden; }
.feature-stack { display: grid; gap: 0; }
.setting-group,
.feature-stack > .feature-block,
.station-setting-panel > .feature-block {
    min-width: 0;
    padding: .9rem 0;
    border-bottom: 1px solid var(--line);
}
.setting-group:last-child,
.feature-stack > .feature-block:last-child,
.station-setting-panel > .feature-block:last-child { border-bottom: 0; }
.setting-title {
    margin: 0 0 .7rem;
    color: var(--text);
    font-size: .95rem;
    font-weight: 650;
}
.setting-master,
.setting-row {
    display: flex;
    align-items: center;
    gap: .65rem;
    min-width: 0;
}
.setting-master {
    width: fit-content;
    color: var(--text);
    font-weight: 650;
    cursor: pointer;
}
.setting-master > input { flex: 0 0 auto; margin: 0; }
.setting-options-stack { display: grid; gap: .8rem; }
.compact-control-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .65rem;
}
.compact-control-row > label,
.control-label {
    display: block;
    margin-bottom: .35rem;
    color: var(--text-dim);
    font-size: .82rem;
    font-weight: 600;
}
.compact-control-row > label { margin-bottom: 0; }
.safety-label {
    margin-left: auto;
    color: var(--text-dim);
    font-size: .78rem;
}
.share-status {
    max-height: 0;
    margin: 0;
    overflow: hidden;
    color: var(--text-dim);
    font-size: .9rem;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-.2rem);
    transition: opacity .2s ease, transform .2s ease, max-height .25s ease,
                margin-top .25s ease, visibility 0s linear .25s;
}
.share-status.show {
    max-height: 2rem;
    margin-top: .55rem;
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    transition-delay: 0s;
}
.controls fieldset {
    border: 1px solid var(--line);
    border-radius: .9rem;
    background: var(--bg-card);
    padding: .9rem 1.1rem 1.1rem;
    margin: 0;
}
.controls fieldset.station-setting-panel,
.controls fieldset#spectrum-settings { padding-top: .2rem; }
.controls legend { padding: 0 .4rem; font-size: .85rem; color: var(--text-dim); }
.seg-group { display: flex; flex-wrap: wrap; gap: .5rem; }
.control-gap { margin-top: .8rem; }

.rating-country-options {
    display: flex;
    align-items: center;
    gap: .8rem;
    margin-top: .65rem;
    color: var(--text-dim);
    font-size: .9rem;
}

/* Radio groups presented as segmented pills - real inputs underneath, so keyboard
   and screen readers see an ordinary radio group. */
.seg input { position: absolute; opacity: 0; pointer-events: none; }
.seg span {
    display: inline-block;
    padding: .4rem .85rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--bg-raised);
    font-size: .9rem;
    cursor: pointer;
    opacity: 1;
    transition: opacity .2s ease, background-color .2s ease, border-color .2s ease,
                color .2s ease;
}
.seg input:checked + span { background: var(--accent); border-color: var(--accent); color: var(--text-inverse); }
.seg input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }
.seg input:disabled + span { cursor: default; }
.transition-choice span { transform-origin: center; backface-visibility: hidden; }
@keyframes settings-preview-crossfade {
    0%, 100% { opacity: 1; }
    48% { opacity: .12; }
}
@keyframes settings-preview-flip-horizontal {
    0%, 100% { transform: perspective(360px) rotateY(0); opacity: 1; }
    50% { transform: perspective(360px) rotateY(90deg); opacity: .25; }
}
@keyframes settings-preview-flip-vertical {
    0%, 100% { transform: perspective(360px) rotateX(0); opacity: 1; }
    50% { transform: perspective(360px) rotateX(90deg); opacity: .25; }
}
.transition-choice span.preview-crossfade {
    animation: settings-preview-crossfade .65s ease both;
}
.transition-choice span.preview-flip-horizontal {
    animation: settings-preview-flip-horizontal .65s ease both;
}
.transition-choice span.preview-flip-vertical {
    animation: settings-preview-flip-vertical .65s ease both;
}
.visualization-setting {
    opacity: 1;
    transition: opacity .2s ease;
}
.visualization-setting.disabled { opacity: .42; }
#spectrum-bars-setting { margin-top: .65rem; }

/* Backdrop providers: a drag-sortable priority list - topmost wins, rest fallback. */
.providers { list-style: none; margin: .8rem 0 0; padding: 0; display: grid; gap: .5rem; }
.provider-followup { margin-top: .6rem; }
.backdrop-error {
    display: flex;
    align-items: baseline;
    gap: .4rem;
    max-height: 0;
    margin-top: 0;
    overflow: hidden;
    color: var(--text-dim);
    font-size: .9rem;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-.2rem);
    transition: opacity .2s ease, transform .2s ease, max-height .2s ease,
                margin-top .2s ease, visibility 0s linear .2s;
}
.backdrop-error.show {
    max-height: 4rem;
    margin-top: .6rem;
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    transition-delay: 0s;
}
.backdrop-error button {
    border: 0;
    padding: 0;
    background: transparent;
    color: var(--accent);
    font: inherit;
    text-decoration: underline;
    cursor: pointer;
    transition: opacity .2s ease;
}
.backdrop-error button:disabled { cursor: default; opacity: .5; }
.provider {
    display: flex; align-items: center; gap: .8rem; flex-wrap: wrap;
    padding: .45rem .6rem;
    border: 1px solid var(--line);
    border-radius: .7rem;
    background: var(--bg-raised);
}
/* touch-action none: a touch on the grip starts a drag, never a page scroll */
.provider .grip {
    display: grid; place-items: center; flex: 0 0 1.75rem;
    width: 1.75rem; height: 1.75rem; padding: 0; border: 0; border-radius: .35rem;
    background: transparent; color: var(--text-dim); font: inherit; line-height: 1;
    cursor: grab; user-select: none; touch-action: none;
}
.provider .grip:hover { background: var(--bg-card); color: var(--text); }
.provider .grip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The dragged row is lifted out of the flow and rides the pointer: fixed at 0/0,
   positioned purely via the inline translate. pointer-events none lets
   elementFromPoint see the rows underneath while it floats. */
.provider.dragging {
    position: fixed;
    left: 0; top: 0;
    z-index: 100;
    margin: 0;
    pointer-events: none;
    background: var(--bg-card);
    box-shadow: 0 12px 32px rgba(0, 0, 0, .35);
}
/* The gap it left behind, sized by JS to the row's height. */
.provider.placeholder {
    box-sizing: border-box;
    border-style: dashed;
    background: transparent;
}
/* Everything under the floating row keeps its own cursor - override globally. */
body.row-dragging, body.row-dragging * { cursor: grabbing !important; }
.provider-key-setting {
    flex: 1 0 100%;
    min-width: 0;
    max-height: 0;
    padding: 0 0 0 2.55rem;
    overflow: hidden;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-.2rem);
    transition: max-height .3s ease, padding-block .3s ease, opacity .2s ease,
                transform .2s ease, visibility 0s linear .3s;
}
.provider-key-setting.provider-selected {
    max-height: 9rem;
    padding-block: .1rem;
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    transition-delay: 0s;
}
.provider-key-control {
    display: flex;
    align-items: center;
    gap: .45rem;
}
.provider-key-control input[type="password"] {
    flex: 1 1 140px;
    width: min(100%, 240px);
    max-width: 240px;
    min-width: 140px;
}
.fanart-key-check {
    position: relative;
    box-sizing: border-box;
    flex: 0 0 auto;
    width: 4.5rem;
    max-width: 0;
    min-height: 1.8rem;
    padding: .25rem 0;
    overflow: hidden;
    border: 0 solid var(--line);
    border-radius: .45rem;
    background: var(--bg-card);
    color: var(--text);
    font: inherit;
    font-size: .8rem;
    cursor: pointer;
    opacity: 0;
    visibility: hidden;
    transform: translateX(-.2rem);
    transition: opacity .2s ease, transform .2s ease, max-width .2s ease,
                padding .2s ease, border-width .2s ease, visibility 0s linear .2s,
                background-color .2s ease, color .2s ease;
}
.provider-key-setting.has-key .fanart-key-check {
    max-width: 4.5rem;
    padding-inline: .65rem;
    border-width: 1px;
    opacity: 1;
    visibility: visible;
    transform: translateX(0);
    transition-delay: 0s;
}
.fanart-key-check:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.fanart-key-check:disabled { cursor: default; }
.fanart-key-check.success {
    border-color: light-dark(#86d89b, #347d4a);
    background: light-dark(#e1fbe8, #173a24);
    color: light-dark(#166534, #7df09a);
}
.fanart-key-check span {
    display: block;
    opacity: 1;
    transition: opacity .15s ease;
}
.fanart-key-check span.changing { opacity: 0; }
.provider-key-help {
    display: flex;
    gap: .8rem;
    max-height: 1.5rem;
    margin: .3rem 0 0;
    overflow: hidden;
    font-size: .8rem;
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    transition: opacity .2s ease, transform .2s ease, max-height .2s ease,
                margin-top .2s ease, visibility 0s linear 0s;
}
.provider-key-setting.has-key .provider-key-help {
    max-height: 0;
    margin-top: 0;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-.2rem);
    transition-delay: 0s, 0s, 0s, 0s, .2s;
}
.provider-key-status {
    max-height: 0;
    margin: 0;
    overflow: hidden;
    color: light-dark(#a52828, #ff9b9b);
    font-size: .8rem;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-.2rem);
    transition: opacity .2s ease, transform .2s ease, max-height .2s ease,
                margin-top .2s ease, visibility 0s linear .2s;
}
.provider-key-status.show {
    max-height: 3rem;
    margin-top: .3rem;
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    transition-delay: 0s;
}

.row { display: flex; align-items: center; gap: .8rem; flex-wrap: wrap; }
.row label { font-size: .9rem; }
input[type="range"] { accent-color: var(--accent); flex: 1; min-width: 120px; max-width: 260px; }
input[type="checkbox"] { accent-color: var(--accent); width: 1.05rem; height: 1.05rem; }
#spectrum-bars-val { min-width: 2ch; font-variant-numeric: tabular-nums; }

.btn-player {
    padding: .55rem 1.1rem;
    border-radius: .7rem;
    border: 1px solid var(--line);
    background: var(--bg-raised);
    color: var(--text);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}
.btn-player[aria-pressed="true"] { background: var(--accent-grad); border: 0; color: var(--text-inverse); }

/* --- noscript ------------------------------------------------------------------- */
.noscript-player {
    position: absolute;
    inset: 0;
    overflow: auto;
    padding: 1.2rem;
    background: var(--bg-card);
    color: var(--text);
}
.noscript-player p { margin: .5rem 0; }
.noscript-player audio { width: 100%; max-width: 420px; display: block; margin-top: .25rem; }

/* --- wider screens --------------------------------------------------------------- */
/* Inside the fullscreen overlay the panel is ~440px, whatever the viewport says -
   AFTER the media query so equal specificity resolves by source order. */
.fs-options .controls { margin-top: 0; }
.fs-options .common-settings-grid { grid-template-columns: 1fr; }

@media (prefers-reduced-motion: reduce) {
    .coverbox img, .coverbox .card, .coverbox, .countdown, .info,
    .info .t, .backchannel-status, .backchannel-pairing,
    .stage-button, .stage-update, .stage-milkdrop, .stage-lasers, .stage-lasers-front, .stage-spectrum,
    .stage-bpm,
    .stage-status, .options-overlay,
    .backdrop, .backdrop-error, .backdrop-error button, .fanart-key-check,
    .fanart-key-check span, .provider-key-help, .provider-key-status,
    .rating-badges, .rating-slot, .rating-card, .rating-face, .rating-descriptors,
    .rating-country-options,
    .coming-next, .coming-next-artist, .visualization-setting, .seg span,
    .station-context-host, .station-context-panel, .share-status,
    .settings-tab, .settings-tab-host, .settings-tab-panel, .feature-options,
    .provider-key-setting {
        transition: none !important;
    }
    .transition-choice span { animation: none !important; }
    .stage-milkdrop, .stage-lasers, .stage-lasers-front, .stage-spectrum { display: none; }
    /* digit rolling is skipped in JS when reduced motion is set */
}
