feat(p12-w4): ambient visualizer slot on scaffold + popover controls on all detail hosts

Add optional Ambient slot to ReleaseDetailScaffold (full-bleed layer behind content; absent = no regression). Cut mounts it + popover; Session mounts the engine directly behind its hero; Mix swaps its inline knob-bar for the lava-lamp popover.
This commit is contained in:
daniel-c-harvey
2026-06-17 12:11:03 -04:00
parent 9009f2c8cf
commit 955182d6da
7 changed files with 87 additions and 32 deletions
@@ -5,7 +5,17 @@
hero visual and metadata block. The Cut/Session/Mix detail pages all compose this;
per-medium variance rides the Hero and MetaContent render fragments. *@
<div class="deepdrft-track-detail-container">
@* Ambient environment layer (Phase 12 §3c/§3f mode B): an optional full-bleed layer rendered BEHIND
the scaffold content. The visualizer mounted here positions itself fixed/inset:0 (its own CSS), so
this slot's only job is to render it before the content and put the content into a foreground
stacking context above it. Absent slot = no ambient layer and no foreground promotion → today's
plain background, byte-for-byte (Liskov). *@
@if (Ambient is not null)
{
@Ambient
}
<div class="deepdrft-track-detail-container @(Ambient is not null ? "deepdrft-track-detail-foreground" : null)">
@* Two-end top row: back link (left) | optional action (right), on one SpaceBetween row. The action
slot stays null for media that don't supply it (Track/Cut/Session), so SpaceBetween collapses to
@@ -51,6 +51,16 @@ public partial class ReleaseDetailScaffold : ComponentBase
/// <summary>Medium-specific hero visual (cover art, hero image, or waveform background).</summary>
[Parameter] public RenderFragment? Hero { get; set; }
/// <summary>
/// Optional full-bleed ambient layer rendered BEHIND the scaffold content (Phase 12 §3c/§3f mode B).
/// A host that wants a living environment behind hero+content — e.g. Cut supplying a
/// <c>WaveformVisualizer</c> — places it here. The mounted layer positions itself fixed/inset:0
/// (its own CSS), so the scaffold only promotes its content into a foreground stacking context above
/// it. Absent = today's plain background, no regression (Liskov). Mode A (Mix) and mode C (the
/// NowPlaying card) mount the engine without this slot — see §3f.
/// </summary>
[Parameter] public RenderFragment? Ambient { get; set; }
/// <summary>
/// Optional body region rendered below the meta block — the Cut album's multi-track listing.
/// Single-track media leave it null.
@@ -3,3 +3,13 @@
justify-content: center;
margin-top: 1.5rem;
}
/* Foreground stacking context — applied only when an Ambient layer is present. Lifts the scaffold
content above the fixed full-bleed visualizer (z-index: 0) so hero + meta + body render over the
living waveform field (Phase 12 §3c — promotes the former Mix-bespoke .mix-detail-foreground into
the shared scaffold). Without an Ambient slot this class is absent and the container keeps its
default flow, so a slot-less host renders exactly as before. */
.deepdrft-track-detail-foreground {
position: relative;
z-index: 1;
}