Note the 4%/bluer-navy --deepdrft-popover-surface values, the new
--deepdrft-popover-surface-dark source token, the theme TS interop module, and the
<body>-class bridge in CLAUDE.md; log Phase 18 Wave 4 in COMPLETED.md.
Retune public-site popover surfaces: light reads as a near-page-background light
surface (8%->4% navy), dark skews bluer (navy-mid + green-accent). Root cause: popovers
portal to <body>, outside the theme wrapper; MainLayout now stamps the theme class on
<body> via a TS interop helper so portaled popovers receive the dark token.
Extracts setBodyThemeClass into DeepDrftShared.Client/Interop/theme/theme.ts;
MainLayout lazy-imports the compiled module and calls it, matching the
established knob/parallax IJSObjectReference pattern. DisposeAsync added.
Only stamps body class on firstRender or _isDarkMode change; adds base call.
Hoists duplicate dark popover mix value to --deepdrft-popover-surface-dark in :root;
both .deepdrft-theme-dark and body.deepdrft-theme-dark reference it via var().
MudBlazor popovers portal to <body>, outside the theme wrapper, so the dark token
was unreachable. MainLayout now stamps deepdrft-theme-dark on <body>. Light: 8%->4%
navy (near page background); dark: navy-mid + 20% green-accent (bluer).
10.3.36 fixes JWT refresh for idle sessions and registers EditModalSaveContextHolder via AddBlazorBlocksWeb() — making the manual stopgap in DeepDrftManager/Program.cs redundant. BlazorBlocks direct refs (10.3.30) resolved without conflict; left unchanged.
EditModalSaveContextHolder is required by ModelView but registered by no BlazorBlocks/AuthBlocks setup extension. Recommends AddBlazorBlocksWeb() called from ConfigureAuthServices.
ModelView has a required [Inject] of this type; without it navigating to /useradmin/users or /useradmin/registrations terminated the circuit. Matches the registration pattern from SkipperHaven.
Mailtrap rejected invite sends because FromAddress was never populated. Adds the missing config assignment alongside Host/Token, and documents the From key in authblocks.example.json.
Provision User nav link was visible to all authenticated CMS users but its target page is UserAdmin-gated. Wraps the MudNavLink in HierarchicalRoleAuthorizeView matching the UserAdminMenu pattern.
Resolve Routes.razor DefaultLayout from cascaded AuthenticationState so unauthenticated AuthBlocks pages (/account/login, /account/register) render in lean CmsHomeLayout instead of the authenticated CmsLayout shell.
Add a MudDrawer with app-bar toggle linking Catalogue, Releases, Upload, SuperRegister, and the self-gating UserAdminMenu fragment so user-admin pages are reachable.
All three AuthBlocks account paths live on DeepDrftManager; public registration is an unauthenticated CMS route like the CMS login. Path 2 reduces to a single auth-state-driven DefaultLayout fix (SkipperHaven pattern).
Cover admin provision-now, public self-service redeem, and admin invite-by-email across CMS + public-site tracks. Add standalone AuthBlocks password-reset team brief.
Re-point neutral page surfaces, play-chip, and default popover from constant brand tokens to theme-aware aliases defined twice in deepdrft-tokens.css. Decorative navy/green sections and bespoke dark-glass panels untouched. Appbar-navy symptom deferred (palette C#, out of CSS scope).
Build the staging path before the copy in both UploadTrack and ReplaceAudio so the finally block deletes it on cancellation or IO error, not only on success.
Relocate both the framework multipart buffer (via ASPNETCORE_TEMP) and the controller staging file to a configurable data-disk directory, so large WAV/FLAC/MP3 uploads no longer fail on the host's small tmpfs.