/* MV2 WebGPU Visualizer -- Player CSS
 * Version: 3.5.28  (tracks the plugin version — see LINEAGE below)
 * DM Mono + Syne loaded via wp_enqueue_style (parallel, non-blocking)
 *
 * ---------------------------------------------------------------------------
 * LINEAGE — read this before "restoring" anything that looks missing
 * ---------------------------------------------------------------------------
 * This file was versioned independently ("v1.4.1") until 3.5.24, which drifted
 * far enough from the plugin version to be actively misleading. It now tracks
 * the plugin version.
 *
 * More importantly: this is NOT the newest CSS that was ever written. It is the
 * 3.5.16 baseline with three surgical fixes on top.
 *
 *   3.5.17 – 3.5.20   A four-release restyle arc: "neon, refined" tokens, then
 *                     alignment fixes, then a full glass/aurora redesign with
 *                     drifting gradient blobs behind translucent chrome. All of
 *                     it was REVERTED. The original cyan-neon direction
 *                     (--mv2-teal #00c8ff, dark surfaces, no glass) is the
 *                     intended look.
 *   3.5.21            Re-based on 3.5.16 CSS. Added the height="" shortcode
 *                     attribute via --mv2-height, plus the WP admin-bar 100vh
 *                     overlap fix.
 *   3.5.22            .mv2-template-btn given a fixed width — template name
 *                     length was shifting the prev/next arrows.
 *   3.5.23            .mv2-selects select given a fixed width — same class of
 *                     bug, material/palette option length reflowed the top bar.
 *
 * So: the CHANGELOG-3.5.17 … 3.5.20 files describe CSS that IS NOT IN THIS
 * FILE. They're kept as a record of a direction that was tried and rejected,
 * not as documentation of current state. If you're reading them to understand
 * how the player looks today, stop — read this file instead.
 * ---------------------------------------------------------------------------
 */

:root {
    --mv2-void:      #080810;
    --mv2-deep:      #0a0a14;
    --mv2-surface:   #0e0e1a;
    --mv2-raised:    #161625;
    --mv2-glass:     rgba(8,8,16,0.90);
    --mv2-glass-hi:  rgba(8,8,16,0.985);   /* (3.5.26) 0.97 -> 0.985: chrome bars
        over bright templates were washing out. The top bar sets its own
        even-more-opaque background + drops the saturate filter; this bump keeps
        the controls bar and panel header consistent with it. */
    --mv2-border:    rgba(255,255,255,0.06);
    --mv2-border-hi: rgba(0,200,255,0.28);
    --mv2-teal:      #00c8ff;
    --mv2-teal-dim:  rgba(0,200,255,0.10);
    --mv2-teal-glow: rgba(0,200,255,0.28);
    /* Text: clean white hierarchy, no blue-cast */
    --mv2-hi:   #eff4fa;
    --mv2-md:   rgba(210,228,248,0.85);
    --mv2-lo:   rgba(140,175,210,0.60);
    --mv2-mute: rgba(90,120,155,0.45);
    --mv2-r-sm:4px; --mv2-r-md:10px; --mv2-r-lg:14px; --mv2-r-xl:20px;
    --mv2-ease:cubic-bezier(0.22,1,0.36,1);
}

.mv2-container {
    width:100%; max-height:100vh;
    /* (3.5.21) height="..." shortcode attribute, threaded through as a CSS
       custom property (see mv2-visualizer.php render_player_html). Falls
       back to `auto` -- i.e. exactly the pre-3.5.21 content-driven sizing
       -- when the attribute is omitted, so existing embeds are unaffected. */
    height: var(--mv2-shortcode-height, auto);
    background:var(--mv2-void);
    border:none; border-radius:0;
    position:relative; box-sizing:border-box;
    font-family:'DM Mono','Courier New',monospace; color:var(--mv2-md);
    overflow:hidden; display:flex; flex-direction:column;
    box-shadow:none;
}
/* (3.5.21) WordPress's admin bar pushes the entire page down via
   `html { margin-top: 32px }` (46px under 783px) for logged-in users who
   can see it -- `body.admin-bar` is the class WP itself adds in that
   case, so this only ever applies for visitors who actually have the bar,
   never for logged-out front-end traffic. Without this, height="100vh"
   ends up 32-46px taller than the actually-visible viewport, producing a
   tiny scroll sliver. Scoped to [data-mv2-vh-height] so it only touches
   instances whose height attribute was actually vh/dvh/svh/lvh -- a
   height="600px" embed doesn't need or want this adjustment. */
body.admin-bar .mv2-container[data-mv2-vh-height] {
    height: calc(var(--mv2-shortcode-height) - 32px);
}
@media (max-width: 782px) {
    body.admin-bar .mv2-container[data-mv2-vh-height] {
        height: calc(var(--mv2-shortcode-height) - 46px);
    }
}
.mv2-top-bar {
    position:relative; z-index:10;
    display:flex; align-items:center; gap:5px; padding:7px 10px;
    /* (3.5.26) Near-solid dark instead of the old 0.97 glass + saturate(1.6).
       Over a bright template (plasma, aurora, anything magenta/cyan) the 3%
       bleed-through was getting *saturation-boosted* by that filter, so the bar
       read as a washed-out purple panel rather than the crisp dark bar it's
       meant to be. 0.992 alpha keeps a whisper of the viz behind it (so it
       still feels connected to the canvas, not bolted on) but is opaque enough
       that no template colour washes it out. blur kept for the soft edge;
       saturate dropped entirely -- it was the main culprit. */
    background: rgba(8,8,16,0.992); backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border-bottom:1px solid var(--mv2-border); flex-shrink:0; flex-wrap:nowrap;
    min-height:44px;
}
.mv2-top-divider { width:1px; height:18px; background:var(--mv2-border); margin:0 3px; flex-shrink:0; }
.mv2-top-spacer  { flex:1; }
/* Template nav arrows */
.mv2-tpl-prev-btn, .mv2-tpl-next-btn {
    /* (3.5.15) All three buttons in the nav cluster (prev / template / next /
       home) share box-sizing:border-box and height:28px so they sit on one
       baseline regardless of intrinsic content size. */
    box-sizing: border-box;
    width: 28px; height: 28px; padding: 0;
    /* (3.5.15) Cycle through the neon palette (cyan → mint → green → pink →
       purple) in sync with the template label. Phase offsets stagger the
       four buttons so the cluster reads as a running neon wave from left to
       right rather than a lockstep blink. */
    color: #00c8ff;
    border-color: var(--mv2-teal-dim);
    animation: mv2HueCycle 5s linear infinite;
    animation-delay: -1.25s;
    transition: border-color .15s, background .15s, box-shadow .15s, transform .12s;
}
.mv2-tpl-next-btn { animation-delay: 1.25s; }
/* (3.5.15) display:block on the SVG fixes the "icon sits at the bottom of
   the button" problem. Inline SVGs sit on the inline-baseline of an
   imaginary text line-box — the browser reserves phantom descender space
   below them, so even with align-items:center the visible glyph gets
   pushed toward the bottom of the button. Switching to block-level
   removes the baseline behavior and lets flex centering land it dead-on.
   The drop-shadow uses currentColor so the neon glow tracks whatever
   palette frame the animation is currently passing through, giving the
   stroke the same lit appearance the label gets from its text-shadow. */
.mv2-tpl-prev-btn svg, .mv2-tpl-next-btn svg, .mv2-home-btn svg {
    display: block;
    filter: drop-shadow(0 0 4px currentColor);
}
.mv2-tpl-prev-btn:hover, .mv2-tpl-next-btn:hover {
    /* (3.5.15) Hover glow was 14px box-shadow at full currentColor — at peak
       saturation phases that flooded the entire 28×28 button into a solid
       coloured blob. 8px reads as "lit" without overwhelming the cluster. */
    border-color: currentColor;
    background: rgba(255,255,255,.04);
    box-shadow: 0 0 8px currentColor;
    transform: scale(1.06);
}
.mv2-tpl-prev-btn:active, .mv2-tpl-next-btn:active { transform: scale(0.94); }


/* ── Controls ───────────────────────────────────────────────────────────── */
.mv2-ctrl-btn {
    background:transparent; border:1px solid var(--mv2-border); border-radius:var(--mv2-r-sm); padding:5px;
    color:var(--mv2-lo); cursor:pointer; display:flex; align-items:center; justify-content:center;
    transition:color .15s,border-color .15s,background .15s; flex-shrink:0;
}
.mv2-home-btn {
    /* (3.5.15) Sized to match prev/next/template buttons (28px box-sizing
       border-box) and runs the same neon hue-cycle so the whole cluster
       pulses through cyan→mint→green→pink→purple together. Phase offset
       chosen so prev / label / next / home stagger across one running wave. */
    box-sizing: border-box;
    width: 28px; height: 28px; padding: 0;
    color: #00c8ff;
    border-color: var(--mv2-teal-dim);
    animation: mv2HueCycle 5s linear infinite;
    animation-delay: 2.5s;
    transition: border-color .15s, background .15s, box-shadow .15s, transform .12s;
}
.mv2-home-btn:hover {
    border-color: currentColor;
    background: rgba(255,255,255,.04);
    box-shadow: 0 0 8px currentColor;
    transform: scale(1.08);
}
.mv2-home-btn:active { transform: scale(0.93); }
.mv2-ctrl-btn:hover   { color:var(--mv2-hi); border-color:var(--mv2-border-hi); background:var(--mv2-raised); }
.mv2-ctrl-btn.mv2-btn-active { color:var(--mv2-teal); border-color:var(--mv2-teal-dim); background:var(--mv2-teal-dim); }
.mv2-selects { display:flex; gap:4px; align-items:center; }
.mv2-selects select {
    /* (3.5.23) Fixed width -- previously unset, so the box sized itself to
       whichever option's text happened to be selected ("Fire & Ember" vs
       "Auto" are very different lengths), shifting everything after it.
       (3.5.25) Widened 100px -> 120px (fits every current material/palette
       name without ellipsis) and given the same 28px box-sizing:border-box
       height as the buttons in the row, so it sits on the shared baseline
       instead of being a couple of px shorter. Vertical padding removed in
       favour of line-height centring within the fixed height, which is what
       makes the "Default"/"Auto" text sit on the same line as the icons. */
    width: 120px; box-sizing: border-box; height: 28px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    background:var(--mv2-raised); border:1px solid var(--mv2-border);
    border-radius:var(--mv2-r-sm); padding:0 18px 0 8px; line-height:26px;
    color:var(--mv2-lo); font:9px 'DM Mono',monospace; letter-spacing:0.8px;
    cursor:pointer; outline:none; transition:border-color .2s, color .2s;
    -webkit-appearance:none; appearance:none;
    background-image:url("data:image/svg+xml,%3Csvg width='8' height='5' viewBox='0 0 8 5' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l3 3 3-3' stroke='%235a7897' stroke-width='1.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat:no-repeat; background-position:right 6px center;
}
.mv2-selects select:hover { border-color:var(--mv2-border-hi); color:var(--mv2-hi); }
.mv2-fullscreen-btn { background:transparent; border:1px solid var(--mv2-border); border-radius:var(--mv2-r-sm); padding:5px; color:var(--mv2-lo); cursor:pointer; display:flex; align-items:center; transition:color .15s,border-color .15s; }
.mv2-fullscreen-btn:hover { color:var(--mv2-hi); border-color:var(--mv2-border-hi); }
.mv2-no-gpu { font:9px 'DM Mono',monospace; color:#f87; background:rgba(255,80,80,.1); border:1px solid rgba(255,80,80,.2); border-radius:var(--mv2-r-sm); padding:3px 8px; }

/* Full-display GPU-unavailable overlay — replaces a black canvas with
   actionable guidance when WebGPU init fails. */
.mv2-gpu-overlay {
    position:absolute; inset:0; display:flex; flex-direction:column;
    align-items:center; justify-content:center; text-align:center;
    padding:24px; gap:10px; z-index:5; pointer-events:auto;
    background:linear-gradient(180deg, rgba(8,8,16,.72), rgba(8,8,16,.92));
    backdrop-filter:blur(6px);
}
.mv2-gpu-overlay-title {
    font:600 14px/1.3 'Syne','Inter',sans-serif; letter-spacing:.04em;
    color:#ffb3b3; text-transform:uppercase;
}
.mv2-gpu-overlay-hint {
    font:400 12px/1.5 'DM Mono','Inter',monospace;
    color:var(--mv2-md); max-width:520px;
}

/* ── Display ─────────────────────────────────────────────────────────────── */
.mv2-display { position:relative; flex:1 1 auto; min-height:max(240px,min(50vw,62vh)); background:#000; overflow:hidden; }

/* ── Now playing ─────────────────────────────────────────────────────────── */
.mv2-now-playing {
    display:flex; align-items:center; gap:10px; padding:7px 14px;
    background:var(--mv2-deep); border-top:1px solid var(--mv2-border); flex-shrink:0;
    position:relative; overflow:hidden;
}
/* Subtle teal left accent line */
.mv2-now-playing::before {
    content:''; position:absolute; left:0; top:0; bottom:0; width:2px;
    background:var(--mv2-teal); opacity:0.6;
}
.mv2-np-label {
    font:600 8px/1 'DM Mono',monospace; letter-spacing:.18em;
    color:var(--mv2-teal); white-space:nowrap; opacity:0.85;
}
.mv2-np-title {
    font:500 11px/1.4 'DM Mono',monospace; color:var(--mv2-hi);
    flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.mv2-np-bpm {
    font:9px/1 'DM Mono',monospace; color:var(--mv2-teal);
    white-space:nowrap; background:var(--mv2-teal-dim);
    padding:2px 6px; border-radius:3px; letter-spacing:.05em;
}

/* ── Transport ───────────────────────────────────────────────────────────── */
.mv2-controls {
    display:flex; align-items:center; gap:7px; padding:9px 12px;
    background:var(--mv2-glass-hi); border-top:1px solid var(--mv2-border); flex-shrink:0;
}
.mv2-play-btn {
    width:36px; height:36px; border-radius:50%;
    background:var(--mv2-teal); border:none; color:#000;
    cursor:pointer; display:flex; align-items:center; justify-content:center;
    transition:transform .15s var(--mv2-ease), box-shadow .2s; flex-shrink:0;
    box-shadow:0 0 0 rgba(0,200,255,0);
}
.mv2-play-btn:hover { transform:scale(1.1); box-shadow:0 0 18px rgba(0,200,255,.45); }
.mv2-play-btn:active { transform:scale(0.95); }
.mv2-play-btn svg { margin-left:2px; }
.mv2-icon-pause svg { margin-left:0; }
/* Prev/next track buttons — slightly larger than other ctrl btns */
.mv2-prev-btn, .mv2-next-btn { width:30px; height:30px; padding:0; }
.mv2-timeline { flex:1; display:flex; flex-direction:column; gap:5px; min-width:0; }
.mv2-progress {
    width:100%; height:3px; cursor:pointer; appearance:none;
    background:var(--mv2-raised); border-radius:99px; outline:none;
    transition:height .12s var(--mv2-ease);
    /* Filled portion via JS-updated --pct custom property */
}
.mv2-progress:hover { height:5px; }
.mv2-progress::-webkit-slider-thumb {
    appearance:none; width:12px; height:12px; border-radius:50%;
    background:var(--mv2-teal); cursor:pointer;
    box-shadow:0 0 6px rgba(0,200,255,.5);
}
.mv2-progress::-moz-range-thumb {
    width:12px; height:12px; border-radius:50%;
    background:var(--mv2-teal); border:none; cursor:pointer;
}
.mv2-time { font:9px/1 'DM Mono',monospace; color:var(--mv2-lo); white-space:nowrap; }
/* Progress fill: updated via style attribute from JS */
.mv2-progress {
    background:linear-gradient(to right,
        var(--mv2-teal) 0%,
        var(--mv2-teal) var(--mv2-pct, 0%),
        var(--mv2-raised) var(--mv2-pct, 0%),
        var(--mv2-raised) 100%
    );
}
.mv2-vol-wrap { display:flex; align-items:center; gap:5px; }
.mv2-volume {
    width:68px; height:3px; appearance:none;
    background:var(--mv2-raised); border-radius:99px; cursor:pointer; outline:none;
    transition:width .15s;
}
.mv2-vol-wrap:hover .mv2-volume { width:80px; }
.mv2-volume::-webkit-slider-thumb {
    appearance:none; width:9px; height:9px; border-radius:50%;
    background:var(--mv2-md); cursor:pointer;
}
.mv2-volume::-moz-range-thumb { width:9px; height:9px; border-radius:50%; background:var(--mv2-md); border:none; cursor:pointer; }

/* ── Playlist ─────────────────────────────────────────────────────────────── */
.mv2-playlist-wrap { max-height:200px; display:flex; flex-direction:column; border-top:1px solid var(--mv2-border); flex-shrink:0; }
.mv2-no-playlist .mv2-playlist-wrap { display:none; }
.mv2-pl-searchbar { display:flex; align-items:center; gap:6px; padding:6px 10px; background:var(--mv2-deep); border-bottom:1px solid var(--mv2-border); flex-shrink:0; }
.mv2-pl-searchbar svg { opacity:.4; flex-shrink:0; }
.mv2-pl-search { flex:1; background:transparent; border:none; outline:none; font:10px 'DM Mono',monospace; color:var(--mv2-hi); }
.mv2-pl-search::placeholder { color:var(--mv2-mute); }
.mv2-pl-clear { background:none; border:none; color:var(--mv2-mute); cursor:pointer; font-size:14px; padding:0; line-height:1; }
.mv2-pl-clear:hover { color:var(--mv2-hi); }
.mv2-playlist { overflow-y:auto; flex:1; scrollbar-width:thin; scrollbar-color:var(--mv2-raised) transparent; }
.mv2-pl-row { display:flex; align-items:center; gap:8px; padding:6px 12px; cursor:pointer; border-bottom:1px solid rgba(255,255,255,.022); transition:background .1s; }
.mv2-pl-row:hover  { background:var(--mv2-raised); }
.mv2-pl-row.mv2-pl-active { background:rgba(0,200,255,0.07); border-left:2px solid var(--mv2-teal); padding-left:10px; }
.mv2-pl-num { font:9px 'DM Mono',monospace; color:var(--mv2-mute); width:20px; text-align:right; flex-shrink:0; }
.mv2-pl-active .mv2-pl-num { color:var(--mv2-teal); }
.mv2-pl-title { flex:1; font:10px 'DM Mono',monospace; color:var(--mv2-md); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.mv2-pl-active .mv2-pl-title { color:var(--mv2-hi); font-weight:500; }
.mv2-pl-row:hover .mv2-pl-title { color:var(--mv2-hi); }
.mv2-pl-dur { font:9px 'DM Mono',monospace; color:var(--mv2-mute); }
.mv2-pl-active .mv2-pl-dur { color:var(--mv2-lo); }

/* ── Fullscreen ─────────────────────────────────────────────────────────── */
.mv2-container:fullscreen,.mv2-container:-webkit-full-screen { border-radius:0; max-height:100vh; height:100vh; }
.mv2-container:fullscreen .mv2-display { flex:1; min-height:0; }
.mv2-no-tracks { color:var(--mv2-lo); font:11px 'DM Mono',monospace; padding:12px; }
.mv2-no-tracks a { color:var(--mv2-teal); }

/* ======================================================================
   TEMPLATE PANEL -- WebGL style floating panel (replaces dropdown)
   ====================================================================== */

.mv2-template-select { position: relative; flex-shrink: 0; }

.mv2-template-btn {
    /* (3.5.27) Centering via flexbox on BOTH axes — the reliable way.
       align-items:center handles vertical (no more line-height guessing that
       let the text ride low); justify-content:center handles horizontal. The
       chevron is absolutely positioned (out of flow — see the svg rule) so it
       plays no part in justify-content and can't push the label off-centre.
       The label is natural-width, so a short name centres and a long name
       ellipsises at max-width; either way it's centred in the button.
       width fixed at 210px so the prev/next arrows never shift. */
    display: flex; align-items: center; justify-content: center;
    position: relative;
    width: 210px; box-sizing: border-box; height: 28px;
    padding: 0 22px;
    font: 800 9px 'DM Mono', monospace; letter-spacing: 1.6px; text-transform: uppercase;
    background: var(--mv2-teal-dim);
    color: var(--mv2-teal);
    border: 1px solid var(--mv2-border-hi);
    border-radius: var(--mv2-r-sm);
    cursor: pointer;
    text-shadow: 0 0 12px var(--mv2-teal-glow);
    transition: all 0.15s var(--mv2-ease);
    white-space: nowrap; overflow: hidden;
}
.mv2-template-btn::before {
    content: ''; position: absolute; inset: 0; border-radius: inherit;
    background: linear-gradient(135deg, rgba(0,200,255,.10) 0%, transparent 60%);
    opacity: 0; transition: opacity .18s;
}
.mv2-template-btn:hover::before,
.mv2-select-open .mv2-template-btn::before { opacity: 1; }
.mv2-template-btn:hover {
    border-color: var(--mv2-teal);
    box-shadow: 0 0 20px var(--mv2-teal-glow);
}
.mv2-select-open .mv2-template-btn {
    border-color: var(--mv2-teal);
    box-shadow: 0 0 24px var(--mv2-teal-glow);
    border-bottom-color: transparent;
}
.mv2-template-btn svg {
    /* (3.5.27) Pinned right, vertically centred, out of flow so it doesn't bias
       the label. height:6px matches the chevron's 0 0 10 6 viewBox exactly —
       previously height:10px on a 6-tall viewBox scaled it and threw the
       vertical centre off, which is why the chevron read as sitting high. */
    position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
    width: 10px; height: 6px; opacity: .45;
    transition: transform .2s var(--mv2-ease), opacity .15s;
}
.mv2-select-open .mv2-template-btn svg { transform: translateY(-50%) rotate(180deg); opacity: .85; }

/* Color cycling label */
@keyframes mv2HueCycle {
    0%   { color: #00c8ff; text-shadow: 0 0 8px rgba(0,200,255,.5); }
    20%  { color: #00ffd0; text-shadow: 0 0 8px rgba(0,255,200,.5); }
    40%  { color: #a0ff60; text-shadow: 0 0 8px rgba(160,255,96,.4); }
    60%  { color: #ff6ac8; text-shadow: 0 0 8px rgba(255,106,200,.5); }
    80%  { color: #c080ff; text-shadow: 0 0 8px rgba(192,128,255,.5); }
    100% { color: #00c8ff; text-shadow: 0 0 8px rgba(0,200,255,.5); }
}
.mv2-template-label {
    /* (3.5.27) Natural-width, centred by the button's justify-content. max-width
       lets a long name ellipsise instead of overflowing. No width:100%, no
       line-height centring — the flex parent handles both axes now. */
    max-width: 100%;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    text-align: center; letter-spacing: 1.4px;
    position: relative; z-index: 1;
    animation: mv2HueCycle 5s linear infinite;
}
.mv2-template-btn:hover .mv2-template-label { animation-duration: 1.5s; }

/* Now-playing also cycles */
.mv2-np-title { animation: mv2HueCycle 6s linear infinite; }

/* Active playlist track */
@keyframes mv2PlCycle {
    0%   { color: #00c8ff; } 33%  { color: #c080ff; }
    66%  { color: #ff6ac8; } 100% { color: #00c8ff; }
}
.mv2-pl-active .mv2-pl-title  { animation: mv2PlCycle 4s linear infinite !important; font-weight: 600; }
.mv2-pl-active .mv2-pl-num    { animation: mv2PlCycle 4s linear infinite; }

/* ── Floating Panel ───────────────────────────────────────────────── */
.mv2-template-panel {
    position: fixed; z-index: 99999; display: none;
    /* Rich layered background: deep space gradient + mesh pattern */
    background:
        radial-gradient(ellipse at 20% 0%, rgba(0,160,255,.06) 0%, transparent 55%),
        radial-gradient(ellipse at 80% 100%, rgba(120,0,255,.05) 0%, transparent 50%),
        linear-gradient(160deg, rgba(4,8,22,1) 0%, rgba(2,4,14,1) 100%);
    backdrop-filter: blur(48px) saturate(2.2) brightness(1.1);
    -webkit-backdrop-filter: blur(48px) saturate(2.2) brightness(1.1);
    border: 1px solid rgba(0,180,255,.18);
    border-top-color: rgba(0,220,255,.28);
    border-radius: var(--mv2-r-lg);
    box-shadow:
        0 0 0 1px rgba(0,200,255,.05),
        0 0 0 2px rgba(0,0,0,.5),
        0 50px 140px rgba(0,0,0,.98),
        0 0 120px rgba(0,160,255,.08),
        inset 0 1px 0 rgba(0,220,255,.08),
        inset 0 -1px 0 rgba(0,0,0,.4);
    width: 880px; max-width: 97vw; overflow: hidden;
    animation: mv2PanelIn .22s cubic-bezier(.16,1,.3,1);
}
.mv2-template-panel.mv2-panel-open { display: flex !important; flex-direction: column; }
@keyframes mv2PanelIn {
    from { opacity:0; transform: translateY(-10px) scale(.96); }
    to   { opacity:1; transform: none; }
}

/* Search bar */
.mv2-template-panel::before {
    content: '';
    position: absolute; inset: 0; pointer-events: none; z-index: 0;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)' opacity='0.04'/%3E%3C/svg%3E");
    border-radius: inherit;
    opacity: 0.6;
}
.mv2-template-panel > * { position: relative; z-index: 1; }

.mv2-tp-search-wrap {
    padding: 10px 12px 9px;
    border-bottom: 1px solid rgba(255,255,255,.05);
    background: rgba(0,0,0,.35); flex-shrink: 0;
}
.mv2-tp-search {
    width: 100%; box-sizing: border-box;
    background: rgba(0,200,255,.03);
    border: 1px solid rgba(255,255,255,.07);
    border-radius: var(--mv2-r-sm);
    color: rgba(255,255,255,.82);
    font: 11px 'DM Mono', monospace; padding: 7px 10px; outline: none;
    transition: border-color .15s, background .15s, box-shadow .15s;
}
.mv2-tp-search:focus {
    border-color: rgba(0,200,255,.32); background: rgba(0,200,255,.04);
    box-shadow: 0 0 0 3px rgba(0,200,255,.06);
}
.mv2-tp-search::placeholder { color: rgba(255,255,255,.18); }

/* Body */
.mv2-tp-body { display: flex; min-height: 0; max-height: 560px; overflow: hidden; }

/* Category sidebar — redesigned for legibility (3.5.7).
   Was 110px @ 8.5px font with `text-overflow: ellipsis` truncating every
   non-trivial label. Now 158px @ 10.5px with proper space for icon + label
   + count and no truncation. Mobile collapses to 48px icon-only. */
.mv2-tp-cats {
    width: 158px; flex-shrink: 0;
    border-right: 1px solid rgba(255,255,255,.05);
    overflow-y: auto; padding: 6px 0;
    background: linear-gradient(180deg, rgba(0,200,255,.015) 0%, transparent 100%);
    scrollbar-width: thin; scrollbar-color: rgba(0,200,255,.08) transparent;
}
.mv2-tp-cats::-webkit-scrollbar { width: 4px; }
.mv2-tp-cats::-webkit-scrollbar-thumb { background: rgba(0,200,255,.18); border-radius: 2px; }

.mv2-tp-cat-btn {
    position: relative;
    display: flex; align-items: center; gap: 8px;
    width: 100%; padding: 8px 14px 8px 16px;
    font: 600 10.5px 'Inter', 'DM Mono', sans-serif;
    letter-spacing: .3px; text-transform: none;
    color: rgba(255,255,255,.42); cursor: pointer;
    border: none; background: transparent;
    text-align: left; white-space: nowrap;
    transition: color .14s var(--mv2-ease), background .14s var(--mv2-ease);
}
/* Animated left accent bar — replaces the static border-left */
.mv2-tp-cat-btn::before {
    content: ''; position: absolute; left: 0; top: 50%;
    width: 2px; height: 0; background: var(--mv2-teal);
    border-radius: 0 2px 2px 0; opacity: 0;
    transition: height .18s var(--mv2-ease), opacity .14s, top .18s var(--mv2-ease);
    box-shadow: 0 0 6px var(--mv2-teal-glow);
    transform: translateY(-50%);
}
.mv2-tp-cat-btn:hover {
    color: rgba(255,255,255,.78);
    background: rgba(255,255,255,.025);
}
.mv2-tp-cat-btn:hover::before { height: 14px; opacity: .35; }
.mv2-tp-cat-btn.mv2-cat-active {
    color: var(--mv2-teal);
    background: linear-gradient(90deg, var(--mv2-teal-dim) 0%, transparent 80%);
    text-shadow: 0 0 10px var(--mv2-teal-glow);
}
.mv2-tp-cat-btn.mv2-cat-active::before { height: 22px; opacity: 1; }

/* Icon slot: an SVG that takes the current text color via fill="currentColor". */
.mv2-cat-ico {
    flex: 0 0 14px; width: 14px; height: 14px;
    display: inline-flex; align-items: center; justify-content: center;
    opacity: .8;
}
.mv2-cat-ico svg { width: 100%; height: 100%; display: block; }
.mv2-cat-active .mv2-cat-ico { opacity: 1; }

/* (3.5.15) Per-category colour for sidebar icons.
   Each icon adopts the same colour family as the matching .mv2-cat-badge--*,
   so the sidebar reads as a colour-coded legend rather than a wall of
   identical muted glyphs. The active state still wins over these (the
   .mv2-cat-active rule in the active-state block sets the icon to teal).
   "All" stays on the default text colour since it isn't a category. */
.mv2-tp-cat-btn[data-cat="Abstract"]    .mv2-cat-ico { color: #c080ff; }
.mv2-tp-cat-btn[data-cat="Audio"]       .mv2-cat-ico { color: #ff7aaf; }
.mv2-tp-cat-btn[data-cat="Electric"]    .mv2-cat-ico { color: #ffd040; }
.mv2-tp-cat-btn[data-cat="Fluid"]       .mv2-cat-ico { color: #40b4ff; }
.mv2-tp-cat-btn[data-cat="Geometric"]   .mv2-cat-ico { color: #40e8c8; }
.mv2-tp-cat-btn[data-cat="Mesh 3D"]     .mv2-cat-ico { color: #a0c0ff; }
.mv2-tp-cat-btn[data-cat="Neon"]        .mv2-cat-ico { color: #ff60d8; }
.mv2-tp-cat-btn[data-cat="Organic"]     .mv2-cat-ico { color: #70d870; }
.mv2-tp-cat-btn[data-cat="Psychedelic"] .mv2-cat-ico { color: #ff9060; }
.mv2-tp-cat-btn[data-cat="Retro"]       .mv2-cat-ico { color: #ffb840; }
.mv2-tp-cat-btn[data-cat="Space"]       .mv2-cat-ico { color: #8090ff; }
.mv2-tp-cat-btn[data-cat="Physics"]     .mv2-cat-ico { color: #40d8ff; }
.mv2-tp-cat-btn[data-cat="Other"]       .mv2-cat-ico { color: rgba(255,255,255,.45); }
/* Active state: the category being viewed switches to the canonical teal so
   the active row reads as the active row regardless of its category colour. */
.mv2-tp-cat-btn.mv2-cat-active .mv2-cat-ico { color: var(--mv2-teal); opacity: 1; }

/* Label: takes whatever's left between the icon and the count badge */
.mv2-cat-label { flex: 1 1 auto; min-width: 0; }

.mv2-cat-count {
    flex: 0 0 auto;
    font: 600 8.5px 'DM Mono', monospace; letter-spacing: .5px;
    background: rgba(255,255,255,.05); border-radius: 3px; padding: 2px 5px;
    /* (3.5.15) Fixed visual width: counts run from 11 ("Audio") to 321 ("All"),
       which without min-width produces a ragged right edge in the sidebar.
       24px fits any 3-digit count and centers shorter ones consistently. */
    min-width: 24px; box-sizing: border-box; text-align: center;
    opacity: .55;
    transition: opacity .14s, background .14s;
}
.mv2-tp-cat-btn:hover .mv2-cat-count { opacity: .8; }
.mv2-tp-cat-btn.mv2-cat-active .mv2-cat-count {
    background: rgba(0,200,255,.14); color: var(--mv2-teal); opacity: 1;
}

/* Grid */
.mv2-tp-grid-wrap {
    flex: 1; overflow-y: auto; padding: 8px;
    scrollbar-width: thin; scrollbar-color: rgba(0,200,255,.08) transparent;
}
.mv2-tp-grid-wrap::-webkit-scrollbar { width: 4px; }
.mv2-tp-grid-wrap::-webkit-scrollbar-thumb { background: rgba(0,200,255,0.2); border-radius: 2px; }
.mv2-tp-grid { display: grid; grid-template-columns: repeat(5,1fr); gap: 5px; align-content: start; }

/* Category label in grid */
.mv2-tp-grid-cat-label {
    grid-column: 1/-1; padding: 7px 6px 3px;
    font: 7.5px 'DM Mono', monospace; letter-spacing: 2.5px; text-transform: uppercase;
    color: rgba(0,200,255,.30);
    border-top: 1px solid rgba(255,255,255,.04); margin-top: 2px; pointer-events: none;
}
.mv2-tp-grid-cat-label:first-child { border-top: none; margin-top: 0; }

/* Template tiles — bumped from 8px to 9.5px font for readability (3.5.7) */
.mv2-tp-tile {
    padding: 8px 8px 7px;
    font: 600 9.5px 'Inter', 'Syne', sans-serif; letter-spacing: .25px; text-transform: none;
    color: rgba(255,255,255,.4); cursor: pointer;
    border-radius: 5px; border: 1px solid rgba(255,255,255,.04);
    background: rgba(255,255,255,.022);
    line-height: 1.35; word-break: break-word;
    transition: all .13s var(--mv2-ease);
    /* Subtle inner glow on each tile */
    box-shadow: inset 0 1px 0 rgba(255,255,255,.04);
}
.mv2-tp-tile:hover {
    color: rgba(255,255,255,.88);
    background: rgba(0,200,255,.07);
    border-color: rgba(0,200,255,.18);
    transform: translateY(-2px) scale(1.01);
    box-shadow:
        0 4px 16px rgba(0,0,0,.4),
        0 0 14px rgba(0,200,255,.08),
        inset 0 1px 0 rgba(0,200,255,.10);
}
.mv2-tp-tile.mv2-tile-active {
    color: #fff;
    background: linear-gradient(145deg, rgba(0,200,255,.12), rgba(0,160,255,.06));
    border-color: rgba(0,200,255,.32);
    box-shadow:
        0 0 20px rgba(0,200,255,.12),
        inset 0 1px 0 rgba(0,220,255,.15),
        inset 0 -1px 0 rgba(0,0,0,.2);
}
/* (3.5.15) Active tile's title takes the teal accent so the active state
   reads at a glance — previously the title stayed white-on-teal-tint which
   was easy to miss when scrolling a long category. */
.mv2-tp-tile.mv2-tile-active .mv2-tile-lbl {
    color: var(--mv2-teal);
    text-shadow: 0 0 8px var(--mv2-teal-glow);
}
.mv2-tp-empty {
    grid-column: 1/-1; padding: 28px 12px; text-align: center;
    color: rgba(255,255,255,.18); font: 10px 'DM Mono', monospace; letter-spacing: 1px;
}

/* Hide old dropdown list if PHP still emits it */
.mv2-template-list { display: none !important; }

/* ── GPU Badges ─────────────────────────────────────── */
.mv2-tp-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding-bottom: 6px;
    /* (3.5.15) Equal height across the row regardless of label wrapping —
       1-line titles and 2-line titles both anchor their bottom badges to
       the same baseline, so a row of 5 tiles reads as a clean ruled grid. */
    min-height: 56px;
}
.mv2-tile-lbl {
    flex: 1;
    font-size: inherit;
    /* (3.5.15) Reserve fixed space at the right edge for the favourite star.
       Was 0 — when the star faded in on hover, it absolutely-positioned over
       the last few characters of long titles like "Neon Grid 80" → "Neon Grid 8".
       Star is 13px char + 2px padding × 2 + 4px right offset ≈ 22px reserve. */
    padding-right: 22px;
}
/* (3.5.15) Anchor the category + GPU badges to the bottom of each tile so
   single-line and two-line titles produce identically-aligned badge rows.
   Without this, a 1-line title pushed its badge up against the title and a
   2-line title pushed it lower, breaking the visual grid. */
.mv2-tp-tile .mv2-cat-badge { margin-top: auto; }
.mv2-gpu-badge {
    display: inline-block;
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.06em;
    padding: 1px 5px;
    border-radius: 3px;
    line-height: 1.5;
    align-self: flex-start;
    pointer-events: none;
    user-select: none;
}
.mv2-gpu-compute {
    background: rgba(80, 200, 255, 0.15);
    color: #50c8ff;
    border: 1px solid rgba(80, 200, 255, 0.35);
}
.mv2-gpu-mesh {
    background: rgba(160, 100, 255, 0.15);
    color: #a064ff;
    border: 1px solid rgba(160, 100, 255, 0.35);
}

/* ── Favorites ──────────────────────────────────────── */
.mv2-tile-star {
    position: absolute;
    top: 4px;
    right: 4px;
    background: none;
    border: none;
    cursor: pointer;
    font-size: 13px;
    line-height: 1;
    /* Was --mv2-text-dim (never defined) → fell back to user-agent
       buttontext (black) which was invisible on the dark tile.
       --mv2-mute is the design system's muted token. */
    color: var(--mv2-mute);
    opacity: 0;
    transition: opacity .15s, color .15s, transform .1s;
    padding: 2px;
}
.mv2-tp-tile:hover .mv2-tile-star,
.mv2-tile-fav .mv2-tile-star {
    opacity: 1;
}
.mv2-tile-fav .mv2-tile-star,
.mv2-tile-star:hover {
    color: #f5c842;
}
.mv2-tile-star:active { transform: scale(0.85); }
.mv2-tile-fav {
    border-color: rgba(245, 200, 66, 0.35) !important;
    background: rgba(245, 200, 66, 0.06) !important;
}

/* ── Category Badges (every tile) ──────────────────── */
.mv2-cat-badge {
    display: inline-block;
    font-size: 7px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    padding: 1px 5px;
    border-radius: 3px;
    line-height: 1.6;
    margin-top: 3px;
    align-self: flex-start;
    pointer-events: none;
    user-select: none;
    opacity: 0.75;
}
/* Category colour map */
.mv2-cat-badge--abstract    { background:rgba(180,80,255,.18); color:#c080ff; border:1px solid rgba(180,80,255,.25); }
.mv2-cat-badge--audio       { background:rgba(255,90,160,.14); color:#ff7aaf; border:1px solid rgba(255,90,160,.22); }
.mv2-cat-badge--electric    { background:rgba(255,200,0,.14);  color:#ffd040; border:1px solid rgba(255,200,0,.22); }
.mv2-cat-badge--fluid       { background:rgba(0,160,255,.15);  color:#40b4ff; border:1px solid rgba(0,160,255,.22); }
.mv2-cat-badge--geometric   { background:rgba(0,220,180,.13);  color:#40e8c8; border:1px solid rgba(0,220,180,.20); }
.mv2-cat-badge--mesh-3d     { background:rgba(140,180,255,.13); color:#a0c0ff; border:1px solid rgba(140,180,255,.22); }
.mv2-cat-badge--neon        { background:rgba(255,80,200,.14); color:#ff60d8; border:1px solid rgba(255,80,200,.22); }
.mv2-cat-badge--organic     { background:rgba(80,200,80,.14);  color:#70d870; border:1px solid rgba(80,200,80,.22); }
.mv2-cat-badge--psyche      { background:rgba(255,120,40,.14); color:#ff9060; border:1px solid rgba(255,120,40,.22); }
.mv2-cat-badge--psychedelic { background:rgba(255,120,40,.14); color:#ff9060; border:1px solid rgba(255,120,40,.22); }
.mv2-cat-badge--retro       { background:rgba(255,180,60,.13); color:#ffb840; border:1px solid rgba(255,180,60,.20); }
.mv2-cat-badge--space       { background:rgba(60,100,255,.15); color:#8090ff; border:1px solid rgba(60,100,255,.22); }
.mv2-cat-badge--physics     { background:rgba(0,220,255,.12);  color:#40d8ff; border:1px solid rgba(0,220,255,.18); }
.mv2-cat-badge--other       { background:rgba(255,255,255,.06); color:rgba(255,255,255,.35); border:1px solid rgba(255,255,255,.10); }

/* Top-edge category accent: a thin coloured stripe at the top of every tile,
   keyed off the same `.mv2-cat-badge--*` modifier the loader already sets on
   the badge. Done as a sibling-relative selector so we don't have to change
   the loader. (3.5.7) */
.mv2-tp-tile { position: relative; }
.mv2-tp-tile::before {
    content: ''; position: absolute; top: 0; left: 8%; right: 8%;
    height: 2px; border-radius: 0 0 2px 2px; opacity: .35;
    transition: opacity .14s, left .14s, right .14s;
    background: rgba(255,255,255,.18);
}
.mv2-tp-tile:hover::before { opacity: .9; left: 4%; right: 4%; }
.mv2-tp-tile.mv2-tile-active::before { opacity: 1; left: 2%; right: 2%; }
.mv2-tp-tile:has(.mv2-cat-badge--abstract)::before    { background:#c080ff; }
.mv2-tp-tile:has(.mv2-cat-badge--audio)::before       { background:#ff7aaf; }
.mv2-tp-tile:has(.mv2-cat-badge--electric)::before    { background:#ffd040; }
.mv2-tp-tile:has(.mv2-cat-badge--fluid)::before       { background:#40b4ff; }
.mv2-tp-tile:has(.mv2-cat-badge--geometric)::before   { background:#40e8c8; }
.mv2-tp-tile:has(.mv2-cat-badge--mesh-3d)::before     { background:#a0c0ff; }
.mv2-tp-tile:has(.mv2-cat-badge--neon)::before        { background:#ff60d8; }
.mv2-tp-tile:has(.mv2-cat-badge--organic)::before     { background:#70d870; }
.mv2-tp-tile:has(.mv2-cat-badge--psyche)::before,
.mv2-tp-tile:has(.mv2-cat-badge--psychedelic)::before { background:#ff9060; }
.mv2-tp-tile:has(.mv2-cat-badge--retro)::before       { background:#ffb840; }
.mv2-tp-tile:has(.mv2-cat-badge--space)::before       { background:#8090ff; }
.mv2-tp-tile:has(.mv2-cat-badge--physics)::before     { background:#40d8ff; }

/* Tile hover: pop badge opacity */
.mv2-tp-tile:hover .mv2-cat-badge,
.mv2-tp-tile.mv2-tile-active .mv2-cat-badge { opacity: 1; }

/* ── Mobile panel (bottom sheet) ───────────────────── */
@media (max-width: 639px) {
    .mv2-template-panel {
        width: 100vw !important;
        left: 0 !important;
        bottom: 0 !important;
        top: auto !important;
        border-radius: 14px 14px 0 0 !important;
        max-height: 75vh;
        animation: mv2PanelUp .22s cubic-bezier(.16,1,.3,1);
    }
    @keyframes mv2PanelUp {
        from { opacity:0; transform: translateY(20px); }
        to   { opacity:1; transform: none; }
    }
    .mv2-tp-body { max-height: calc(75vh - 90px); }
    .mv2-tp-grid { grid-template-columns: repeat(3,1fr); }
    /* Collapse sidebar to icon-only on narrow viewports so the wider 158px
       desktop sidebar doesn't cannibalise the grid (3.5.7) */
    .mv2-tp-cats        { width: 48px; }
    .mv2-tp-cat-btn     { padding: 9px 0; justify-content: center; gap: 0; }
    .mv2-cat-label      { display: none; }
    .mv2-cat-count      { display: none; }
    .mv2-cat-ico        { flex: 0 0 16px; width: 16px; height: 16px; }
}

/* ── Filter chips ───────────────────────────────────── */
.mv2-tp-filter-chips {
    display: flex;
    gap: 5px;
    margin-top: 7px;
    flex-wrap: wrap;
}
/* (3.5.15) Each chip stretches equally so the three filters read as a
   proper segmented-control row instead of three left-clumped pills with
   wasted whitespace to the right. justify-content centers icon+label
   inside each equal-width segment. */
.mv2-tp-filter-chips .mv2-chip { flex: 1 1 0; justify-content: center; }
.mv2-chip {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 3px 9px;
    border-radius: 20px;
    border: 1px solid rgba(255,255,255,.10);
    background: rgba(255,255,255,.04);
    color: rgba(255,255,255,.40);
    font: 600 8px 'DM Mono', monospace;
    letter-spacing: 0.08em;
    cursor: pointer;
    transition: all .13s var(--mv2-ease);
    white-space: nowrap;
}
.mv2-chip:hover {
    border-color: rgba(255,255,255,.22);
    color: rgba(255,255,255,.75);
    background: rgba(255,255,255,.07);
}
/* (3.5.15) Chip icons take their semantic colour at rest so the filter
   row reads as three distinct controls — compute=cyan, mesh=purple,
   favorites=gold — instead of three muted-gray glyphs that all look the
   same until clicked. The icon stays coloured even when the chip itself
   is at rest; the chip *background* still tints only when active. */
.mv2-chip-ico {
    display: inline-block;
    font-size: 1.05em;
    line-height: 1;
    transition: text-shadow .15s, transform .15s;
}
.mv2-chip[data-filter="compute"] .mv2-chip-ico { color: #50c8ff; text-shadow: 0 0 6px rgba(80,200,255,.35); }
.mv2-chip[data-filter="mesh"]    .mv2-chip-ico { color: #a064ff; text-shadow: 0 0 6px rgba(160,100,255,.35); }
.mv2-chip[data-filter="fav"]     .mv2-chip-ico { color: #f5c842; text-shadow: 0 0 6px rgba(245,200,66,.35); }
.mv2-chip:hover .mv2-chip-ico { transform: scale(1.12); }
.mv2-chip.mv2-chip-active .mv2-chip-ico { transform: scale(1.08); }
/* Chip active states — each type gets its own colour */
.mv2-chip[data-filter="compute"].mv2-chip-active {
    border-color: rgba(80,200,255,.5);
    background: rgba(80,200,255,.12);
    color: #50c8ff;
    box-shadow: 0 0 10px rgba(80,200,255,.15);
}
.mv2-chip[data-filter="mesh"].mv2-chip-active {
    border-color: rgba(160,100,255,.5);
    background: rgba(160,100,255,.12);
    color: #a064ff;
    box-shadow: 0 0 10px rgba(160,100,255,.15);
}
.mv2-chip[data-filter="fav"].mv2-chip-active {
    border-color: rgba(245,200,66,.5);
    background: rgba(245,200,66,.12);
    color: #f5c842;
    box-shadow: 0 0 10px rgba(245,200,66,.12);
}

/* BPM badge pulse */
@keyframes mv2-bpm-pulse {
    0%,100% { opacity:0.85; box-shadow:0 0 0 rgba(0,200,255,0); }
    50%      { opacity:1;    box-shadow:0 0 6px rgba(0,200,255,.35); }
}
.mv2-np-bpm.mv2-bpm-beat { animation: mv2-bpm-pulse 0.12s ease-out; }


/* ═══ LIGHT MODE — comprehensive sidebar sync ═══
   Canvas stays dark. All UI chrome adapts. */

/* Override all CSS variables when esh-light is active */
html.esh-light .mv2-container {
  --mv2-void:      #f0f0f6;
  --mv2-deep:      #e8e8f0;
  --mv2-surface:   #e0e0ea;
  --mv2-raised:    #d4d4e0;
  --mv2-glass:     rgba(240,240,246,0.92);
  --mv2-glass-hi:  rgba(240,240,246,0.97);
  --mv2-border:    rgba(0,0,0,0.08);
  --mv2-border-hi: rgba(0,150,200,0.3);
  --mv2-teal-dim:  rgba(0,180,230,0.12);
  --mv2-teal-glow: rgba(0,180,230,0.2);
  --mv2-hi:   #0a0a1a;
  --mv2-md:   rgba(10,10,30,0.75);
  --mv2-lo:   rgba(40,50,80,0.55);
  --mv2-mute: rgba(80,90,120,0.35);
}

/* Keep canvas/display always dark */
html.esh-light .mv2-display { background: #000 !important; }

/* Template button — readable on light */
html.esh-light .mv2-template-btn {
  text-shadow: none;
}
html.esh-light .mv2-template-btn:hover {
  box-shadow: 0 0 12px rgba(0,180,230,0.15);
}

/* Template panel stays dark (it's a floating overlay) */
html.esh-light .mv2-template-panel {
  background:
    radial-gradient(ellipse at 20% 0%, rgba(0,160,255,.06) 0%, transparent 55%),
    radial-gradient(ellipse at 80% 100%, rgba(120,0,255,.05) 0%, transparent 50%),
    linear-gradient(160deg, rgba(4,8,22,1) 0%, rgba(2,4,14,1) 100%);
  --mv2-hi: #eff4fa;
  --mv2-md: rgba(210,228,248,0.85);
  --mv2-lo: rgba(140,175,210,0.60);
  --mv2-mute: rgba(90,120,155,0.45);
  --mv2-border: rgba(255,255,255,0.06);
  --mv2-raised: #0e1525;
  --mv2-surface: #080d18;
}

/* Play button always cyan */
html.esh-light .mv2-play-btn { background: var(--mv2-teal); color: #000; }
html.esh-light .mv2-play-btn:hover { box-shadow: 0 0 12px rgba(0,200,255,0.3); }

/* Progress/volume sliders */
html.esh-light .mv2-progress,
html.esh-light .mv2-volume {
  background-color: var(--mv2-raised);
}

/* Playlist rows */
html.esh-light .mv2-pl-row { border-bottom-color: rgba(0,0,0,0.04); }
html.esh-light .mv2-pl-row:hover { background: rgba(0,0,0,0.04); }
html.esh-light .mv2-pl-row.mv2-pl-active { background: rgba(0,180,230,0.08); }

/* No-GPU warning */
html.esh-light .mv2-no-gpu {
  background: rgba(255,80,80,.06);
  border-color: rgba(255,80,80,.12);
}

/* ── Reduced-motion accessibility (added 3.5.15) ────────────────────────────
   Honors prefers-reduced-motion at the OS level. Disables the hue-cycling
   title animations, the BPM pulse, and the slide-in panel transitions, while
   leaving the WebGPU canvas itself untouched (the visualizer is the explicit
   point of the plugin and the user has opted into watching it). Static
   cross-fades and instant state changes replace motion-heavy transitions.
   ───────────────────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    .mv2-np-title,
    .mv2-pl-active .mv2-pl-title,
    .mv2-pl-active .mv2-pl-num {
        animation: none !important;
        color: var(--mv2-teal);
    }
    .mv2-np-bpm.mv2-bpm-beat { animation: none !important; }
    .mv2-template-panel { animation: none !important; }
    @media (max-width: 640px) {
        .mv2-template-panel { animation: none !important; }
    }
    /* Keep functional transitions (focus rings, hover feedback) snappy but
       don't suppress them entirely — removing all transitions makes the UI
       feel broken. Cap at 80ms so motion is barely perceptible. */
    .mv2-container *,
    .mv2-container *::before,
    .mv2-container *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 80ms !important;
    }
}
