/* ── Jukebox cabinet, page-specific (not used elsewhere) ──
   Built entirely in CSS to evoke the synthwave-arcade-cabinet
   aesthetic. No bitmap assets, gradients, shadows and borders
   do the work. */

.jb-wrap {
    max-width: 1200px;
    margin: 0 auto;
    padding-bottom: 2rem;
}

/* ── About section (below the cabinet) ──
   Prose + per-track links for SEO crawlers. Style is deliberately
   readable rather than flashy, this is content, not chrome. */
.jb-about {
    max-width: 720px;
    margin: 4rem auto 1.5rem;
    padding: 0 1rem;
    color: rgba(255, 255, 255, 0.78);
    font-family: 'Rajdhani', sans-serif;
    font-size: 1.05rem;
    line-height: 1.6;
}
.jb-about p { margin: 0 0 1rem; }
.jb-about strong { color: var(--ar-neon-cyan); font-weight: 500; }
.jb-about a {
    color: var(--ar-neon-cyan);
    text-decoration: none;
    border-bottom: 1px dotted rgba(0, 240, 255, 0.4);
    transition: color 0.18s, border-color 0.18s;
}
.jb-about a:hover {
    color: #fff;
    border-bottom-color: var(--ar-neon-cyan);
}
.jb-about-heading {
    font-family: 'Orbitron', sans-serif;
    font-size: clamp(1.3rem, 2.6vw, 1.7rem);
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ar-neon-pink);
    text-shadow: 0 0 12px rgba(255, 45, 123, 0.45);
    margin: 0 0 1rem;
}
.jb-about-lead {
    font-size: 1.15rem;
}
/* Inline CTA pill linking from the About prose to the full track
   catalog at /jukebox/tracks. Stands out a bit more than a plain
   text link so visitors notice the bridge to the browse page. */
.jb-about-catalog-link {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.15rem 0.6rem;
    border: 1px solid rgba(0, 240, 255, 0.45) !important;
    border-radius: 0.35rem;
    background: rgba(0, 240, 255, 0.06);
    font-weight: 600;
    white-space: nowrap;
}
.jb-about-catalog-link i { font-size: 0.85em; }
.jb-about-catalog-link:hover {
    background: rgba(0, 240, 255, 0.12);
    border-color: var(--ar-neon-cyan) !important;
}
.jb-about-fineprint {
    font-size: 0.9rem;
    color: rgba(255, 255, 255, 0.5);
    margin-top: 2rem;
    padding-top: 1rem;
    border-top: 1px solid rgba(255, 45, 123, 0.15);
}
@media (max-width: 540px) {
    .jb-about {
        font-size: 1rem;
        margin: 1.5rem auto 1rem;
    }
}

/* ── Cabinet shell ──
   Double-piping built from box-shadow rings:
     border:        2px neon-pink (innermost)
     ring 1 (4px):  black gap
     ring 2 (2px):  neon-pink outer piping
     ring 3 (2px):  dark line (final edge)
   Then ambient pink + violet glow + inner inset glow.

   The piping stack is hoisted into --jb-piping so .jb-cabinet AND
   .jb-cabinet.is-playing both share it, otherwise the pulse-glow
   regel below would overwrite the piping on every audio frame and
   make it visibly flicker. */
.jb-cabinet {
    --jb-piping:
        0 0 0 4px #06060f,
        0 0 0 6px var(--ar-neon-pink),
        0 0 0 8px rgba(0, 0, 0, 0.9);
    --pulse: 0;
    position: relative;
    margin: 1rem 0 2rem;
    padding: 1.25rem 1.25rem 1rem;
    background:
        radial-gradient(ellipse at 50% 0%, rgba(123, 47, 247, 0.18) 0%, transparent 60%),
        linear-gradient(180deg, #0a0420 0%, #06060f 70%, #0a0420 100%);
    border: 2px solid var(--ar-neon-pink);
    border-radius: 1.5rem;
    box-shadow:
        var(--jb-piping),
        0 0 32px rgba(255, 45, 123, 0.45),
        0 0 64px rgba(123, 47, 247, 0.25),
        inset 0 0 28px rgba(123, 47, 247, 0.18);
}

/* ── Header ──
   Three-column grid: speaker | centre block | speaker.
   On mobile (<=540px) the grills collapse via display:none and the
   centre block fills the row. */
.jb-head {
    display: grid;
    /* 1fr auto 1fr puts the title in its natural-width centre column,
       and lets the side columns share the remaining space equally.
       justify-self: center on the speakers then centres them within
       each side column → speakers sit halfway between the title and
       the cabinet edge instead of flush against the edge. */
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 1.25rem;
    padding: 0;
}
.jb-head-center {
    text-align: center;
    min-width: 0;
}
.jb-speaker-grill {
    justify-self: center;
}

/* Speaker grill, chamfered chrome-framed panel with a larger
   perforated dot pattern. Re-uses the .jb-btn design language so it
   reads as part of the same cabinet hardware family. Structure:

     .jb-speaker-grill              outer cyan colour layer (1px outline)
     .jb-speaker-grill::before      dark frame, inset 1px (reveals
                                    .jb-speaker-grill's colour as a
                                    chamfer-following outline)
     .jb-speaker-grill::after       the actual perforated dot grid,
                                    inset 4px, with chrome inner recess */
.jb-speaker-grill {
    position: relative;
    width: 182px;                   /* 30% wider than the 140px square */
    height: 140px;                  /* fixed, no stretch */
    align-self: center;             /* aligns to title baseline */
    background: rgba(0, 240, 255, 0.55);
    clip-path: polygon(
        12% 0%, 88% 0%,
        100% 12%, 100% 88%,
        88% 100%, 12% 100%,
        0% 88%, 0% 12%
    );
    filter: drop-shadow(0 0 6px rgba(0, 240, 255, 0.25));
}
.jb-speaker-grill::before {
    content: '';
    position: absolute;
    inset: 1px;
    clip-path: polygon(
        12% 0%, 88% 0%,
        100% 12%, 100% 88%,
        88% 100%, 12% 100%,
        0% 88%, 0% 12%
    );
    background: linear-gradient(180deg, #14062a 0%, #06060f 100%);
    z-index: 0;
}
.jb-speaker-grill::after {
    content: '';
    position: absolute;
    inset: 6px;
    clip-path: polygon(
        12% 0%, 88% 0%,
        100% 12%, 100% 88%,
        88% 100%, 12% 100%,
        0% 88%, 0% 12%
    );
    /* Only the dot grid, NO opaque base layer here. The dark backdrop
       is provided by ::before (which covers everything inset:1px and
       inward) and the .jb-speaker-pulse element sits between them, so
       the pulse can shine through the transparent gaps in the dot
       pattern. If we put an opaque base in ::after, it would block the
       pulse and nothing animates visibly, that was the bug. */
    background:
        radial-gradient(
            circle at center,
            #06060f 0 3px,
            rgba(0, 240, 255, 0.35) 3px 4px,
            transparent 4.5px
        );
    background-size: 14px 14px;
    background-position: center center;
    background-repeat: repeat;
    box-shadow:
        inset 0 0 14px rgba(0, 0, 0, 0.85),
        inset 0 1px 0 rgba(255, 255, 255, 0.06);
    z-index: 2;
}

/* Pulse ring, sits between the dark frame (::before) and the dot
   pattern (::after) so it appears to wash through the perforations
   as it expands. transform: scale() + opacity are both GPU-accelerated
   so no per-frame repaint. Kept deliberately dim, meant as a vague
   "something's happening in there" hint, not a loud visual. */
.jb-speaker-pulse {
    position: absolute;
    inset: 6px;
    pointer-events: none;
    z-index: 1;
    display: block;
    background: radial-gradient(circle at center,
        rgba(0, 240, 255, 0.33) 0%,
        rgba(0, 240, 255, 0.17) 35%,
        rgba(0, 240, 255, 0) 65%);
    transform: scale(0.3);
    opacity: 0;
    will-change: transform, opacity;
}

@keyframes jbSpeakerRipple {
    0%   { transform: scale(0.3); opacity: 0.50; }
    35%  { transform: scale(0.9); opacity: 0.31; }
    100% { transform: scale(1.7); opacity: 0;    }
}
.jb-cabinet.is-playing .jb-speaker-pulse {
    animation: jbSpeakerRipple 1.8s ease-out infinite;
}
@media (prefers-reduced-motion: reduce) {
    .jb-cabinet.is-playing .jb-speaker-pulse { animation: none; }
}

.jb-title {
    font-family: 'Orbitron', sans-serif;
    font-weight: 900;
    font-size: clamp(2rem, 6.5vw, 3.6rem);
    letter-spacing: 0.16em;
    line-height: 1;
    color: transparent;
    -webkit-text-stroke: 2px rgba(255, 45, 123, 0.9);
    text-shadow:
        0 0 18px rgba(255, 45, 123, 0.55),
        0 0 36px rgba(123, 47, 247, 0.4);
    margin: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.4em;
}

/* Wings, three short pink stripes on each side of "ARCYNIA" with a
   gradient mask that fades the outer end. Made via the element's
   background + two box-shadows so we get three lines from one node.
   Pure-CSS, no SVG. */
.jb-title-wing {
    display: inline-block;
    width: 0.9em;
    height: 0.08em;
    background: var(--ar-neon-pink);
    box-shadow:
        0 -0.22em 0 var(--ar-neon-pink),
        0  0.22em 0 var(--ar-neon-pink);
    filter: drop-shadow(0 0 6px rgba(255, 45, 123, 0.7));
    flex: 0 0 auto;
}
.jb-title-wing--left {
    -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 90%);
    mask-image:        linear-gradient(90deg, transparent 0%, #000 90%);
}
.jb-title-wing--right {
    -webkit-mask-image: linear-gradient(90deg, #000 10%, transparent 100%);
    mask-image:        linear-gradient(90deg, #000 10%, transparent 100%);
}
/* Hide the pink wings flanking ARCYNIA when there isn't enough room
   for them to read clearly. */
@media (max-width: 1100px) {
    .jb-title-wing { display: none; }
}
/* At 740px the speakers hide and the header collapses to a single
   centred column. The button-related 740 rules live further down,
   AFTER the .jb-btn definition (otherwise the later .jb-btn rule
   would override them at same specificity). */
@media (max-width: 740px) {
    .jb-speaker-grill { display: none; }
    .jb-head {
        grid-template-columns: 1fr;
        gap: 0;
    }
}
/* Sub-line of the cabinet wordmark: lives INSIDE the h1 so the
   visible heading reads "ARCYNIA JUKEBOX" for SEO and AI extractors
   (Google and crawlers heavily devalue visually-hidden h1 fragments).
   The h1 is the parent and provides Orbitron + the pink stroke / glow;
   we reset the stroke here so JUKEBOX renders as solid cyan instead. */
.jb-title-sub {
    display: block;
    font-weight: 600;
    font-size: clamp(1rem, 2.6vw, 1.5rem);
    letter-spacing: 0.4em;
    color: var(--ar-neon-cyan);
    -webkit-text-stroke: 0;
    text-shadow: 0 0 10px rgba(0, 240, 255, 0.6);
    margin: 0.4rem 0 0.55rem;
}
.jb-tagline {
    font-family: 'Rajdhani', sans-serif;
    font-style: italic;
    font-weight: 400;
    font-size: clamp(0.9rem, 1.9vw, 1.15rem);
    color: rgba(255, 130, 180, 0.95);
    letter-spacing: 0.04em;
    margin: 0;
}

/* ── Main grid (Now Playing | Track Select) ── */
.jb-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1.1rem;
    margin: 1rem 0 1.25rem;
}
@media (max-width: 860px) {
    .jb-grid { grid-template-columns: 1fr; }
}

/* Inner panels, recessed 3D feel via stacked inset shadows.
   Top inner shadow reads as "the panel is inset into the cabinet",
   bottom inner highlight is the soft rim where light catches the
   far edge of the recess, outer top hairline highlights the cabinet
   surface around the panel. */
.jb-panel {
    background:
        linear-gradient(180deg,
            rgba(6, 6, 15, 0.85) 0%,
            rgba(6, 6, 15, 0.7) 100%);
    border: 1px solid rgba(0, 0, 0, 0.85);
    border-radius: 0.85rem;
    padding: 1rem;
    box-shadow:
        inset 0 2px 0 rgba(0, 0, 0, 0.85),
        inset 0 -1px 0 rgba(255, 255, 255, 0.04),
        inset 0 0 24px rgba(0, 240, 255, 0.06),
        0 1px 0 rgba(255, 255, 255, 0.03);
}

/* Section heads styled as little screen-printed decals: a thin gradient
   pill with its own border + glow, sized to fit content rather than
   filling the panel width. Reusable by .jb-controls-head and
   .jb-viz-label later (they'll inherit the same look). */
.jb-panel-head {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.3rem 0.7rem;
    font-family: 'Orbitron', sans-serif;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.32em;
    text-transform: uppercase;
    color: rgba(255, 45, 123, 0.95);
    margin: 0 0 0.85rem;
    background:
        linear-gradient(180deg,
            rgba(255, 45, 123, 0.18) 0%,
            rgba(255, 45, 123, 0.05) 100%);
    border: 1px solid rgba(255, 45, 123, 0.45);
    border-radius: 0.3rem;
    box-shadow:
        0 0 8px rgba(255, 45, 123, 0.25),
        inset 0 1px 0 rgba(255, 255, 255, 0.08);
    text-shadow: 0 0 6px rgba(255, 45, 123, 0.4);
}
.jb-panel-head i { color: var(--ar-neon-pink); }

/* Cyan variant, used on TRACK SELECT for visual separation between
   the two panels (left = pink Now Playing, right = cyan Track Select). */
.jb-panel-head--cyan {
    color: rgba(0, 240, 255, 0.95);
    background:
        linear-gradient(180deg,
            rgba(0, 240, 255, 0.18) 0%,
            rgba(0, 240, 255, 0.05) 100%);
    border-color: rgba(0, 240, 255, 0.45);
    box-shadow:
        0 0 8px rgba(0, 240, 255, 0.25),
        inset 0 1px 0 rgba(255, 255, 255, 0.08);
    text-shadow: 0 0 6px rgba(0, 240, 255, 0.4);
}
.jb-panel-head--cyan i { color: var(--ar-neon-cyan); }

/* ── Now Playing panel ── */
.jb-now-row {
    display: grid;
    grid-template-columns: 160px 1fr;
    gap: 1rem;
    align-items: start;
}
@media (max-width: 540px) {
    .jb-now-row { grid-template-columns: 110px 1fr; gap: 0.75rem; }
}

.jb-cover {
    position: relative;
    aspect-ratio: 1 / 1;
    border-radius: 0.5rem;
    overflow: hidden;
    background:
        linear-gradient(180deg, #06060f 0%, #1a0a3a 50%, #06060f 100%);
    border: 1px solid rgba(255, 45, 123, 0.4);
    box-shadow: 0 0 16px rgba(255, 45, 123, 0.3);
    cursor: pointer;
}
.jb-cover img {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
}
/* CSS-generated fallback when no cover image is present:
   a stylised mini-horizon (palms-and-sun synthwave look). */
.jb-cover.is-fallback::before {
    content: '';
    position: absolute; inset: 0;
    background:
        radial-gradient(circle 38% 38% at 50% 64%,
            #ffd86b 0%, #ff8c42 28%, #ff2d7b 60%, transparent 80%),
        linear-gradient(180deg,
            #1a0a3a 0%,
            #3d1565 35%,
            #832566 55%,
            #d8417a 70%,
            #ff6b6b 76%,
            #14063a 100%);
}
.jb-cover.is-fallback::after {
    content: '';
    position: absolute;
    left: 0; right: 0;
    bottom: 0; top: 60%;
    background:
        repeating-linear-gradient(0deg,
            transparent 0 7px, rgba(255, 45, 123, 0.45) 7px 8px),
        repeating-linear-gradient(90deg,
            transparent 0 7px, rgba(255, 45, 123, 0.35) 7px 8px);
    transform: perspective(80px) rotateX(40deg);
    transform-origin: 50% 0%;
}

.jb-now-meta { min-width: 0; }
.jb-track-title {
    font-family: 'Rajdhani', sans-serif;
    font-weight: 600;
    font-size: clamp(1.4rem, 3.2vw, 2rem);
    line-height: 1.15;
    color: #fff;
    margin: 0 0 0.2rem;
    text-shadow: 0 0 10px rgba(255, 255, 255, 0.15);
    overflow: hidden;
    text-overflow: ellipsis;
}
.jb-track-artist {
    font-family: 'Rajdhani', sans-serif;
    font-weight: 500;
    font-size: clamp(1rem, 2vw, 1.2rem);
    color: var(--ar-neon-cyan);
    margin: 0 0 0.85rem;
    text-shadow: 0 0 8px rgba(0, 240, 255, 0.45);
}
.jb-badges,
.jb-share {
    /* Badges + share button must render at IDENTICAL pixel heights
       since they sit side-by-side. Browsers give <button> a
       different default font / line-height than <span>, so we
       reset both with explicit values. */
    box-sizing: border-box;
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.4rem 0.75rem;
    border: 1px solid rgba(255, 45, 123, 0.5);
    border-radius: 0.4rem;
    background: rgba(6, 6, 15, 0.5);
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.72rem;
    font-weight: 400;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    line-height: 1;
    color: rgba(255, 255, 255, 0.85);
}
.jb-badges i { color: var(--ar-neon-pink); }
.jb-badge-bpm { color: rgba(0, 240, 255, 0.85); }

/* Share button, sits inline with the badges row. Reveals
   "COPIED" briefly after a successful clipboard write. */
.jb-now-pillrow {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}
/* Share inherits the geometry from .jb-badges + .jb-share above;
   only the cyan accent and interaction states live here. */
.jb-share {
    border-color: rgba(0, 240, 255, 0.5);
    color: var(--ar-neon-cyan);
    cursor: pointer;
    transition: background 0.18s, border-color 0.18s, color 0.18s, box-shadow 0.18s;
}
.jb-share:hover {
    border-color: var(--ar-neon-cyan);
    background: rgba(0, 240, 255, 0.08);
    box-shadow: 0 0 10px rgba(0, 240, 255, 0.4);
}
.jb-share.is-copied {
    border-color: var(--ar-neon-pink);
    color: var(--ar-neon-pink);
    box-shadow: 0 0 10px rgba(255, 45, 123, 0.45);
}

/* Progress + time row */
.jb-progress {
    margin-top: 1rem;
}
.jb-progress-bar {
    position: relative;
    height: 6px;
    background:
        linear-gradient(180deg, rgba(0, 0, 0, 0.6), rgba(0, 240, 255, 0.12));
    border-radius: 3px;
    cursor: pointer;
    overflow: hidden;
    box-shadow:
        inset 0 1px 2px rgba(0, 0, 0, 0.85),
        inset 0 -1px 0 rgba(255, 255, 255, 0.04);
}
.jb-progress-fill {
    position: absolute;
    inset: 0 auto 0 0;
    width: 0%;
    background: linear-gradient(90deg, var(--ar-neon-cyan), var(--ar-neon-pink));
    box-shadow: 0 0 10px rgba(255, 45, 123, 0.65);
    border-radius: 3px;
    transition: width 0.1s linear;
}
.jb-progress-handle {
    position: absolute;
    top: 50%;
    left: 0%;
    transform: translate(-50%, -50%);
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 0 8px rgba(255, 45, 123, 0.9);
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.2s;
}
.jb-progress-bar:hover .jb-progress-handle { opacity: 1; }
.jb-times {
    display: flex;
    justify-content: space-between;
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.78rem;
    color: rgba(255, 255, 255, 0.7);
    margin-top: 0.45rem;
    letter-spacing: 0.04em;
}

/* Mini waveform area (canvas, populated in phase 5; placeholder for now) */
.jb-waveform {
    margin-top: 0.65rem;
    height: 38px;
    border-radius: 0.3rem;
    background:
        linear-gradient(90deg,
            rgba(0, 240, 255, 0.06) 0%,
            rgba(123, 47, 247, 0.08) 50%,
            rgba(0, 240, 255, 0.06) 100%);
    position: relative;
    overflow: hidden;
    /* Inset frame so the waveform reads as a tiny screen rather than
       a flat strip, matches the rest of the panel's recessed feel. */
    box-shadow:
        inset 0 0 12px rgba(0, 0, 0, 0.6),
        inset 0 0 0 1px rgba(0, 240, 255, 0.15);
}
.jb-waveform::before {
    content: '';
    position: absolute;
    left: 0; right: 0;
    top: 50%;
    height: 1px;
    background: rgba(0, 240, 255, 0.3);
}
.jb-waveform-canvas {
    position: relative;
    z-index: 1;
    display: block;
    width: 100%;
    height: 100%;
}

/* Drop the mini waveform AND the NOW PLAYING decal on tighter
   viewports, both add height we don't have room for, and the
   cover + title already make the panel's intent obvious. JS keeps
   animating the (hidden) waveform canvas; the per-frame cost is
   one fillRect-and-bar pass, well below 0.1ms, not worth wiring
   a hidden-check into the JS just for this. */
@media (max-width: 860px) {
    .jb-waveform { display: none; }
    .jb-now > .jb-panel-head { display: none; }
}

/* ── Track Select panel ── */
.jb-tracks-scroll {
    max-height: 275px;
    overflow-y: auto;
    margin: 0;
    padding: 0;
    list-style: none;
    scrollbar-width: thin;
    scrollbar-color: rgba(255, 45, 123, 0.6) transparent;
}
.jb-tracks-scroll::-webkit-scrollbar { width: 6px; }
.jb-tracks-scroll::-webkit-scrollbar-track { background: transparent; }
.jb-tracks-scroll::-webkit-scrollbar-thumb {
    background: rgba(255, 45, 123, 0.5);
    border-radius: 3px;
}
.jb-track-row {
    /* Now an <a> for SEO crawlers, reset link defaults to match the
       previous <li> styling. */
    display: grid;
    grid-template-columns: 2em 1fr auto auto;
    gap: 0.65rem;
    align-items: center;
    padding: 0.55rem 0.65rem;
    border: 1px solid transparent;
    border-radius: 0.35rem;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
    font-family: 'Rajdhani', sans-serif;
    color: inherit;
    text-decoration: none;
}
.jb-track-row:hover {
    background: rgba(255, 45, 123, 0.06);
    border-color: rgba(255, 45, 123, 0.25);
}
.jb-track-row.is-active {
    background: linear-gradient(90deg,
        rgba(255, 45, 123, 0.18),
        rgba(255, 45, 123, 0.04));
    border-color: rgba(255, 45, 123, 0.6);
    box-shadow: 0 0 14px rgba(255, 45, 123, 0.25);
}
.jb-track-num {
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.85rem;
    color: rgba(255, 255, 255, 0.55);
    text-align: right;
}
.jb-track-row.is-active .jb-track-num {
    color: var(--ar-neon-pink);
}
.jb-track-row.is-active .jb-track-num::before {
    content: '\F4F4'; /* bi-play-fill */
    font-family: 'bootstrap-icons';
    margin-right: 0.25em;
}
.jb-track-row.is-active .jb-track-num-text { display: none; }
.jb-track-cell-title {
    font-weight: 500;
    font-size: 1rem;
    color: #fff;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.jb-track-cell-artist {
    font-size: 0.9rem;
    color: rgba(0, 240, 255, 0.85);
    white-space: nowrap;
}
.jb-track-cell-time {
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.85rem;
    color: rgba(255, 255, 255, 0.65);
}

.jb-tracks-empty {
    text-align: center;
    padding: 2rem 1rem;
    color: rgba(255, 255, 255, 0.5);
    font-family: 'Rajdhani', sans-serif;
    font-size: 0.95rem;
}

/* ── Control panel ──
   Single-row grid: power | btns | vol. The previous "CONTROL PANEL"
   decal head row was removed to keep the cabinet shorter vertically.
   Mobile (<=740px) restacks to a single column. */
.jb-controls {
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-areas: "power btns vol";
    gap: 0 1rem;
    align-items: center;
    padding: 0.85rem 1rem;
    background:
        linear-gradient(180deg,
            rgba(6, 6, 15, 0.85) 0%,
            rgba(6, 6, 15, 0.7) 100%);
    border: 1px solid rgba(0, 0, 0, 0.85);
    border-radius: 0.85rem;
    box-shadow:
        inset 0 2px 0 rgba(0, 0, 0, 0.85),
        inset 0 -1px 0 rgba(255, 255, 255, 0.04),
        inset 0 0 24px rgba(123, 47, 247, 0.10),
        0 1px 0 rgba(255, 255, 255, 0.03);
}
.jb-power { grid-area: power; }
.jb-btns  { grid-area: btns; }
.jb-vol   { grid-area: vol; }

/* POWER block, recessed chrome panel that holds:
     1. .jb-power-led (pulsing green LED)
     2. .jb-power-label "POWER"
     3. .jb-power-glyph (the big "A" rendered in chrome)
     4. .jb-power-sub "ARCYNIA / AUDIO SYS" brand stamp */
.jb-power {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.25rem;
    min-width: 72px;
    padding: 0.5rem 0.7rem;
    /* Same silver-slate palette as the volume knob: a top-left highlight
       lifts the surface, then a vertical brushed-metal gradient. The
       result is a polished plate that visually pairs with the knob. */
    background:
        radial-gradient(ellipse at 30% 0%,
            rgba(255, 255, 255, 0.22) 0%,
            rgba(255, 255, 255, 0) 60%),
        linear-gradient(180deg,
            #6a6a82 0%,
            #4a4a62 45%,
            #2a2a40 100%);
    border: 1px solid rgba(0, 0, 0, 0.85);
    border-radius: 0.4rem;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.18),
        inset 0 -2px 4px rgba(0, 0, 0, 0.6),
        0 1px 0 rgba(0, 0, 0, 0.6);
}
.jb-power-led {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    /* Bright green LED with a subtle bright spot at top-left */
    background: radial-gradient(circle at 30% 30%,
        #6cff6c 0%, #00b300 60%, #003300 100%);
    box-shadow:
        0 0 6px rgba(108, 255, 108, 0.85),
        0 0 12px rgba(108, 255, 108, 0.45),
        inset 0 -1px 1px rgba(0, 0, 0, 0.5);
    animation: jbPowerPulse 2.4s ease-in-out infinite;
}
@keyframes jbPowerPulse {
    0%, 100% {
        box-shadow:
            0 0 6px rgba(108, 255, 108, 0.85),
            0 0 12px rgba(108, 255, 108, 0.45),
            inset 0 -1px 1px rgba(0, 0, 0, 0.5);
    }
    50% {
        box-shadow:
            0 0 4px rgba(108, 255, 108, 0.55),
            0 0 8px  rgba(108, 255, 108, 0.25),
            inset 0 -1px 1px rgba(0, 0, 0, 0.5);
    }
}
@media (prefers-reduced-motion: reduce) {
    .jb-power-led { animation: none; }
}
.jb-power-label {
    font-family: 'Orbitron', sans-serif;
    font-size: 0.6rem;
    letter-spacing: 0.32em;
    color: rgba(0, 240, 255, 0.7);
    margin: 0;
}
/* Chrome "A", gradient-clipped text + drop-shadow gives it the look
   of a polished metal monogram instead of plain pink Orbitron. */
.jb-power-glyph {
    font-family: 'Orbitron', sans-serif;
    font-weight: 900;
    font-size: 1.4rem;
    line-height: 1;
    background: linear-gradient(180deg,
        #fff 0%,
        var(--ar-neon-pink) 50%,
        var(--ar-neon-purple) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
    filter: drop-shadow(0 0 6px rgba(255, 45, 123, 0.6));
}
.jb-power-sub {
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.55rem;
    letter-spacing: 0.18em;
    color: rgba(255, 255, 255, 0.55);
    text-align: center;
    line-height: 1.2;
    margin: 0;
}

/* Responsive control-panel collapses. Both media queries live AFTER
   the .jb-power definition above so the display:none actually sticks
   (later rule wins at same specificity). 740px-rule has to come after
   the 860px-rule so its grid template wins between 0–740px. */
@media (max-width: 860px) {
    .jb-power { display: none; }
    .jb-controls {
        grid-template-columns: 1fr auto;
        grid-template-areas: "btns vol";
    }
}
@media (max-width: 740px) {
    .jb-controls {
        grid-template-columns: 1fr;
        grid-template-areas:
            "btns"
            "vol";
        gap: 0.85rem;
        text-align: center;
    }
}

/* Button cluster */
.jb-btns {
    display: flex;
    justify-content: center;
    gap: 0.7rem;
    flex-wrap: wrap;
}

/* Chamfered "armoured plate" buttons, four stacked chamfered layers
   modeled on the synthwave-arcade-cabinet reference image. Structure:

     .jb-btn        outer colour ring, its background is the outer
                    outline colour, visible as a 1px stroke around the
                    full polygon (including 45° edges, which is the
                    whole point of this trick: clip-path defines the
                    polygon, and a solid background plus a polygon-
                    clipped inset cover gives a uniform outline that
                    follows the chamfer, whereas inset box-shadow
                    can't because it's drawn at the rectangular box
                    edge and then clipped to the polygon).
     .jb-btn::before dark metal frame, inset 1px with matching polygon
                    clip → reveals .jb-btn's colour as a 1px outline
                    ring all the way round, chamfer included.
     .jb-btn::after inner outline colour, inset 5px with matching
                    polygon clip → the inner ring shown around the
                    glass face, also following the chamfer.
     .jb-btn-face   actual glass face, inset 6px (= 1px inside ::after)
                    with its own polygon clip + glass-tint background
                    + inset glow. Hosts the icon + label.

   The icon and label live INSIDE .jb-btn-face so they stay above the
   layered backgrounds without needing extra z-index plumbing. */
.jb-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 88px;
    min-height: 68px;
    padding: 0;
    background: rgba(255, 45, 123, 0.7);
    border: 0;
    border-radius: 0;
    clip-path: polygon(
        14% 0%, 86% 0%,
        100% 14%, 100% 86%,
        86% 100%, 14% 100%,
        0% 86%, 0% 14%
    );
    /* Outer halo via drop-shadow (follows clip-path; box-shadow doesn't). */
    filter:
        drop-shadow(0 0 4px rgba(255, 45, 123, 0.45))
        drop-shadow(0 0 12px rgba(255, 45, 123, 0.22));
    color: var(--ar-neon-pink);
    cursor: pointer;
    transition: transform 0.08s, filter 0.18s, color 0.18s, background 0.18s;
}

/* Dark metal frame, covers .jb-btn except for a 1px outline ring. */
.jb-btn::before {
    content: '';
    position: absolute;
    inset: 1px;
    clip-path: polygon(
        14% 0%, 86% 0%,
        100% 14%, 100% 86%,
        86% 100%, 14% 100%,
        0% 86%, 0% 14%
    );
    background: linear-gradient(180deg,
        #2a1545 0%,
        #14062a 55%,
        #06060f 100%);
    z-index: 0;
}

/* Inner outline colour, same trick as the outer ring, one layer in.
   ::after sits inset 5px and .jb-btn-face will sit inset 1px inside
   THIS, so we'll see a 1px outline ring around the glass face that
   also follows the chamfered polygon. */
.jb-btn::after {
    content: '';
    position: absolute;
    inset: 5px;
    clip-path: polygon(
        14% 0%, 86% 0%,
        100% 14%, 100% 86%,
        86% 100%, 14% 100%,
        0% 86%, 0% 14%
    );
    background: rgba(255, 45, 123, 0.55);
    z-index: 1;
    transition: background 0.18s;
}

/* Inner glass face, chamfered, glass-tint background, inset inner
   glow. Sits 1px inside ::after so the colour bleeds through as the
   inner outline ring. Layout (flex column) for icon + label happens
   here, not on .jb-btn. */
.jb-btn-face {
    position: absolute;
    inset: 6px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.15rem;
    padding: 0.4rem 0.5rem;
    clip-path: polygon(
        14% 0%, 86% 0%,
        100% 14%, 100% 86%,
        86% 100%, 14% 100%,
        0% 86%, 0% 14%
    );
    background:
        radial-gradient(ellipse at 50% 0%,
            rgba(255, 45, 123, 0.28) 0%,
            transparent 75%),
        linear-gradient(180deg,
            #2a0a18 0%,
            #14062a 50%,
            #06060f 100%);
    box-shadow: inset 0 0 14px rgba(255, 45, 123, 0.45);
    font-family: 'Orbitron', sans-serif;
    font-weight: 700;
    font-size: 0.62rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    text-shadow: 0 0 6px rgba(255, 45, 123, 0.6);
    pointer-events: none;        /* clicks pass through to .jb-btn */
    z-index: 2;
    transition: background 0.18s, box-shadow 0.18s;
}
.jb-btn-face i {
    font-size: 1.3rem;
    text-shadow: 0 0 8px rgba(255, 45, 123, 0.7);
}

/* Hover, both outline rings brighten + halo grows + glass tints. */
.jb-btn:hover {
    background: var(--ar-neon-pink);
    color: #fff;
    filter:
        drop-shadow(0 0 6px rgba(255, 45, 123, 0.7))
        drop-shadow(0 0 16px rgba(255, 45, 123, 0.35));
}
.jb-btn:hover::after { background: rgba(255, 45, 123, 0.8); }
.jb-btn:hover .jb-btn-face {
    background:
        radial-gradient(ellipse at 50% 0%,
            rgba(255, 45, 123, 0.42) 0%,
            transparent 75%),
        linear-gradient(180deg,
            #3a0e22 0%,
            #1a0830 50%,
            #06060f 100%);
    box-shadow: inset 0 0 18px rgba(255, 45, 123, 0.6);
}
.jb-btn:hover .jb-btn-face i {
    text-shadow: 0 0 12px rgba(255, 45, 123, 0.95);
}

/* Pressed, push down 2px and dim the halo. */
.jb-btn:active {
    transform: translateY(2px);
    filter:
        drop-shadow(0 0 3px rgba(255, 45, 123, 0.35))
        drop-shadow(0 0 8px rgba(255, 45, 123, 0.18));
}

/* PLAY, primary action, larger + cyan tonality */
.jb-btn-play {
    min-width: 90px;
    min-height: 90px;
    background: rgba(0, 240, 255, 0.75);
    color: var(--ar-neon-cyan);
    filter:
        drop-shadow(0 0 5px rgba(0, 240, 255, 0.5))
        drop-shadow(0 0 14px rgba(0, 240, 255, 0.28));
}
.jb-btn-play::before {
    background: linear-gradient(180deg,
        #142a3a 0%,
        #0a1e2c 55%,
        #06060f 100%);
}
.jb-btn-play::after { background: rgba(0, 240, 255, 0.6); }
.jb-btn-play .jb-btn-face {
    background:
        radial-gradient(ellipse at 50% 0%,
            rgba(0, 240, 255, 0.32) 0%,
            transparent 75%),
        linear-gradient(180deg,
            #0a1e2c 0%,
            #061826 50%,
            #06060f 100%);
    box-shadow: inset 0 0 16px rgba(0, 240, 255, 0.5);
    text-shadow: 0 0 6px rgba(0, 240, 255, 0.75);
}
.jb-btn-play .jb-btn-face i {
    color: var(--ar-neon-cyan);
    text-shadow: 0 0 10px rgba(0, 240, 255, 0.85);
    font-size: 1.7rem;
}
.jb-btn-play:hover {
    background: var(--ar-neon-cyan);
    filter:
        drop-shadow(0 0 7px rgba(0, 240, 255, 0.75))
        drop-shadow(0 0 18px rgba(0, 240, 255, 0.4));
}
.jb-btn-play:hover::after { background: rgba(0, 240, 255, 0.85); }
.jb-btn-play:hover .jb-btn-face {
    background:
        radial-gradient(ellipse at 50% 0%,
            rgba(0, 240, 255, 0.46) 0%,
            transparent 75%),
        linear-gradient(180deg,
            #0e2a3a 0%,
            #0a2030 50%,
            #06060f 100%);
    box-shadow: inset 0 0 20px rgba(0, 240, 255, 0.65);
}
.jb-btn-play:active {
    filter:
        drop-shadow(0 0 4px rgba(0, 240, 255, 0.4))
        drop-shadow(0 0 10px rgba(0, 240, 255, 0.22));
}

/* SHUFFLE / LOOP engaged, both outlines + glass switch to cyan */
.jb-btn.is-active {
    background: rgba(0, 240, 255, 0.85);
    color: var(--ar-neon-cyan);
    filter:
        drop-shadow(0 0 5px rgba(0, 240, 255, 0.6))
        drop-shadow(0 0 14px rgba(0, 240, 255, 0.3));
}
.jb-btn.is-active::after { background: rgba(0, 240, 255, 0.7); }
.jb-btn.is-active .jb-btn-face {
    background:
        radial-gradient(ellipse at 50% 0%,
            rgba(0, 240, 255, 0.32) 0%,
            transparent 75%),
        linear-gradient(180deg,
            #0a1e2c 0%,
            #061826 50%,
            #06060f 100%);
    box-shadow: inset 0 0 16px rgba(0, 240, 255, 0.55);
    text-shadow: 0 0 6px rgba(0, 240, 255, 0.75);
}
.jb-btn.is-active i {
    color: var(--ar-neon-cyan);
    text-shadow: 0 0 10px rgba(0, 240, 255, 0.85);
}
/* LOOP mode "one", small superscript indicator handled via
   data-mode in JS; the icon switches to bi-repeat-1 directly. */

/* Under 740px the button labels hide (only icons remain) and ALL
   buttons, including PLAY, shrink to a 56×56 square. Placed AFTER
   the .jb-btn / .jb-btn-play defaults above so the width reduction
   actually wins at same specificity. */
@media (max-width: 740px) {
    .jb-btn-face > span { display: none; }
    .jb-btn,
    .jb-btn-play {
        min-width: 56px;
        min-height: 56px;
    }
}

/* Volume */
.jb-vol {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: 110px;
}
.jb-vol-label {
    font-family: 'Orbitron', sans-serif;
    font-size: 0.62rem;
    letter-spacing: 0.32em;
    color: rgba(0, 240, 255, 0.75);
    margin: 0 0 0.5rem;
}
.jb-vol-row {
    display: flex;
    align-items: center;
    gap: 0.55rem;
}

/* Circular knob, outer is the click/drag/keyboard target,
   inner .jb-knob-dial rotates from -135° (mute) to +135° (max).
   The tick is a small white dot at 12 o'clock that "points" to
   the value as the dial rotates underneath it. */
/* Knob, outer ring is brushed metallic via conic-gradient (six
   alternating light/dark wedges) blended with a radial highlight.
   Inset top-light + bottom-shadow + drop-shadow give it physical
   depth. The pink halo glow sits OUTSIDE the metal so it reads as
   a backlight rather than a metal tint. */
.jb-knob {
    position: relative;
    width: 64px;
    height: 64px;
    border-radius: 50%;
    background:
        radial-gradient(circle at 30% 25%,
            rgba(255, 255, 255, 0.32) 0%,
            rgba(255, 255, 255, 0) 50%),
        conic-gradient(from 0deg,
            #5a5a72 0deg,   #2a2a40 60deg,
            #5a5a72 120deg, #2a2a40 180deg,
            #5a5a72 240deg, #2a2a40 300deg,
            #5a5a72 360deg);
    border: 1px solid rgba(0, 0, 0, 0.85);
    box-shadow:
        inset 0 2px 4px rgba(255, 255, 255, 0.18),
        inset 0 -3px 6px rgba(0, 0, 0, 0.7),
        0 3px 6px rgba(0, 0, 0, 0.7),
        0 0 12px rgba(255, 45, 123, 0.4);
    cursor: ns-resize;
    outline: none;
    user-select: none;
    -webkit-user-select: none;
    touch-action: none;
    transition: box-shadow 0.18s;
}
.jb-knob:hover,
.jb-knob:focus-visible {
    box-shadow:
        inset 0 2px 4px rgba(255, 255, 255, 0.22),
        inset 0 -3px 6px rgba(0, 0, 0, 0.7),
        0 3px 6px rgba(0, 0, 0, 0.7),
        0 0 18px rgba(255, 45, 123, 0.65);
}
.jb-knob.is-dragging {
    box-shadow:
        inset 0 2px 4px rgba(255, 255, 255, 0.25),
        inset 0 -3px 6px rgba(0, 0, 0, 0.7),
        0 3px 6px rgba(0, 0, 0, 0.7),
        0 0 22px rgba(0, 240, 255, 0.7);
}

/* Inner face, recessed dark disc that the value text + tick ride on. */
.jb-knob-face {
    position: absolute;
    inset: 8px;
    border-radius: 50%;
    background: radial-gradient(circle at 30% 30%, #1a0a3a 0%, #06060f 75%);
    box-shadow:
        inset 0 2px 6px rgba(0, 0, 0, 0.85),
        inset 0 -1px 0 rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(0, 0, 0, 0.95);
    display: flex;
    align-items: center;
    justify-content: center;
}
.jb-knob-dial {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    transform: rotate(-135deg);
    transition: transform 0.05s linear;
}
.jb-knob-tick {
    position: absolute;
    top: 3px;
    left: 50%;
    transform: translateX(-50%);
    width: 3px;
    height: 10px;
    border-radius: 2px;
    /* Tip glows white, fades into the pink shaft so it looks like a
       lit LED indicator on the knob's edge. */
    background: linear-gradient(180deg, #fff 0%, var(--ar-neon-pink) 60%);
    box-shadow:
        0 0 6px rgba(255, 45, 123, 0.95),
        0 0 12px rgba(255, 45, 123, 0.6);
}
.jb-knob-value {
    position: relative;
    z-index: 1;
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.78rem;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.85);
    text-shadow: 0 0 6px rgba(255, 45, 123, 0.5);
}

/* LED column to the right of the knob. The meter sits in a recessed
   "tunnel" (dark gradient + inset shadow) so the LEDs read as if
   they're embedded in the cabinet rather than floating on the
   surface. Bottom 3 cyan, middle 3 pink, top 2 hot orange, tone
   shift signals louder volume. */
.jb-vol-meter {
    display: flex;
    flex-direction: column-reverse;
    gap: 2px;
    height: 64px;
    padding: 4px 3px;
    align-items: stretch;
    justify-content: space-between;
    background: linear-gradient(180deg, #06060f 0%, #14062a 100%);
    border: 1px solid rgba(0, 0, 0, 0.85);
    border-radius: 0.3rem;
    box-shadow:
        inset 0 2px 4px rgba(0, 0, 0, 0.85),
        inset 0 -1px 0 rgba(255, 255, 255, 0.05);
}
.jb-vol-led {
    display: block;
    width: 12px;
    height: 6px;
    border-radius: 2px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.07);
    transition: background 0.12s, box-shadow 0.18s, border-color 0.18s;
}
/* Lit-state inner highlight (rgba(255, 255, 255, 0.3) inset) gives each
   LED a small bulb-glow on top so it reads as a lit lamp not a flat
   block. */
.jb-vol-led:nth-child(-n+3).is-lit {
    background: var(--ar-neon-cyan);
    border-color: rgba(0, 240, 255, 0.7);
    box-shadow:
        0 0 6px rgba(0, 240, 255, 0.85),
        inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
.jb-vol-led:nth-child(n+4):nth-child(-n+6).is-lit {
    background: var(--ar-neon-pink);
    border-color: rgba(255, 45, 123, 0.7);
    box-shadow:
        0 0 6px rgba(255, 45, 123, 0.85),
        inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
.jb-vol-led:nth-child(n+7).is-lit {
    background: #ff6b35;
    border-color: rgba(255, 107, 53, 0.7);
    box-shadow:
        0 0 7px rgba(255, 107, 53, 0.95),
        inset 0 1px 0 rgba(255, 255, 255, 0.3);
}

/* ── Arcynia-styled native range slider ─────────────────────────────
   Hidden by default (desktop uses the round knob). At <=540px the
   mobile media query flips display to block. JS sets --vol on the
   element each time the volume changes so the webkit track's
   calc-based fill gradient updates live. Placed here, BEFORE the
   mobile media query, so the 540 rule's display: block actually
   wins (same specificity, later rule wins in cascade). */
.jb-vol-range {
    display: none;
    -webkit-appearance: none;
    appearance: none;
    width: 150px;
    height: 26px;
    background: transparent;
    cursor: pointer;
    outline: none;
    margin: 0;
    padding: 0;
    --vol: 80;
}
/* Webkit / Blink track */
.jb-vol-range::-webkit-slider-runnable-track {
    height: 8px;
    border-radius: 4px;
    background: linear-gradient(90deg,
        var(--ar-neon-cyan)        0%,
        var(--ar-neon-pink)        calc(var(--vol) * 1%),
        rgba(0, 0, 0, 0.7)         calc(var(--vol) * 1%),
        rgba(20, 6, 42, 0.85)      100%);
    border: 1px solid rgba(0, 0, 0, 0.9);
    box-shadow:
        inset 0 1px 2px rgba(0, 0, 0, 0.85),
        0 0 6px rgba(0, 240, 255, 0.25);
}
.jb-vol-range::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 18px;
    height: 18px;
    margin-top: -6px;                /* centre on 8 px track + 1 px border */
    border-radius: 50%;
    background: radial-gradient(circle at 30% 30%,
        #fff 0%,
        var(--ar-neon-pink) 45%,
        var(--ar-neon-purple) 100%);
    border: 1px solid rgba(0, 0, 0, 0.9);
    box-shadow:
        0 0 8px rgba(255, 45, 123, 0.7),
        inset 0 1px 0 rgba(255, 255, 255, 0.4);
    cursor: pointer;
}
.jb-vol-range:focus::-webkit-slider-thumb {
    box-shadow:
        0 0 12px rgba(0, 240, 255, 0.85),
        inset 0 1px 0 rgba(255, 255, 255, 0.5);
}
/* Firefox track + progress */
.jb-vol-range::-moz-range-track {
    height: 8px;
    border-radius: 4px;
    background: rgba(20, 6, 42, 0.85);
    border: 1px solid rgba(0, 0, 0, 0.9);
    box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.85);
}
.jb-vol-range::-moz-range-progress {
    height: 8px;
    border-radius: 4px;
    background: linear-gradient(90deg,
        var(--ar-neon-cyan) 0%,
        var(--ar-neon-pink) 100%);
    box-shadow: 0 0 6px rgba(255, 45, 123, 0.35);
}
.jb-vol-range::-moz-range-thumb {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: radial-gradient(circle at 30% 30%,
        #fff 0%,
        var(--ar-neon-pink) 45%,
        var(--ar-neon-purple) 100%);
    border: 1px solid rgba(0, 0, 0, 0.9);
    box-shadow:
        0 0 8px rgba(255, 45, 123, 0.7),
        inset 0 1px 0 rgba(255, 255, 255, 0.4);
    cursor: pointer;
}

/* ── Visualizer ──
   Frame styled as a recessed "TV screen", same inset 3D treatment
   as the inner panels (phase 3) so the canvas reads as a small CRT
   set into the cabinet rather than glued on top. */
.jb-viz-frame {
    margin-top: 1.1rem;
    padding: 0.85rem;
    background:
        radial-gradient(ellipse at 50% 100%,
            rgba(255, 45, 123, 0.10) 0%,
            transparent 70%),
        linear-gradient(180deg,
            rgba(6, 6, 15, 0.9) 0%,
            rgba(6, 6, 15, 0.7) 100%);
    border: 1px solid rgba(0, 0, 0, 0.85);
    border-radius: 0.85rem;
    box-shadow:
        inset 0 2px 0 rgba(0, 0, 0, 0.85),
        inset 0 -1px 0 rgba(255, 255, 255, 0.04),
        inset 0 0 24px rgba(123, 47, 247, 0.12),
        0 1px 0 rgba(255, 255, 255, 0.03);
}

/* Top bar: "AUDIO VISUALIZER" decal on the left, fullscreen toggle
   on the right. Switched from centred + absolutely-positioned FS
   button to a flexbox space-between, which is simpler and removes
   the absolute-position offset. */
.jb-viz-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}
.jb-viz-label {
    /* Same decal pill style as .jb-panel-head but in the violet
       palette, since the visualizer's accent colour is purple. */
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.3rem 0.7rem;
    font-family: 'Orbitron', sans-serif;
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.32em;
    text-transform: uppercase;
    color: rgba(123, 47, 247, 0.95);
    background:
        linear-gradient(180deg,
            rgba(123, 47, 247, 0.20) 0%,
            rgba(123, 47, 247, 0.05) 100%);
    border: 1px solid rgba(123, 47, 247, 0.45);
    border-radius: 0.3rem;
    box-shadow:
        0 0 8px rgba(123, 47, 247, 0.25),
        inset 0 1px 0 rgba(255, 255, 255, 0.08);
    text-shadow: 0 0 6px rgba(123, 47, 247, 0.4);
    cursor: pointer;
    transition: color 0.18s, border-color 0.18s, box-shadow 0.18s, background 0.18s;
}
.jb-viz-label:hover {
    color: var(--ar-neon-cyan);
    border-color: rgba(0, 240, 255, 0.55);
    background:
        linear-gradient(180deg,
            rgba(0, 240, 255, 0.20) 0%,
            rgba(0, 240, 255, 0.05) 100%);
    box-shadow:
        0 0 10px rgba(0, 240, 255, 0.35),
        inset 0 1px 0 rgba(255, 255, 255, 0.10);
    text-shadow: 0 0 8px rgba(0, 240, 255, 0.5);
}
.jb-viz-fs {
    background: transparent;
    border: 1px solid rgba(123, 47, 247, 0.4);
    color: rgba(123, 47, 247, 0.85);
    border-radius: 0.3rem;
    padding: 0.3rem 0.5rem;
    cursor: pointer;
    font-size: 0.85rem;
    line-height: 1;
    transition: color 0.18s, border-color 0.18s, box-shadow 0.18s;
}
.jb-viz-fs:hover {
    color: var(--ar-neon-cyan);
    border-color: var(--ar-neon-cyan);
    box-shadow: 0 0 8px rgba(0, 240, 255, 0.4);
}

/* ── "Fullscreen" visualizer takeover ──
   Despite the name, we don't lift the cabinet out of flow, we
   keep it on the page and just expand it within its container.
   All non-visualizer panels are hidden, the visualizer fills
   the cabinet (with a comfortable min-height), and the track
   metadata + exit button are positioned absolutely WITHIN the
   cabinet so they ride the cabinet's neon frame. */
.jb-fs-meta,
.jb-fs-exit,
.jb-fs-next {
    display: none;
    position: absolute;
    z-index: 5;
}
.jb-cabinet.is-fullscreen {
    position: relative;
    min-height: min(78vh, 720px);
}
.jb-cabinet.is-fullscreen .jb-head,
.jb-cabinet.is-fullscreen .jb-grid,
.jb-cabinet.is-fullscreen .jb-controls,
.jb-cabinet.is-fullscreen .jb-foot { display: none; }
.jb-cabinet.is-fullscreen .jb-viz-frame {
    position: absolute;
    inset: 1.25rem;
    margin: 0;
    padding: 0;
    background: transparent;
    border: 0;
    border-radius: 0.4rem;
    overflow: hidden;
}
.jb-cabinet.is-fullscreen .jb-viz-bar { display: none; }
.jb-cabinet.is-fullscreen .jb-viz {
    width: 100%;
    height: 100%;
    background: transparent;
}
.jb-cabinet.is-fullscreen .jb-fs-meta,
.jb-cabinet.is-fullscreen .jb-fs-exit { display: block; }

.jb-fs-meta {
    top: 2rem;
    right: 2rem;
    text-align: right;
    color: #fff;
    pointer-events: none;
    max-width: 60%;
}
.jb-fs-eyebrow {
    font-family: 'Orbitron', sans-serif;
    font-size: 0.65rem;
    font-weight: 600;
    letter-spacing: 0.4em;
    color: rgba(255, 45, 123, 0.85);
    text-shadow: 0 0 10px rgba(255, 45, 123, 0.6);
    margin: 0 0 0.4rem;
}
.jb-fs-title {
    font-family: 'Rajdhani', sans-serif;
    font-weight: 700;
    font-size: clamp(1.6rem, 4vw, 3rem);
    line-height: 1.05;
    color: #fff;
    text-shadow: 0 0 18px rgba(255, 255, 255, 0.25);
    margin: 0 0 0.25rem;
}
.jb-fs-artist {
    font-family: 'Rajdhani', sans-serif;
    font-weight: 500;
    font-size: clamp(1rem, 2.4vw, 1.5rem);
    color: var(--ar-neon-cyan);
    text-shadow: 0 0 8px rgba(0, 240, 255, 0.45);
    margin: 0 0 0.85rem;
}
.jb-fs-times {
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.95rem;
    color: rgba(255, 255, 255, 0.65);
    letter-spacing: 0.08em;
}
.jb-fs-times-sep { margin: 0 0.45em; color: rgba(255, 45, 123, 0.7); }

.jb-fs-exit,
.jb-fs-next {
    background: rgba(6, 6, 15, 0.6);
    color: #fff;
    border-radius: 50%;
    width: 40px;
    height: 40px;
    display: none;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    font-size: 1rem;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    transition: background 0.18s, border-color 0.18s, box-shadow 0.18s, transform 0.18s;
}
/* Exit at 2rem from left, next picks up 8px past the exit's
   right edge (40px button + 2rem left + 8px gap = 5.5rem). */
.jb-fs-exit { top: 2rem; left: 2rem;   border: 1px solid rgba(255, 45, 123, 0.5); }
.jb-fs-next { top: 2rem; left: 5.5rem; border: 1px solid rgba(0, 240, 255, 0.5); color: var(--ar-neon-cyan); }
.jb-cabinet.is-fullscreen .jb-fs-exit,
.jb-cabinet.is-fullscreen .jb-fs-next { display: flex; }
.jb-fs-exit:hover {
    background: rgba(255, 45, 123, 0.18);
    border-color: var(--ar-neon-pink);
    box-shadow: 0 0 14px rgba(255, 45, 123, 0.5);
    transform: scale(1.05);
}
.jb-fs-next:hover {
    background: rgba(0, 240, 255, 0.15);
    border-color: var(--ar-neon-cyan);
    box-shadow: 0 0 14px rgba(0, 240, 255, 0.5);
    transform: scale(1.05) rotate(45deg);
    color: #fff;
}

/* Mobile: tighten the overlay so it doesn't dominate small screens */
@media (max-width: 540px) {
    .jb-fs-meta { top: 1rem; right: 1rem; max-width: 70vw; }
    .jb-fs-exit { top: 1rem; left: 1rem;     width: 38px; height: 38px; }
    /* 38px button + 1rem left + 6px gap ≈ 4rem */
    .jb-fs-next { top: 1rem; left: 4rem;     width: 38px; height: 38px; }
}
.jb-viz {
    display: block;
    width: 100%;
    height: 90px;
    background:
        radial-gradient(ellipse at 50% 100%,
            rgba(255, 45, 123, 0.12) 0%,
            transparent 70%);
    border-radius: 0.4rem;
}

/* ── Cover fullscreen overlay (cabinet takeover, NOT browser fullscreen)
   Same pattern as the visualiser takeover: cabinet expands in place
   to min(78vh, 720px), the head / grid / controls / foot / viz-frame
   hide, the cover overlay shows centered with the track meta below. */
.jb-cabinet.is-cover-fs {
    position: relative;
    min-height: min(78vh, 720px);
}
/* Body scroll lock, when the cover-fs overlay is open, prevent the
   page behind it from scrolling (otherwise on mobile, pull-down on
   the overlay surfaces the footer or content below the cabinet). JS
   toggles this class on cover-fs enter/exit. */
body.jb-no-scroll {
    overflow: hidden;
}
.jb-cabinet.is-cover-fs .jb-head,
.jb-cabinet.is-cover-fs .jb-grid,
.jb-cabinet.is-cover-fs .jb-controls,
.jb-cabinet.is-cover-fs .jb-foot,
.jb-cabinet.is-cover-fs .jb-viz-frame { display: none; }

.jb-cover-fs {
    display: none;
    position: absolute;
    inset: 1.5rem;     /* trimmed from 2rem → buys 16 px of cover-height room */
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1rem;          /* trimmed from 1.5rem → buys another 8 px */
}
.jb-cabinet.is-cover-fs .jb-cover-fs { display: flex; }
.jb-cover-fs-art {
    position: relative;
    /* Square wrapper sized to fit within the cabinet's vertical budget:
         cabinet height cap          720 px (or 78vh, whichever smaller)
         minus inset (1.5rem × 2)    -48 px
         minus gap (1rem)            -16 px
         minus meta (eyebrow+title+
                     artist+margins) -~120 px
         = max cover height          ≈ 536 px on the capped cabinet
       Using 54vh for short viewports (where 78vh < 720px) and the
       530px hard cap for tall viewports (where cabinet is capped). */
    aspect-ratio: 1 / 1;
    width: 100%;
    max-width: min(54vh, 530px);
    height: auto;
    border-radius: 1rem;
    overflow: hidden;
    background:
        linear-gradient(180deg, #06060f 0%, #1a0a3a 50%, #06060f 100%);
    border: 1px solid rgba(255, 45, 123, 0.45);
    box-shadow:
        0 0 32px rgba(255, 45, 123, 0.45),
        0 0 64px rgba(123, 47, 247, 0.3);
}
.jb-cover-fs-art img {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
}
/* CSS synthwave-horizon placeholder when there's no cover image.
   Same look as .jb-cover.is-fallback but scaled up for the overlay. */
.jb-cover-fs-art .jb-cover-fs-fallback {
    display: none;
    position: absolute; inset: 0;
}
.jb-cover-fs-art.is-fallback .jb-cover-fs-fallback { display: block; }
.jb-cover-fs-art.is-fallback img { display: none; }
.jb-cover-fs-art.is-fallback .jb-cover-fs-fallback::before {
    content: '';
    position: absolute; inset: 0;
    background:
        radial-gradient(circle 38% 38% at 50% 64%,
            #ffd86b 0%, #ff8c42 28%, #ff2d7b 60%, transparent 80%),
        linear-gradient(180deg,
            #1a0a3a 0%, #3d1565 35%, #832566 55%,
            #d8417a 70%, #ff6b6b 76%, #14063a 100%);
}
.jb-cover-fs-art.is-fallback .jb-cover-fs-fallback::after {
    content: '';
    position: absolute;
    left: 0; right: 0;
    bottom: 0; top: 60%;
    background:
        repeating-linear-gradient(0deg,
            transparent 0 7px, rgba(255, 45, 123, 0.45) 7px 8px),
        repeating-linear-gradient(90deg,
            transparent 0 7px, rgba(255, 45, 123, 0.35) 7px 8px);
    transform: perspective(80px) rotateX(40deg);
    transform-origin: 50% 0%;
}

.jb-cover-fs-meta {
    color: #fff;
    min-width: 0;
    text-align: center;
    max-width: 80ch;
}
.jb-cover-fs-eyebrow {
    font-family: 'Orbitron', sans-serif;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.4em;
    color: rgba(255, 45, 123, 0.85);
    text-shadow: 0 0 10px rgba(255, 45, 123, 0.6);
    margin: 0 0 0.6rem;
}
.jb-cover-fs-title {
    font-family: 'Rajdhani', sans-serif;
    font-weight: 700;
    font-size: clamp(1.8rem, 4.5vw, 3.4rem);
    line-height: 1.05;
    color: #fff;
    text-shadow: 0 0 18px rgba(255, 255, 255, 0.25);
    margin: 0 0 0.45rem;
}
.jb-cover-fs-artist {
    font-family: 'Rajdhani', sans-serif;
    font-weight: 500;
    font-size: clamp(1.1rem, 2.6vw, 1.7rem);
    color: var(--ar-neon-cyan);
    text-shadow: 0 0 8px rgba(0, 240, 255, 0.45);
    margin: 0;
}

/* Exit button, top-left of the cover-fs overlay, same corner as the
   visualiser fullscreen exit so the two takeover views share muscle
   memory. Inside the overlay so stacking context is unambiguous. */
.jb-cover-fs-exit {
    position: absolute;
    top: 0.75rem;
    left: 0.75rem;
    z-index: 10;
    display: flex;
    width: 44px; height: 44px;
    align-items: center;
    justify-content: center;
    background: rgba(6, 6, 15, 0.7);
    color: #fff;
    border: 1px solid rgba(255, 45, 123, 0.55);
    border-radius: 50%;
    cursor: pointer;
    font-size: 1.05rem;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    transition: background 0.18s, border-color 0.18s, box-shadow 0.18s, transform 0.18s;
}
.jb-cover-fs-exit:hover {
    background: rgba(255, 45, 123, 0.18);
    border-color: var(--ar-neon-pink);
    box-shadow: 0 0 14px rgba(255, 45, 123, 0.5);
    transform: scale(1.05);
}

/* Mobile: escape the cabinet and fill the viewport BELOW the site
   topbar. Starting at 64px (topbar height) keeps site navigation
   accessible, user can leave cover view via the X OR via the
   normal nav. position: fixed detaches the overlay from cabinet
   flow so it claims the full width-and-rest-of-height area. */
@media (max-width: 740px) {
    .jb-cabinet.is-cover-fs .jb-cover-fs {
        position: fixed;
        top: 64px;           /* below the .topbar (which is 64px tall) */
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 1035;       /* under .topbar's 1040, over everything else */
        padding: 2rem 1.25rem;
        gap: 1.5rem;
        background:
            radial-gradient(ellipse at 50% 0%,
                rgba(123, 47, 247, 0.20) 0%,
                transparent 60%),
            linear-gradient(180deg,
                #0a0420 0%,
                #06060f 70%,
                #0a0420 100%);
    }
    .jb-cabinet.is-cover-fs .jb-cover-fs-art {
        /* Way more room to grow now that we own the viewport.
           80vh - topbar height = ~80vh - 64px → keep at min(75vh, 90vw)
           so meta still fits below comfortably. */
        max-width: min(75vh, 90vw);
    }
}

/* Footer "model number"-style strip flanked by faint pink rules,
   like the markings on the bottom of a real arcade cabinet. The
   ::before/::after rules are fade-to-transparent gradient strips so
   the eye is led into the centred text. */
.jb-foot {
    position: relative;
    text-align: center;
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.65rem;
    letter-spacing: 0.32em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.4);
    margin: 0.85rem 0 0.5rem;
    padding: 0.5rem 0;
}
.jb-foot::before,
.jb-foot::after {
    content: '';
    position: absolute;
    top: 50%;
    width: 22%;
    height: 1px;
    pointer-events: none;
}
.jb-foot::before {
    left: 4%;
    background: linear-gradient(90deg, transparent, rgba(255, 45, 123, 0.4));
}
.jb-foot::after {
    right: 4%;
    background: linear-gradient(90deg, rgba(255, 45, 123, 0.4), transparent);
}
.jb-foot-dot { color: var(--ar-neon-pink); margin: 0 0.35em; }
@media (max-width: 540px) {
    /* Drop the flanks on mobile, too thin to read, and the centred
       text needs the horizontal room. */
    .jb-foot::before,
    .jb-foot::after { display: none; }
}

.jukebox-back { text-align: center; margin-bottom: 2rem; }

/* Visually mark playing state on the cabinet (subtle).
   --pulse is updated each animation frame from the analyser
   amplitude (0–1) so the glow breathes with the music. The piping
   stack (--jb-piping defined on .jb-cabinet) is included here too
   so it doesn't get overwritten when the cabinet enters playing
   state, without it the dark/pink rings would visibly disappear
   the moment audio starts. */
.jb-cabinet.is-playing {
    box-shadow:
        var(--jb-piping),
        0 0 calc(40px + var(--pulse) * 32px) rgba(255, 45, 123, calc(0.55 + var(--pulse) * 0.25)),
        0 0 calc(80px + var(--pulse) * 64px) rgba(123, 47, 247, calc(0.32 + var(--pulse) * 0.2)),
        inset 0 0 calc(28px + var(--pulse) * 16px) rgba(123, 47, 247, 0.22);
}
@media (prefers-reduced-motion: reduce) {
    /* Hold the cabinet glow steady, no pulse */
    .jb-cabinet.is-playing { --pulse: 0; }
}

/* ── Boot-up flicker ──
   Only the ARCYNIA logo flickers on first paint, feels like a
   CRT warming up. The cabinet itself used to flicker too but it
   was distracting on every reload, so we let the title carry the
   "powering on" beat alone. */
@keyframes jbBootTitle {
    0%, 8%   { opacity: 0; }
    12%, 18% { opacity: 1; }
    22%      { opacity: 0.2; }
    28%      { opacity: 1; }
    32%      { opacity: 0.6; }
    38%      { opacity: 1; }
    100%     { opacity: 1; }
}
.jb-title { animation: jbBootTitle 1.3s ease-out 1; }

/* Phase 6 button :active overrides removed in phase-4 redesign:
   the new 3D buttons (.jb-btn / .jb-btn-play) now define their own
   pressed state inline with the rest of the rule. Progress-handle
   active scaling kept since it's unrelated. */
.jb-progress-bar:active .jb-progress-handle {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.3);
}

/* ── Phase 6: mobile tweaks ── */
@media (max-width: 540px) {
    .jb-cabinet {
        padding: 0.5rem;
        border-radius: 1rem;
        /* Slimmer piping on mobile so it doesn't dominate the small frame. */
        --jb-piping:
            0 0 0 3px #06060f,
            0 0 0 4px var(--ar-neon-pink),
            0 0 0 5px rgba(0, 0, 0, 0.9);
    }
    /* Header already collapses at 740px (single-col, no padding),
       nothing extra to do here for it. Wings hidden by 1100px rule.
       Buttons already 56×56 from the 740 rule. */
    .jb-controls { padding-inline: 0.5rem; }
    .jb-btn { padding: 0.3rem 0.45rem; font-size: 0.55rem; }
    .jb-btn i { font-size: 1.1rem; }
    .jb-btn-play i { font-size: 1.45rem; }
    .jb-tracks-scroll { max-height: 260px; }
    /* Volume becomes a native horizontal range slider on tight screens.
       The round knob + LED meter both hide; the .jb-vol-range input
       shows in their place, styled to match the Arcynia palette. JS
       (setVol) keeps both controls in sync via the same code path.
       The slider stretches to fill its grid cell so it reads clearly
       as the volume control, with a touch of top padding above it. */
    .jb-vol-label,
    .jb-knob,
    .jb-vol-meter { display: none; }
    .jb-vol {
        min-width: 0;
        width: 100%;
    }
    .jb-vol-row {
        width: 100%;
        padding-top: 0.5rem;
    }
    .jb-vol-range {
        display: block;
        width: 100%;
        height: 36px;
    }
    /* Beefier track + thumb on mobile, easier touch target and the
       fill reads as a proper volume bar. */
    .jb-vol-range::-webkit-slider-runnable-track {
        height: 10px;
        border-radius: 5px;
    }
    .jb-vol-range::-webkit-slider-thumb {
        width: 22px;
        height: 22px;
        margin-top: -7px;
    }
    .jb-vol-range::-moz-range-track {
        height: 10px;
        border-radius: 5px;
    }
    .jb-vol-range::-moz-range-progress {
        height: 10px;
        border-radius: 5px;
    }
    .jb-vol-range::-moz-range-thumb {
        width: 22px;
        height: 22px;
    }
}

/* ── Reduced motion: skip flicker animation entirely ── */
@media (prefers-reduced-motion: reduce) {
    .jb-title { animation: none; }
    .jb-knob-dial { transition: none; }
}
