/* =============================================================================
 * AltSounds Universal NP Widget — v1.5.1
 *
 * Two layouts sharing one DOM structure. Selectors hang off
 *   .alts-np-widget--corner  (single-gallery, compact)
 *   .alts-np-widget--rail    (Mothership, stacked list)
 *
 * Row structure (same in both layouts):
 *
 *   .alts-np-row
 *     .alts-np-row__thumb
 *     .alts-np-row__text
 *       .alts-np-row__meta   (optional — badges go here)
 *       .alts-np-row__lines
 *         .alts-np-row__artist
 *         .alts-np-row__title
 *     .alts-np-row__remove   (optional — only for user/station rows)
 *
 * The widget uses tokens from altsounds-tokens.css where possible so
 * theming stays consistent with the playbar and other surfaces.
 * ===========================================================================*/

/* ============================================================================
   LEGACY SURFACE SUPPRESSION
   ----------------------------------------------------------------------------
   The theme's eyebrow widget (.as-eyebrow-upnext) renders its own UP NEXT
   row with thumbnail + title — the widget now handles this. Hide the old
   block. We DON'T delete its DOM (it still backs the taunt + station-
   toggle + unqueue handlers that read/write .as-eyebrow-upnext-title),
   just make it visually gone.
   
   Same for the flavor row (.as-eyebrow-flavor) which lived on the
   eyebrow and has been subsumed by the playbar's flavor pills.
   
   Mothership rail's flavor bar is also hidden — playbar owns flavor
   globally now.
   
   IMPORTANT: only hide when the widget is actually present (mount
   contains a .alts-np-widget child). Otherwise we'd risk hiding the
   legacy UI with nothing to replace it. */

.as-chrome-eyebrow:has([data-alts-np-mount="corner"] .alts-np-widget) .as-eyebrow-upnext,
.as-chrome-eyebrow:has([data-alts-np-mount="corner"] .alts-np-widget) .as-eyebrow-flavor {
    display: none !important;
}

/* Fallback for browsers without :has() — hide whenever the widget
   mount is present, even if not yet populated. Safe because the
   mount element only appears on pages where the widget loads. */
@supports not selector(:has(*)) {
    .as-chrome-eyebrow .as-eyebrow-upnext,
    .as-chrome-eyebrow .as-eyebrow-flavor {
        display: none !important;
    }
}

/* Mothership rail: suppress the legacy flavor bar. The rail's
   data-np-flavor-pills container lives inside the rail shell and
   shows duplicate pills. Playbar owns flavor globally now. */
.alts-guide__np-rail [data-np-flavor-pills],
.alts-guide__np-rail [data-np-flavor],
.alts-guide__np-flavor {
    display: none !important;
}

/* ============================================================================
   Mount-fill — the widget should span the available container width
   ----------------------------------------------------------------------------
   v1.5.10: Without an explicit width directive, the corner mount
   ([data-alts-np-mount="corner"]) is `flex: 0 1 auto` from its
   .as-eyebrow-row classmate styling and shrinks to its CONTENT width.
   That left ~160-200px of usable space inside a ~355px eyebrow chrome —
   row text wraps tight, the X-button hovers right at the edge, and the
   skeleton placeholders look like one-third-width fragments rather
   than full rows.

   Forcing the mount + the widget inside it to fill the full inline
   axis fixes both the skeleton "stub" appearance and the cramped real
   rows. Rail mount is unaffected because it lives inside the Mothership
   panel which already controls its own sizing.

   `width: 100%` on the mount itself is necessary because the parent
   .as-chrome-eyebrow is flex-direction: column with align-items:
   flex-start — children don't auto-stretch. min-width: 0 keeps flex
   children ellipsizable rather than overflowing on long titles. */
[data-alts-np-mount="corner"] {
    width: 100%;
    min-width: 0;
}
[data-alts-np-mount="corner"] .alts-np-widget {
    width: 100%;
    min-width: 0;
}

/* v1.5.15.12 — Theme eyebrow blend bar. Injected by altsounds-np.js
   into .as-chrome-eyebrow ABOVE the original .as-eyebrow-current
   when blends are active; that p gets hidden so the blend strip
   becomes the surface's "what's playing" indicator. Width and
   spacing mirror the corner widget mount so the strip lines up
   with the rows below. */
.as-chrome-eyebrow .as-eyebrow-blend-strip {
    width: 100%;
    min-width: 0;
}

.alts-np-widget {
    color: #fff;
    font-family: var(--alts-font-sans, Inter, system-ui, sans-serif);
    line-height: 1.3;
    /* Hard-reset text alignment. The host eyebrow container is text-
       align: right; we always want our rows left-aligned. */
    text-align: left;
}
/* v1.5.19 — Label "Queue" reads white; icon stays lime so the
   accent color still anchors the row visually. call: lime
   "Queue" text against the lime hover state of rows below felt
   over-saturated. */
.alts-np-widget__label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: #fff;
    margin: 0 0 8px;
    line-height: 1;
    /* v1.5.24 — Label spans the full row width so the View-all arrow
       (margin-left:auto) is pushed to the right edge of the rail.
       Padding-right matches the row's right padding (10px in the rail
       variant) so the arrow icon aligns visually with the row × buttons
       sitting in the rows below. */
    width: 100%;
    box-sizing: border-box;
    padding-right: 10px;
}
.alts-np-widget__label-icon {
    font-size: 11px;
    color: var(--alts-color-accent, #bfff00);
    line-height: 1;
}
.alts-np-widget__label-text {
    line-height: 1;
}

[data-alts-np-layout="corner"] .alts-np-widget__label {
    color: rgba(255, 255, 255, 0.55);
}
[data-alts-np-layout="corner"] .alts-np-widget__label-icon {
    color: inherit;
}
/* v1.5.19 — "View all" link sits at the right end of the rail's
   Queue label. Opens the full-page Mothership Queue panel. Plain
   underlined link on hover; muted-lime to start so it doesn't shout. */

.alts-np-widget__view-all {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 6px;
    color: #fff;
    background: transparent;
    text-decoration: none;
    transition: color 0.12s, background 0.12s;
}
.alts-np-widget__view-all:hover {
    color: #BFFF00;
    background: rgba(191, 255, 0, 0.12);
    text-decoration: none;
}
.alts-np-widget__view-all i { font-size: 11px; }

/* v1.5.15.15 — Compact blend-aware Queue header on NP rail.
   Layout when blends are active:
     [Selected blends] [kind-icon · Name] [kind-icon · Name] … [maximize]
   No chip backgrounds, no per-blend X — the icon + name pair IS
   the chip, both rendered in that blend's color. Click any chip
   to navigate to that blend's taxonomy page. The "Selected blends"
   title makes the row read as a labeled summary rather than a
   mystery cluster of glyphs. */
.alts-np-widget__label--blends {
    gap: 8px;
    flex-wrap: wrap;     /* allow chips to wrap if four blends + names overflow */
    row-gap: 6px;
}
.alts-np-widget__blendlabel-title {
    font: 700 9px/1 var(--alts-mono, 'IBM Plex Mono', monospace);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.55);
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 5px;
}
/* v1.5.15.18 — Blend glyph inherits the title's exact size +
   color. No puck, no shadow — "no other formatting." */
.alts-np-widget__blendlabel-title i {
    font-size: inherit;
    color: inherit;
}
.alts-np-widget__blendlabel-icons {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    
    margin-left: auto;
    flex-direction: row-reverse;
    justify-content: flex-start;  /* with row-reverse this packs items right */
    flex-wrap: wrap-reverse;       /* multi-row overflow stays bottom-up */
    min-width: 0;
}
/* v1.6.15 — Each blend renders as a rounded pill with dark text +
   icon on the blend's palette color. Mirrors the NP rail artist
   blend button recipe (mono-caps, 9px text, 11px icon, tight padding)
   for visual consistency across NP rail and NP widget. Dark text on
   a colored background reads as AltSounds chrome (vs white-on-color
   which read as Spotify-ish). The X sits naked inside the pill — no
   inner container slot — since the pill itself is the container. */

.alts-np-widget__blendlabel-chipwrap {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    flex-shrink: 0;
    padding: 4px 8px 4px 10px;
    border-radius: 999px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
    color: #0a0b0a;
    line-height: 1;
    height: 22px;
    min-height: 22px;
    max-height: 22px;
    box-sizing: border-box;
    vertical-align: middle;
    /* background is set inline per blend on this wrapper. */
}
.alts-np-widget__blendlabel-chipwrap > * {
    align-self: center;
}
.alts-np-widget__blendlabel-chip {
    display: inline-flex;
    align-items: center;
    align-self: center;
    gap: 5px;
    text-decoration: none;
    color: #0a0b0a;
    line-height: 1;
    transition: transform 0.12s ease, filter 0.12s ease;
}
.alts-np-widget__blendlabel-chip:hover {
    transform: translateY(-1px);
    filter: brightness(0.92);
}
.alts-np-widget__blendlabel-chip-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    line-height: 1;
    height: 11px;
    color: #0a0b0a;
    text-shadow: none;
}
.alts-np-widget__blendlabel-chip-name {
    display: inline-flex;
    align-items: center;
    font: 700 9px/1 var(--alts-mono, 'IBM Plex Mono', monospace);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: #0a0b0a;
    white-space: nowrap;
    height: 11px;
}
/* Per-chip remove. Bare × inside the pill — no container slot
   behind it (the pill IS the container). Hover dims, no background.
   v1.6.21 — Was a `&times;` glyph that sat high inside the chip
   because the character's font baseline didn't match the chip's
   visual center. Now an fa-xmark icon with explicit display:block +
   line-height:1 + an icon-height that matches the chip body so the
   X lands dead-center vertically. */
.alts-np-widget__blendlabel-chip-remove {
    appearance: none;
    -webkit-appearance: none;
    background: transparent;
    border: 0;
    padding: 0 2px;
    margin: 0;
    color: #0a0b0a;
    opacity: 0.7;
    font-size: 9px;
    line-height: 1;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 14px;
    transition: opacity 0.12s ease, transform 0.12s ease;
}
.alts-np-widget__blendlabel-chip-remove > i {
    display: block;
    line-height: 1;
    font-size: 9px;
}
.alts-np-widget__blendlabel-chip-remove:hover {
    opacity: 1;
    transform: scale(1.15);
}
.alts-np-widget__rows {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}

/* ============================================================================
   Row — shared base
   ---------------------------------------------------------------------------- */

.alts-np-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 8px;
    border-radius: 8px;
    color: inherit;
    text-decoration: none;
    position: relative;
    min-width: 0;
    /* v1.5.8.1 — explicit full-width so the row hit-target spans the
       whole widget, not just its text content. Pointer cursor on the
       whole row reinforces "this is one tap target" — previously only
       the title text felt clickable on hover. */
    width: 100%;
    box-sizing: border-box;
    cursor: pointer;
    /* v1.5.15 — Default rounded-rectangle background at rest. Previously
       the row had no fill until hover, so the Up Next list read as plain
       text rows. The rail (and corner widget) look more structured with
       a faint always-on bar. Hover bumps brightness for the interaction
       cue. */
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.1);
    transition:
        background var(--alts-trans-fast, 160ms) ease,
        border-color var(--alts-trans-fast, 160ms) ease,
        transform  var(--alts-trans-fast, 160ms) ease;
}

/* v1.5.9.3 — Hit-area anchor inside the row.
   Previously the row WAS the anchor (<a class="alts-np-row">). Now
   the row is a wrapper <div> and the anchor is an inner element
   (<a class="alts-np-row__hit">) that contains the thumb + text but
   NOT the X button. This keeps the X button structurally outside
   the navigation surface, so clicking X can never bubble through
   to the anchor's navigation handler. The anchor expands to fill
   the row's flex space, leaving the X button visually anchored to
   the right edge as before. */
.alts-np-row__hit {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1 1 auto;
    min-width: 0;
    color: inherit;
    text-decoration: none;
    /* The wrapper's padding/border-radius live on .alts-np-row;
       this child fills the inside of that. */
}
.alts-np-row__hit:focus-visible {
    outline: none;   /* parent handles focus ring via :focus-visible below */
}
/* v1.7.x — Focus ring only for KEYBOARD focus. This was `:focus-within`, which
   also fires for MOUSE focus — so press-dragging a rail row focused the hit and
   left a lime ring that lingered until you clicked elsewhere (looked "grabbed",
   implied it was still movable; a click never showed it because it plays →
   re-renders → blurs). `:has(:focus-visible)` shows the ring for keyboard nav
   only, never for a mouse press/drag. */
.alts-np-row:has(.alts-np-row__hit:focus-visible) {
    outline: 2px solid var(--alts-color-accent, #bfff00);
    outline-offset: 2px;
}
.alts-np-row:hover,
.alts-np-row:focus-visible {
    /* v1.5.15 — Brighter than the default rest state (0.06) so the
       hover feedback still reads. */
    background: rgba(191, 255, 0, 0.1);
    border-color: rgba(191, 255, 0, 0.3);
    outline: none;
}
.alts-np-row:focus-visible {
    outline: 2px solid var(--alts-color-accent, #bfff00);
    outline-offset: 2px;
}
.alts-np-row:active {
    transform: scale(0.98);
}
/* Non-clickable rows (repeat-one placeholder) shouldn't have hover feedback. */
.alts-np-row--repeat {
    cursor: default;
}
.alts-np-row--repeat:hover {
    background: transparent;
    transform: none;
}

/* v1.5.19 — Drag-reorder affordances. Whole row is draggable=true (no
   explicit handle to keep the rail tight). cursor hints + drop-line
   indicators mirror the Queue panel pattern. Drop lines render via
   the row's own ::before/::after at top/bottom edge.
   v1.5.20 — Rows expand margin when target so the user has a forgiving
   drop zone, not a tight 3px line. Smooth margin transition makes
   the gap "open" as the user hovers in. */
.alts-np-row {
    transition:
        background var(--alts-trans-fast, 160ms) ease,
        border-color var(--alts-trans-fast, 160ms) ease,
        transform  var(--alts-trans-fast, 160ms) ease;
}
.alts-np-row[draggable="true"] { cursor: grab; }
.alts-np-row[draggable="true"]:active { cursor: grabbing; }
/* v1.7.x — Rail rows are tap-to-play ONLY (rail drag-reorder removed in
   v1.6.19). Kill the text-selection highlight AND the native link-drag ghost
   so pressing + dragging a rail row doesn't visually "grab"/highlight it, which
   falsely implied it was still movable. Scoped to the rail mount so the
   reorderable NP-FULL / Queue rows are untouched. */
[data-alts-np-mount="rail"] .alts-np-row,
[data-alts-np-mount="rail"] .alts-np-row a {
    -webkit-user-select: none;
    user-select: none;
    -webkit-user-drag: none;
}
/* v1.5.27 — Revert lift-out (broke dragging). Back to darkened
   source row in place. */
.alts-np-row.is-dragging {
    background: rgba(0, 0, 0, 0.55);
    border-color: rgba(191, 255, 0, 0.4);
}
.alts-np-row.is-dragging:hover {
    background: rgba(0, 0, 0, 0.55);
    border-color: rgba(191, 255, 0, 0.4);
}
/* v1.5.25 — Mute hover briefly after drop so the row under the
   cursor doesn't keep its highlight. Mirrors the Queue panel's
   .is-post-drop-hover-mute class. */
.alts-np-widget__rows.is-post-drop-hover-mute .alts-np-row:hover {
    background: rgba(255, 255, 255, 0.06);
    border-color: rgba(255, 255, 255, 0.1);
}
/* v1.5.28 — During an active drag, mute hover on all non-target
   rows so the lime hover doesn't fire on every row the cursor
   passes over. Drop indicators (is-drop-before / after) handle
   the targeting feedback. */
.alts-np-widget__rows.is-queue-dragging .alts-np-row:not(.is-drop-before):not(.is-drop-after):hover {
    background: rgba(255, 255, 255, 0.06);
    border-color: rgba(255, 255, 255, 0.1);
}

/* v1.5.21 — Six-dot grip handle. Decorative (the whole row is the
   drag surface), but makes the reorder affordance obvious at a
   glance. Subtle by default, brightens on row hover. */
.alts-np-row__grip {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 12px;
    height: 100%;
    font-size: 11px;
    line-height: 1;
    color: rgba(255, 255, 255, 0.32);
    cursor: grab;
    user-select: none;
    /* v1.5.22 — touch-action: none so mobile reorder (pointer-event
       fallback) can capture the gesture without the browser hijacking
       it for page scroll. */
    touch-action: none;
    transition: color var(--alts-trans-fast, 160ms) ease;
}
.alts-np-row:hover .alts-np-row__grip {
    color: rgba(255, 255, 255, 0.7);
}
.alts-np-row__grip:active { cursor: grabbing; }

/* v1.5.25 — Reorder-confirmation badge. Visual recipe matches the
   Queue panel's .alts-guide__queue-row-pin exactly: lime tinted
   circle, lime icon, same dimensions. Identical badge on both
   surfaces so the user reads "you moved this" the same way
   everywhere. */
.alts-np-row__moved {
    flex: 0 0 22px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    background: rgba(191, 255, 0, 0.12);
    color: #BFFF00;
    border-radius: 50%;
    font-size: 10px;
    margin-right: 4px;
    text-shadow: none;
    line-height: 1;
}
/* v1.5.22 — Drop-targeting rewrite. Earlier versions expanded the
   targeted row's margin, shifting it out from under the cursor →
   indicator dropped → row snapped back → flicker. New model: the
   CONTAINER uniformly expands its gap once at dragstart (stable),
   pseudo-elements extend each row's hit area INTO the wide gap, and
   the drop line is drawn via box-shadow so targeting doesn't push
   any row around. */
.alts-np-widget__rows {
    transition: gap 160ms ease, padding 160ms ease;
}
/* v1.5.24 — Bumped from 24px → 32px so the rail's drop zones are
   more forgiving (less precise mouse targeting). Plus padding-bottom
   during drag so the last row's outset box-shadow drop indicator
   isn't clipped by the rail container's edge. */
/* v1.5.31 — Match Queue panel exactly: 28px gap during drag,
   15px hit-zone pseudos above/below each row. Was 32/17 which
   felt over-spaced in the narrow rail vs Queue's clean rhythm. */
.alts-np-widget__rows.is-queue-dragging {
    gap: 28px;
    padding-bottom: 16px;
    overflow: visible;
}
.alts-np-widget__rows.is-queue-dragging .alts-np-row::before,
.alts-np-widget__rows.is-queue-dragging .alts-np-row::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    height: 15px;
    pointer-events: auto;
    z-index: 1;
}
.alts-np-widget__rows.is-queue-dragging .alts-np-row::before {
    top: -15px;
}
.alts-np-widget__rows.is-queue-dragging .alts-np-row::after {
    bottom: -15px;
}
/* v1.5.31 — Drop indicator matches Queue panel exactly. Was a
   gradient on the hit-zone pseudo which felt off vs Queue's
   clean box-shadow line. Unified recipe: 3px lime band sitting
   in the gap above/below the row via box-shadow, identical to
   Queue's .alts-guide__queue-row.is-drop-* rule. */
/* v1.5.27 — Drop indicator color = SOURCE row's blend tint (via
   --alts-drag-color set on documentElement during dragstart). Lets
   the user see "I'm placing the red track here" rather than the
   indicator shifting color based on which row they happen to hover.
   Falls back to the hovered row's --alts-blend-tint when no drag
   color is set, then AS green. */
.alts-np-row.is-drop-before {
    box-shadow:
        0 -3px 0 0 var(--alts-drag-color, var(--alts-blend-tint, #BFFF00)),
        0 -3px 14px 0 color-mix(in srgb, var(--alts-drag-color, var(--alts-blend-tint, #BFFF00)) 70%, transparent);
}
.alts-np-row.is-drop-after {
    box-shadow:
        0 3px 0 0 var(--alts-drag-color, var(--alts-blend-tint, #BFFF00)),
        0 3px 14px 0 color-mix(in srgb, var(--alts-drag-color, var(--alts-blend-tint, #BFFF00)) 70%, transparent);
}

/* v1.5.20 — Reorder success flash. Brief lime pulse after a drop so
   the user has clear confirmation the move landed. */
.alts-np-row.is-just-moved {
    animation: alts-np-row-flash 900ms ease-out;
}
@keyframes alts-np-row-flash {
    0% {
        background: rgba(191, 255, 0, 0.3);
        box-shadow: 0 0 0 6px rgba(191, 255, 0, 0);
    }
    35% {
        background: rgba(191, 255, 0, 0.22);
        box-shadow: 0 0 0 4px rgba(191, 255, 0, 0.2);
    }
    100% {
        background: transparent;
        box-shadow: 0 0 0 0 rgba(191, 255, 0, 0);
    }
}

/* Thumb — 40px square, rounded corners. Uses background-image on a
   <span> instead of <img> to avoid fighting global '#page img' rules
   from the theme/responsive stylesheets. */
.alts-np-row__thumb {
    flex: 0 0 auto;
    display: block;
    width: 40px;
    height: 40px;
    border-radius: 6px;
    background-color: rgba(255, 255, 255, 0.06);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}
.alts-np-row__thumb--empty {
    background-image:
        linear-gradient(135deg, rgba(255,255,255,0.08), rgba(255,255,255,0.02));
}

/* ============================================================================
   RAIL VERTICAL CASCADE
   ----------------------------------------------------------------------------
   data-np-density attribute is set by altsounds-np.js based on available
   height in the rail's parent container. As space shrinks we progressively
   hide:
     comfort   → all 3 rows + badges
     squeeze-1 → hide row 3
     squeeze-2 → hide rows 2 & 3 (only row 1 visible)
     tight     → hide rows 2 & 3 + source badges
   Always preserve the widget label so the user knows they're looking at
   "Up next" (or whatever source-mode the head is).
   ---------------------------------------------------------------------------- */

[data-alts-np-mount="rail"],
[data-alts-np-mount="rail"] *,
[data-alts-np-mount="rail"] a,
[data-alts-np-mount="rail"] .alts-np-row {
    -webkit-user-drag: none;
    user-drag: none;
}

[data-alts-np-mount="rail"][data-np-density="squeeze-1"] .alts-np-row[data-alts-np-row-index="2"] {
    display: none !important;
}
[data-alts-np-mount="rail"][data-np-density="squeeze-2"] .alts-np-row[data-alts-np-row-index="1"],
[data-alts-np-mount="rail"][data-np-density="squeeze-2"] .alts-np-row[data-alts-np-row-index="2"],
[data-alts-np-mount="rail"][data-np-density="tight"]     .alts-np-row[data-alts-np-row-index="1"],
[data-alts-np-mount="rail"][data-np-density="tight"]     .alts-np-row[data-alts-np-row-index="2"] {
    display: none !important;
}
[data-alts-np-mount="rail"][data-np-density="tight"] .alts-np-row__badge {
    display: none !important;
}

/* ============================================================================
   SKELETON ROWS (artist-station prefetch in flight)
   ----------------------------------------------------------------------------
   When user toggles station ON, the theme writes a placeholder to AS_Queue
   immediately and resolves to a real song ~500-1500ms later via async fetch.
   During that window we show a shimmer skeleton instead of the bare artist
   placeholder — clearer "loading" affordance than an empty row.
   ---------------------------------------------------------------------------- */

@keyframes alts-np-shimmer {
    0%   { background-position: -200% 0; }
    100% { background-position:  200% 0; }
}

.alts-np-row--skeleton {
    cursor: default;
    pointer-events: none;
}
/* v1.5.9.4 — Skeleton hit-wrapper. Mirrors the real row's __hit so the
   text column flex-grows correctly. Without this the skeleton was
   visually left-stranded inside an over-wide row container. */
.alts-np-row__hit--skeleton {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1 1 auto;
    min-width: 0;
}
.alts-np-row__thumb--skeleton,
.alts-np-row__artist--skeleton,
.alts-np-row__title--skeleton {
    background: linear-gradient(
        90deg,
        rgba(255,255,255,0.05) 0%,
        rgba(255,255,255,0.12) 50%,
        rgba(255,255,255,0.05) 100%
    );
    background-size: 200% 100%;
    animation: alts-np-shimmer 1.4s linear infinite;
    color: transparent;            /* hide nbsp / inherited content */
    border-radius: 4px;
    /* v1.5.9.0 — make sure inner box dimensions actually compute. The
       previous .alts-np-row__artist rule set white-space: nowrap +
       overflow: hidden which is fine for real text but fights an empty
       skeleton span — content-less inline-blocks were collapsing to
       near-zero baseline in some browsers. Forcing display: block,
       box-sizing, and min-height gives the bars deterministic size
       regardless of inherited inline-text rules. */
    box-sizing: border-box;
}
.alts-np-row__thumb--skeleton {
    border-radius: 6px;
}
.alts-np-row__artist--skeleton {
    /* v1.5.9.4 — Percentage-based widths with min-width: 0. The previous
       fixed pixel min-widths (120/160px) overflowed the corner mount
       (~280px wide) and created visible row width pop-out. Percentages
       scale gracefully across both layouts (corner ~280px, rail ~340px). */
    display: block !important;
    width: 60%;
    height: 14px;
    min-height: 14px;
    min-width: 0;
    margin: 2px 0 4px;
    /* Hard reset of inherited inline-text rules from .alts-np-row__artist
       so the skeleton doesn't try to collapse to ellipsis-width. */
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
}
.alts-np-row__title--skeleton {
    display: block !important;
    width: 80%;
    height: 14px;
    min-height: 14px;
    min-width: 0;
    margin: 0 0 0;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
}
@media (prefers-reduced-motion: reduce) {
    .alts-np-row__thumb--skeleton,
    .alts-np-row__artist--skeleton,
    .alts-np-row__title--skeleton {
        animation: none;
    }
}

/* Text column. min-width:0 is the magic that lets flex children ellipsize. */
.alts-np-row__text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    flex: 1 1 auto;
}
.alts-np-row__meta {
    display: flex;
    gap: 4px;
    align-items: center;
    line-height: 1;
}
.alts-np-row__lines {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

/* Artist (white, primary) and title (gray, secondary).
   Forced font-family: Inter overrides any inherited sans (the theme's
   .alts-guide__np-rail uses Montserrat for headers; rows want Inter).
   No uppercasing — natural casing reads better at this size.
   v1.5.8.1 — letter-spacing forced to 0. The corner widget mounts
   inside the theme's .as-chrome-eyebrow, which sets letter-spacing:
   0.12em for its uppercase eyebrow text. That cascades down to
   .alts-np-row__title (a sibling of the eyebrow) and the song title
   inherits ~1.7px of tracking — looks like a label, not a song name.
   Both selectors below explicitly override. */
.alts-np-row__artist {
    font-family: var(--alts-font-sans, Inter), Inter, system-ui, sans-serif;
    font-size: 12px;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.95);
    text-transform: none;
    letter-spacing: 0 !important;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
.alts-np-row__title {
    font-family: var(--alts-font-sans, Inter), Inter, system-ui, sans-serif;
    font-size: 12px;
    font-weight: 400;
    color: rgba(255, 255, 255, 0.55);
    text-transform: none;
    letter-spacing: 0 !important;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
.alts-np-row__title--empty {
    color: rgba(255, 255, 255, 0.35);
    font-weight: 500;
}

/* Source badges — small uppercase chips. Colors picked for quick
   semantic read: green=user intent, amber=station, cyan=repeat. */
.alts-np-row__badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 6px;
    border-radius: 999px;
    font-size: 8.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    line-height: 1;
    white-space: nowrap;
}
.alts-np-row__badge--user {
    /* v1.5.11.9 — was AltSounds-green. Now uses the canonical
       --alts-color-queued amber so users instantly read "this is the
       row I just queued." Matches the artist-profile per-tile queue
       button's queued state (amber pill with check) for visual continuity. */
    background: rgba(245, 158, 11, 0.18);
    color: var(--alts-color-queued, #f59e0b);
    border: 1px solid rgba(245, 158, 11, 0.45);
}
.alts-np-row__badge--station {
    /* v1.5.16 — Station badges now use the BLEND palette per active
       term (--station-color is set inline per row from the active
       station artist's slug). Was hardcoded AS-green; that color
       slot belongs to REPEAT now. The history of trying cyan
       (v1.5.11.10) and back-to-green (v1.5.11.11) is preserved in
       git log for context. */
    background: color-mix(in srgb, var(--station-color, #bfff00) 15%, transparent);
    color: var(--station-color, #bfff00);
    border: 1px solid color-mix(in srgb, var(--station-color, #bfff00) 35%, transparent);
}
.alts-np-row__badge--repeat {
    /* v1.5.8.4 — AltSounds green (was cyan #8ed8ff). Same treatment as
       --user so "Queued" and "Plays again" read as the same family of
       user-driven intent rather than two competing tags. The position
       (Plays again is always the row that's CURRENTLY playing, while
       Queued is what's coming next) is enough disambiguation. */
    background: rgba(191, 255, 0, 0.15);
    color: var(--alts-color-accent, #bfff00);
    border: 1px solid rgba(191, 255, 0, 0.35);
}
/* v1.5.8.5 — Last-on-station badge. Same warm amber family as the plain
   Station badge so the relationship reads ("still a station row, just
   the last one"), but with a slow attention-pulse to signal "heads up,
   this is the final one before the queue falls back to general scope."
   The animation is intentionally gentle — 2.6s cycle, low-alpha glow —
   so it draws the eye without harassing it. */
.alts-np-row__badge--last-station {
    background: rgba(255, 170, 0, 0.18);
    color: #ffd06a;
    border: 1px solid rgba(255, 170, 0, 0.55);
    animation: alts-np-last-station-pulse 2.6s ease-in-out infinite;
}
@keyframes alts-np-last-station-pulse {
    0%, 100% { box-shadow: 0 0 0 0   rgba(255, 170, 0, 0.0); }
    50%      { box-shadow: 0 0 0 4px rgba(255, 170, 0, 0.18); }
}
@media (prefers-reduced-motion: reduce) {
    .alts-np-row__badge--last-station { animation: none; }
}

/* v1.5.17.2 — Reason pill. Names the scope this row came from
   (BAD OMENS, 2020s, etc) in the per-term palette color. --reason-color
   is set inline by reasonPillHtml from blendColorFor / decadeColorFor.
   Falls back to a neutral outline when no color is available. */
.alts-np-row__reason {
    flex: 0 0 auto;
    align-self: center;
    margin-right: 6px;
    font: 700 8px/1 var(--alts-mono, "IBM Plex Mono", ui-monospace, monospace);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    white-space: nowrap;
    padding: 3px 6px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--reason-color, rgba(255,255,255,0.5)) 14%, transparent);
    color: var(--reason-color, rgba(255, 255, 255, 0.7));
    border: 1px solid color-mix(in srgb, var(--reason-color, rgba(255,255,255,0.18)) 32%, transparent);
    text-shadow: none;
    max-width: 110px;
    overflow: hidden;
    text-overflow: ellipsis;
}

.alts-np-row__reason--recipe {
    /* no overrides — fall through to .alts-np-row__reason base. */
}

a.alts-np-row__reason--clickable {
    text-decoration: none;
    cursor: pointer;
    transition: transform 0.14s ease, filter 0.14s ease;
}
a.alts-np-row__reason--clickable:hover,
a.alts-np-row__reason--clickable:focus-visible {
    transform: translateY(-1px);
    filter: brightness(1.18);
    outline: none;
}

/* v1.7.43 — Focus-flash on the recipe tile the user landed on
   after clicking a recipe pill.  The handler in altsounds-np.js
   scrolls the matching [data-recipe-slug] / [data-mix-slug] into
   view and adds .alts-np-recipe-flash for 1.4s so the user sees
   where they are.  Reads against both .alts-guide__blends-recipe-
   tile (featured) and .alts-guide__blends-mix-pill (Simple/Daring/
   Strange) shapes via outer ring + soft inner glow that fades. */
@keyframes alts-np-recipe-flash {
    0%   { box-shadow: 0 0 0 0   rgba(191, 255, 0, 0.0),
                       0 0 0 0   rgba(191, 255, 0, 0.0) inset; }
    25%  { box-shadow: 0 0 0 4px rgba(191, 255, 0, 0.55),
                       0 0 24px 4px rgba(191, 255, 0, 0.35) inset; }
    100% { box-shadow: 0 0 0 0   rgba(191, 255, 0, 0.0),
                       0 0 0 0   rgba(191, 255, 0, 0.0) inset; }
}
.alts-np-recipe-flash {
    animation: alts-np-recipe-flash 1.4s ease-out;
    /* Ensure the box-shadow renders over neighboring tiles. */
    position: relative;
    z-index: 5;
}

/* Remove button — only appears on user/station rows. Sits at the end,
   becomes visible on row hover (less visual noise at rest). */
/* Remove button — appears on user/station/default rows. Subtly visible
   at rest (so users discover it exists) and fully revealed on row
   hover. v1.5.9.3 — was opacity: 0 at rest which made the user
   complaint "there is no x function in the NP widget" — the button
   was invisible until exact-hover. Now it's a barely-there mark at
   rest so its presence is felt across the whole widget surface. */
.alts-np-row__remove {
    appearance: none;
    -webkit-appearance: none;
    background: rgba(0, 0, 0, 0.32);
    border: 1px solid rgba(255, 255, 255, 0.18);
    color: rgba(255, 255, 255, 0.85);
    cursor: pointer;
    font-size: 18px;
    line-height: 1;
    padding: 2px 6px;
    border-radius: 50%;
    /* v1.5.10 — Bumped from 0.35 → 0.7 resting opacity. The previous
       value was effectively invisible against bright video frames, so
       users couldn't find the affordance at all. The dark glass pill
       (rgba background + thin white ring) gives the X a stable contrast
       floor that survives every video background while still feeling
       chrome-y on dark frames. Hover lifts to full opacity + brighter
       fill, same as before. */
    opacity: 0.7;
    flex: 0 0 auto;
    /* Light text-shadow for the × glyph itself — keeps it legible on
       video frames where the background pill might be partially
       transparent over a busy area. */
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.7);
    transition:
        opacity    var(--alts-trans-fast, 160ms) ease,
        color      var(--alts-trans-fast, 160ms) ease,
        background var(--alts-trans-fast, 160ms) ease,
        border-color var(--alts-trans-fast, 160ms) ease;
}
.alts-np-row:hover .alts-np-row__remove,
.alts-np-row:focus-within .alts-np-row__remove {
    opacity: 1;
}

.alts-np-row__remove:hover,
.alts-np-row__remove:focus-visible {
    color: #fff;
    background: rgba(255, 80, 80, 0.95);
    border-color: rgba(255, 80, 80, 0.95);
    transform: scale(1.08);
    outline: none;
}
/* Touch devices have no hover, so the X button is permanently visible
   at full opacity. */
@media (hover: none) {
    .alts-np-row__remove {
        opacity: 0.9;
    }
}

/* Empty state — invisible placeholder so layout doesn't jump. */
.alts-np-empty {
    height: 40px;
}

/* ============================================================================
   Layout: CORNER  (single-gallery eyebrow area)
   ----------------------------------------------------------------------------
   Compact single-row treatment. Appears over the video, so row text needs
   legibility shadow. Matches the eyebrow widget's visual weight. */

.alts-np-widget--corner {
    /* No padding — the host eyebrow container controls outer space. */
}
.alts-np-widget--corner .alts-np-widget__label {
    /* Corner layout owns its own label now — the legacy eyebrow UP NEXT
       block is hidden (see LEGACY SURFACE SUPPRESSION above). */
}
.alts-np-widget--rail .alts-np-widget__label {
    /* v1.6.16 — Hairline restored below the Mix header. Now that the
       border below the description is gone, this rule does the
       single-divider job — Mix header is the one separator between
       the song chrome and the zoned list. */
    display: flex;
    align-items: center;
    margin: 0;
    width: 100%;
    box-sizing: border-box;
}
.alts-np-widget--rail .alts-np-widget__head-wrap {
    
    margin: 0 0 5px;
    padding-bottom: 9px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.alts-np-widget--rail .alts-np-widget__head-sub {
    
    font: 400 11px/1 var(--alts-font-sans, Inter, system-ui, sans-serif);
    color: rgba(255, 255, 255, 0.45);
    text-transform: none;
    letter-spacing: 0;
    margin: 0 0 0 10px;
    padding: 0;
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.alts-np-widget--rail .alts-np-widget__label .alts-guide__np-rail-mix-cta {
    margin-left: auto;
    margin-top: 0;
    padding: 0;
    flex: 0 0 auto;
    text-transform: none;
    letter-spacing: 0;
    font: 400 11px/1 var(--alts-font-sans, Inter, system-ui, sans-serif);
}
/* Hide Mothership's legacy plain-text UP NEXT header — the widget
   renders its own styled version. */
.alts-guide__np-rail .alts-guide__np-upnext-head {
    display: none !important;
}
.alts-np-widget--corner .alts-np-row {
    padding: 4px 6px;
}
/* v1.5.21 — Corner widget is space-constrained; hide the grip icon
   there. The whole row is still draggable for reorder, but the
   visual handle is reserved for the rail where it has room to
   breathe. */
.alts-np-widget--corner .alts-np-row__grip {
    display: none;
}
/* v1.5.19 — 16:9 thumb (was 44×44 square). Matches the Queue panel
   row's recipe — the video frame is the content here, so we want it
   wide enough to actually look like the video. */
.alts-np-widget--corner .alts-np-row__thumb {
    width: 72px;
    height: 41px;  /* 72 * 9/16 ≈ 40.5 */
    border-radius: 4px;
}
.alts-np-widget--corner .alts-np-row__title,
.alts-np-widget--corner .alts-np-row__artist {
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.65);
}
/* v1.5.23 — Next-arrow icon ONLY exists on corner rows (renderRow
   gates by layout). Sits before the thumb to read as "this plays
   next" without needing a header label above. */
.alts-np-row__next-icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 14px;
    height: 100%;
    color: rgba(191, 255, 0, 0.7);
    font-size: 11px;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.65);
}

/* ============================================================================
   Layout: RAIL  (Mothership np-rail list)
   ----------------------------------------------------------------------------
   Stacked list, 3 items, sits inside the dark panel so no extra shadows.
   Slightly more vertical rhythm than corner layout. */

.alts-np-widget--rail {
    padding: 0;
}
.alts-np-widget--rail .alts-np-widget__rows {
    /* v1.6.12 — Tighter gap to fit more rows comfortably. The parent
       .alts-guide__np-rail already owns overflow-y:auto for the whole
       rail column, so we don't double-scroll here — the rail mini-
       player, meta, and rows scroll as one surface. */
    gap: 4px;
}
/* v1.6.12 — Tighter tile so more rows fit in the same vertical budget,
   matching "smaller tiles" ask. Padding + thumb dimensions
   shrink ~25%; type bumps down one notch but stays legible. */
.alts-np-widget--rail .alts-np-row {
    padding: 4px 8px;
    border-radius: 6px;
}
.alts-np-widget--rail .alts-np-row__thumb {
    width: 60px;
    height: 34px;  /* 60 * 9/16 ≈ 33.75 */
    border-radius: 3px;
}
.alts-np-widget--rail .alts-np-row__title {
    font-size: 12px;
}
.alts-np-widget--rail .alts-np-row__artist {
    font-size: 11px;
}

/* v1.6.12 — Inline zone subheaders. Mono-caps, AS-green icon, mirrors
   the NP-FULL queue panel zone-head recipe so the two surfaces read
   as the same UI at different densities. First subhead has no top
   border (it sits flush at the rail's content top). */
.alts-np-widget--rail .alts-np-widget__zone-head {
    /* v1.6.18 — Pin the row to a fixed min-height matching the
       chipwrap's outer height (~22px content + 1px breathing). Every
       direct child uses align-self:center + explicit line-height 1
       so their tiny boxes (the 10/9px icon + label) land on the
       same horizontal band as the taller chip pill.
       v1.6.32 — Border-top removed. The thin separator line at the
       boundary between zones (rendered as the top edge of the
       following zone-head) read as visual clutter against the
       already-clear zone subhead label + hint copy. Pure whitespace
       between zones now. */
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 24px;
    padding: 9px 2px 5px;
    pointer-events: none;
}
.alts-np-widget--rail .alts-np-widget__zone-head:first-child {
    padding-top: 3px;
}
.alts-np-widget--rail .alts-np-widget__zone-head > * {
    align-self: center;
    line-height: 1;
}
.alts-np-widget--rail .alts-np-widget__zone-head .alts-np-widget__blendlabel-chip-icon,
.alts-np-widget--rail .alts-np-widget__zone-head .alts-np-widget__blendlabel-chip-name,
.alts-np-widget--rail .alts-np-widget__zone-head .alts-np-widget__blendlabel-chip-remove {
    line-height: 1;
    vertical-align: middle;
}
.alts-np-widget--rail .alts-np-widget__zone-icon {
    
    font-size: 10px;
    color: rgba(255, 255, 255, 0.65);
    line-height: 1;
}
.alts-np-widget--rail .alts-np-widget__zone-label {
    font: 700 9px/1 var(--alts-mono, 'IBM Plex Mono', monospace);
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.65);
}
/* v1.6.20 — Hint line appended to the FLOW zone subhead when BLENDS
   immediately precedes it. Reads "picks up when the blend ends" in a
   small mixed-case sans so it's clearly a description, not a label. */
.alts-np-widget--rail .alts-np-widget__zone-hint {
    font: 400 10px/1 var(--alts-font-sans, Inter, system-ui, sans-serif);
    color: rgba(255, 255, 255, 0.45);
    letter-spacing: 0.01em;
    text-transform: none;
    margin-left: 6px;
    pointer-events: none;
}
/* v1.6.27 (Ship #2) — Inline rail divider between blend-track rows
   and related-fill rows. Renders "More like [Scope]" so the listener
   knows the scope's catalog ends and what follows is taste-engine
   picks. Same recipe as the NP FULL divider, compact for the rail. */
.alts-np-row__zone-divider {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 2px 6px;
    pointer-events: none;
}
/* v1.7.7 — Divider line color now resolves from the parent blend
   via the --divider-color CSS variable set inline by the rail
   renderer.  Falls back to AS-green at 0.18 opacity for the rare
   case where no parent blend slug is available (older items in the
   queue, etc).  Same fallback color also paints the label at the
   tinted-low-emphasis level matching the rest of the rail chrome. */
.alts-np-row__zone-divider-line {
    flex: 1 1 auto;
    height: 1px;
    background: color-mix(in srgb, var(--divider-color, #BFFF00) 32%, transparent);
}
/* v1.7.31 — Gradient divider variant.  When the parent blend belongs
   to a MIX recipe (Top of the Pops, Crowd Pleasers, Underground —
   the 48 server-defined predefined blends), the rail renderer stamps
   --divider-gradient with a buildBandGradient string AND sets
   [data-divider-gradient="1"] so this rule wins over the color-mix
   default.  Two line spans share the same gradient — they're visually
   continuous because they share start + end colors. Opacity 32% to
   match the single-color tint level so the visual weight stays
   consistent across solid + gradient dividers. */
.alts-np-row__zone-divider[data-divider-gradient="1"] .alts-np-row__zone-divider-line {
    background: var(--divider-gradient);
    opacity: 0.32;
}
.alts-np-row__zone-divider-label {
    flex: 0 0 auto;
    font: 700 9px/1 var(--alts-mono, 'IBM Plex Mono', monospace);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: color-mix(in srgb, var(--divider-color, rgba(255,255,255,0.55)) 75%, rgba(255,255,255,0.55));
}
/* v1.6.13 — Flavor pills bar slot inside the FLOW zone subhead. Pushed
   to the right with margin-left:auto so the pills sit at the far edge
   of the subhead, flush right, while the FLOW label + icon stay left. */
.alts-np-widget--rail .alts-np-widget__zone-flavor {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    pointer-events: auto;
}
.alts-np-widget--rail .alts-np-widget__zone-flavor[hidden] {
    display: none;
}
.alts-np-widget--rail .alts-np-widget__zone-flavor .alts-guide__np-flavor-pills {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

/* v1.6.15 — Layout positioning for blend chips inside the BLENDS
   zone subhead. The --compact class no longer downsizes (   landed on "NP rail artist blend pill is the right size" for
   the chips themselves); it only handles positioning so chips
   push flush right while the BLENDS label stays anchored left. */
.alts-np-widget__blendlabel-chipwrap--compact {
    margin-left: auto;
}
.alts-np-widget--rail .alts-np-widget__zone-head .alts-np-widget__blendlabel-chipwrap--compact {
    flex: 0 0 auto;
    /* v1.6.18 — Centered shadow (was 0 2px 8px which weighted the
       pill visually below center). Combined with align-self:center
       on zone-head children, the chip now reads on the same
       horizontal band as the BLENDS label + icon. */
    box-shadow: 0 0 6px rgba(0, 0, 0, 0.32);
    
    pointer-events: auto;
}
.alts-np-widget--rail .alts-np-widget__zone-head .alts-np-widget__blendlabel-chipwrap--compact + .alts-np-widget__blendlabel-chipwrap--compact {
    margin-left: 4px;
}

/* ============================================================================
   Reduced motion — respect user preference
   ---------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
    .alts-np-row,
    .alts-np-row__remove {
        transition: none;
    }
}

/* ============================================================================
 * v1.5.12.0 — BLEND ROW
 * ============================================================================
 * Single-blend: full-bleed term color row with pitchfork icon + name + ×.
 * Multi-blend: horizontal gradient row (one color per blend) + chip strip.
 *
 * Visual constants:
 *   AS green orb behind the pitchfork icon stays consistent across both
 *     variants — that's the "blend-ness" tell.
 *   No "BLEND" text label anywhere — the icon is the language.
 *   Same 56px row height as a regular track row, no exceptions.
 * ========================================================================== */

.alts-np-row--blend {
    /* v1.5.12.6 — VARIANT B (Apple-style liquid glass).
       The bar reads as a frosted glass capsule with the term color
       as a subtle inner tint. Heavy backdrop blur for the glass effect,
       strong inner top highlight + soft inner bottom shadow for the
       Apple-style "edge of polished material" tell. The colored tint
       lives in a low-opacity ::before layer (inset:0) so the bar's
       full footprint is colored without bleeding outside, but the
       opacity is dialed down to ~35% so the glass remains visibly
       see-through. The ::after adds a top-half spec highlight that
       catches like real glass.
       Earlier v1.5.12.4 had a bigger -10px blur bleed which made the
       color appear saturated; this version keeps tint contained. */
    height: 32px;
    border-radius: 999px;
    position: relative;
    margin-bottom: 6px;
    padding: 0 6px 0 4px;
    display: flex;
    align-items: center;
    gap: 6px;
    box-sizing: border-box;
    background: rgba(255, 255, 255, 0.06);
    backdrop-filter: blur(20px) saturate(180%);
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    /* The Apple inner-rim treatment: bright top edge, dim bottom edge,
       subtle side reflectivity, soft outer drop. */
    box-shadow:
        inset 0  1.5px 1px rgba(255, 255, 255, 0.50),
        inset 0 -1.5px 1px rgba(255, 255, 255, 0.08),
        inset 1px 0 1px rgba(255, 255, 255, 0.18),
        inset -1px 0 1px rgba(255, 255, 255, 0.18),
        0 1px 3px rgba(0, 0, 0, 0.40);
    isolation: isolate;
    overflow: hidden;
}

/* Color tint layer — sits BEHIND content, fills the bar exactly,
   ~35% opacity so the glass reads see-through. */
.alts-np-row--blend::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    pointer-events: none;
}
.alts-np-row--blend-single::before {
    background-color: var(--alts-blend-color, #444);
    opacity: 0.40;
}
.alts-np-row--blend-multi::before {
    background-image: var(--alts-blend-gradient, none);
    opacity: 0.40;
}

/* Top-half spec highlight — the gloss curve along the upper inside. */
.alts-np-row--blend::after {
    content: '';
    position: absolute;
    inset: 0 0 50% 0;
    border-radius: 999px 999px 0 0;
    background: linear-gradient(180deg,
        rgba(255, 255, 255, 0.22),
        rgba(255, 255, 255, 0.0));
    pointer-events: none;
}

/* All contents float above the tint + spec layers. */
.alts-np-row--blend > * {
    position: relative;
    z-index: 1;
}

/* Variant base classes still drive the ::before color but no longer
   need their own background-image. */
.alts-np-row--blend-single,
.alts-np-row--blend-multi {
    background-image: none;
}

/* Pitchfork icon orb — green circle, 28px round in multi mode. */
.alts-np-row__blend-icon {
    flex-shrink: 0;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--alts-color-accent, #BFFF00);
    color: var(--alts-color-bg, #0a0d0a);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* Legacy SVG sizing kept for any consumer still rendering the old
   custom pitchfork. v1.5.15.4 switched the icon to fa-merge
   (<i> element), sized below. */
.alts-np-row__blend-icon svg {
    display: block;
    width: 12px;
    height: 12px;
}
/* v1.5.15.5 — fa-merge inside the puck. Default FA font-size
   for compact rail/corner pucks (22px) reads too large; explicit
   font-size: 10px keeps the glyph properly contained. Queue-panel
   puck (36px) gets a larger override below. */
.alts-np-row__blend-icon i {
    font-size: 10px;
    line-height: 1;
}
.alts-np-row--queue-panel .alts-np-row__blend-icon i {
    font-size: 16px;
}

/* Single-blend: name + link area takes the rest of the row. */
.alts-np-row__blend-link {
    flex: 1 1 auto;
    min-width: 0;
    display: inline-flex;
    align-items: center;
    gap: 12px;
    text-decoration: none;
    color: #fff;
    overflow: hidden;
}

.alts-np-row__blend-name {
    font-size: 13px;
    font-weight: 500;
    line-height: 1;
    color: #fff;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    /* v1.5.12.1 — Defensive: kill any inherited uppercase from parent
       eyebrow/chrome containers. The corner widget mounts inside
       .as-eyebrow-cluster which has text-transform: uppercase from
       theme styles, and that cascades down to the blend name unless
       we override it here. Term names should always render in their
       proper case (e.g. "Pop", not "POP"). */
    text-transform: none;
    letter-spacing: normal;
}

/* Single-blend × button — black bg with subtle border. */
.alts-np-row--blend-single .alts-np-row__remove {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.45);
    border: 1px solid rgba(255, 255, 255, 0.2);
    color: rgba(255, 255, 255, 0.9);
    cursor: pointer;
    font-size: 12px;
    line-height: 1;
    padding: 0;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.18s ease, color 0.18s ease;
}
.alts-np-row--blend-single .alts-np-row__remove:hover {
    background: rgba(0, 0, 0, 0.7);
    color: #fff;
}

/* ============================================================================
 * Multi-blend chips
 * ========================================================================== */

.alts-np-row__blend-chips {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 6px;
    overflow: hidden;
}

.alts-np-row__blend-chip {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 2px 3px 2px 2px;
    background: rgba(0, 0, 0, 0.45);
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: 999px;
    flex-shrink: 0;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    height: 22px;
    box-sizing: border-box;
}

.alts-np-row__blend-chip-link {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    text-decoration: none;
    color: #fff;
    padding: 0 2px;
}

.alts-np-row__blend-chip-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    flex-shrink: 0;
    border: 1px solid rgba(255, 255, 255, 0.18);
}

.alts-np-row__blend-chip-name {
    font-size: 11px;
    font-weight: 500;
    color: #fff;
    line-height: 1;
    padding: 0 2px;
    /* v1.5.12.1 — see blend-name above; same uppercase override. */
    text-transform: none;
    letter-spacing: normal;
}

.alts-np-row__blend-chip-remove {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.18);
    border: 0;
    color: rgba(255, 255, 255, 0.95);
    cursor: pointer;
    /* v1.6.19 — fa-xmark inside; size the glyph explicitly + force
       flex centering on the icon so the X sits dead-center vertically
       in the 14px chip (the &times; character used its own font
       baseline and shifted up). */
    font-size: 8px;
    line-height: 1;
    padding: 0;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.18s ease;
}
.alts-np-row__blend-chip-remove > i {
    display: block;
    line-height: 1;
    font-size: 8px;
}
.alts-np-row__blend-chip-remove:hover {
    background: rgba(255, 0, 0, 0.55);
}

/* ============================================================================
 * v1.5.12.3 — Blend-match dot strip
 * ============================================================================
 * Tiny color circles at the right edge of each track row, one per matching
 * blend. Communicates "this track is in the queue because of THESE active
 * blends." Hover reveals the term names via the title attribute.
 *
 * Position: right edge of the row, vertically centered, just inside the
 * × button slot. Dots are 7px circles with a 1px dark ring so they stay
 * readable against any thumb tint that bleeds through.
 *
 * Empty state: when the item has no blendMatches, the .alts-np-row__blend-dots
 * span isn't rendered at all (zero footprint, no whitespace).
 * ========================================================================== */
.alts-np-row__blend-dots {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    flex-shrink: 0;
    padding: 0 4px;
    margin-left: auto;
    cursor: help;
}
.alts-np-row__blend-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);
    transition: opacity 0.18s ease, transform 0.18s ease;
}
.alts-np-row__blend-dots:hover .alts-np-row__blend-dot {
    transform: scale(1.18);
}
/* When a row's parent blend has been removed (e.g. user X'd the Pop blend
   but the Backstreet Boys track is still in the queue), the dot dims
   to half opacity as a visual receipt. The next render pass will drop
   the dot entirely; this is the in-between visual. */
.alts-np-row__blend-dot[data-stale="1"] {
    opacity: 0.4;
}

/* ============================================================================
 * BLEND ROW v1.5.15.2
 *  - Black puck + white icon (was lime, fought multi-color backdrops)
 *  - Kind prefix in each chip ("DIRECTOR · Hype Williams")
 *  - Grid-spaced chips so each sits over its gradient color zone
 *  - "Selected blends" label visible on queue-panel layout (hidden on
 *    rail / corner where space is tight)
 *  - Queue-panel layout: bigger, fully-rounded pill
 * ========================================================================== */

/* Black puck + white icon — universally readable over any color. */
.alts-np-row__blend-icon {
    background: #000;
    color: #fff;
}

/* "Selected blends" label — hidden by default; revealed on queue-panel. */
.alts-np-row__blend-label {
    display: none;
    font: 700 11px/1 var(--alts-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.92);
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.55);
    flex-shrink: 0;
    padding-left: 4px;
}

/* Chip grid — one column per blend so chips sit evenly across the row.
   Each chip is centered within its column so it lines up with the
   corresponding color zone in the gradient. */
.alts-np-row__blend-chips {
    flex: 1 1 auto;
    min-width: 0;
    display: grid;
    grid-template-columns: repeat(var(--blend-count, 1), 1fr);
    align-items: center;
    gap: 6px;
}
.alts-np-row__blend-chip {
    justify-self: center;
    min-width: 0;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    max-width: 100%;
}
.alts-np-row__blend-chip-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    text-decoration: none;
    color: #fff;
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.55);
}
.alts-np-row__blend-chip-meta {
    display: inline-flex;
    flex-direction: column;
    min-width: 0;
    line-height: 1.1;
}
.alts-np-row__blend-chip-kind {
    font: 700 9px/1 var(--alts-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.78);
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
}
/* On the rail / corner layouts, the kind prefix is hidden to keep
   chips compact — name only. The queue-panel rule below re-shows it. */
.alts-np-row--rail .alts-np-row__blend-chip-kind,
.alts-np-row--corner .alts-np-row__blend-chip-kind {
    display: none;
}

/* ============================================================================
 * QUEUE-PANEL LAYOUT (NP FULL)
 *  Bigger pill, "Selected blends" label visible, kind prefix shown on
 *  every chip, fully rounded.
 * ========================================================================== */
.alts-np-row--queue-panel.alts-np-row--blend {
    height: 64px;
    padding: 0 16px 0 10px;
    gap: 14px;
    border-radius: 999px;
    /* Slightly stronger glass for the bigger surface. */
    box-shadow:
        inset 0  2px 1.5px rgba(255, 255, 255, 0.55),
        inset 0 -2px 1.5px rgba(255, 255, 255, 0.10),
        inset 1.5px 0 1px rgba(255, 255, 255, 0.20),
        inset -1.5px 0 1px rgba(255, 255, 255, 0.20),
        0 2px 6px rgba(0, 0, 0, 0.45);
}
.alts-np-row--queue-panel .alts-np-row__blend-icon {
    width: 36px;
    height: 36px;
}
.alts-np-row--queue-panel .alts-np-row__blend-icon svg {
    width: 18px;
    height: 18px;
}
.alts-np-row--queue-panel .alts-np-row__blend-label {
    display: inline-flex;
    align-items: center;
    font-size: 12px;
    padding-left: 0;
}
.alts-np-row--queue-panel .alts-np-row__blend-chip-name {
    font-size: 14px;
}
.alts-np-row--queue-panel .alts-np-row__blend-chip-dot {
    width: 12px;
    height: 12px;
}

/* ============================================================================
 * BLEND ROW v1.5.15.3 — kind icons + queue-panel chip padding
 *
 * The kind icon (clapperboard for director, tags for genre, etc.) lives
 * inside every chip. On compact layouts (rail / corner) it's the
 * primary affordance — the kind text label is hidden, the icon stands
 * in for it. On queue-panel it's hidden (the text "DIRECTOR · ..."
 * label takes over because we have the room).
 * ========================================================================== */

.alts-np-row__blend-chip-kindicon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    font-size: 11px;
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.55);
}

/* Rail / corner: icon visible, text kind hidden, dot already small. */
.alts-np-row--rail .alts-np-row__blend-chip-kindicon,
.alts-np-row--corner .alts-np-row__blend-chip-kindicon {
    display: inline-flex;
}
/* Queue-panel: hide the inline icon (the text kind takes the role) and
   the dot stays as the color anchor. */
.alts-np-row--queue-panel .alts-np-row__blend-chip-kindicon {
    display: none;
}

.alts-np-row--queue-panel .alts-np-row__blend-chip {
    padding: 6px 12px;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.22);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    gap: 10px;
}
.alts-np-row--queue-panel .alts-np-row__blend-chip-link {
    gap: 10px;
}
.alts-np-row--queue-panel .alts-np-row__blend-chip-dot {
    width: 12px;
    height: 12px;
}
.alts-np-row--queue-panel .alts-np-row__blend-chip-kind {
    font-size: 10px;
}
.alts-np-row--queue-panel .alts-np-row__blend-chip-name {
    font-size: 14px;
    font-weight: 600;
}

/* ============================================================================
 * BLEND ROW v1.5.15.4 — kind icon owns the color, bigger queue-panel chips,
 *                     clear-all-blends X, whole-row source tints
 *
 * Dot was redundant once the kind icon adopted the blend color — drop the
 * dot rule visibility entirely (the class still ships in case external
 * consumers hung on it, but renderBlendRow no longer emits it).
 * ========================================================================== */

.alts-np-row__blend-chip-dot {
    display: none !important;
}

/* Make the kind icon visible on EVERY layout (the dot was the rail/corner
   anchor; now the icon does both jobs). */
.alts-np-row--rail .alts-np-row__blend-chip-kindicon,
.alts-np-row--corner .alts-np-row__blend-chip-kindicon,
.alts-np-row--queue-panel .alts-np-row__blend-chip-kindicon {
    display: inline-flex;
}

.alts-np-row--queue-panel .alts-np-row__blend-chip {
    height: 36px;
    padding: 0 16px;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.32);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.10);
    gap: 10px;
    align-items: center;
}
.alts-np-row--queue-panel .alts-np-row__blend-chip-link {
    gap: 8px;
    align-items: center;
}
.alts-np-row--queue-panel .alts-np-row__blend-chip-kindicon {
    font-size: 14px;
}

/* Clear-all-blends button — sits at the right edge of the blend row.
   Black puck mirroring the icon orb so the row reads symmetrical. */
.alts-np-row__blend-clear-all {
    flex-shrink: 0;
    margin-left: 6px;
    appearance: none;
    -webkit-appearance: none;
    border: 0;
    width: 22px;
    height: 22px;
    border-radius: 999px;
    background: #000;
    color: #fff;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s ease, color 0.15s ease;
    padding: 0;
}
.alts-np-row__blend-clear-all i { font-size: 10px; }
.alts-np-row__blend-clear-all:hover,
.alts-np-row__blend-clear-all:focus-visible {
    background: #ffb029;
    color: #000;
    outline: none;
}
.alts-np-row--queue-panel .alts-np-row__blend-clear-all {
    width: 32px;
    height: 32px;
    margin-left: 10px;
}
.alts-np-row--queue-panel .alts-np-row__blend-clear-all i { font-size: 13px; }

/* ============================================================================
 * WHOLE-ROW SOURCE TINTS (v1.5.15.4)
 *
 * Replaces the per-row blend-dot strip with a row-level color treatment.
 * Queue items get orange (#ffb029, matching the source pill canon).
 * Blend-tracks get whatever color the originating blend carries, exposed
 * via the --alts-blend-tint CSS variable set inline on the row.
 * ========================================================================== */

.alts-np-row--src-queue {
    background: rgba(255, 176, 41, 0.07) !important;
    border-color: rgba(255, 176, 41, 0.20) !important;
    box-shadow: inset 3px 0 0 #ffb029;
}
.alts-np-row--src-queue:hover {
    background: rgba(255, 176, 41, 0.12) !important;
    border-color: rgba(255, 176, 41, 0.32) !important;
}

.alts-np-row--src-blendtrack {
    background:
        linear-gradient(90deg,
            color-mix(in srgb, var(--alts-blend-tint, #888) 9%, transparent) 0%,
            color-mix(in srgb, var(--alts-blend-tint, #888) 5%, transparent) 100%) !important;
    border-color: color-mix(in srgb, var(--alts-blend-tint, #888) 22%, transparent) !important;
    box-shadow: inset 3px 0 0 var(--alts-blend-tint, #888);
}
.alts-np-row--src-blendtrack:hover {
    background:
        linear-gradient(90deg,
            color-mix(in srgb, var(--alts-blend-tint, #888) 14%, transparent) 0%,
            color-mix(in srgb, var(--alts-blend-tint, #888) 8%, transparent) 100%) !important;
    border-color: color-mix(in srgb, var(--alts-blend-tint, #888) 36%, transparent) !important;
}

.alts-np-row--recipe-track {
    position: relative;
}
.alts-np-row--recipe-track::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image: var(--alts-recipe-row-gradient, none);
    opacity: 0.16;
    border-radius: inherit;
    pointer-events: none;
    z-index: 0;
}
.alts-np-row--recipe-track:hover::before {
    opacity: 0.26;
}
/* Make sure row contents (thumb / text / chips / X button) stay above the
   gradient overlay. Without this, the pseudo paints over the thumb on
   browsers that don't auto-stack children of position:relative parents. */
.alts-np-row--recipe-track > * {
    position: relative;
    z-index: 1;
}

/* ── MIX LOADING SHIMMER (v1.7.57) ─────────────────────────────────────
   While a recipe/mix install materializes, html.alts-mix-loading is set
   (altsounds-player.js beginMixLoading). Fade + blur the blend/recipe
   LABEL + chip strip so the user never sees the flow→blend→recipe churn.
   Clears on the first song-changed (or a bounded hard cap) — the v1.7.57
   controller can't get re-armed, so it never sticks. */
html.alts-mix-loading .alts-np-widget__label,
html.alts-mix-loading .alts-np-widget__rows,
html.alts-mix-loading .alts-m-landscape-blends {
    pointer-events: none !important;
    filter: blur(2px);
    animation: alts-mix-loading-pulse 1.05s ease-in-out infinite;
}
@keyframes alts-mix-loading-pulse {
    0%, 100% { opacity: 0.18; }
    50%      { opacity: 0.42; }
}
@media (prefers-reduced-motion: reduce) {
    html.alts-mix-loading .alts-np-widget__label,
    html.alts-mix-loading .alts-np-widget__rows,
    html.alts-mix-loading .alts-m-landscape-blends {
        animation: none;
        opacity: 0.3;
    }
}

/* ---------------------------------------------------------------------------
   Corner NP widget — taunt pill (v1.7.86)
   The ORIGINAL single-gallery taunt design (glass pill, circular thumb,
   "Watch <Song> by <Artist>?" question, Now / Queue buttons), relocated into
   the corner widget directly under the up-next row. Ported from the perf
   plugin's `.as-chrome-cluster .as-taunt*` rules, re-scoped here so the same
   markup reads identically in the widget. Built by altsounds-np.js
   buildTauntSection(); buttons ride the NP taunt-play / taunt-queue delegation.
--------------------------------------------------------------------------- */
.alts-np-widget__taunt {
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid rgba(255, 255, 255, 0.10);
}
.alts-np-widget__taunt .as-taunt {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 5px 6px;
    background: rgba(10, 10, 10, 0.55);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 999px;
    color: rgba(255, 255, 255, 0.78);
    font-size: 12px;
    line-height: 1;
    transition: background 0.2s ease, border-color 0.2s ease;
}
.alts-np-widget__taunt .as-taunt:hover,
.alts-np-widget__taunt .as-taunt:focus-within {
    background: rgba(10, 10, 10, 0.85);
    border-color: rgba(191, 255, 0, 0.25);
}
.alts-np-widget__taunt .as-taunt-thumb {
    /* !important + max-* none beat the widget's generic `img { width:100% }`,
       which otherwise blows the thumb up to the container width (same reason
       the original .as-chrome-cluster rule used !important). */
    width: 28px !important;
    height: 28px !important;
    max-width: none !important;
    max-height: none !important;
    border-radius: 50%;
    object-fit: cover;
    flex: 0 0 auto;
    display: block;
    background: #000;
}
.alts-np-widget__taunt .as-taunt-text {
    flex: 1 1 auto;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-transform: none;   /* the widget forces uppercase on labels; not here */
}
.alts-np-widget__taunt .as-taunt-artist {
    color: #BFFF00;
    font-weight: 600;
}
.alts-np-widget__taunt .as-taunt-song {
    color: #fff;
    font-weight: 600;
    font-style: italic;
}
.alts-np-widget__taunt .as-taunt-actions {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    flex: 0 0 auto;
}
.alts-np-widget__taunt .as-taunt-btn {
    appearance: none;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.12);
    color: rgba(255, 255, 255, 0.82);
    font-size: 11px;
    font-weight: 600;
    font-family: inherit;
    letter-spacing: 0.04em;
    padding: 5px 9px;
    border-radius: 999px;
    cursor: pointer;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    text-decoration: none;
    text-transform: none;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease, transform 0.1s ease;
}
.alts-np-widget__taunt .as-taunt-btn i { font-size: 9px; }
.alts-np-widget__taunt .as-taunt-btn:hover,
.alts-np-widget__taunt .as-taunt-btn:focus {
    background: rgba(191, 255, 0, 0.12);
    border-color: rgba(191, 255, 0, 0.5);
    color: #BFFF00;
    outline: none;
}
.alts-np-widget__taunt .as-taunt-btn:active { transform: scale(0.96); }
.alts-np-widget__taunt .as-taunt-btn.is-queued {
    background: rgba(191, 255, 0, 0.18);
    border-color: rgba(191, 255, 0, 0.6);
    color: #BFFF00;
}
