Files
deepdrft/product-notes/mix-detail-hero-overlay.md
T

381 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Mix Detail — Hero + MetaContent overlay rework (mirror the Sessions hero) — Design Spec
Status: **shipped on dev** (2026-06-16). Author: product-designer. Date: 2026-06-16.
## 0. Goal
Rework the **Mix detail** Hero + MetaContent so the **cover art becomes a background image** with **all
metadata laid out on top of it**, inside a **max-medium square cover-art region** — mirroring the
already-shipped **Session detail** hero-overlay composition. This consolidates the masthead + cover +
meta into one overlaid block, frees vertical room for the lava-lamp visualizer behind the content, and
cleans up the aesthetic so the Mix page reads as a member of the same design family as Sessions.
The Mix visualizer + eight-knob controls layout landed in the Phase 10 reframe (`TopRowCenter` slot,
in-flow controls container between the back link and the lava-lamp) **must be preserved unchanged**
this rework touches only what sits *below* that top row.
---
## 1. What is actually shipped today (confirmed from live source, not spec)
Read of the live tree on 2026-06-16:
**Session detail** (`Pages/SessionDetail.razor` + `.razor.css`) — the target aesthetic, **already shipped**:
- **Does NOT use `ReleaseDetailScaffold`.** It composes its own hero overlay directly inside a
`MudContainer MaxWidth="Large"`, and wires `PlayTrack` in its own `@code` block (the scaffold's
play-toggle logic is duplicated here deliberately — see `SessionDetail.razor` lines 131145).
- Structure: a `.session-hero` positioning context (`position: relative; aspect-ratio: 16/10;
max-height: 70vh; min-height: 420px; overflow: hidden; border-radius: 8px`) holding:
- `.session-hero-img` — a `MudPaper` whose `background-image` is the hero/cover image
(`background-size: cover`), or a `.session-hero-placeholder` when none.
- `.session-hero-shim` — a plain `<div>` darkening gradient (stronger top + bottom) for overlay legibility.
- `.session-hero-top` — absolutely-positioned overlay row: genre chip + release-date + `SharePopover`.
- `.session-hero-bottom` — absolutely-positioned overlay row: optional cover thumbnail + title/artist +
`PlayStateIcon`.
- The back link (`.deepdrft-track-detail-back`) sits **above** the hero, in normal flow.
- CSS uses `::deep` on every class that lands on a MudBlazor child component's native output
(`.session-hero-img` on `MudPaper`, `.session-overlay-chip.mud-chip` on `MudChip`,
`.session-hero-bottom-row` on `MudStack`, the play/share icon-color overrides on `MudIconButton`).
**Mix detail** (`Pages/MixDetail.razor` + `.razor.css`) — the page to rework, **uses the scaffold**:
- `<MixWaveformVisualizer>` paints a fixed full-viewport backdrop (`z-index: 0`); a
`.mix-detail-foreground` wrapper (`z-index: 1`) lifts the content above it; inside a
`MudContainer MaxWidth="Large"` (class `mix-detail-container`) sits a `<ReleaseDetailScaffold>`.
- The scaffold's **`TopRowCenter`** slot holds `<MixVisualizerControls>`; **`TopRightAction`** holds the
lava-lamp `MudIconButton` (the Phase 10 reframe layout — **do not touch**).
- The scaffold's **`Hero`** slot today is a `.mix-detail-cover` (square, `max-width: 220px`, centered)
holding a `MudPaper` cover-art / placeholder. The **`MetaContent`** slot holds genre chip + release
date. The masthead (title + artist) is rendered by the scaffold's **default header region** above the
hero. The `BodyContent` slot holds the share row.
**The scaffold** (`Controls/ReleaseDetailScaffold.razor` + `.razor.cs`) — the shared chrome:
- Vertical order: top row (back | `TopRowCenter` | `TopRightAction`) → `TopContent` → header
(masthead + play, or a custom `Header`) → `Hero` → divider + `MetaContent` (gated by `ShowMeta`) →
`BodyContent` → default track-keyed share row (gated by `ShowShareRow`).
- Owns the back link and the play-toggle wiring. Cut and Track also consume it; Session does **not**.
**Render boundary (both detail pages are identical here — confirmed):** both `SessionDetail` and
`MixDetail` derive from `ReleaseDetailBase`, run under `InteractiveAuto`, and bridge the prerendered
release+track across the prerender→WASM seam via `PersistentComponentState` (keyed `session-detail` /
`mix-detail`). The release DTO (title, artist, genre, release date, `ImagePath`) is **available at first
render in both passes** — there is no render-mode divergence to design around. The image is served from
`api/image/{EscapeDataString(key)}` in both pages. **Conclusion: the data read at render time is the
same shape and same timing for both; the overlay rework is purely presentational.**
---
## 2. The DRY/SOLID question — where is the source of truth?
This is the load-bearing decision. The brief asks: extract a shared hero-overlay shell that both Sessions
and Mixes consume, or per-page duplication?
### The honest finding: the two pages are structurally divergent *by design today*
Sessions **deliberately does not use the scaffold** — it forks the whole chrome to get the overlay
composition. Mixes **does use the scaffold**, and the scaffold's `Hero`/`MetaContent`/masthead are
*separate stacked regions*, which is the opposite of "everything overlaid on the cover." So the two
pages do not share a hero today, and the scaffold's stacked-region model is not the overlay model.
Three meaningfully different directions, in shape:
**Direction A — Per-page copy.** Inline the `.session-hero` overlay structure into `MixDetail.razor`,
copy the `.session-*` CSS into `MixDetail.razor.css` (renamed `.mix-hero-*`), tune the square/medium
sizing. No shared component.
- *Pro:* fastest; zero risk to Sessions; Mix can diverge freely (it has a visualizer behind it that
Sessions does not).
- *Con:* **two copies of the overlay cascade to keep in sync** — exactly the drift the `track-view-css-
consolidation` note spent a pass undoing. The shim gradient, the overlay-label typography, the
`::deep` icon-color overrides, the responsive wrap rules all get duplicated. A future overlay tweak
has to land twice. Violates the "one source, multiple views" instinct (Daniel's standing preference).
**Direction B — Extract a shared `ReleaseHeroOverlay` component, both pages consume it.** Pull the
`.session-hero` overlay (image/placeholder + shim + top overlay + bottom overlay) into a new
`Controls/ReleaseHeroOverlay.razor` that takes the release data (+ optional play affordance + optional
share slot + optional cover-thumb) and renders the overlaid hero. Sessions swaps its inline hero for the
component; Mix renders the component inside its `.mix-detail-foreground`, **bypassing the scaffold's
`Hero`/`MetaContent`/masthead regions** (Mix stops using the scaffold for the hero, same as Sessions
already does).
- *Pro:* **one source of truth for the overlay** — the headline DRY win. Honors "same data shape,
different rendering": both pages feed the same release DTO into one overlay VM/parameter set, the
divergence (visualizer backdrop, square-vs-wide aspect) rides parameters/CSS, not a second copy.
SOLID: the overlay is a single-responsibility presentational component; the pages compose it.
- *Con:* touches the **already-shipped Sessions page** (regression surface on a working view) and means
Mix no longer routes its hero through the scaffold — Mix keeps the scaffold only for the back/controls
top row, or drops the scaffold entirely (see §4 sub-decision). More upfront work; a real refactor.
**Direction C — Teach the scaffold an overlay mode.** Add an overlay-hero capability to
`ReleaseDetailScaffold` so it can render its `Hero`/masthead/`MetaContent` as one overlaid block when a
flag/slot says so; migrate Sessions onto the scaffold in overlay mode and switch Mix to overlay mode.
- *Pro:* one component owns *all* detail chrome including the overlay; Sessions finally joins the scaffold.
- *Con:* **largest blast radius and worst SOLID.** It bloats the scaffold with a second layout
personality (stacked-regions *and* overlaid-hero) gated by a flag — exactly the "variance rides a
flag" anti-pattern the scaffold's own convention forbids (Phase 9 §5.3: "layout variance rides a
slot, never a boolean"). It also drags the working Sessions page through a chrome migration for no
user-visible gain. Over-engineered for a two-consumer overlay.
### Recommendation: **Direction B — extract `ReleaseHeroOverlay`, both pages consume it.**
Rationale, with the trade-off stated plainly:
- **The source of truth should be one overlay component, not the scaffold and not a copy.** Sessions
already proved the overlay wants to live *outside* the scaffold's stacked-region model — forcing it
back into the scaffold (Direction C) fights that and bloats shared chrome. Copying it (Direction A)
reintroduces the exact cascade-duplication this codebase already paid down once.
- **It satisfies "one source, multiple views" directly:** one overlay fed the same release DTO,
rendering differences (Mix's visualizer backdrop, the square medium cover vs. Sessions' wide hero)
expressed as component parameters + a CSS class, never as a forked structure.
- **The cost is real and must be owned:** Direction B edits the shipped Sessions page. That is a
regression surface on a working view. Mitigation: extract by *moving* Sessions' exact current markup +
CSS into the component first (a behavior-preserving lift — Sessions should look pixel-identical after),
then point Mix at it. The Sessions migration is the risky step; treat it as its own wave with a
before/after visual check (§7 acceptance).
**If Daniel wants to minimize risk to Sessions and ship Mix fast, Direction A is the acceptable
fallback** — but it takes on the duplication debt knowingly, and a later consolidation pass (like the
track-card one) becomes likely. Recommended only if the Sessions page is considered too load-bearing to
touch right now. **Flagging this as the one open decision for Daniel (§8, Q1).**
---
## 3. The shared component — `ReleaseHeroOverlay` (Direction B shape)
A new plain-shell presentational component: `DeepDrftPublic.Client/Controls/ReleaseHeroOverlay.razor`
(+ `.razor.css` carrying the overlay cascade, moved from `SessionDetail.razor.css`).
**Responsibility (single):** given a release's display data, render the background-image hero with the
metadata overlaid (top row: genre/date + share slot; bottom row: optional cover thumb + title/artist +
optional play affordance). It owns no data fetch, no player wiring beyond invoking a passed-in callback,
no JS interop.
**Parameters (the "same data shape" contract):**
| Parameter | Type | Purpose |
|-----------|------|---------|
| `HeroImageKey` | `string?` | The background image entry key (Sessions: hero-then-cover precedence; Mix: cover). Null → placeholder. |
| `PlaceholderIcon` | `string` | Material icon for the no-image placeholder (Sessions: `Piano`; Mix: `Album`). |
| `CoverThumbKey` | `string?` | Optional small cover thumbnail shown in the bottom row (Sessions shows it only when it differs from the hero image; Mix likely null — see §4). |
| `Title` / `Artist` | `string` / `string?` | Overlaid title + artist. |
| `Genre` | `string?` | Genre chip (top overlay) when present. |
| `ReleaseDate` | `DateOnly?` | Release date (top overlay) when present. |
| `ShareContent` | `RenderFragment?` | The share affordance (each page passes its `SharePopover` with the right release params). |
| `PlayContent` | `RenderFragment?` | The play affordance (each page passes its `PlayStateIcon` wired to its own toggle). |
| `Class` | `string?` | Extra class for per-page aspect/sizing variance (`mix-hero` vs default wide). |
Play/share ride as **slots**, not as wired-in player logic, so the component stays presentational and
each page keeps owning its own play-toggle (Sessions already does; the scaffold does for Mix today —
this preserves that ownership without the component reaching for the cascaded player).
**House constraint — plain-div shell.** The overlay shell (`.release-hero`, `.release-hero-shim`,
`.release-hero-top`, `.release-hero-bottom`) is **plain `<div>`s with project CSS classes**, exactly as
Sessions does today. The **background-image surface stays a `MudPaper`** *only because Sessions already
uses one there* — but note it carries no card affordances; if staff-engineer prefers, it can become a
plain `<div class="release-hero-img">` with the same `background-image` style, which is *more* aligned
with the plain-shell rule. **Recommendation: make it a plain `<div>`** and drop the `MudPaper` — there
is no MudPaper behavior being used (Elevation=0, Square=true), so the Mud wrapper is dead weight. This is
a small improvement over the shipped Sessions code; flag it to Daniel as an incidental cleanup
(§8, Q2). Chips/icons remain Mud components (they carry real behavior) and keep their `::deep` overrides.
---
## 4. Mix-specific composition + the square medium cover
Daniel's ask: "metadata laid out on top of a **max-medium square cover-art region**." Two design notes
specific to Mix that differ from Sessions:
### 4a. Aspect ratio — square medium, not Sessions' wide hero
Sessions uses `aspect-ratio: 16/10; max-height: 70vh; min-height: 420px` (a wide, tall hero). Daniel
wants Mix to use a **square** region at a **max-medium** size. The current Mix cover is `max-width:
220px` (small). "Max-medium square" reads as: a centered `aspect-ratio: 1/1` block, capped at a medium
width — recommend **`max-width: 420480px`** (medium: bigger than the 360px track cover, smaller than
the Large container) — so the overlaid metadata has room to sit on the cover without crowding, while the
lava-lamp visualizer keeps the surrounding canvas. This rides the `Class` parameter (`mix-hero`) + a Mix
CSS rule, not a forked component.
- *Why square, not wide:* the Mix cover art is square album art; a 16/10 crop would letterbox or
distort it, and the point is to free room for the visualizer, which a smaller square does better than a
full-bleed wide hero.
- *Open sub-question (§8, Q3):* on a square overlay, the **top overlay row (genre/date/share) + bottom
overlay row (title/artist/play) over a 480px square** may feel cramped versus Sessions' tall hero.
Acceptable mitigation: keep the same two-overlay structure but let the shim carry more darkening, or
drop the cover thumbnail (4b). Daniel tunes on screen per his standing preference.
### 4b. No cover thumbnail in the bottom row (recommended)
Sessions shows a small cover thumb in the bottom overlay *only when the hero image differs from the
cover* (it has a dedicated hero image distinct from the cover). **Mix has no separate hero image — the
cover art *is* the background.** So a cover thumbnail would duplicate the background. **Recommend Mix
passes `CoverThumbKey = null`** — the bottom overlay is just title/artist + play. This falls out of the
shared component's existing `showCover` logic for free.
### 4c. Mix keeps the scaffold for the top row only — or drops it
With the hero now a self-composed overlay (not the scaffold's `Hero`/`MetaContent`/masthead regions),
Mix has a sub-decision:
- **Option (i) — keep `ReleaseDetailScaffold` for the back/controls top row only.** Mix keeps supplying
`TopRowCenter` (controls) + `TopRightAction` (lava-lamp) to the scaffold, but stops supplying `Hero` /
`MetaContent` / the default masthead, and instead renders `<ReleaseHeroOverlay>` in `BodyContent` (or a
new slot). **Problem:** the scaffold's default header region renders the masthead (title+artist) *and*
a second `PlayStateIcon` — which would now duplicate the overlay's title/artist/play. Suppressing the
scaffold's masthead requires the `Header` slot to render *nothing*, which is awkward (the slot exists to
*replace* the masthead, and an empty `Header` is a smell).
- **Option (ii) — Mix drops the scaffold, composes directly like Sessions does.** Mix renders the back
link + the controls top row + the lava-lamp + `<ReleaseHeroOverlay>` itself inside
`.mix-detail-foreground`, mirroring how Sessions composes directly. **But** this loses the scaffold's
ownership of the back link and the three-zone top-row structure that the Phase 10 reframe specifically
built into the scaffold (`TopRowCenter`), and would duplicate that row.
**Recommendation: Option (i), with the scaffold's masthead suppressed by passing an empty-but-present
`Header` fragment** — *or*, cleaner, **the overlay renders inside the scaffold's existing `Hero` slot and
Mix passes a `Header` fragment that renders nothing** so the scaffold contributes only the top row +
hero. The least-smelly realization: **keep the scaffold (it owns the Phase 10 top row — the constraint we
must preserve), put `<ReleaseHeroOverlay>` in the `Hero` slot, leave `MetaContent` null (metadata now
lives in the overlay), and pass a no-op `Header` fragment to suppress the duplicate masthead/play.**
This preserves the visualizer/controls layout exactly (§5) while moving the hero+meta into the overlay.
> **Flag for staff-engineer:** an empty `Header` fragment to suppress the masthead is slightly awkward
> but is the lowest-risk way to keep the Phase 10 top row intact. If it reads badly in code, the
> alternative is a small scaffold change (a `ShowHeader` gate mirroring `ShowMeta`/`ShowShareRow`) — that
> is a *minimal, slot-consistent* scaffold edit (a gate, not a layout flag), acceptable under the Phase 9
> §5.3 convention because it suppresses an optional region rather than switching layouts. **Recommend the
> `ShowHeader` gate over the empty-fragment hack** if staff-engineer touches the scaffold anyway. Noted
> as §8 Q4.
---
## 5. Preserving the Phase 10 visualizer + controls layout (hard constraint)
The Phase 10 reframe put the controls in the scaffold's **`TopRowCenter`** slot and the lava-lamp in
**`TopRightAction`**, with the in-flow grow/collapse behavior and the `flex-wrap` responsive drop. This
rework **must not disturb any of that.** Concretely:
- **Keep the scaffold and its three-zone top row.** Mix continues supplying `TopRowCenter`
(`<MixVisualizerControls>`) and `TopRightAction` (the lava-lamp `MudIconButton`) unchanged. The
`_controlsExpanded` flag, `ToggleSettings`, and the filled/outline lamp glyph are untouched.
- **The overlay goes *below* the top row**, in the `Hero` slot — it occupies the space the old
`.mix-detail-cover` + masthead + `MetaContent` occupied. Net effect: the region below the controls row
gets *shorter* (one overlaid block instead of masthead + 220px cover + meta divider + meta row),
which **frees more canvas for the lava-lamp visualizer** — exactly Daniel's stated goal.
- **`MixWaveformVisualizer` is unchanged.** It is a fixed full-viewport backdrop at `z-index: 0`; the
overlay rides inside `.mix-detail-foreground` (`z-index: 1`) like the current cover does. The footer
clip / lava-rest-line work (Phase 10 §2c) is independent of this and unaffected.
- **`MetaContent` becomes null** (metadata moves into the overlay), so the scaffold's divider +
`.deepdrft-track-detail-meta` row no longer renders for Mix. The share row moves into the overlay's
`ShareContent` slot (matching Sessions, which overlays share top-right).
**Acceptance tie-in:** after the rework, expanding the controls still grows the in-flow container between
back and lamp, still wraps on narrow widths, and the lava-lamp still toggles it — no change to that
interaction (§7).
---
## 6. CSS approach
- **The overlay cascade moves into `ReleaseHeroOverlay.razor.css`** (Direction B), renamed from
`.session-*` to neutral `.release-hero-*`. Sessions' `SessionDetail.razor.css` keeps only what is
page-specific (the `.session-detail-page` padding, the per-page aspect overrides if any); Mix's
`MixDetail.razor.css` keeps only the `.mix-detail-foreground` z-index lift + the `mix-hero` square/
medium sizing override.
- **`::deep` is required** wherever a class lands on a MudBlazor child component's native output — this
is unchanged from Sessions today and must carry into the shared component's scoped CSS:
- the background-image surface **if it stays `MudPaper`** (`::deep .release-hero-img`) — *avoidable by
making it a plain `<div>`, §3, which removes the `::deep` need for that element entirely*;
- the genre chip on `MudChip` (`::deep .release-overlay-chip.mud-chip`);
- the bottom-row `MudStack` wrap (`::deep .release-hero-bottom-row`);
- the play/share icon-color overrides on `MudIconButton`/`MudProgressCircular`
(`::deep .release-hero-play .mud-icon-button`, etc.).
- **The square/medium aspect for Mix** is a single override scoped to the `mix-hero` class
(`aspect-ratio: 1/1; max-width: ~480px; margin-inline: auto`) — it overrides the component's default
wide aspect. This is the "divergence rides CSS, not structure" realization of the one-source rule.
- **No `MudCard`/`MudPaper` shell for the overlay container** (house rule). The `.release-hero`
positioning context, the shim, and both overlay rows are plain `<div>`s — as Sessions already does.
The only Mud wrappers that remain are the chip, the icon buttons, and (optionally) the image surface —
each carrying real component behavior, none acting as a layout shell. **If staff-engineer reaches for a
Mud wrapper as a layout shell, stop and flag it** (per the standing constraint).
---
## 7. Acceptance criteria (observable)
1. **Cover-as-background.** On a Mix detail page with a cover image, the cover renders as a
background-image hero (`background-size: cover`), not as a bordered thumbnail; metadata sits overlaid
on top of it.
2. **Square medium region.** The hero is a centered square (`aspect-ratio: 1/1`) capped at a medium
width (~480px), visibly larger than the old 220px cover, with the surrounding canvas left to the
visualizer.
3. **All metadata overlaid.** Title, artist, genre (when present), release date (when present), and the
share affordance all render *over* the cover image — no separate masthead block, no separate meta
divider/row below the cover.
4. **Placeholder path.** A Mix with no cover image shows the placeholder treatment (Album icon over the
soft-secondary gradient) with metadata still legibly overlaid.
5. **Legibility.** The darkening shim keeps overlaid text readable over light and dark cover art, in both
light and dark theme.
6. **Visualizer/controls preserved.** The lava-lamp toggle still opens the in-flow eight-knob controls
container between the back link and the lamp; it still grows in place, still wraps on narrow widths,
and the lava-lamp glyph still swaps filled/outline. No regression to the Phase 10 layout.
7. **More canvas for the visualizer.** The content block below the controls row is shorter than before
(one overlaid hero vs. masthead + cover + meta), leaving more visible visualizer area.
8. **Sessions unchanged (Direction B).** After the shared-component extraction, the Session detail page
renders pixel-identically to before (before/after visual check on a session with a dedicated hero
image, a session with cover-only, and a session with no image).
9. **One source of truth (Direction B).** The overlay markup + cascade exists in exactly one component;
grepping for `.session-hero` / `.release-hero` finds no duplicate structure across the two pages.
10. **No Mud layout shell.** The overlay container, shim, and overlay rows are plain `<div>`s; no
`MudCard`/`MudPaper` acts as a layout wrapper (image surface excepted only if kept as `MudPaper`).
---
## 8. Open questions for Daniel
1. **DRY decision (the load-bearing one).** Recommended **Direction B** — extract a shared
`ReleaseHeroOverlay` both pages consume (one source of truth; edits the shipped Sessions page).
Fallback **Direction A** — per-page copy in Mix (fast, zero Sessions risk, takes on duplication debt).
**Which do you want?** (B is the "one source, multiple views" call; A is the play-it-safe-on-Sessions
call.)
2. **Drop the `MudPaper` on the image surface** for a plain `<div class="release-hero-img">` while we're
in here? It carries no Mud behavior (Elevation=0/Square=true), so it is dead weight and a plain div is
more on-spec with the plain-shell rule. Incidental cleanup — yes/no?
3. **Square hero size.** Recommended ~**480px max-width** square. Crowded vs. roomy is a feel call you'll
tune on screen — is ~480px the right starting point, or do you want it bigger (closer to the Sessions
hero scale) / smaller (more visualizer room)?
4. **Suppressing the duplicate masthead.** Mix's hero now carries title/artist/play, so the scaffold's
default masthead must be suppressed. Recommended: add a small **`ShowHeader` gate** to the scaffold
(slot-consistent with `ShowMeta`/`ShowShareRow`), vs. the hackier empty-`Header`-fragment. OK to add
the `ShowHeader` gate?
5. **Square overlay crowding (deferrable).** If the genre/date/share top row + title/artist/play bottom
row feel cramped on a 480px square, are you fine tuning shim/spacing on screen, or do you want a
different overlay arrangement for the square (e.g. a single bottom-stacked overlay) speced now?
---
## 9. File-change inventory (for staff-engineer, Direction B)
**New:**
- `DeepDrftPublic.Client/Controls/ReleaseHeroOverlay.razor` (+ `.razor.css`) — the shared overlay,
lifted from Sessions' current hero markup + CSS, parameterized per §3.
**Edited:**
- `DeepDrftPublic.Client/Pages/SessionDetail.razor` — replace the inline `.session-hero` block with
`<ReleaseHeroOverlay ... />`, passing its hero/cover precedence, share, and play slots. Behavior-
preserving (must render identically).
- `DeepDrftPublic.Client/Pages/SessionDetail.razor.css` — remove the overlay cascade now living in the
component; keep only page-specific rules.
- `DeepDrftPublic.Client/Pages/MixDetail.razor` — replace the `.mix-detail-cover` `Hero` slot with
`<ReleaseHeroOverlay Class="mix-hero" ... CoverThumbKey="null" />`; drop `MetaContent`; move the share
row into the overlay's `ShareContent` slot; suppress the scaffold masthead (per Q4). **`TopRowCenter` /
`TopRightAction` / the visualizer / `_controlsExpanded` wiring untouched.**
- `DeepDrftPublic.Client/Pages/MixDetail.razor.css` — keep `.mix-detail-foreground`; add the `mix-hero`
square/medium sizing override; remove `.mix-detail-cover`.
- `DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor[.cs]` — **only if Q4 = ShowHeader gate:**
add a `bool ShowHeader = true` gate around the default header region.
- `DeepDrftPublic/wwwroot/styles/deepdrft-styles.css` — no change required; `.deepdrft-track-detail-
cover*` stays (Track detail still uses it). The `.mix-detail-container .deepdrft-track-detail-
container` width override stays (the scaffold still hosts the top row).
**For Direction A (fallback):** no new component; the overlay markup is inlined into `MixDetail.razor`
and the `.session-*` cascade is copied + renamed into `MixDetail.razor.css`. Sessions untouched.