Files
deepdrft/DeepDrftPublic/wwwroot/styles/deepdrft-styles.css
T
daniel-c-harvey f9d99b2c98 fix: dark-theme hero buttons green in dark mode; correct source-order comment
Both ::deep and global selectors are (0,3,0); override wins on source order
(deepdrft-styles.css linked after scoped bundle in App.razor).
2026-06-20 01:49:55 -04:00

1112 lines
40 KiB
CSS

/* DeepDrft Global Styles - Simplified & Maintainable
Note: the palette / token layer (--deepdrft-*, --theme-*, --gradient-*, and the
.deepdrft-theme-dark override block) lives in DeepDrftShared.Client and is
served at _content/DeepDrftShared.Client/styles/deepdrft-tokens.css. Link that
file BEFORE this one in App.razor — every rule below depends on those tokens. */
/* =============================================================================
1. PAGE BASELINE
============================================================================= */
/* Base page colours — use MudBlazor's theme-injected variables so they
switch automatically when IsDarkMode toggles. The --mud-palette-background
and --mud-palette-text-primary variables are injected by MudThemeProvider
and update in both light and dark modes. */
html, body {
background-color: var(--mud-palette-background);
color: var(--mud-palette-text-primary);
}
/* Main-content clearance for the fixed frosted-glass nav (.dd-nav). The nav is
position:fixed (so content scrolls under its backdrop blur) and thus out of flow;
in MainLayout's flex column the content would otherwise start at the top and slide
under the bar. Pad the top by the shared --deepdrft-nav-height token so the clearance
tracks the bar exactly across breakpoints. Replaces the old hardcoded MudBlazor pt-16. */
.dd-main-content {
padding-top: var(--deepdrft-nav-height, 88px);
}
/* Ensure the theme wrapper fills the full viewport so no background gap shows. */
.deepdrft-theme-dark,
.deepdrft-theme-light {
min-height: 100vh;
}
/* =============================================================================
2. GRADIENTS
============================================================================= */
.deepdrft-gradient-primary,
.deepdrft-gradient-hero {
background: linear-gradient(135deg,
var(--gradient-base) 0%,
color-mix(in srgb, var(--gradient-base) 90%, var(--gradient-accent) 10%) 50%,
color-mix(in srgb, var(--gradient-base) 80%, var(--gradient-accent) 20%) 100%);
}
.deepdrft-gradient-soft-primary {
background: linear-gradient(45deg,
color-mix(in srgb, var(--gradient-accent) 4%, transparent) 0%,
color-mix(in srgb, var(--gradient-warm) 6%, transparent) 100%);
}
.deepdrft-gradient-soft-secondary {
background: linear-gradient(45deg,
color-mix(in srgb, var(--gradient-light) 6%, transparent) 0%,
color-mix(in srgb, var(--gradient-accent) 4%, transparent) 100%);
}
.deepdrft-gradient-soft-accent {
background: linear-gradient(135deg,
color-mix(in srgb, var(--gradient-accent) 3%, transparent) 0%,
color-mix(in srgb, var(--gradient-light) 5%, transparent) 100%);
}
.deepdrft-gradient-soft-tertiary {
background: linear-gradient(135deg,
color-mix(in srgb, var(--gradient-warm) 5%, transparent) 0%,
color-mix(in srgb, var(--gradient-accent) 3%, transparent) 100%);
}
.deepdrft-gradient-features {
background: linear-gradient(to right,
color-mix(in srgb, var(--gradient-accent) 2%, transparent) 0%,
color-mix(in srgb, var(--gradient-warm) 3%, transparent) 100%);
}
/* =============================================================================
3. TYPOGRAPHY
============================================================================= */
/* Hero text */
h1, .deepdrft-text-hero {
font-family: var(--deepdrft-font-display);
font-weight: bold;
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
}
/* Suppress the browser focus ring that FocusOnNavigate triggers on h1 after navigation. */
h1:focus-visible { outline: none; }
/* Headers */
h2, h3, h4, h5, h6,
.deepdrft-text-subtitle {
font-family: var(--deepdrft-font-display);
}
.deepdrft-text-subtitle {
font-weight: 300;
text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
}
/* Body */
.deepdrft-text-description,
.deepdrft-text-readable {
font-family: var(--deepdrft-font-body);
}
.deepdrft-text-description {
font-weight: 400;
opacity: 0.9;
}
.deepdrft-text-bold { font-weight: bold; }
.deepdrft-text-readable { line-height: 1.6; }
/* MudBlazor font overrides */
.mud-typography-h1,
.mud-typography-h2, .mud-typography-h3, .mud-typography-h4,
.mud-typography-h5, .mud-typography-h6,
.mud-navlink-text, .mud-appbar-content {
font-family: var(--deepdrft-font-display) !important;
}
.mud-button-text,
.mud-typography-caption, .mud-typography-overline {
font-family: var(--deepdrft-font-mono) !important;
}
.mud-typography-body1, .mud-typography-body2,
.mud-input-text, .mud-select-text, .mud-form-label {
font-family: var(--deepdrft-font-body) !important;
}
/* =============================================================================
4. HERO SECTION
============================================================================= */
.deepdrft-hero-container {
min-height: 60vh;
display: flex;
flex-direction: column;
justify-content: center;
}
.deepdrft-hero-text-container {
max-width: 600px;
}
/* Light mode hero text */
.deepdrft-theme-light .deepdrft-hero-title { color: var(--deepdrft-primary); }
.deepdrft-theme-light .deepdrft-hero-subtitle { color: var(--deepdrft-secondary); }
.deepdrft-theme-light .deepdrft-hero-description { color: var(--theme-surface-soft); }
/* Dark mode hero text */
.deepdrft-theme-dark .deepdrft-hero-title { color: var(--theme-surface); }
.deepdrft-theme-dark .deepdrft-hero-subtitle { color: var(--deepdrft-tertiary); }
.deepdrft-theme-dark .deepdrft-hero-description { color: var(--theme-surface-soft); }
/* Hero buttons - Light */
.deepdrft-theme-light .deepdrft-hero-button-filled.mud-button-filled {
background-color: var(--deepdrft-primary);
color: var(--gradient-base);
}
.deepdrft-theme-light .deepdrft-hero-button-outlined.mud-button-outlined {
border-color: var(--deepdrft-primary);
color: var(--deepdrft-primary);
}
/* Hero buttons - Dark */
.deepdrft-theme-dark .deepdrft-hero-button-filled.mud-button-filled {
background-color: var(--deepdrft-primary);
color: var(--gradient-base);
}
.deepdrft-theme-dark .deepdrft-hero-button-outlined.mud-button-outlined {
border-color: var(--theme-surface);
color: var(--theme-surface);
}
/* =============================================================================
5. APPBAR
============================================================================= */
.deepdrft-theme-light .mud-appbar,
.deepdrft-theme-light .mud-appbar *,
.deepdrft-theme-light .mud-appbar .mud-icon-button {
color: var(--gradient-base);
}
.deepdrft-theme-dark .mud-appbar,
.deepdrft-theme-dark .mud-appbar *,
.deepdrft-theme-dark .mud-appbar .mud-icon-button {
color: var(--theme-surface);
}
/* =============================================================================
6. BORDERS (Only used variants)
============================================================================= */
.deepdrft-border-left-secondary { border-left: 4px solid var(--theme-secondary); }
.deepdrft-border-left-tertiary { border-left: 4px solid var(--theme-tertiary); }
.deepdrft-border-top-quaternary { border-top: 4px solid var(--theme-quaternary); }
.deepdrft-border-top-senary { border-top: 4px solid var(--theme-senary); }
/* =============================================================================
7. CARDS & TINTS (Only used variants)
============================================================================= */
.deepdrft-feature-card,
.deepdrft-about-card { height: 100%; }
.deepdrft-feature-icon-container { text-align: center; }
/* Card tints - using theme variables */
.deepdrft-card-purple-tint { background: color-mix(in srgb, var(--deepdrft-secondary) 10%, transparent); }
.deepdrft-card-pink-tint { background: color-mix(in srgb, var(--gradient-warm) 10%, transparent); }
.deepdrft-card-indigo-tint { background: color-mix(in srgb, var(--gradient-accent) 8%, transparent); }
.deepdrft-card-lavender-tint { background: color-mix(in srgb, var(--theme-quinary) 10%, transparent); }
/* =============================================================================
8. (moved to TrackCard.razor.css and TracksGallery.razor.css)
============================================================================= */
/* =============================================================================
9. CHIPS & BUTTONS
============================================================================= */
.deepdrft-chip-spacing { margin: 2px; }
.deepdrft-genre-chip { opacity: 0.9; margin-top: 4px; }
.deepdrft-button-spaced { margin: 8px; }
/* Extended palette chips */
.mud-chip.deepdrft-chip-quaternary {
background-color: var(--theme-quaternary);
color: white;
}
.mud-chip.deepdrft-chip-quinary {
background-color: var(--theme-quinary);
color: white;
}
.mud-chip.deepdrft-chip-senary {
background-color: var(--theme-senary);
color: white;
}
/* =============================================================================
10. EXTENDED PALETTE TEXT COLORS (Only used variants)
============================================================================= */
.deepdrft-text-quaternary { color: var(--theme-quaternary); }
.deepdrft-text-quinary { color: var(--theme-quinary); }
.deepdrft-text-senary { color: var(--theme-senary); }
/* =============================================================================
11. CTA SECTION
============================================================================= */
.deepdrft-cta-container {
border-radius: 16px;
text-align: center;
}
.deepdrft-cta-buttons { margin-bottom: 16px; }
/* =============================================================================
12. ICONS & UTILITIES
============================================================================= */
.deepdrft-icon-large { font-size: 3rem; }
/* =============================================================================
13. RESPONSIVE
============================================================================= */
@media (max-width: 768px) {
.deepdrft-hero-text {
font-size: clamp(1.5rem, 6vw, 3rem) !important;
}
.deepdrft-cta-buttons .mud-button {
margin: 4px !important;
width: 100%;
}
}
/* =============================================================================
14. TRACK DETAIL PAGE
============================================================================= */
.deepdrft-track-detail-container {
max-width: 760px;
margin: 0 auto;
padding: 3rem 1.5rem 4rem;
}
/* Mix detail widens its body to the Sessions detail width (MudContainer Large, ~1280px) by hosting the
scaffold inside a MudContainer Large and neutralizing the scaffold's own 760px cap for that instance.
Both classes are global, so a plain descendant selector reaches the scaffold div without ::deep. The
horizontal gutter is dropped here because the wrapping MudContainer supplies its own. Mix-scoped, so
Track detail (which also uses .deepdrft-track-detail-container) stays at 760px. */
.mix-detail-container .deepdrft-track-detail-container {
max-width: none;
padding-left: 0;
padding-right: 0;
}
.deepdrft-track-detail-back {
display: inline-flex;
align-items: center;
gap: 4px;
margin-bottom: 1.5rem;
opacity: 0.65;
transition: opacity 0.15s ease;
}
.deepdrft-track-detail-back:hover {
opacity: 1;
}
/* Square cover frame — the placeholder MudPaper fills it. */
.deepdrft-track-detail-cover {
aspect-ratio: 1 / 1;
max-width: 360px;
margin: 0 auto 2rem;
overflow: hidden;
box-shadow: 0 8px 28px color-mix(in srgb, var(--mud-palette-text-secondary) 18%, transparent);
}
/* Stat-card parallel: elevated surface with a soft secondary wash, album icon centered. */
.deepdrft-track-detail-cover-placeholder {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: var(--mud-palette-surface);
}
.deepdrft-track-detail-cover-placeholder .mud-icon-root {
font-size: 72px;
}
/* Album art fills the square frame; background-size:cover handles any aspect ratio. */
.deepdrft-track-detail-cover-art {
height: 100%;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
.deepdrft-track-detail-masthead {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.deepdrft-track-detail-meta {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-around;
gap: 2rem;
margin-top: 1.5rem;
}
/* Small-caps mono labels match the caption/overline typography override. */
.deepdrft-track-detail-meta .mud-typography-overline {
font-variant: small-caps;
opacity: 0.6;
font-family: var(--deepdrft-font-mono) !important;
}
/* Default MudBlazor popover surface (Phase 18, T4 — symptom #1). Selects, menus, and the
share-popover body render inside .mud-popover. (Tooltips are NOT covered here — MudBlazor
tooltips paint from --mud-palette-text, not the popover surface.) Their visible surface is the
inner .mud-paper, which paints background-color: var(--mud-palette-surface). Inspection settled
the root cause: the "too dark" is NOT --deepdrft-panel-ground leakage (the bespoke dark-glass
panels are MudOverlay .mud-overlay-content surfaces and never match .mud-popover) — it is simply
that the popover surface tracks --mud-palette-surface with no desaturated-navy treatment. So
re-point --mud-palette-surface to the theme-aware --deepdrft-popover-surface *within the popover
scope only*: a soft desaturated-navy wash in light, the existing panel-ground charcoal in dark.
Scoping the variable (not a flat background) means any inner .mud-paper, .mud-list, or menu picks
it up for free, while the global surface used elsewhere on the page is unaffected. */
.mud-popover {
--mud-palette-surface: var(--deepdrft-popover-surface);
background-color: var(--deepdrft-popover-surface);
}
.deepdrft-share-popover-body {
padding: 0.75rem 1rem;
min-width: 280px;
max-width: 360px;
}
/* Monospace snippet so the iframe markup stays legible inside the readonly field. */
.deepdrft-share-embed-field {
flex: 1 1 auto;
}
.deepdrft-share-embed-field .mud-input-slot {
font-family: var(--deepdrft-font-mono) !important;
font-size: 0.75rem;
word-break: break-all;
}
/* =============================================================================
WAVEFORM VISUALIZER CONTROL PANEL (Phase 12 §3d-revised / §3g → Phase 15 re-layout)
The control deck hosted inside WaveformVisualizerControlPopover, now a screen-centered
tinted MudOverlay (Phase 15 §4). MudOverlay — like the former MudPopover — PORTALS its
content out of the component's DOM subtree, so Blazor CSS isolation never reaches the
rendered panel: its chrome, the three-row/section LAYOUT, the section labels, the slider,
and the toggles all live here in the global sheet, not in the scoped
WaveformVisualizerControls.razor.css. (The scoped file keeps only the legacy inline-bar
fallback Mix's old TopRowCenter mount used, which is not portaled.)
The waveform-visualizer-control-panel class is applied ONLY when the component's
PanelChrome="true" parameter is set — which the popover host does and Mix's inline mount
does NOT — so the chrome never leaks onto an inline bar.
CHROME (Phase 15 §5 — NowPlayingCard treatment): SQUARE corners, lighter-navy ground
(navy-mid), a thin LIGHT border (--deepdrft-border-light, the NowPlayingCard 0.12-alpha
light-on-dark idiom as a token). All token-sourced; no hardcoded hex.
COLOUR PRINCIPLE (§5 — green = interactive, light = non-interactive): the RadialKnob reads
--mud-palette-* for its arc/pointer/center/label; we pin --mud-palette-primary to the green
accent (interactive arcs/pointers) and --mud-palette-text-primary to light. Caption icons and
section labels are LIGHT (static). The slider track/thumb and the lamp toggles are green.
============================================================================= */
.waveform-visualizer-control-panel.mix-visualizer-controls-bar {
/* Theme-aware glass ground — dark charcoal in dark theme, light translucent glass in light
(so the deck reads against the light page). Tunable in deepdrft-tokens.css. */
background: var(--deepdrft-panel-surface);
/* Square corners + thin theme-aware border — NowPlayingCard chrome (§5). */
border: 1px solid var(--deepdrft-panel-border);
border-radius: 0;
/* Optional backdrop blur — cheap on a small modal panel, nice over the visualizer (§5). */
backdrop-filter: blur(8px);
padding: 1rem 1.25rem;
/* Three-row sectioned deck: stack the rows top-to-bottom; conditional rows reserve no permanent
height (§3 reflow discipline). This OVERRIDES the inline-bar min-height + flex-wrap (which only
matter for Mix's non-portaled legacy mount). */
display: flex;
flex-direction: column;
gap: 0.75rem;
min-height: 0;
max-width: 420px;
/* Pin the MudBlazor palette vars the portaled RadialKnob + slider consume. */
--mud-palette-primary: var(--deepdrft-green-accent); /* knob arc/pointer + slider track/thumb (interactive) */
--mud-palette-surface: var(--deepdrft-navy); /* knob center fill — darkest navy reads against the panel */
--mud-palette-surface-variant: var(--deepdrft-muted); /* knob background track — muted-navy filler */
--mud-palette-text-primary: var(--deepdrft-panel-text); /* knob value label — flips dark on light glass */
}
/* ── Row layout (§3). Each row is a horizontal band. Row 1 (MODE) and row 3 (WAVE) use
space-between so the right-pinned control (color / width) hugs the far edge. Row 2 (LAVA) uses
flex-start so its label + four knobs group left rather than spreading edge-to-edge.
align-items: center so the section label and knobs vertically center with each other. ── */
.waveform-visualizer-control-panel .wvc-row {
display: flex;
flex-direction: row;
align-items: center;
gap: 0.85rem 1rem;
}
/* Row 1 (MODE): two direct flex children — the left toggle group and the color knob tooltip wrapper.
space-between pins the color knob to the far right and keeps it there when collisions hides. */
.waveform-visualizer-control-panel .wvc-row-mode {
justify-content: space-between;
}
/* Row 2 (LAVA): label + four knobs group left — no right-pinned control. */
.waveform-visualizer-control-panel .wvc-row-section {
justify-content: flex-start;
}
/* Row 3 (WAVE): label + scroll-slider + width-knob tooltip wrappers are direct flex children.
space-between pins the width knob to the far right while the label + slider sit left. */
.waveform-visualizer-control-panel .wvc-row-wave {
justify-content: space-between;
}
/* The left group of row 1 (toggles + conditional collisions) flows left; the color knob is the
space-between right sibling, so it stays put when collisions hides (§3). */
.waveform-visualizer-control-panel .wvc-row-left {
display: flex;
flex-direction: row;
align-items: center;
gap: 0.85rem 1rem;
}
/* ── Section label "LAVA:" / "WAVE:" (§3, §5). NowPlayingCard .np-label TYPOGRAPHY (mono, uppercase,
tracked), coloured via --deepdrft-panel-text — theme-aware (navy in light, off-white in dark). ── */
.waveform-visualizer-control-panel .wvc-section-label {
font-family: var(--deepdrft-font-mono);
font-size: 0.6rem;
letter-spacing: 0.25em;
text-transform: uppercase;
color: var(--deepdrft-panel-text);
align-self: center;
flex: 0 0 auto;
opacity: 0.85;
}
/* ── The toggles (§3 row 1). Two state classes control the active-state chip treatment:
ON (.wvc-toggle-on): green-accent filled chip — unmistakably active at a glance.
OFF (.wvc-toggle-off): fully transparent background, glyph at low opacity — clearly inactive.
The MudIconButton glyph is already driven green (Color.Primary → pinned green accent, interactive §5).
The chip background reinforces state without recolouring the glyph further. ── */
.waveform-visualizer-control-panel .wvc-toggle {
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
transition: background 0.15s ease;
}
.waveform-visualizer-control-panel .wvc-toggle-on {
background: color-mix(in srgb, var(--deepdrft-green-accent) 28%, transparent);
box-shadow: 0 0 0 1px color-mix(in srgb, var(--deepdrft-green-accent) 55%, transparent);
}
.waveform-visualizer-control-panel .wvc-toggle-off .mud-icon-button {
opacity: 0.38;
}
/* Caption icons inherit the portaled panel's body text — theme-aware (dark text on light glass,
off-white on dark glass). !important beats the scoped .mix-visualizer-control ::deep
.mix-visualizer-control-icon rule (which sets green for the legacy inline mount) when the icon also
carries mix-visualizer-control-icon. Lamp toggles are MudIconButton not MudIcon so they are
unaffected — they stay green (interactive, Color.Primary). (defect #3) */
.waveform-visualizer-control-panel .waveform-visualizer-control-icon {
opacity: 0.85;
translate: 0 -1rem;
}
/* ── The modal overlay (Phase 15 §4). MudOverlay is already a full-viewport flex scrim that centers its
content (.mud-overlay { display:flex; align-items:center; justify-content:center }), which gives the
screen-centered panel on every host for free — we do NOT fight that positioning. We:
(a) Raise the overlay z-index above the header (100) and the player-dock footer (1200/1300) so the
scrim tints the ENTIRE viewport uniformly — header and footer included (defect #7). The panel
content needs z-index: auto (inherits from stacking context) so it sits above the scrim naturally;
the RadialKnob capture div at 9999 remains above everything.
(b) Set the mild tint from the SINGLE --deepdrft-modal-scrim-alpha token (§10.5, defect #6).
(c) Remove overflow-y:auto on the content wrapper — it was the source of the drag scrollbar (defect #2).
The panel's max-width/flex-column already contain its size; the outer overlay clips at 100vh.
(d) Suppress body scroll while the overlay is present so no page-scroll occurs during a drag (defect #2).
The overlay portals to the body, so these are plain global rules (no scope attribute). The doubled
.mud-overlay-scrim.mud-overlay-dark selector (0,2,0) outranks MudBlazor's own .mud-overlay-dark (0,1,0),
so the tint wins regardless of stylesheet load order. ── */
/* Raise the overlay itself above the sticky header (z-index:100) and the fixed player dock (z-index:1200).
Use 1400 so it sits above the minimized-dock FAB (1300) too. The panel content inherits this context
and stacks above the scrim; the RadialKnob capture div (z-index:9999) stays highest. */
.waveform-visualizer-control-overlay {
z-index: 1400 !important;
}
.waveform-visualizer-control-overlay .mud-overlay-scrim.mud-overlay-dark {
background-color: rgba(var(--deepdrft-scrim-rgb), var(--deepdrft-modal-scrim-alpha));
}
/* No overflow-y:auto — removing it eliminates the spurious scrollbar that appeared while dragging a
knob (defect #2). The panel's flex-column layout is self-contained and never overflows the overlay. */
.waveform-visualizer-control-overlay .mud-overlay-content {
max-height: 90vh;
overflow: visible;
}
/* Lock body scroll while the controls overlay is open so the page cannot be scrolled during a
knob drag (defect #2). :has() degrades gracefully in older browsers (no lock, no crash). */
body:has(.waveform-visualizer-control-overlay) {
overflow: hidden;
}
@media (max-width: 419.98px) {
.deepdrft-track-detail-meta {
flex-direction: column;
}
}
/* =============================================================================
BUTTON UTILITIES (btn-primary, btn-ghost)
============================================================================= */
.btn-primary {
font-family: var(--deepdrft-font-mono);
font-size: 0.68rem;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--deepdrft-white);
background: var(--deepdrft-navy);
border: none;
padding: 1rem 2.2rem;
cursor: pointer;
text-decoration: none;
transition: background 0.25s, transform 0.2s;
display: inline-block;
}
.btn-primary:hover {
background: var(--deepdrft-green);
transform: translateY(-1px);
}
.btn-ghost {
font-family: var(--deepdrft-font-mono);
font-size: 0.68rem;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--deepdrft-navy);
background: transparent;
border: 1px solid var(--deepdrft-border);
padding: 1rem 2.2rem;
cursor: pointer;
text-decoration: none;
transition: border-color 0.25s, color 0.25s;
display: inline-block;
}
.btn-ghost:hover { border-color: var(--deepdrft-navy); }
@media (max-width: 599px) {
.btn-primary,
.btn-ghost {
text-align: center;
}
}
/* Dark-mode button overrides (Phase 18, Wave 3).
In dark, --deepdrft-navy fill/text blends into the #0D1B2A page ground.
Primary: green-accent fill + navy text reads as a clear CTA (matches play-chip language).
Ghost: white text + light border stands off the dark ground. */
.deepdrft-theme-dark .btn-primary {
background: var(--deepdrft-green-accent);
color: var(--deepdrft-navy);
}
.deepdrft-theme-dark .btn-primary:hover {
background: var(--deepdrft-green-interactive);
}
.deepdrft-theme-dark .btn-ghost {
color: var(--deepdrft-page-text);
border-color: var(--deepdrft-border-light);
}
.deepdrft-theme-dark .btn-ghost:hover {
border-color: var(--deepdrft-page-text);
}
/* =============================================================================
CUT ALBUM DETAIL (/cuts/{id})
Header splits left-meta / right-cover; the cover carries an explicit theme
border (the new visual element vs. the borderless Session/Mix covers).
============================================================================= */
.cut-detail-header {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: space-between;
gap: 2rem;
margin: 2rem 0 1.5rem;
}
.cut-detail-meta {
display: flex;
flex-direction: column;
gap: 0.5rem;
min-width: 0;
flex: 1 1 auto;
}
.cut-detail-subline {
display: flex;
align-items: center;
gap: 0.5rem;
opacity: 0.75;
font-family: var(--deepdrft-font-mono);
font-size: 0.85rem;
margin-top: 0.25rem;
}
.cut-detail-sep { opacity: 0.5; }
.cut-detail-actions {
display: flex;
flex-direction: row;
align-items: center;
gap: 0.75rem;
margin-top: 1rem;
}
/* Square cover with a framed theme border — the new visual element this page introduces. */
.cut-detail-cover {
aspect-ratio: 1 / 1;
width: 260px;
flex: 0 0 auto;
overflow: hidden;
box-shadow: 0 8px 28px color-mix(in srgb, var(--mud-palette-text-secondary) 18%, transparent);
}
.cut-detail-divider { margin: 1.5rem 0 0.5rem; }
.cut-detail-empty {
opacity: 0.7;
padding: 1rem 0;
}
.cut-detail-tracklist {
display: flex;
flex-direction: column;
}
.cut-detail-track-row {
display: flex;
flex-direction: row;
align-items: center;
gap: 0.75rem;
padding: 0.25rem 0;
border-bottom: 1px solid color-mix(in srgb, var(--mud-palette-text-secondary) 12%, transparent);
}
.cut-detail-track-row:last-child { border-bottom: none; }
.cut-detail-track-number {
width: 1.75rem;
text-align: right;
flex: 0 0 auto;
opacity: 0.55;
font-family: var(--deepdrft-font-mono);
font-size: 0.9rem;
}
.cut-detail-track-play { flex: 0 0 auto; }
.cut-detail-track-name {
flex: 1 1 auto;
min-width: 0;
}
/* Stack the header on narrow screens: cover above the meta column. */
@media (max-width: 599px) {
.cut-detail-header {
flex-direction: column-reverse;
align-items: stretch;
gap: 1.25rem;
}
.cut-detail-cover {
width: 100%;
max-width: 320px;
margin: 0 auto;
}
}
/* Dark-theme interactive affordances in the Cut detail header and track rows.
In dark mode, Color.Secondary resolves to off-white (#FAFAF8), making filled
and icon buttons unreadable on the light-ish dark-navy surface. Override to
green (--deepdrft-primary = green-accent in dark, --deepdrft-navy as text) to
match the "green = interactive" convention (see hero button dark rule above).
Scoped to .cut-detail-actions and .cut-detail-track-row so the hero-overlay
icons (.release-hero-play / .release-hero-share, forced white in
ReleaseHeroOverlay.razor.css) are never touched. */
/* Play button (Variant.Filled, Color.Secondary) in the cut detail header */
.deepdrft-theme-dark .cut-detail-actions .mud-button-filled {
background-color: var(--deepdrft-primary);
color: var(--gradient-base);
}
/* Share + Queue icon buttons in the cut detail header */
.deepdrft-theme-dark .cut-detail-actions .mud-icon-button {
color: var(--deepdrft-primary);
}
/* Share + Queue icon buttons in each cut detail track row */
.deepdrft-theme-dark .cut-detail-track-row .mud-icon-button {
color: var(--deepdrft-primary);
}
/* Dark-theme interactive affordances in the Session/Mix release-detail hero overlay.
The co-located ReleaseHeroOverlay.razor.css forces .release-hero-play and
.release-hero-share icons to --deepdrft-white unconditionally (correct for the
over-image light-theme contract). In dark theme the play and share glyphs adopt
green to match the "green = interactive" convention. Both the scoped ::deep rule
([b-xxx] .release-hero-play .mud-icon-button) and this global rule
(.deepdrft-theme-dark .release-hero-play .mud-icon-button) compute to specificity
(0,3,0) — a tie — so the override wins on source order: deepdrft-styles.css is
linked after DeepDrftPublic.styles.css in App.razor, making it the later rule.
No !important needed. .mud-progress-circular is included alongside .mud-icon-button
in the play slot to colour the loading spinner green as well.
Light theme: pixel-identical to before. */
.deepdrft-theme-dark .release-hero-play .mud-icon-button,
.deepdrft-theme-dark .release-hero-play .mud-progress-circular,
.deepdrft-theme-dark .release-hero-share .mud-icon-button {
color: var(--deepdrft-primary);
}
/* Dark-theme lava-lamp visualizer-settings trigger.
The MudIconButton uses Color.Secondary, which resolves to off-white in dark mode.
The .dd-lava-lamp-trigger marker div (added to WaveformVisualizerControlPopover.razor)
scopes this rule to only that trigger, preventing bleed onto other Secondary icon
buttons sharing the same host pages. Applied at all host sites (Mix, Cut, Session,
NowPlaying) since the wrapper travels with the component. Light theme: unchanged. */
.deepdrft-theme-dark .dd-lava-lamp-trigger .mud-icon-button {
color: var(--deepdrft-primary);
}
/* Dark-theme gas-lamp dark-mode toggle.
The MudIconButton uses Color.Inherit and sits inside .dd-nav-actions (the right-side
cluster of the nav bar). In dark theme it inherits the nav text colour (off-white);
green makes it consistent with the "green = interactive" convention. The selector is
scoped to .dd-nav-actions so no other icon buttons are affected. Covers both the
desktop nav and the mobile nav (which also renders its gas-lamp inside .dd-nav-actions).
Light theme: unchanged. */
.deepdrft-theme-dark .dd-nav-actions .mud-icon-button {
color: var(--deepdrft-primary);
}
/* =============================================================================
RELEASE DESCRIPTION BLURB
Shared block rendered just below the hero/header on every release detail page
(Session, Mix, Cut). Theme-driven colours keep it legible in both palettes.
Borrows the eyebrow-label + divider-rule motif from the home page.
============================================================================= */
.deepdrft-release-description {
margin: 2rem 0 2.5rem;
}
/* Header row: eyebrow label left + thin rule filling the rest */
.deepdrft-release-description-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1.25rem;
}
/* Eyebrow label — mirrors .section-label / .split-eyebrow from Home */
.deepdrft-release-description-label {
font-family: var(--deepdrft-font-mono);
font-size: 0.62rem;
letter-spacing: 0.28em;
color: var(--deepdrft-green-accent);
text-transform: uppercase;
white-space: nowrap;
flex-shrink: 0;
}
/* Thin rule to the right of the label — mirrors .divider-line from Home */
.deepdrft-release-description-rule {
flex: 1;
height: 1px;
background: color-mix(in srgb, var(--deepdrft-muted) 35%, transparent);
}
/* Body paragraph — body font, display-serif feel via generous line-height */
.deepdrft-release-description-text {
margin: 0;
font-family: var(--deepdrft-font-display);
font-size: 1.1rem;
font-weight: 300;
line-height: 1.75;
color: var(--mud-palette-text-primary);
opacity: 0.85;
}
/* =============================================================================
QUEUE OVERLAY + LIST (Phase 17 wave 17.2 — docked queue panel)
The overlay is a direct lift of the visualizer-control modal (Phase 15 §4): a centered MudOverlay
whose scrim tint + z-index + body-scroll lock match that idiom exactly. The panel chrome (square
corners, lighter-navy ground, thin light border) is the NowPlayingCard treatment (§5). MudOverlay
portals out of the component subtree to the body, so these are plain GLOBAL rules — CSS isolation
cannot reach portaled content.
============================================================================= */
/* Raise the overlay above the sticky header (100), the fixed player dock (1200), and the minimized
FAB (1300) — same stacking decision as the visualizer overlay so the scrim tints the whole viewport. */
.deepdrft-queue-overlay {
z-index: 1400 !important;
}
/* Mild modal tint from the shared scrim token. The doubled selector (0,2,0) outranks MudBlazor's own
.mud-overlay-dark (0,1,0) regardless of stylesheet load order. */
.deepdrft-queue-overlay .mud-overlay-scrim.mud-overlay-dark {
background-color: rgba(var(--deepdrft-scrim-rgb), var(--deepdrft-modal-scrim-alpha));
}
.deepdrft-queue-overlay .mud-overlay-content {
max-height: 90vh;
overflow: visible;
}
/* Lock body scroll while the queue overlay is open (matches the visualizer overlay). */
body:has(.deepdrft-queue-overlay) {
overflow: hidden;
}
/* The mostly-square panel (§3.2: min(90vw, 520px)). NowPlayingCard chrome: square corners, lighter-navy
ground, thin light border. Internal column: fixed header over a scrollable list body. */
.deepdrft-queue-modal {
display: flex;
flex-direction: column;
width: min(90vw, 520px);
height: min(90vw, 520px);
max-height: 90vh;
background: var(--deepdrft-panel-surface);
border: 1px solid var(--deepdrft-panel-border);
border-radius: 0;
backdrop-filter: blur(8px);
overflow: hidden;
}
.deepdrft-queue-modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.85rem 1rem;
border-bottom: 1px solid var(--deepdrft-panel-border);
}
/* Mono uppercase eyebrow — the NowPlayingCard .np-label typography, theme-aware (static). */
.deepdrft-queue-modal-title {
font-family: var(--deepdrft-font-mono);
font-size: 0.72rem;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--deepdrft-panel-text);
opacity: 0.85;
}
.deepdrft-queue-modal-body {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
padding: 0.5rem 0.5rem 0.75rem;
}
/* ── The list itself (consumed by QueueList in both modes; styled here once). ── */
.deepdrft-queue-list {
display: flex;
flex-direction: column;
}
.deepdrft-queue-zone {
display: flex;
flex-direction: column;
}
.deepdrft-queue-row {
display: flex;
align-items: center;
gap: 0.6rem;
padding: 0.45rem 0.5rem;
border-radius: 4px;
color: var(--deepdrft-panel-text);
transition: background 0.15s ease;
}
.deepdrft-queue-row:hover {
background: var(--deepdrft-panel-row-hover);
}
/* Current track: a subtle green wash + left accent, matching the green = active principle. */
.deepdrft-queue-row-current {
background: color-mix(in srgb, var(--deepdrft-green-accent) 14%, transparent);
box-shadow: inset 2px 0 0 0 var(--deepdrft-green-accent);
}
.deepdrft-queue-drag-handle {
cursor: grab;
opacity: 0.45;
flex: 0 0 auto;
}
.deepdrft-queue-position {
font-family: var(--deepdrft-font-mono);
font-size: 0.72rem;
color: var(--deepdrft-panel-text-muted);
min-width: 1.4rem;
text-align: right;
flex: 0 0 auto;
}
/* Row body grows + truncates; clicking it jumps playback (OQ2). */
.deepdrft-queue-body {
display: flex;
flex-direction: column;
gap: 0.1rem;
flex: 1 1 auto;
min-width: 0;
cursor: pointer;
}
.deepdrft-queue-title {
font-size: 0.92rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.deepdrft-queue-artist {
font-size: 0.74rem;
color: var(--deepdrft-panel-text-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.deepdrft-queue-nowplaying,
.deepdrft-queue-remove {
flex: 0 0 auto;
}
/* Active (open) state for the bar's Queue toggle — a soft green chip behind the glyph, matching the
visualizer toggle's on-state idiom. */
.deepdrft-queue-toggle-active {
background: color-mix(in srgb, var(--deepdrft-green-accent) 22%, transparent);
border-radius: 6px;
}
/* ── Fixed (embed) inline queue panel (Phase 17 §4, OQ6). ──
Rendered below the player controls inside the embed surface. A fixed sensible height with internal
scroll past N rows (NOT grow-to-cap): ~4.5 rows are visible, the rest scroll. A top hairline
separates it from the controls. The list rows reuse the shared .deepdrft-queue-* styles above. */
.deepdrft-queue-embed-panel {
margin-top: 0.5rem;
padding-top: 0.5rem;
border-top: 1px solid var(--deepdrft-border-light);
max-height: 184px;
overflow-y: auto;
}
/* =============================================================================
PRIVACY OVERLAY
Screen-centered modal following the same MudOverlay idiom as the visualizer
controls and queue overlays. MudOverlay portals to body — CSS isolation cannot
reach portaled content, so chrome lives here in the global sheet.
============================================================================= */
/* Raise above the sticky header (100), player dock (1200), and minimized FAB (1300). */
.deepdrft-privacy-overlay {
z-index: 1400 !important;
}
/* Mild tint: doubled selector (0,2,0) outranks MudBlazor's .mud-overlay-dark (0,1,0). */
.deepdrft-privacy-overlay .mud-overlay-scrim.mud-overlay-dark {
background-color: rgba(var(--deepdrft-scrim-rgb), var(--deepdrft-modal-scrim-alpha));
}
.deepdrft-privacy-overlay .mud-overlay-content {
max-height: 90vh;
overflow: visible;
}
/* Lock body scroll while the overlay is open. */
body:has(.deepdrft-privacy-overlay) {
overflow: hidden;
}
/* Panel: compact width, navy-panel ground, thin light border — matches queue/visualizer chrome. */
.deepdrft-privacy-modal {
display: flex;
flex-direction: column;
width: min(90vw, 480px);
background: var(--deepdrft-panel-surface);
border: 1px solid var(--deepdrft-panel-border);
border-radius: 0;
backdrop-filter: blur(8px);
overflow: hidden;
}
.deepdrft-privacy-modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.85rem 0.85rem 0.85rem 1rem;
border-bottom: 1px solid var(--deepdrft-panel-border);
}
/* Mono uppercase eyebrow — matches queue modal title. */
.deepdrft-privacy-modal-title {
font-family: var(--deepdrft-font-mono);
font-size: 0.72rem;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--deepdrft-panel-text);
opacity: 0.85;
}
/* Tuck the close icon flush with the panel edge; keep it subtle. */
.deepdrft-privacy-modal-close {
opacity: 0.6;
color: var(--deepdrft-panel-text) !important;
}
.deepdrft-privacy-modal-close:hover {
opacity: 1;
}
/* Privacy copy: same mono treatment as the former inline paragraph; theme-aware text colour
so it stays legible on both the dark-glass (dark) and light-glass (light) panel surfaces. */
.deepdrft-privacy-modal-body {
font-family: var(--deepdrft-font-mono);
font-size: 0.72rem;
letter-spacing: 0.06em;
line-height: 1.7;
color: var(--deepdrft-panel-text);
opacity: 0.8;
margin: 0;
padding: 1rem 1rem 1.25rem;
}