@using DeepDrftPublic.Client.Services @implements IDisposable @* Hero-right panel (owns the navy backdrop + clipping, formerly Home.razor's .hero-right wrapper). *@
@* Full-bleed waveform background. The visualizer mounts with Fill="true" (position:absolute; inset:0), so .np-visualizer-bg is the positioned, sized ancestor that lets the lava fill the whole hero-right panel. Driven by the live cascaded player — keyed on the current track via ReleaseEntryKey/TrackId/ TrackEntryKey so it scrolls to the real signal and sits at-rest when nothing plays. Read-only: the background visualizes, it never seeks. Sits behind the rings + content (z-order via stacking below). *@
@* The lava-lamp trigger lands in the panel's top-right corner (full parity, §8e). Above the canvas and pointer-enabled so the icon is clickable even though the visualizer layer is pointer-events:none. The panel itself opens screen-centered (Phase 15 §4 — no per-host anchor), so only the icon size is host-specific now. *@
@* Pulsing rings *@
@* Stat row — live aggregate figures (Cut track count + type breakdown, Mix sets + runtime); the Plays card is a static placeholder pending real play tracking. *@
@code { [CascadingParameter] public IStreamingPlayerService? Player { get; set; } private IStreamingPlayerService? _subscribedPlayer; protected override void OnParametersSet() { if (Player != null && !ReferenceEquals(Player, _subscribedPlayer)) { if (_subscribedPlayer != null) _subscribedPlayer.StateChanged -= OnPlayerStateChanged; Player.StateChanged += OnPlayerStateChanged; _subscribedPlayer = Player; } } private void OnPlayerStateChanged() => InvokeAsync(StateHasChanged); public void Dispose() { if (_subscribedPlayer != null) { _subscribedPlayer.StateChanged -= OnPlayerStateChanged; _subscribedPlayer = null; } } }