From 9cf6bb4cf26e1ca31195986969d1912eaadbd06d Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Tue, 16 Jun 2026 20:34:13 -0400 Subject: [PATCH] docs(phase-10): spec Mix detail hero+meta overlay mirroring Sessions (shared ReleaseHeroOverlay recommended) --- PLAN.md | 8 + product-notes/mix-detail-hero-overlay.md | 381 +++++++++++++++++++++++ 2 files changed, 389 insertions(+) create mode 100644 product-notes/mix-detail-hero-overlay.md diff --git a/PLAN.md b/PLAN.md index 2e4fa8a..559a390 100644 --- a/PLAN.md +++ b/PLAN.md @@ -219,6 +219,14 @@ Heat→intensity and collision soft↔hard transfer functions are **staff-engine **Sequenced as four reframe waves.** `Wave R1 → Wave R2 → (Wave R3 ‖ Wave R4)`. Wave R1 (de-noise + dynamic footer clip + icon redraw) is a cheap unblock for a clean substrate. Wave R2 (wax-blob physics + 2D collision) is the load-bearing prerequisite — prove the lava before the color and the UI. Wave R3 (OKLab three-color gradient, the three motions) and Wave R4 (seven controls — including the new waveform-width knob — + NowPlaying-styled inline knob-bar + widened state to seven properties + extended bridge handle) both depend on Wave R2 but are independent of each other. **Both prior open Daniel calls are now decided:** controls-UI is an inline collapse/expand knob-bar (not popover/drawer); per-segment color is mix-time-keyed. +### Phase 10 — Mix detail Hero + MetaContent overlay (presentation only) + +A presentation pass over the **Mix detail Hero + MetaContent** that mirrors the already-shipped **Session detail** hero-overlay composition: the **cover art becomes a background image** with **all metadata overlaid** on top of a **max-medium square cover-art region**, replacing the stacked masthead + 220px cover + meta-divider block. Consolidates the view, frees more canvas for the lava-lamp visualizer, and brings Mix into the same design family as Sessions. **No renderer/state/bridge change; the Phase 10 reframe top row (`TopRowCenter` controls + lava-lamp `TopRightAction`) is preserved unchanged** — this touches only the region below it. + +**DRY recommendation (load-bearing, open for Daniel):** extract a shared **`ReleaseHeroOverlay`** presentational component (lifted from Sessions' current inline hero) that both pages consume — one source of truth for the overlay, divergence (Mix's visualizer backdrop, the square-medium aspect vs. Sessions' wide hero) riding parameters + a CSS class, per the standing "one source, multiple views" preference. Cost: edits the shipped Sessions page (regression surface — mitigated by a behavior-preserving lift + before/after visual check). Fallback: per-page copy in Mix (fast, zero Sessions risk, takes on duplication debt — recommended only if Sessions is considered too load-bearing to touch). **Plain-`
` overlay shell, no `MudCard`/`MudPaper` layout wrapper; `::deep` required on every class landing on a Mud child's native output.** + +Full design, the three DRY directions with trade-offs, the square/medium sizing, how the Phase 10 layout is preserved, acceptance criteria, and the open questions for Daniel: `product-notes/mix-detail-hero-overlay.md`. + --- ## Phase 11 — Public Site Enhancements diff --git a/product-notes/mix-detail-hero-overlay.md b/product-notes/mix-detail-hero-overlay.md new file mode 100644 index 0000000..9aae997 --- /dev/null +++ b/product-notes/mix-detail-hero-overlay.md @@ -0,0 +1,381 @@ +# Mix Detail — Hero + MetaContent overlay rework (mirror the Sessions hero) — Design Spec + +Status: **design-complete, implementation-ready.** Author: product-designer. Date: 2026-06-16. +**No code has been written by this doc.** + +## 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 + seven-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 131–145). +- 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 `
` 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**: + +- `` 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 ``. +- The scaffold's **`TopRowCenter`** slot holds ``; **`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 `
`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 `
` with the same `background-image` style, which is *more* aligned +with the plain-shell rule. **Recommendation: make it a plain `
`** 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: 420–480px`** (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 `` 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 + `` 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 `` 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` + (``) 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 `
`, §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 `
`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 seven-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 `
`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 `
` 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 + ``, 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 + ``; 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.