Files
media-player-server/media_server/static/css/styles.css
T
alexei.dolgolyov 25a492d5dd
Lint & Test / test (push) Successful in 17s
ui(player): meaningful caps for tablet/small-desktop range + tighter footer
The 720–1240 px viewport range was a "strange zone": below 1240 the layout
is single-column, but above 720 none of the Pocket Edition rules fire, so
the vinyl stage stretched to full content width (~1100 px) and the masthead
ran to a 1000 px+ measure on a small-desktop window.

Caps now degrade in three steps:
- ≤ 720 px: vinyl 460 px / 92% width (mobile hero unchanged)
- 721–1240 px: vinyl 480 px, masthead 640 px, both centered
- ≥ 1241 px: two-column layout (no caps needed; grid does it)

Also reduce the bottom dead space:
- footer margin-top 80 → 36, inner top padding 28 → 20
- .container bottom padding 140 → 64 (desktop) / 56 (mobile)

And a small mobile-volume fix in the same range:
- .controls flex-wrap nowrap → wrap so the vu-cluster can take its own row
- vu-cluster gets flex-basis 100 % (forces own row in the wrapping flexbox)
- volume-slider drops the max-width: 200 cap so it fills the row width
- vinyl-stage on mobile bumped 320 → 460 px / 78% → 92% width
2026-04-25 20:19:37 +03:00

9231 lines
232 KiB
CSS
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.
/* ============================================================
STUDIO REFERENCE — local fonts (Fraunces · Geist · Geist Mono)
Subsets: Latin, Latin-ext, Cyrillic. WOFF2 variable, OFL.
============================================================ */
@font-face {
font-family: 'Fraunces';
font-style: italic;
font-weight: 300 900;
font-display: swap;
src: url('/static/fonts/Fraunces-italic-latin-ext.woff2') format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'Fraunces';
font-style: italic;
font-weight: 300 900;
font-display: swap;
src: url('/static/fonts/Fraunces-italic-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Fraunces';
font-style: normal;
font-weight: 300 900;
font-display: swap;
src: url('/static/fonts/Fraunces-latin-ext.woff2') format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'Fraunces';
font-style: normal;
font-weight: 300 900;
font-display: swap;
src: url('/static/fonts/Fraunces-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Geist';
font-style: normal;
font-weight: 300 700;
font-display: swap;
src: url('/static/fonts/Geist-cyrillic.woff2') format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
font-family: 'Geist';
font-style: normal;
font-weight: 300 700;
font-display: swap;
src: url('/static/fonts/Geist-latin-ext.woff2') format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'Geist';
font-style: normal;
font-weight: 300 700;
font-display: swap;
src: url('/static/fonts/Geist-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Geist Mono';
font-style: normal;
font-weight: 300 600;
font-display: swap;
src: url('/static/fonts/GeistMono-cyrillic.woff2') format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
font-family: 'Geist Mono';
font-style: normal;
font-weight: 300 600;
font-display: swap;
src: url('/static/fonts/GeistMono-latin-ext.woff2') format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'Geist Mono';
font-style: normal;
font-weight: 300 600;
font-display: swap;
src: url('/static/fonts/GeistMono-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* ============================================================
Studio Reference — design tokens (default = warm dark)
Light theme = Paper Edition (cream + emerald)
Old token names preserved as aliases for unmigrated components.
============================================================ */
:root {
/* Tells browsers we're using a dark UI so native widgets
(select dropdowns, scrollbars, form controls) match. */
color-scheme: dark;
/* Studio Reference (warm dark) */
--bg-deep: #0E0D0B;
--bg-paper: #18150F;
--bg-card: #211E18;
--bg-card-2: #26211A;
--bg-rule: #2E2820;
--ink: #F2EBDC;
--ink-soft: #D6CDB9;
--ink-mute: #9C937F;
--ink-faint: #5C5447;
--ink-ghost: #3A3528;
--copper: #E08038;
--copper-hi: #F4A064;
--copper-lo: #B0561F;
--copper-rgb: 224, 128, 56;
--copper-glow: rgba(var(--copper-rgb), 0.35);
--amber: #F5C26B;
--jade: #7AB294;
--rust: #C2553F;
--rule: rgba(242, 235, 220, 0.08);
--rule-strong: rgba(242, 235, 220, 0.18);
--serif: 'Fraunces', 'Iowan Old Style', Georgia, 'Times New Roman', serif;
--sans: 'Geist', 'Inter', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
--mono: 'Geist Mono', 'JetBrains Mono', 'Cascadia Code', ui-monospace, monospace;
--ease: cubic-bezier(.2, .7, .2, 1);
--ease-out: cubic-bezier(.16, 1, .3, 1);
/* Backwards-compatible aliases — components not yet migrated */
--bg-primary: var(--bg-deep);
--bg-secondary: var(--bg-paper);
--bg-tertiary: var(--bg-card);
--text-primary: var(--ink);
--text-secondary: var(--ink-soft);
--text-muted: var(--ink-mute);
--accent: var(--copper);
--accent-hover: var(--copper-hi);
--border: var(--bg-rule);
--error: var(--rust);
--vinyl-ring: #1a1611;
--vinyl-groove: #2a241c;
--vinyl-highlight: rgba(255,255,255,0.05);
--vinyl-highlight-dim: rgba(255,255,255,0.03);
--vinyl-edge: #0a0907;
--vinyl-spindle: #050402;
--shadow-elevation: rgba(0, 0, 0, 0.5);
}
:root[data-theme="light"] {
color-scheme: light;
/* Paper Edition (warm cream) */
--bg-deep: #F5F1EA;
--bg-paper: #EDE7DC;
--bg-card: #FFFFFF;
--bg-card-2: #FAF6EE;
--bg-rule: #D4CCB9;
--ink: #1A1715;
--ink-soft: #4A4540;
--ink-mute: #8A8278;
--ink-faint: #B0A89A;
--ink-ghost: #C8C0B3;
--copper: #1F4E3D; /* hunter emerald in light mode */
--copper-hi: #2D6A53;
--copper-lo: #143E2F;
--copper-rgb: 31, 78, 61;
--copper-glow: rgba(var(--copper-rgb), 0.18);
--amber: #C29D31;
--jade: #4D8C6F;
--rust: #B82F1A;
--rule: rgba(26, 23, 21, 0.08);
--rule-strong: rgba(26, 23, 21, 0.18);
/* Aliases */
--bg-primary: var(--bg-deep);
--bg-secondary: var(--bg-paper);
--bg-tertiary: var(--bg-card);
--text-primary: var(--ink);
--text-secondary: var(--ink-soft);
--text-muted: var(--ink-mute);
--accent: var(--copper);
--accent-hover: var(--copper-hi);
--border: var(--bg-rule);
--error: var(--rust);
--vinyl-ring: #C8BFA8;
--vinyl-groove: #B0A892;
--vinyl-highlight: rgba(255,255,255,0.3);
--vinyl-highlight-dim: rgba(255,255,255,0.15);
--vinyl-edge: #968D78;
--vinyl-spindle: #8A8278;
--shadow-elevation: rgba(26, 23, 21, 0.15);
}
:root[data-theme="light"] .player-container,
:root[data-theme="light"] .browser-container,
:root[data-theme="light"] .scripts-container,
:root[data-theme="light"] .script-management,
:root[data-theme="light"] .settings-section,
:root[data-theme="light"] .display-container {
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}
:root[data-theme="light"] .toast {
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
:root[data-theme="light"] dialog {
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);
}
:root[data-theme="light"] dialog::backdrop {
background: rgba(0, 0, 0, 0.4);
}
:root[data-theme="light"] .mini-player {
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.08);
}
/* Dynamic background removed per user request — canvas stays in DOM
so JS doesn't break, but is hidden + the toggle button is hidden. */
.bg-shader-canvas { display: none !important; }
#bgToggle { display: none !important; }
body.dynamic-bg-active {
background: transparent;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
scrollbar-width: thin;
scrollbar-color: var(--border) var(--bg-primary);
}
html {
overflow-y: scroll;
}
body {
font-family: var(--sans);
font-feature-settings: 'ss01', 'cv11';
background: var(--bg-primary);
color: var(--text-primary);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
/* Film-grain overlay — adds analog warmth to the dark theme.
Disabled in light theme where it muddies cream paper. */
body::before {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
z-index: 9999;
opacity: 0.05;
mix-blend-mode: overlay;
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.95 0 0 0 0 0.92 0 0 0 0 0.86 0 0 0 0.7 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
:root[data-theme="light"] body::before {
opacity: 0.04;
mix-blend-mode: multiply;
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.45 0 0 0 0 0.40 0 0 0 0 0.32 0 0 0 0.5 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* Reduced-motion fallback: drop the grain entirely. */
@media (prefers-reduced-motion: reduce) {
body::before { display: none; }
}
/* Heading defaults — components may override per-element. */
h1, h2, h3, .editorial-heading {
font-family: var(--serif);
font-weight: 400;
letter-spacing: -0.015em;
}
/* Mono utility — for technical readouts (timecodes, bitrates, etc.) */
.mono, .timecode, .meta-mono {
font-family: var(--mono);
font-feature-settings: 'tnum', 'cv01';
}
/* Prevent flash of untranslated content */
body.loading-translations {
opacity: 0;
transition: opacity 0.1s ease-in;
}
body.translations-loaded {
opacity: 1;
}
/* Custom Scrollbars */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-primary);
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-muted);
}
/* Focus-visible states for keyboard accessibility */
:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
button:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
box-shadow: 0 0 0 4px rgba(29, 185, 84, 0.2);
}
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(29, 185, 84, 0.15);
}
.tab-btn:focus-visible {
outline: 2px solid var(--accent);
outline-offset: -2px;
}
/* Prevent scrolling when dialog is open */
body.dialog-open {
overflow: hidden;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 0.75rem 0.75rem 0.5rem;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.75rem;
padding-bottom: 0.5rem;
}
h1 {
font-size: 1.5rem;
font-weight: 600;
}
.version-label {
font-size: 0.7rem;
color: var(--text-muted);
background: var(--bg-tertiary);
padding: 0.15rem 0.5rem;
border-radius: 1rem;
font-weight: 500;
align-self: center;
}
.status-dot {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 0.75rem;
color: var(--text-muted);
transition: color 0.3s;
}
.status-dot::before {
content: '';
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--error);
flex-shrink: 0;
transition: background 0.3s;
}
.status-dot.connected::before,
.status-dot.status-online::before {
background: var(--accent);
}
.status-dot.status-offline::before {
background: var(--error);
}
/* Folder management */
.folder-unavailable-badge,
.folder-disabled-badge {
font-size: 0.75rem;
padding: 1px 6px;
border-radius: 4px;
vertical-align: middle;
margin-left: 4px;
}
.folder-unavailable-badge {
background: color-mix(in srgb, var(--error) 20%, transparent);
color: var(--error);
}
.folder-disabled-badge {
background: color-mix(in srgb, var(--text-secondary) 20%, transparent);
color: var(--text-secondary);
}
.browser-item.unavailable,
.browser-list-item.unavailable {
opacity: 0.5;
cursor: default;
}
.path-cell {
max-width: 250px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.header-toolbar {
display: flex;
align-items: center;
gap: 2px;
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 8px;
padding: 3px 4px;
}
.header-toolbar-sep {
width: 1px;
height: 18px;
background: var(--border);
margin: 0 3px;
flex-shrink: 0;
}
.header-btn {
background: transparent;
border: none;
padding: 4px 6px;
border-radius: 5px;
cursor: pointer;
color: var(--text-secondary);
transition: color 0.2s, background 0.2s;
display: inline-flex;
align-items: center;
line-height: 1;
}
.header-btn:hover {
color: var(--text-primary);
background: var(--bg-tertiary);
}
.header-btn.active {
color: var(--accent);
}
.header-btn svg {
width: 16px;
height: 16px;
fill: currentColor;
}
.header-btn-logout:hover {
color: var(--error);
}
.header-locale {
background: transparent;
border: none;
color: var(--text-secondary);
font-size: 0.75rem;
font-weight: 600;
padding: 4px 4px 4px 8px;
border-radius: 5px;
cursor: pointer;
transition: color 0.2s, background 0.2s;
}
.header-locale:hover {
color: var(--text-primary);
background: var(--bg-tertiary);
}
.header-locale:focus {
outline: none;
}
.header-locale option {
background: var(--bg-secondary);
color: var(--text-primary);
}
/* Header Quick Links */
.header-links {
display: flex;
align-items: center;
gap: 2px;
}
.header-links:not(:empty) {
padding-right: 3px;
margin-right: 3px;
border-right: 1px solid var(--border);
}
.header-link {
display: flex;
align-items: center;
justify-content: center;
padding: 4px 6px;
border-radius: 5px;
color: var(--text-secondary);
transition: color 0.2s, background 0.2s;
text-decoration: none;
}
.header-link:hover {
color: var(--text-primary);
background: var(--bg-tertiary);
}
.header-link svg {
width: 16px;
height: 16px;
}
/* Icon Input with Preview */
.icon-input-wrapper {
display: flex;
align-items: center;
gap: 0.5rem;
}
.icon-input-wrapper input {
flex: 1;
}
.icon-preview {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
min-width: 36px;
border-radius: 6px;
background: var(--bg-tertiary);
color: var(--text-primary);
}
.icon-preview svg {
width: 20px;
height: 20px;
}
.icon-preview:empty {
display: none;
}
/* Accent Color Picker */
.accent-picker {
position: relative;
}
.accent-dot {
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--accent);
border: 2px solid var(--border);
display: block;
}
.accent-picker-dropdown {
display: none;
position: absolute;
right: 0;
top: calc(100% + 4px);
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 12px;
padding: 10px;
gap: 6px;
z-index: 100;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
grid-template-columns: repeat(3, 28px);
}
.accent-picker-dropdown.open {
display: grid;
}
.accent-swatch {
width: 28px;
height: 28px;
border-radius: 50%;
border: 2px solid transparent;
cursor: pointer;
transition: transform 0.15s, border-color 0.15s;
}
.accent-swatch:hover {
transform: scale(1.15);
}
.accent-swatch.active {
border-color: var(--text-primary);
}
.accent-custom-row {
grid-column: 1 / -1;
display: flex;
align-items: center;
gap: 8px;
padding: 6px 2px 2px;
margin-top: 4px;
border-top: 1px solid var(--border);
cursor: pointer;
border-radius: 4px;
}
.accent-custom-row:hover .accent-custom-label {
color: var(--text-primary);
}
.accent-custom-row.active .accent-custom-swatch {
outline: 2px solid var(--text-primary);
outline-offset: 1px;
}
.accent-custom-swatch {
width: 20px;
height: 20px;
border-radius: 4px;
flex-shrink: 0;
}
.accent-custom-label {
font-size: 0.75rem;
color: var(--text-muted);
transition: color 0.15s;
}
.accent-custom-row input[type="color"] {
width: 0;
height: 0;
padding: 0;
border: none;
opacity: 0;
position: absolute;
pointer-events: none;
}
/* Tab Bar */
.tab-bar {
display: flex;
gap: 0.25rem;
margin-bottom: 1.5rem;
padding: 0.25rem;
background: var(--bg-secondary);
border-radius: 10px;
border: 1px solid var(--border);
position: relative;
}
.tab-indicator {
position: absolute;
top: 0.25rem;
bottom: 0.25rem;
left: 0;
background: var(--bg-tertiary);
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
z-index: 0;
pointer-events: none;
}
.tab-btn {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 0.4rem;
padding: 0.6rem 0.5rem;
background: transparent;
border: none;
border-radius: 8px;
color: var(--text-muted);
font-size: 0.8rem;
font-weight: 500;
cursor: pointer;
transition: color 0.2s;
width: auto;
height: auto;
position: relative;
z-index: 1;
}
.tab-btn:hover {
color: var(--text-primary);
transform: none !important;
}
.tab-btn.active {
color: var(--accent);
background: transparent;
}
.tab-btn svg {
width: 16px;
height: 16px;
flex-shrink: 0;
}
[data-tab-content] {
display: none;
opacity: 0;
}
[data-tab-content].active {
display: block;
animation: tabFadeIn 0.25s ease-out forwards;
}
@keyframes tabFadeIn {
from { opacity: 0; transform: translateY(6px); }
to { opacity: 1; transform: translateY(0); }
}
@media (max-width: 600px) {
.tab-btn span {
display: none;
}
.tab-btn {
padding: 0.6rem;
}
}
.player-container {
background: var(--bg-secondary);
border-radius: 12px;
padding: 1rem;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
}
.player-layout {
display: flex;
flex-direction: column;
perspective: 800px;
}
.player-details {
flex: 1;
min-width: 0;
}
.album-art-container {
display: flex;
justify-content: center;
margin-bottom: 2rem;
position: relative;
transform-style: preserve-3d;
animation: albumArt3D 8s ease-in-out infinite;
}
@keyframes albumArt3D {
0%, 100% { transform: rotateY(-8deg) rotateX(2deg); }
50% { transform: rotateY(8deg) rotateX(-2deg); }
}
.album-art-glow {
position: absolute;
width: 300px;
height: 300px;
object-fit: cover;
border-radius: 8px;
filter: blur(40px) saturate(1.5);
opacity: 0.5;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(1.1);
z-index: 0;
pointer-events: none;
transition: opacity 0.5s ease, border-radius 0.6s ease;
}
#album-art {
width: 300px;
height: 300px;
object-fit: cover;
border-radius: 8px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
background: var(--bg-tertiary);
position: relative;
z-index: 1;
margin: 0;
transition: border-radius 0.6s ease, width 0.6s ease, height 0.6s ease, box-shadow 0.6s ease, margin 0.6s ease, filter 0.6s ease;
}
:root[data-theme="light"] .album-art-glow {
opacity: 0.35;
filter: blur(50px) saturate(1.8);
}
/* Legacy "vinyl mode" toggle (.album-art-container.vinyl + JS-driven
spinning class) was removed alongside the sleeve+disc redesign — the
disc now spins structurally via .vinyl-stage .vinyl when playstate
is "playing" (see SLEEVE FRAME section below). */
/* Audio Spectrogram Visualization */
.spectrogram-canvas {
position: absolute;
bottom: -4px;
left: 50%;
transform: translateX(-50%);
z-index: 2;
pointer-events: none;
opacity: 0;
transition: opacity 0.3s ease;
border-radius: 0 0 8px 8px;
}
.visualizer-active .spectrogram-canvas {
opacity: 1;
}
.visualizer-active #album-art {
transition: transform 0.08s ease-out;
}
.visualizer-active .album-art-glow {
transition: opacity 0.08s ease-out;
}
.track-info {
text-align: center;
margin-bottom: 2rem;
}
#track-title {
font-size: 1.75rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: var(--text-primary);
}
#artist {
font-size: 1.125rem;
color: var(--text-secondary);
margin-bottom: 0.25rem;
}
#album {
font-size: 0.875rem;
color: var(--text-muted);
}
.playback-state {
display: flex;
justify-content: center;
align-items: center;
gap: 0.5rem;
font-size: 0.875rem;
color: var(--text-secondary);
margin-top: 0.5rem;
}
.state-icon {
width: 16px;
height: 16px;
}
.progress-container {
margin-bottom: 2rem;
}
.time-display {
display: flex;
justify-content: space-between;
font-size: 0.75rem;
color: var(--text-secondary);
margin-bottom: 0.5rem;
}
.progress-bar {
width: 100%;
height: 6px;
background: var(--bg-tertiary);
border-radius: 3px;
cursor: pointer;
position: relative;
transition: transform 0.15s ease;
}
.progress-bar:hover {
transform: scaleY(1.4);
}
.progress-fill {
height: 100%;
background: var(--accent);
border-radius: 3px;
width: 0;
transition: width 0.1s linear;
position: relative;
}
.progress-fill::after {
content: '';
position: absolute;
right: -6px;
top: 50%;
transform: translateY(-50%) scale(0);
width: 12px;
height: 12px;
background: var(--accent);
border-radius: 50%;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
transition: transform 0.15s ease;
}
.progress-bar:hover .progress-fill::after,
.progress-bar.dragging .progress-fill::after {
transform: translateY(-50%) scale(1);
}
.progress-bar.dragging {
transform: scaleY(1.4);
}
.progress-bar.dragging .progress-fill {
transition: none;
}
.controls {
display: flex;
gap: 1rem;
justify-content: center;
align-items: center;
margin-bottom: 2rem;
}
button {
border: none;
background: none;
color: inherit;
font: inherit;
cursor: pointer;
padding: 0;
}
button:disabled {
opacity: 0.3;
cursor: not-allowed;
}
.controls button {
background: var(--bg-tertiary);
color: var(--text-primary);
border-radius: 50%;
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.controls button:hover:not(:disabled) {
background: var(--accent);
color: #fff;
transform: scale(1.05);
}
.controls button:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 3px;
box-shadow: 0 0 0 4px rgba(29, 185, 84, 0.25);
}
.mute-btn:focus-visible,
.mini-control-btn:focus-visible,
.vinyl-toggle-btn:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
box-shadow: 0 0 0 4px rgba(29, 185, 84, 0.25);
}
.controls button.primary {
width: 56px;
height: 56px;
background: var(--accent);
}
.controls button.primary:hover:not(:disabled) {
background: var(--accent-hover);
color: #fff;
transform: scale(1.1);
}
.volume-container {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem;
background: var(--bg-tertiary);
border-radius: 8px;
margin-bottom: 1rem;
}
#volume-slider {
flex: 1;
height: 6px;
-webkit-appearance: none;
appearance: none;
background: color-mix(in srgb, var(--accent) 15%, var(--border));
border-radius: 3px;
outline: none;
}
#volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
background: var(--accent);
border-radius: 50%;
cursor: pointer;
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
#volume-slider:hover::-webkit-slider-thumb {
transform: scale(1.3);
box-shadow: 0 0 6px rgba(29, 185, 84, 0.4);
}
#volume-slider::-moz-range-thumb {
width: 16px;
height: 16px;
background: var(--accent);
border-radius: 50%;
cursor: pointer;
border: none;
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
#volume-slider:hover::-moz-range-thumb {
transform: scale(1.3);
box-shadow: 0 0 6px rgba(29, 185, 84, 0.4);
}
.volume-display {
font-size: 0.875rem;
color: var(--text-secondary);
min-width: 40px;
text-align: right;
}
.mute-btn {
width: 40px;
height: 40px;
background: var(--bg-tertiary);
border-radius: 50%;
color: var(--text-primary);
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.mute-btn:hover:not(:disabled) {
background: var(--accent);
color: #fff;
transform: scale(1.05);
}
.source-info {
text-align: center;
font-size: 0.75rem;
color: var(--text-muted);
padding-top: 1rem;
border-top: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: center;
gap: 0.75rem;
min-width: 0;
}
.source-label {
display: inline-flex;
align-items: center;
gap: 0.35rem;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 200px;
}
.source-icon {
display: inline-flex;
align-items: center;
}
.source-icon svg {
width: 14px;
height: 14px;
}
.player-toggles {
display: flex;
align-items: center;
gap: 0.375rem;
margin-left: auto;
}
.vinyl-toggle-btn {
width: 28px;
height: 28px;
padding: 0;
background: transparent;
border: 1px solid var(--border);
border-radius: 50%;
color: var(--text-muted);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.vinyl-toggle-btn:hover {
color: var(--accent);
border-color: var(--accent);
background: transparent;
transform: none;
}
.vinyl-toggle-btn.active {
color: var(--accent);
border-color: var(--accent);
background: rgba(29, 185, 84, 0.1);
}
.vinyl-toggle-btn svg {
width: 16px;
height: 16px;
}
/* Scripts Section */
.scripts-container {
background: var(--bg-secondary);
border-radius: 12px;
padding: 1rem;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
}
.scripts-container h2 {
font-size: 1.25rem;
margin-bottom: 1rem;
color: var(--text-primary);
}
.scripts-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(180px, 100%), 1fr));
gap: 1rem;
}
.script-btn {
width: 100%;
height: auto;
min-height: 80px;
padding: 1rem;
border-radius: 8px;
background: var(--bg-tertiary);
border: 1px solid var(--border);
color: var(--text-primary);
cursor: pointer;
transition: all 0.2s;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.script-btn:hover:not(:disabled) {
background: var(--accent);
border-color: var(--accent);
color: #fff;
transform: translateY(-2px);
}
.script-btn:disabled {
opacity: 0.3;
cursor: not-allowed;
}
.script-btn .script-label {
font-weight: 600;
font-size: 0.875rem;
text-align: center;
overflow-wrap: break-word;
word-break: break-word;
max-width: 100%;
}
.script-btn .script-description {
font-size: 0.75rem;
color: var(--text-secondary);
text-align: center;
transition: color 0.2s;
}
.script-btn:hover:not(:disabled) .script-description {
color: rgba(255, 255, 255, 0.85);
}
.script-btn.executing {
opacity: 0.6;
pointer-events: none;
}
.script-btn .script-icon {
color: var(--accent);
line-height: 0;
}
.script-btn .script-icon svg {
width: 24px;
height: 24px;
}
.script-btn:hover:not(:disabled) .script-icon {
color: #fff;
}
/* Inline icon in table name cells */
.name-with-icon {
display: inline-flex;
align-items: center;
gap: 0.375rem;
}
.table-icon {
display: inline-flex;
color: var(--text-secondary);
line-height: 0;
}
.table-icon svg {
width: 16px;
height: 16px;
}
/* Script Management Styles */
.script-management {
background: var(--bg-secondary);
border-radius: 12px;
padding: 2rem;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
overflow-x: auto;
}
.script-management h2 {
font-size: 1.25rem;
margin-bottom: 1rem;
color: var(--text-primary);
}
/* Settings Container */
.settings-container.active {
display: flex;
flex-direction: column;
gap: 1rem;
}
.settings-section {
background: var(--bg-secondary);
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
overflow: hidden;
}
.settings-section summary {
padding: 1rem;
font-size: 1rem;
font-weight: 600;
color: var(--text-primary);
cursor: pointer;
user-select: none;
display: flex;
align-items: center;
gap: 0.5rem;
transition: background 0.2s;
list-style: none;
}
.settings-section summary::-webkit-details-marker {
display: none;
}
.settings-section summary::before {
content: '';
display: inline-block;
width: 0.5rem;
height: 0.5rem;
border-right: 2px solid var(--text-muted);
border-bottom: 2px solid var(--text-muted);
transform: rotate(-45deg);
transition: transform 0.3s ease;
flex-shrink: 0;
}
.settings-section[open] > summary::before {
transform: rotate(45deg);
}
.settings-section summary:hover {
background: var(--bg-tertiary);
}
.settings-section-content {
padding: 0 1rem 1rem;
overflow-x: auto;
animation: settingsExpand 0.3s ease-out;
}
@keyframes settingsExpand {
from { opacity: 0; transform: translateY(-8px); }
to { opacity: 1; transform: translateY(0); }
}
.settings-section-description {
color: var(--text-secondary);
font-size: 0.875rem;
margin-bottom: 1rem;
}
.audio-device-selector {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.audio-device-selector label {
display: flex;
flex-direction: column;
gap: 0.375rem;
}
.audio-device-selector label span {
font-size: 0.8125rem;
font-weight: 500;
color: var(--text-secondary);
}
.audio-device-selector select {
width: 100%;
padding: 10px;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--bg-tertiary);
color: var(--text-primary);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
font-size: 1rem;
cursor: pointer;
transition: border-color 0.25s ease, box-shadow 0.25s ease;
}
.audio-device-selector select:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(29, 185, 84, 0.15);
}
.audio-device-status {
font-size: 0.75rem;
display: flex;
align-items: center;
gap: 0.375rem;
}
.audio-device-status::before {
content: '';
display: inline-block;
width: 6px;
height: 6px;
border-radius: 50%;
}
.audio-device-status.active {
color: var(--accent);
}
.audio-device-status.active::before {
background: var(--accent);
}
.audio-device-status.available {
color: var(--text-secondary);
}
.audio-device-status.available::before {
background: var(--text-muted);
}
.audio-device-status.unavailable {
color: var(--text-muted);
}
.audio-device-status.unavailable::before {
background: var(--text-muted);
opacity: 0.5;
}
/* Link card in Quick Access */
.link-card {
text-decoration: none;
color: var(--text-primary);
border: 1px dashed var(--border);
}
.link-card:hover {
border-style: solid;
}
/* Mini player nav buttons */
.mini-nav-btn {
width: 28px;
height: 28px;
}
.mini-nav-btn svg {
width: 16px;
height: 16px;
}
/* Display Control Section */
.display-container {
background: var(--bg-secondary);
border-radius: 12px;
padding: 1rem;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
}
.display-monitors {
display: flex;
flex-direction: column;
gap: 1rem;
}
.display-monitor-card {
background: var(--bg-tertiary);
border: 1px solid var(--border);
border-radius: 8px;
padding: 1rem 1.25rem;
}
.display-monitor-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.75rem;
}
.display-monitor-icon {
color: var(--text-muted);
flex-shrink: 0;
}
.display-monitor-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 0.125rem;
}
.display-monitor-name {
font-weight: 500;
color: var(--text-primary);
font-size: 0.875rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.display-primary-badge {
display: inline-block;
background: var(--accent);
color: #fff;
font-size: 0.625rem;
font-weight: 600;
padding: 1px 6px;
border-radius: 8px;
margin-left: 6px;
vertical-align: middle;
text-transform: uppercase;
letter-spacing: 0.03em;
}
.display-monitor-details {
font-size: 0.75rem;
color: var(--text-muted);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.display-power-btn {
width: 30px;
height: 30px;
border-radius: 6px;
border: 1px solid var(--border);
background: transparent;
color: var(--text-muted);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: all 0.2s;
}
.display-power-btn.on {
color: var(--accent);
border-color: var(--accent);
}
.display-power-btn.on:hover {
color: var(--error);
border-color: var(--error);
}
.display-power-btn.off {
color: var(--error);
border-color: var(--error);
opacity: 0.6;
}
.display-power-btn.off:hover {
color: var(--accent);
border-color: var(--accent);
opacity: 1;
}
.display-brightness-control {
display: flex;
align-items: center;
gap: 0.75rem;
}
.display-brightness-icon {
color: var(--text-muted);
flex-shrink: 0;
}
.display-brightness-slider {
flex: 1;
-webkit-appearance: none;
appearance: none;
height: 6px;
border-radius: 3px;
background: color-mix(in srgb, var(--accent) 15%, var(--border));
outline: none;
cursor: pointer;
}
.display-brightness-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
background: var(--accent);
cursor: pointer;
transition: transform 0.15s;
}
.display-brightness-slider::-moz-range-thumb {
width: 16px;
height: 16px;
border-radius: 50%;
background: var(--accent);
cursor: pointer;
border: none;
}
.display-brightness-slider:hover::-webkit-slider-thumb {
transform: scale(1.2);
}
.display-brightness-slider:hover::-moz-range-thumb {
transform: scale(1.2);
}
.display-brightness-slider:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.display-brightness-value {
font-size: 0.813rem;
color: var(--text-secondary);
min-width: 36px;
text-align: right;
font-variant-numeric: tabular-nums;
}
.add-card {
display: flex;
align-items: center;
justify-content: center;
border: 2px dashed var(--border);
border-radius: 8px;
padding: 1.5rem;
margin-top: 1rem;
cursor: pointer;
transition: border-color 0.2s, background 0.2s;
}
.add-card:hover {
border-color: var(--text-muted);
background: var(--bg-tertiary);
}
.add-card-icon {
font-size: 1.5rem;
color: var(--text-muted);
font-weight: 300;
line-height: 1;
}
.add-card-grid {
border: 2px dashed var(--border);
background: transparent;
}
.add-card-grid:hover:not(:disabled) {
border-color: var(--text-muted);
background: var(--bg-tertiary);
}
.scripts-table {
width: 100%;
min-width: 500px;
border-collapse: collapse;
font-size: 0.875rem;
}
.scripts-table th {
text-align: left;
padding: 0.75rem;
border-bottom: 2px solid var(--border);
color: var(--text-secondary);
font-weight: 600;
font-size: 0.75rem;
text-transform: uppercase;
}
.scripts-table td {
padding: 0.75rem;
word-break: break-word;
border-bottom: 1px solid var(--border);
}
.scripts-table tr:hover {
background: var(--bg-tertiary);
}
.scripts-table code {
background: var(--bg-tertiary);
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
color: var(--accent);
white-space: nowrap;
}
.action-btn {
padding: 0.5rem;
border-radius: 6px;
border: 1px solid var(--border);
background: var(--bg-tertiary);
color: var(--text-primary);
cursor: pointer;
transition: all 0.2s;
width: 32px;
height: 32px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.action-btn svg {
width: 16px;
height: 16px;
fill: currentColor;
}
.action-btn:hover {
background: var(--accent);
border-color: var(--accent);
color: #fff;
transform: translateY(-1px);
}
.action-btn.delete:hover {
background: var(--error);
border-color: var(--error);
color: #fff;
}
.action-buttons {
display: flex;
gap: 0.5rem;
align-items: center;
}
.action-btn.execute:hover {
background: #3b82f6;
border-color: #3b82f6;
color: #fff;
}
/* Execution Result Dialog */
.execution-result {
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
background: var(--bg-primary);
border-radius: 6px;
padding: 1rem;
margin: 0.5rem 0;
max-height: 400px;
overflow-y: auto;
}
.execution-result pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
font-size: 0.813rem;
line-height: 1.5;
}
.execution-status {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.status-item {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.status-item label {
font-size: 0.75rem;
color: var(--text-muted);
text-transform: uppercase;
font-weight: 600;
}
.status-item value {
font-size: 0.875rem;
color: var(--text-primary);
font-weight: 500;
}
.status-item.success value {
color: var(--accent);
}
.status-item.error value {
color: var(--error);
}
.result-section {
margin-bottom: 1rem;
}
.result-section h4 {
font-size: 0.875rem;
color: var(--text-secondary);
margin-bottom: 0.5rem;
font-weight: 600;
}
.loading-spinner {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid var(--bg-tertiary);
border-top-color: var(--accent);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Dialog Styles */
dialog {
background: var(--bg-secondary);
color: var(--text-primary);
border: 1px solid var(--border);
border-radius: 12px;
padding: 0;
max-width: 500px;
width: 90%;
margin: auto;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.8);
animation: dialogIn 0.25s ease-out;
overflow: visible;
}
dialog form {
overflow: visible;
}
dialog.dialog-closing {
animation: dialogOut 0.2s ease-in forwards;
}
/* Ensure dialogs are hidden until explicitly opened */
dialog:not([open]) {
display: none;
}
dialog::backdrop {
background: rgba(0, 0, 0, 0.8);
animation: backdropIn 0.25s ease-out;
}
dialog.dialog-closing::backdrop {
animation: backdropOut 0.2s ease-in forwards;
}
@keyframes dialogIn {
from { opacity: 0; transform: scale(0.9) translateY(10px); }
to { opacity: 1; transform: scale(1) translateY(0); }
}
@keyframes dialogOut {
from { opacity: 1; transform: scale(1) translateY(0); }
to { opacity: 0; transform: scale(0.9) translateY(10px); }
}
@keyframes backdropIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes backdropOut {
from { opacity: 1; }
to { opacity: 0; }
}
.confirm-dialog {
max-width: 400px;
padding: 1.5rem;
}
.confirm-dialog p {
margin-bottom: 1.25rem;
font-size: 0.95rem;
line-height: 1.5;
color: var(--text-primary);
}
.confirm-dialog-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
}
.confirm-dialog-actions button {
padding: 8px 20px;
border-radius: 6px;
font-size: 0.85rem;
font-weight: 500;
transition: all 0.2s;
}
.btn-cancel {
background: var(--bg-tertiary);
color: var(--text-primary);
}
.btn-cancel:hover {
background: var(--border);
}
.btn-danger {
background: var(--error);
color: #fff;
}
.btn-danger:hover {
filter: brightness(1.15);
}
.dialog-header {
padding: 1.5rem;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
}
.dialog-header h3 {
margin: 0;
font-size: 1.25rem;
}
.dialog-body {
padding: 1.5rem;
}
.dialog-body label {
display: block;
margin-bottom: 1rem;
color: var(--text-secondary);
font-size: 0.875rem;
}
.dialog-body input,
.dialog-body textarea,
.dialog-body select {
display: block;
width: 100%;
padding: 0.5rem;
margin-top: 0.25rem;
background: var(--bg-tertiary);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--text-primary);
font-family: inherit;
font-size: 0.875rem;
}
.dialog-body textarea {
min-height: 80px;
resize: vertical;
}
.dialog-body input:focus,
.dialog-body textarea:focus,
.dialog-body select:focus {
outline: none;
border-color: var(--accent);
}
.dialog-footer {
padding: 1.5rem;
border-top: 1px solid var(--border);
display: flex;
justify-content: flex-end;
gap: 0.5rem;
}
.dialog-footer button {
padding: 0.625rem 1.5rem;
border-radius: 6px;
border: none;
font-size: 0.875rem;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
min-width: 100px;
white-space: nowrap;
}
.dialog-footer .btn-primary {
background: var(--accent);
color: var(--text-primary);
}
.dialog-footer .btn-primary:hover {
background: var(--accent-hover);
}
.dialog-footer .btn-secondary {
background: var(--bg-tertiary);
color: var(--text-primary);
border: 1px solid var(--border);
}
.dialog-footer .btn-secondary:hover {
background: var(--border);
}
/* Parameters editor (CRUD dialog) */
.params-section {
margin-top: 0.5rem;
}
.params-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0.5rem;
color: var(--text-secondary);
font-size: 0.875rem;
}
.btn-small {
padding: 0.25rem 0.75rem;
font-size: 0.75rem;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--bg-tertiary);
color: var(--text-primary);
cursor: pointer;
transition: background 0.2s;
}
.btn-small:hover {
background: var(--border);
}
.param-row {
border: 1px solid var(--border);
border-radius: 6px;
padding: 0.5rem;
margin-bottom: 0.5rem;
background: var(--bg-tertiary);
}
.param-row-header {
display: flex;
gap: 0.375rem;
align-items: center;
}
.param-row-header .param-name {
flex: 1;
min-width: 0;
}
.param-row-header .param-type {
width: 100px;
flex-shrink: 0;
}
.param-required-label {
display: flex !important;
align-items: center;
gap: 0.125rem;
margin-bottom: 0 !important;
cursor: pointer;
color: var(--accent);
font-weight: 700;
font-size: 1rem;
flex-shrink: 0;
}
.param-required-label input {
width: auto !important;
margin: 0 !important;
}
.param-remove-btn {
background: none;
border: none;
color: var(--text-muted);
cursor: pointer;
font-size: 1.25rem;
line-height: 1;
padding: 0 0.25rem;
flex-shrink: 0;
transition: color 0.2s;
}
.param-remove-btn:hover {
color: var(--error);
}
.param-row-details {
margin-top: 0.375rem;
}
.param-row-details .param-description {
width: 100%;
margin-bottom: 0.25rem;
font-size: 0.8rem;
color: var(--text-muted);
}
.param-row-extra {
display: flex;
gap: 0.375rem;
}
.param-row-extra input {
flex: 1;
min-width: 0;
}
.param-row-header input,
.param-row-header select,
.param-row-details input,
.param-row-extra input {
padding: 0.35rem 0.5rem;
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 4px;
color: var(--text-primary);
font-family: inherit;
font-size: 0.8rem;
}
.param-row-header input:focus,
.param-row-header select:focus,
.param-row-details input:focus,
.param-row-extra input:focus {
outline: none;
border-color: var(--accent);
}
/* Parameter hint in execution dialog */
.param-hint {
display: block;
color: var(--text-muted);
font-size: 0.75rem;
margin-top: 0.125rem;
}
/* ── Icon Select ──────────────────────────────────────────── */
.icon-select-trigger {
display: flex;
align-items: center;
gap: 0.375rem;
width: 100%;
padding: 0.35rem 0.5rem;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--bg-tertiary);
color: var(--text-primary);
font-size: 0.875rem;
cursor: pointer;
transition: border-color 0.15s;
text-align: left;
font-family: inherit;
box-sizing: border-box;
}
/* Match dialog input height when inside a dialog */
.dialog-body .icon-select-trigger {
padding: 0.5rem;
margin-top: 0.25rem;
}
/* Compact trigger inside param rows */
.param-row-header .icon-select-trigger {
padding: 0.3rem 0.4rem;
margin-top: 0;
font-size: 0.75rem;
width: 110px;
flex-shrink: 0;
}
.icon-select-trigger:hover {
border-color: var(--accent);
}
.icon-select-trigger-icon {
display: flex;
align-items: center;
flex-shrink: 0;
}
.icon-select-trigger-icon svg {
width: 16px;
height: 16px;
fill: currentColor;
}
.param-row-header .icon-select-trigger-icon svg {
width: 14px;
height: 14px;
}
.icon-select-trigger-label {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.icon-select-trigger-arrow {
flex-shrink: 0;
font-size: 0.8rem;
opacity: 0.5;
margin-left: auto;
}
.icon-select-popup {
position: absolute;
z-index: 100;
max-height: 260px;
overflow: hidden;
opacity: 0;
transform: translateY(-4px) scale(0.97);
transition: opacity 0.12s ease-out, transform 0.12s cubic-bezier(0.16, 1, 0.3, 1);
pointer-events: none;
box-sizing: border-box;
}
.icon-select-popup.open {
opacity: 1;
transform: translateY(0) scale(1);
overflow-y: auto;
pointer-events: auto;
}
.icon-select-grid {
display: grid;
grid-auto-rows: 1fr;
gap: 6px;
padding: 6px;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--bg-secondary);
}
.icon-select-cell {
display: flex;
flex-direction: column;
align-items: center;
gap: 3px;
padding: 6px 4px;
border: 2px solid transparent;
border-radius: 6px;
background: var(--bg-tertiary);
cursor: pointer;
transition: border-color 0.15s, background 0.15s, transform 0.1s;
text-align: center;
}
.icon-select-cell:hover {
border-color: var(--accent);
transform: scale(1.03);
}
.icon-select-cell.active {
border-color: var(--accent);
background: color-mix(in srgb, var(--accent) 12%, var(--bg-tertiary));
}
.icon-select-cell-icon {
display: flex;
align-items: center;
justify-content: center;
}
.icon-select-cell-icon svg {
width: 20px;
height: 20px;
fill: currentColor;
}
.icon-select-cell.active .icon-select-cell-icon svg {
fill: var(--accent);
}
.icon-select-cell-label {
font-size: 0.75rem;
font-weight: 600;
line-height: 1.2;
}
.icon-select-cell-desc {
font-size: 0.7rem;
opacity: 0.6;
line-height: 1.3;
}
/* Horizontal layout for single-column grids (e.g. audio device list) */
.icon-select-grid--horizontal .icon-select-cell {
flex-direction: row;
gap: 0.5rem;
padding: 0.4rem 0.75rem;
text-align: left;
}
.icon-select-grid--horizontal .icon-select-cell-icon svg {
width: 16px;
height: 16px;
}
.icon-select-grid--horizontal .icon-select-cell-label {
font-size: 0.8rem;
}
@media (max-width: 480px) {
.icon-select-cell-desc { display: none; }
.icon-select-cell { padding: 6px 4px; }
.icon-select-grid { gap: 4px; padding: 4px; }
}
.empty-state {
text-align: center;
padding: 2rem;
color: var(--text-muted);
}
.scripts-empty {
text-align: center;
color: var(--text-muted);
padding: 2rem;
font-size: 0.875rem;
}
.empty-state-illustration {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
padding: 3rem 2rem;
}
.empty-state-illustration svg {
width: 64px;
height: 64px;
fill: none;
stroke: var(--text-muted);
stroke-width: 1.5;
stroke-linecap: round;
stroke-linejoin: round;
opacity: 0.5;
}
.empty-state-illustration p {
color: var(--text-muted);
font-size: 0.875rem;
margin: 0;
}
.toast-container {
position: fixed;
bottom: 2rem;
right: 2rem;
display: flex;
flex-direction: column-reverse;
gap: 8px;
z-index: 1000;
pointer-events: none;
}
.toast {
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 8px;
padding: 1rem 1.5rem;
padding-left: 1.25rem;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
opacity: 0;
transform: translateY(20px);
transition: all 0.3s;
pointer-events: auto;
border-left: 4px solid var(--border);
}
.toast.show {
opacity: 1;
transform: translateY(0);
}
.toast.success {
border-color: var(--accent);
border-left-color: var(--accent);
}
.toast.error {
border-color: var(--error);
border-left-color: var(--error);
}
/* Auth Modal */
#auth-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.9);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
#auth-overlay.hidden {
display: none;
}
.auth-modal {
background: var(--bg-secondary);
padding: 2rem;
border-radius: 12px;
max-width: 400px;
width: 90%;
}
.auth-modal h2 {
margin-bottom: 1rem;
font-size: 1.5rem;
}
.auth-modal p {
margin-bottom: 1rem;
color: var(--text-secondary);
font-size: 0.875rem;
}
#token-input {
width: 100%;
padding: 0.75rem;
background: var(--bg-tertiary);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--text-primary);
font-size: 0.875rem;
margin-bottom: 1rem;
}
#token-input:focus {
outline: none;
border-color: var(--accent);
}
.btn-connect {
width: 100%;
height: auto;
padding: 0.75rem;
border-radius: 6px;
background: var(--accent);
color: var(--text-primary);
font-weight: 600;
transition: background 0.2s;
}
.btn-connect:hover {
background: var(--accent-hover);
transform: none;
}
.help-text {
background: var(--bg-tertiary);
padding: 0.75rem;
border-radius: 6px;
margin-top: 1rem;
}
.help-text code {
background: var(--bg-primary);
padding: 0.25rem 0.5rem;
border-radius: 3px;
font-family: monospace;
font-size: 0.875rem;
}
.error-message {
color: var(--error);
font-size: 0.875rem;
margin-top: 0.5rem;
display: none;
}
.error-message.visible {
display: block;
}
/* Mini Player (Sticky) */
.mini-player {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: rgba(30, 30, 30, 0.8);
-webkit-backdrop-filter: blur(20px) saturate(1.5);
backdrop-filter: blur(20px) saturate(1.5);
border-top: 1px solid rgba(255, 255, 255, 0.08);
padding: 0.75rem 1rem;
padding-top: calc(0.75rem + 2px);
display: flex;
align-items: center;
gap: 1.5rem;
z-index: 1000;
transform: translateY(0);
transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.3);
}
:root[data-theme="light"] .mini-player {
background: rgba(245, 245, 245, 0.75);
border-top: 1px solid rgba(0, 0, 0, 0.08);
box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.1);
}
.mini-player::before {
content: '';
position: absolute;
top: 0;
left: 0;
height: 2px;
width: var(--mini-progress, 0%);
background: var(--accent);
transition: width 0.1s linear;
}
.mini-player.hidden {
transform: translateY(100%);
opacity: 0;
pointer-events: none;
}
.mini-player-info {
display: flex;
align-items: center;
gap: 0.75rem;
min-width: 200px;
flex-shrink: 0;
}
.mini-album-art {
width: 40px;
height: 40px;
border-radius: 4px;
object-fit: cover;
flex-shrink: 0;
}
.mini-track-details {
display: flex;
flex-direction: column;
min-width: 0;
}
.mini-track-title {
font-size: 0.875rem;
font-weight: 600;
color: var(--text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.mini-artist {
font-size: 0.75rem;
color: var(--text-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.mini-progress-container {
flex: 1;
display: flex;
align-items: center;
gap: 1rem;
min-width: 0;
}
.mini-time-display {
display: flex;
gap: 0.5rem;
font-size: 0.75rem;
color: var(--text-secondary);
flex-shrink: 0;
}
.mini-progress-bar {
flex: 1;
height: 4px;
background: var(--bg-tertiary);
border-radius: 2px;
cursor: pointer;
position: relative;
min-width: 100px;
}
.mini-progress-bar:hover {
height: 6px;
}
.mini-progress-fill {
height: 100%;
background: var(--accent);
border-radius: 2px;
width: 0%;
transition: width 0.1s linear;
position: relative;
}
.mini-progress-fill::after {
content: '';
position: absolute;
right: -5px;
top: 50%;
transform: translateY(-50%) scale(0);
width: 10px;
height: 10px;
background: var(--accent);
border-radius: 50%;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
transition: transform 0.15s ease;
}
.mini-progress-bar:hover .mini-progress-fill::after {
transform: translateY(-50%) scale(1);
}
.mini-controls {
display: flex;
align-items: center;
gap: 0.5rem;
flex-shrink: 0;
}
.mini-control-btn {
background: var(--bg-tertiary);
border: 1px solid var(--border);
border-radius: 50%;
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s;
padding: 0;
}
.mini-control-btn:hover {
background: var(--accent);
border-color: var(--accent);
color: #fff;
transform: scale(1.05);
}
.mini-control-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.mini-control-btn svg {
width: 20px;
height: 20px;
fill: currentColor;
}
.mini-volume-container {
display: flex;
align-items: center;
gap: 0.75rem;
flex-shrink: 0;
min-width: 180px;
}
.mini-volume-slider {
flex: 1;
height: 4px;
-webkit-appearance: none;
appearance: none;
background: color-mix(in srgb, var(--accent) 15%, var(--border));
border-radius: 2px;
outline: none;
cursor: pointer;
min-width: 80px;
}
.mini-volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 12px;
height: 12px;
background: var(--accent);
border-radius: 50%;
cursor: pointer;
}
.mini-volume-slider::-moz-range-thumb {
width: 12px;
height: 12px;
background: var(--accent);
border-radius: 50%;
cursor: pointer;
border: none;
}
.mini-volume-slider:hover::-webkit-slider-thumb {
transform: scale(1.2);
}
.mini-volume-slider:hover::-moz-range-thumb {
transform: scale(1.2);
}
.mini-volume-display {
font-size: 0.75rem;
color: var(--text-secondary);
min-width: 36px;
text-align: right;
}
/* SVG Icons */
svg {
width: 24px;
height: 24px;
fill: currentColor;
}
button.primary svg {
width: 28px;
height: 28px;
}
@media (max-width: 600px) {
.container {
padding: 0.5rem;
}
#album-art {
width: 250px;
height: 250px;
}
#track-title {
font-size: 1.5rem;
}
}
/* Footer */
footer {
text-align: center;
padding: 0.75rem 1rem;
margin-top: 0.5rem;
color: var(--text-muted);
font-size: 0.75rem;
transition: padding-bottom 0.3s ease-in-out;
}
body.mini-player-visible footer {
padding-bottom: 70px;
}
footer a {
color: var(--accent);
text-decoration: none;
transition: color 0.2s;
}
footer a:hover {
color: var(--accent-hover);
text-decoration: underline;
}
footer .separator {
margin: 0 0.5rem;
color: var(--text-muted);
}
/* ========================================
Media Browser Styles
======================================== */
.browser-container {
background: var(--bg-secondary);
border-radius: 12px;
padding: 1.25rem;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
}
.browser-header-section {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
}
.browser-header-section h2 {
font-size: 1.25rem;
color: var(--text-primary);
margin: 0;
}
/* Breadcrumb Navigation */
.breadcrumb {
display: flex;
align-items: center;
gap: 0.25rem;
margin-bottom: 1rem;
padding: 0.5rem 0.75rem;
background: var(--bg-tertiary);
border-radius: 8px;
font-size: 0.813rem;
overflow-x: auto;
white-space: nowrap;
scrollbar-width: none;
border: 1px solid var(--border);
}
.breadcrumb::-webkit-scrollbar {
display: none;
}
.breadcrumb:empty {
display: none;
}
.breadcrumb-item {
color: var(--text-secondary);
cursor: pointer;
transition: all 0.2s;
padding: 0.2rem 0.5rem;
border-radius: 4px;
font-weight: 500;
}
.breadcrumb-item:hover {
color: var(--accent);
background: rgba(29, 185, 84, 0.08);
text-decoration: none;
}
.breadcrumb-item:last-child {
color: var(--text-primary);
font-weight: 600;
cursor: default;
pointer-events: none;
}
.breadcrumb-home {
display: flex;
align-items: center;
padding: 0.25rem;
color: var(--text-muted);
}
.breadcrumb-home:hover {
text-decoration: none;
color: var(--accent);
}
.breadcrumb-separator {
color: var(--text-muted);
margin: 0;
opacity: 0.5;
font-size: 0.75rem;
}
/* Browser Toolbar */
.browser-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
gap: 1rem;
}
.browser-toolbar-left,
.browser-toolbar-right {
display: flex;
align-items: center;
gap: 0.75rem;
}
/* Browser Search */
.browser-search-wrapper {
flex: 1;
position: relative;
min-width: 0;
max-width: 300px;
}
.browser-search-icon {
position: absolute;
left: 0.6rem;
top: 50%;
transform: translateY(-50%);
color: var(--text-muted);
pointer-events: none;
}
.browser-search-input {
width: 100%;
padding: 0.4rem 2rem 0.4rem 2rem;
background: var(--bg-tertiary);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--text-primary);
font-size: 0.813rem;
outline: none;
transition: border-color 0.2s;
}
.browser-search-input:focus {
border-color: var(--accent);
}
.browser-search-input::placeholder {
color: var(--text-muted);
}
.browser-search-clear {
position: absolute;
right: 0.4rem;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
color: var(--text-muted);
cursor: pointer;
padding: 0.15rem;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
width: auto;
height: auto;
transition: color 0.15s;
}
.browser-search-clear:hover {
color: var(--text-primary);
background: transparent !important;
transform: translateY(-50%) !important;
}
.view-toggle {
display: flex;
background: var(--bg-tertiary);
border-radius: 6px;
border: 1px solid var(--border);
overflow: hidden;
}
.view-toggle-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 0.4rem 0.6rem;
background: transparent;
border: none;
color: var(--text-muted);
cursor: pointer;
transition: all 0.2s;
width: auto;
height: auto;
border-radius: 0;
}
.view-toggle-btn:hover {
color: var(--text-primary);
background: transparent !important;
transform: none !important;
}
.view-toggle-btn.active {
color: var(--accent);
background: var(--bg-primary);
}
.browser-refresh-btn {
margin-left: 0.35rem;
}
.view-toggle-btn svg {
fill: currentColor;
}
.browser-play-all-btn {
display: flex;
align-items: center;
gap: 0.35rem;
padding: 0.35rem 0.7rem;
background: var(--accent);
border: none;
border-radius: 6px;
color: #fff;
font-size: 0.75rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
width: auto;
height: auto;
margin-left: 0.5rem;
}
.browser-play-all-btn:hover {
background: var(--accent-hover);
transform: none;
}
.items-per-page-label {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.813rem;
color: var(--text-secondary);
}
.items-per-page-label select {
padding: 0.3rem 0.5rem;
background: var(--bg-tertiary);
border: 1px solid var(--border);
border-radius: 4px;
color: var(--text-primary);
font-size: 0.813rem;
cursor: pointer;
}
.items-per-page-label select:hover {
border-color: var(--accent);
}
/* Browser Grid */
.browser-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
gap: 0.75rem;
margin-bottom: 1.5rem;
min-height: 200px;
align-items: stretch;
}
/* Root folder grid — wider cards */
.browser-grid.browser-root-grid {
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 1rem;
}
/* Compact Grid */
.browser-grid.browser-grid-compact {
grid-template-columns: repeat(auto-fill, minmax(80px, 100px));
gap: 0.5rem;
}
.browser-grid-compact .browser-item {
padding: 0.4rem;
gap: 0.3rem;
}
.browser-grid-compact .browser-icon {
font-size: 2rem;
}
.browser-grid-compact .browser-item-name {
font-size: 0.688rem;
-webkit-line-clamp: 1;
}
.browser-grid-compact .browser-item-meta {
font-size: 0.625rem;
}
.browser-grid-compact .browser-item-type {
font-size: 0.5rem;
padding: 0.15rem 0.35rem;
top: 0.25rem;
right: 0.25rem;
}
/* Browser List View */
.browser-list {
display: flex;
flex-direction: column;
gap: 1px;
margin-bottom: 1.5rem;
min-height: 200px;
}
/* List view column header */
.browser-list-header {
display: grid;
grid-template-columns: 40px 1fr auto auto auto auto;
align-items: center;
gap: 0.75rem;
padding: 0.4rem 0.75rem;
font-size: 0.688rem;
font-weight: 600;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.05em;
border-bottom: 1px solid var(--border);
margin-bottom: 0.25rem;
user-select: none;
}
.browser-list-header span:nth-child(n+3) {
text-align: right;
}
.browser-list-item {
display: grid;
grid-template-columns: 40px 1fr auto auto auto auto;
align-items: center;
gap: 0.75rem;
padding: 0.5rem 0.75rem;
background: transparent;
border: 1px solid transparent;
border-radius: 6px;
cursor: pointer;
transition: all 0.15s;
animation: itemFadeIn 0.3s ease-out backwards;
animation-delay: calc(var(--item-index, 0) * 15ms);
}
.browser-list-item:hover {
background: var(--bg-tertiary);
border-color: var(--border);
}
.browser-list-item:active {
background: var(--border);
}
.browser-list-icon {
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.25rem;
border-radius: 6px;
background: var(--bg-tertiary);
flex-shrink: 0;
overflow: hidden;
position: relative;
}
.browser-list-play-overlay {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.55);
border-radius: 6px;
opacity: 0;
transition: opacity 0.15s;
pointer-events: none;
}
.browser-list-play-overlay svg {
width: 16px;
height: 16px;
color: #fff;
}
.browser-list-item:hover .browser-list-play-overlay {
opacity: 1;
}
.browser-list-thumbnail {
width: 36px;
height: 36px;
object-fit: cover;
border-radius: 6px;
}
.browser-list-thumbnail.loading {
opacity: 0;
}
.browser-list-thumbnail.loaded {
animation: fadeIn 0.3s ease-out forwards;
}
.browser-list-name {
font-size: 0.813rem;
font-weight: 500;
color: var(--text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
min-width: 0;
}
.browser-list-bitrate {
font-size: 0.75rem;
color: var(--text-muted);
white-space: nowrap;
min-width: 55px;
text-align: right;
font-variant-numeric: tabular-nums;
}
.browser-list-duration {
font-size: 0.75rem;
color: var(--text-muted);
white-space: nowrap;
min-width: 45px;
text-align: right;
font-variant-numeric: tabular-nums;
}
.browser-list-size {
font-size: 0.75rem;
color: var(--text-muted);
white-space: nowrap;
min-width: 60px;
text-align: right;
font-variant-numeric: tabular-nums;
}
.browser-loading {
grid-column: 1 / -1;
display: flex;
justify-content: center;
padding: 3rem;
}
.browser-loading .loading-spinner {
width: 28px;
height: 28px;
}
.browser-empty {
grid-column: 1 / -1;
text-align: center;
padding: 3rem 2rem;
color: var(--text-muted);
font-size: 0.875rem;
}
.browser-item {
background: var(--bg-tertiary);
border: 1px solid transparent;
border-radius: 10px;
padding: 0.6rem;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
position: relative;
animation: itemFadeIn 0.3s ease-out backwards;
animation-delay: calc(var(--item-index, 0) * 25ms);
}
@keyframes itemFadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.browser-item:hover {
border-color: var(--border);
transform: translateY(-3px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
}
.browser-item:active {
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
/* Root Folder Cards — distinctive hero style */
.browser-item.browser-root-folder {
padding: 1.25rem 1rem;
gap: 0.75rem;
border: 1px solid var(--border);
background: linear-gradient(135deg, var(--bg-tertiary) 0%, var(--bg-secondary) 100%);
min-height: 120px;
justify-content: center;
}
.browser-item.browser-root-folder .browser-thumb-wrapper {
width: auto;
height: auto;
}
.browser-item.browser-root-folder .browser-icon {
width: 56px;
height: 56px;
font-size: 1.75rem;
border-radius: 14px;
background: rgba(29, 185, 84, 0.1);
border: 1px solid rgba(29, 185, 84, 0.15);
transition: all 0.25s;
}
.browser-item.browser-root-folder:hover .browser-icon {
background: rgba(29, 185, 84, 0.18);
border-color: rgba(29, 185, 84, 0.3);
transform: scale(1.05);
}
.browser-item.browser-root-folder .browser-item-name {
font-size: 0.875rem;
font-weight: 600;
}
/* Unavailable root folder overlay */
.browser-item.browser-root-folder.unavailable .browser-icon {
background: rgba(231, 76, 60, 0.08);
border-color: rgba(231, 76, 60, 0.12);
opacity: 0.6;
}
/* Thumbnail Display */
.browser-thumbnail {
width: 100%;
aspect-ratio: 1;
object-fit: cover;
border-radius: 8px;
background: var(--bg-primary);
display: block;
}
.browser-thumbnail.loading {
background: linear-gradient(
110deg,
var(--bg-primary) 30%,
var(--bg-tertiary) 50%,
var(--bg-primary) 70%
);
background-size: 200% 100%;
animation: shimmer 1.8s ease-in-out infinite;
opacity: 1;
}
.browser-thumbnail.loaded {
animation: fadeIn 0.4s ease-out forwards;
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
@keyframes fadeIn {
from {
opacity: 0;
transform: scale(0.97);
}
to {
opacity: 1;
transform: scale(1);
}
}
/* File/Folder Icons */
.browser-icon {
width: 100%;
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 2.5rem;
border-radius: 8px;
background: var(--bg-primary);
}
.browser-item-info {
width: 100%;
text-align: center;
margin-top: auto;
padding: 0 0.15rem;
}
.browser-item-name {
font-size: 0.75rem;
font-weight: 500;
color: var(--text-primary);
word-break: break-word;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
line-height: 1.3;
}
.browser-item-meta {
font-size: 0.688rem;
color: var(--text-muted);
margin-top: 0.2rem;
line-height: 1.3;
}
.browser-item-type {
position: absolute;
top: 0.35rem;
right: 0.35rem;
background: var(--bg-primary);
padding: 0.2rem;
border-radius: 4px;
color: var(--text-secondary);
z-index: 10;
line-height: 0;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.2s;
}
.browser-item:hover .browser-item-type {
opacity: 0.85;
}
.browser-item-type.audio {
color: var(--accent);
}
.browser-item-type.video {
color: #3b82f6;
}
/* Thumbnail Wrapper & Play Overlay */
.browser-thumb-wrapper {
position: relative;
width: 100%;
aspect-ratio: 1;
flex-shrink: 0;
border-radius: 8px;
overflow: hidden;
}
.browser-thumb-wrapper .browser-thumbnail,
.browser-thumb-wrapper .browser-icon {
width: 100%;
height: 100%;
}
.browser-play-overlay {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.5);
border-radius: 8px;
opacity: 0;
transition: opacity 0.2s;
pointer-events: none;
}
.browser-play-overlay svg {
width: 36px;
height: 36px;
color: #fff;
filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.5));
transition: transform 0.15s;
}
.browser-item:hover .browser-play-overlay {
opacity: 1;
}
.browser-item:hover .browser-play-overlay svg {
transform: scale(1.1);
}
/* Compact grid overrides */
.browser-grid-compact .browser-thumb-wrapper {
width: 100%;
aspect-ratio: 1;
}
.browser-grid-compact .browser-play-overlay svg {
width: 24px;
height: 24px;
}
/* Download Button (list view only) */
.browser-list-download {
background: transparent;
border: none;
border-radius: 4px;
padding: 0.25rem;
color: var(--text-muted);
cursor: pointer;
transition: color 0.15s;
line-height: 0;
display: flex;
align-items: center;
justify-content: center;
width: auto;
height: auto;
opacity: 0;
}
.browser-list-item:hover .browser-list-download {
opacity: 1;
}
.browser-list-download:hover {
color: var(--accent);
background: transparent !important;
transform: none;
}
/* Pagination */
.pagination {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
padding-top: 1rem;
border-top: 1px solid var(--border);
}
.pagination button {
padding: 0.4rem 1.25rem;
border-radius: 6px;
background: var(--bg-tertiary);
border: 1px solid var(--border);
color: var(--text-primary);
cursor: pointer;
font-size: 0.813rem;
font-weight: 600;
transition: all 0.2s;
width: auto;
height: auto;
}
.pagination button:hover:not(:disabled) {
background: var(--accent);
border-color: var(--accent);
color: #fff;
transform: none;
}
.pagination button:disabled {
opacity: 0.3;
cursor: not-allowed;
}
.pagination-center {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.813rem;
color: var(--text-secondary);
}
.pagination-showing {
font-size: 0.75rem;
color: var(--text-muted);
white-space: nowrap;
}
.page-input {
width: 3rem;
padding: 0.25rem 0.35rem;
text-align: center;
background: var(--bg-tertiary);
border: 1px solid var(--border);
border-radius: 4px;
color: var(--text-primary);
font-size: 0.813rem;
-moz-appearance: textfield;
}
.page-input::-webkit-outer-spin-button,
.page-input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.page-input:focus {
outline: none;
border-color: var(--accent);
}
/* Responsive Design */
@media (max-width: 600px) {
.browser-container {
padding: 0.75rem;
}
.browser-grid {
grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
gap: 0.5rem;
}
.browser-grid.browser-root-grid {
grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
gap: 0.75rem;
}
.browser-grid.browser-grid-compact {
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
}
.browser-item {
padding: 0.5rem;
}
.browser-item.browser-root-folder {
padding: 1rem 0.75rem;
min-height: 100px;
}
.browser-header-section {
flex-direction: column;
align-items: stretch;
gap: 1rem;
}
.browser-header-section button {
width: 100%;
}
.browser-toolbar {
flex-wrap: wrap;
gap: 0.5rem;
}
.browser-toolbar-left {
gap: 0.35rem;
}
.browser-search-wrapper {
max-width: none;
flex-basis: 100%;
order: 10;
}
.browser-toolbar-right {
margin-left: auto;
}
.items-per-page-label span {
display: none;
}
.browser-list-header {
grid-template-columns: 32px 1fr auto auto;
gap: 0.5rem;
padding: 0.35rem 0.5rem;
}
.browser-list-header span:nth-child(n+3):nth-child(-n+4) {
display: none;
}
.browser-list-item {
grid-template-columns: 32px 1fr auto auto;
gap: 0.5rem;
padding: 0.4rem 0.5rem;
}
.browser-list-icon {
width: 32px;
height: 32px;
}
.browser-list-duration {
display: none;
}
.browser-list-size {
display: none;
}
.browser-list-type {
display: none;
}
.pagination {
flex-wrap: wrap;
gap: 0.5rem;
}
.pagination-showing {
flex-basis: 100%;
text-align: center;
order: -1;
}
.album-art-glow {
width: 250px;
height: 250px;
}
.mini-volume-container {
display: none;
}
.mini-player {
gap: 0.75rem;
padding: 0.5rem 0.75rem;
padding-top: calc(0.5rem + 2px);
}
.mini-nav-btn {
display: none;
}
.mini-player-info {
min-width: 120px;
}
.mini-progress-container {
gap: 0.5rem;
}
}
/* Tablet breakpoint */
@media (min-width: 601px) and (max-width: 900px) {
.mini-volume-container {
display: none;
}
.browser-list-bitrate {
display: none;
}
.browser-list-header {
grid-template-columns: 40px 1fr auto auto auto;
}
.browser-list-header span:nth-child(3) {
display: none;
}
.browser-list-item {
grid-template-columns: 40px 1fr auto auto auto;
}
}
/* Update Banner */
.update-banner {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1001;
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
padding: 10px 16px;
background: var(--accent);
color: #fff;
font-size: 0.85rem;
font-weight: 500;
text-align: center;
transition: transform 0.3s ease;
}
.update-banner:not(.hidden) {
animation: bannerSlideIn 0.4s ease-out;
}
.update-banner.hidden {
transform: translateY(-100%);
pointer-events: none;
}
.update-banner a {
color: #fff;
text-decoration: underline;
font-weight: 600;
}
.update-banner a:hover {
opacity: 0.85;
}
.update-banner-close {
background: none;
color: #fff;
font-size: 1.2rem;
padding: 0 4px;
opacity: 0.7;
cursor: pointer;
line-height: 1;
display: flex;
align-items: center;
}
.update-banner-close:hover {
opacity: 1;
}
/* Connection Banner */
.connection-banner {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
padding: 10px 16px;
background: var(--error);
color: #fff;
font-size: 0.85rem;
font-weight: 500;
text-align: center;
transition: transform 0.3s ease;
}
.connection-banner:not(.hidden) {
animation: bannerSlideIn 0.4s ease-out, bannerPulse 2s ease-in-out 0.4s 2;
}
.connection-banner.hidden {
transform: translateY(-100%);
pointer-events: none;
}
@keyframes bannerSlideIn {
from { transform: translateY(-100%); }
to { transform: translateY(0); }
}
@keyframes bannerPulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
.connection-banner-btn {
padding: 4px 14px;
background: rgba(255, 255, 255, 0.2);
color: #fff;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 600;
transition: background 0.2s;
}
.connection-banner-btn:hover {
background: rgba(255, 255, 255, 0.35);
}
/* Wide screens - horizontal player layout */
@media (min-width: 900px) {
.container {
max-width: 960px;
}
.player-layout {
flex-direction: row;
gap: 2.5rem;
align-items: flex-start;
}
.album-art-container {
margin-bottom: 0;
flex-shrink: 0;
}
.player-details .track-info {
text-align: left;
}
.player-details .playback-state {
justify-content: flex-start;
}
.player-details .controls {
justify-content: flex-start;
}
.player-details .source-info {
text-align: left;
justify-content: flex-start;
}
}
/* ========================================
PWA Standalone & Mobile Polish
======================================== */
html {
overscroll-behavior: none;
}
/* Safe area insets for notched phones (viewport-fit=cover) */
.container {
padding-left: max(0.75rem, env(safe-area-inset-left));
padding-right: max(0.75rem, env(safe-area-inset-right));
}
.mini-player {
padding-bottom: max(0.75rem, env(safe-area-inset-bottom));
padding-left: max(1rem, env(safe-area-inset-left));
padding-right: max(1rem, env(safe-area-inset-right));
}
footer {
padding-bottom: max(0.75rem, env(safe-area-inset-bottom));
}
body.mini-player-visible footer {
padding-bottom: calc(70px + env(safe-area-inset-bottom, 0px));
}
.connection-banner {
padding-top: max(10px, env(safe-area-inset-top));
}
/* Touch optimization: eliminate 300ms tap delay */
.controls button,
.mini-controls button,
.mini-control-btn,
.tab-btn,
.header-btn,
.header-link,
.mute-btn,
.vinyl-toggle-btn,
.view-toggle-btn,
.browser-item,
.browser-list-item,
.script-btn,
.action-btn {
touch-action: manipulation;
}
@media (max-width: 600px) {
.container {
padding-left: max(0.5rem, env(safe-area-inset-left));
padding-right: max(0.5rem, env(safe-area-inset-right));
}
.mini-player {
padding-bottom: max(0.5rem, env(safe-area-inset-bottom));
padding-left: max(0.75rem, env(safe-area-inset-left));
padding-right: max(0.75rem, env(safe-area-inset-right));
}
}
@media (display-mode: standalone) {
body {
overscroll-behavior-y: none;
-webkit-overflow-scrolling: touch;
}
}
/* Accessibility: reduce motion for users who prefer it */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* ════════════════════════════════════════════════════════════════
STUDIO REFERENCE — editorial overrides
Applied on top of the legacy stylesheet. Mapped to existing
selectors so no JS / DOM changes are required.
════════════════════════════════════════════════════════════════ */
/* ─── Container & header ────────────────────────────────────── */
.container {
max-width: 1280px;
padding: 56px 48px 64px;
}
@media (max-width: 720px) {
.container { padding: 48px 18px 56px; }
}
/* ─── Folio marks (page corners, all tabs) ────────────────── */
body > .folio {
position: fixed;
top: 16px;
z-index: 50;
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--ink-faint);
display: inline-flex;
align-items: center;
gap: 8px;
pointer-events: none;
}
body > .folio.tl { left: 24px; }
body > .folio.tr { right: 24px; }
@media (max-width: 720px) {
body > .folio { font-size: 9px; letter-spacing: 0.16em; }
body > .folio.tl { left: 14px; }
body > .folio.tr { right: 14px; }
}
/* The status-dot now lives inside the folio */
body > .folio .status-dot {
width: 7px;
height: 7px;
background: var(--jade);
border-radius: 50%;
box-shadow: 0 0 0 0 rgba(122, 178, 148, 0.55);
animation: sr-pulse 2.4s ease-in-out infinite;
}
@keyframes sr-pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(122, 178, 148, 0.55); }
50% { box-shadow: 0 0 0 6px rgba(122, 178, 148, 0); }
}
/* Hide the old in-header status-dot if any rendering remnants exist */
header .status-dot { display: none; }
/* The version-label now lives inside the folio.tr — remove the old badge styling */
.version-label,
body > .folio #version-label {
background: transparent;
border: 0;
padding: 0;
color: var(--copper);
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.16em;
text-transform: uppercase;
border-radius: 0;
}
/* ─── Header (3-column: brand center, toolbar right) ─────── */
header {
padding: 0 0 22px 0;
border-bottom: 1px solid var(--rule-strong);
margin-bottom: 28px;
position: relative;
display: grid;
grid-template-columns: 1fr auto 1fr;
align-items: center;
gap: 20px;
}
/* Brand wordmark (centered) */
header .brand {
text-align: center;
grid-column: 2;
line-height: 1;
}
header .brand-name {
font-family: var(--serif);
font-style: italic;
font-weight: 400;
font-size: 30px;
letter-spacing: -0.015em;
color: var(--ink);
font-variation-settings: 'opsz' 144;
display: block;
}
header .brand-sub {
display: block;
font-family: var(--mono);
font-size: 9px;
letter-spacing: 0.32em;
text-transform: uppercase;
color: var(--ink-mute);
margin-top: 6px;
}
@media (max-width: 720px) {
header { grid-template-columns: 1fr; gap: 14px; }
header .brand { grid-column: 1; }
header .brand-name { font-size: 24px; }
header .header-toolbar { justify-self: center; }
}
.header-toolbar {
grid-column: 3;
justify-self: end;
/* Strip the legacy dark capsule */
background: transparent !important;
border: 0 !important;
border-radius: 0 !important;
padding: 0 !important;
gap: 6px;
display: flex;
align-items: center;
}
@media (max-width: 720px) {
.header-toolbar { grid-column: 1; }
}
.header-btn,
.header-btn-logout,
.header-link {
background: transparent;
border: 1px solid transparent;
color: var(--ink-soft);
border-radius: 0;
width: 36px;
height: 36px;
/* Centre the icon inside the hit box; reset legacy padding. */
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0;
box-sizing: border-box;
transition: all 200ms var(--ease);
}
.header-btn > svg,
.header-btn-logout > svg,
.header-link > svg {
display: block;
width: 16px;
height: 16px;
}
.header-btn:hover,
.header-btn-logout:hover,
.header-link:hover {
color: var(--copper);
border-color: var(--rule-strong);
background: transparent;
}
.header-locale {
background: transparent;
border: 1px solid transparent;
color: var(--ink-soft);
border-radius: 0;
font-family: var(--mono);
font-size: 11px;
letter-spacing: 0.12em;
text-transform: uppercase;
height: 36px;
padding: 0 10px 0 12px;
}
.header-locale:hover {
color: var(--copper);
border-color: var(--rule-strong);
}
.header-toolbar-sep {
background: var(--rule-strong);
height: 20px;
margin: 0 6px;
}
.accent-dot {
box-shadow: 0 0 0 1px var(--rule-strong), 0 0 12px var(--copper-glow);
}
.accent-picker-dropdown {
background: var(--bg-card);
border: 1px solid var(--rule-strong);
border-radius: 0;
box-shadow: 0 14px 40px rgba(0,0,0,0.5);
}
/* ─── Tab bar (numbered editorial) — only the bottom rule is visible */
.tab-bar {
background: transparent !important;
border: 0 !important;
border-bottom: 1px solid var(--rule-strong) !important;
padding: 0 !important;
margin: 14px 0 36px !important;
border-radius: 0 !important;
gap: 0;
position: relative;
overflow-x: auto;
scrollbar-width: none;
}
.tab-bar::-webkit-scrollbar { display: none; }
/* Hide the legacy tab-indicator entirely — we use ::after on .tab-btn.active instead */
.tab-indicator {
display: none !important;
}
.tab-btn {
background: transparent;
border: 0;
color: var(--ink-mute);
padding: 18px 26px 16px;
border-radius: 0;
font-family: var(--sans);
font-size: 13px;
font-weight: 400;
letter-spacing: 0.04em;
transition: color 180ms var(--ease);
position: relative;
display: inline-flex;
align-items: baseline;
gap: 10px;
white-space: nowrap;
cursor: pointer;
}
.tab-btn:hover { color: var(--ink-soft); background: transparent; }
.tab-btn.active {
color: var(--ink);
background: transparent;
font-family: var(--serif);
font-style: italic;
font-size: 17px;
font-weight: 400;
font-variation-settings: 'opsz' 60;
}
/* Tab number badge (replaces the icon) */
.tab-btn .tab-num {
font-family: var(--mono);
font-style: normal;
font-size: 10px;
color: var(--ink-faint);
letter-spacing: 0.15em;
font-weight: 400;
}
.tab-btn.active .tab-num {
color: var(--copper);
}
/* Hide any remaining legacy SVG icons on tabs */
.tab-btn svg {
display: none;
}
/* Active tab underline — only under the active tab */
.tab-btn.active::after {
content: "";
position: absolute;
bottom: -1px;
left: 0;
right: 0;
height: 2px;
background: var(--copper);
box-shadow: 0 0 12px var(--copper-glow);
}
/* Tablet range: editorial top-tabs but compressed so all 5 labels fit. */
@media (min-width: 721px) and (max-width: 980px) {
.tab-btn {
padding: 14px 12px 12px;
font-size: 12px;
gap: 8px;
}
.tab-btn.active { font-size: 15px; }
.tab-btn .tab-num { font-size: 9px; letter-spacing: 0.12em; }
.container { padding-left: 28px; padding-right: 28px; }
}
/* ─── Update + connection banners (editorial) ────────────────── */
.update-banner,
.connection-banner {
/* Override legacy fixed-top + accent background */
position: fixed !important;
top: 0 !important;
left: 0 !important;
right: 0 !important;
z-index: 1001;
background: rgba(33, 30, 24, 0.94) !important;
color: var(--ink-soft) !important;
border: 0 !important;
border-bottom: 1px solid var(--copper) !important;
border-radius: 0 !important;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4) !important;
padding: 12px 32px !important;
font-family: var(--mono) !important;
font-size: 11px !important;
letter-spacing: 0.12em !important;
text-transform: uppercase !important;
font-weight: 400 !important;
backdrop-filter: blur(20px) saturate(160%);
-webkit-backdrop-filter: blur(20px) saturate(160%);
display: flex;
align-items: center;
justify-content: center;
gap: 18px;
}
/* Tiny copper hairline accent on the bottom edge */
.update-banner::before,
.connection-banner::before {
content: "";
position: absolute;
bottom: -1px;
left: 32px;
right: 32px;
height: 1px;
background: linear-gradient(90deg, transparent, var(--copper), transparent);
opacity: 0.7;
}
/* Brand prefix */
.update-banner > span:first-child::before,
.connection-banner > span:first-child::before {
content: "● ";
color: var(--copper);
margin-right: 6px;
}
.update-banner a {
color: var(--copper) !important;
text-decoration: none !important;
font-family: var(--mono) !important;
font-size: 11px !important;
letter-spacing: 0.18em !important;
text-transform: uppercase !important;
font-weight: 500 !important;
border: 1px solid var(--copper);
padding: 6px 14px;
transition: all 180ms var(--ease);
}
.update-banner a:hover {
background: var(--copper);
color: var(--bg-deep) !important;
opacity: 1 !important;
}
.update-banner-close {
background: transparent !important;
color: var(--ink-mute) !important;
border: 0 !important;
font-size: 18px !important;
width: 24px;
height: 24px;
padding: 0 !important;
margin-left: 8px;
cursor: pointer;
transition: color 180ms var(--ease);
opacity: 1 !important;
}
.update-banner-close:hover {
color: var(--copper) !important;
}
.connection-banner-btn {
color: var(--copper) !important;
font-family: var(--mono) !important;
font-size: 11px !important;
letter-spacing: 0.18em !important;
text-transform: uppercase !important;
border: 1px solid var(--copper) !important;
border-radius: 0 !important;
background: transparent !important;
padding: 6px 14px !important;
transition: all 180ms var(--ease);
}
.connection-banner-btn:hover {
background: var(--copper) !important;
color: var(--bg-deep) !important;
}
/* ═══════════════════════════════════════════════════════════════
PLAYER VIEW — verbatim from Studio Reference mockup
═══════════════════════════════════════════════════════════════ */
.player-container {
background: transparent;
border: 0;
padding: 0;
box-shadow: none;
margin-top: 12px;
position: relative;
}
/* Visually hidden — kept in DOM for a11y/JS but invisible. */
.now-playing .visually-hidden,
.player-container .visually-hidden {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.now-playing {
display: grid;
grid-template-columns: minmax(320px, 520px) 1fr;
gap: 64px;
align-items: start;
margin-top: 28px;
position: relative;
}
.player-layout {
display: grid;
grid-template-columns: minmax(320px, 520px) 1fr;
gap: 64px;
align-items: start;
margin-top: 28px;
}
@media (max-width: 1240px) {
.player-layout,
.now-playing {
grid-template-columns: 1fr;
gap: 36px;
}
/* Single-column hero sizing — prevents the vinyl stage from
growing to full content width (which becomes 800px+ on a
small-desktop or tablet-landscape window). */
.album-art-container.vinyl-stage {
max-width: 520px;
margin-left: auto;
margin-right: auto;
}
/* Cap the right-column copy so a short artist line and meta grid
don't sprawl across 1000px of measure. The masthead stays
flush-left under the vinyl, just narrower. */
.now-playing .track-masthead {
max-width: 720px;
}
}
@media (min-width: 721px) and (max-width: 1240px) {
/* In the 7211240 zone the vinyl can be larger than on phone but
smaller than the full-column 800px+ default. */
.album-art-container.vinyl-stage {
max-width: 480px;
}
.now-playing .track-masthead {
max-width: 640px;
margin-left: auto;
margin-right: auto;
padding-right: 0;
text-align: left;
}
}
/* ─── Vinyl stage ──────────────────────────────────────────── */
.album-art-container.vinyl-stage {
position: relative;
aspect-ratio: 1;
width: 100%;
max-width: none;
padding: 0;
background: transparent;
border: 0;
box-shadow: none;
display: flex;
align-items: center;
justify-content: center;
overflow: visible;
/* Kill the legacy albumArt3D rotateY/rotateX wobble inherited from
.album-art-container — the vinyl stage is a flat composition
and should not tilt. */
animation: none;
transform: none;
transform-style: flat;
}
.album-art-container.vinyl-stage:hover { transform: none; }
.vinyl-stage .vinyl {
position: relative;
width: 86%;
aspect-ratio: 1;
border-radius: 50%;
background:
radial-gradient(circle at 50% 50%,
#0a0907 0%,
#0a0907 18%,
#1a1611 18.3%,
#0a0907 18.6%,
#14110c 22%,
#0a0907 22.3%,
#14110c 26%,
#0a0907 26.3%,
#14110c 30%,
#0a0907 30.3%,
#14110c 34%,
#0a0907 34.3%,
#14110c 38%,
#0a0907 38.3%,
#14110c 42%,
#0a0907 42.3%,
#14110c 46%,
#0a0907 46.3%,
#1c1812 47%,
#0a0907 100%);
box-shadow:
inset 0 0 60px rgba(0,0,0,0.7),
0 30px 80px rgba(0,0,0,0.6),
0 6px 20px rgba(0,0,0,0.5);
animation: sr-vinyl-spin 14s linear infinite;
animation-play-state: paused;
}
:root[data-playstate="playing"] .vinyl-stage .vinyl {
animation-play-state: running;
}
.vinyl-stage .vinyl::before {
content: "";
position: absolute;
inset: 12%;
border-radius: 50%;
background:
conic-gradient(from 0deg,
rgba(255,255,255,0.04) 0deg,
transparent 30deg,
rgba(255,255,255,0.06) 90deg,
transparent 150deg,
rgba(255,255,255,0.03) 210deg,
transparent 270deg,
rgba(255,255,255,0.05) 330deg,
transparent 360deg);
mix-blend-mode: screen;
pointer-events: none;
}
.vinyl-stage .vinyl-label {
position: absolute;
inset: 28%;
border-radius: 50%;
overflow: hidden;
box-shadow:
inset 0 0 24px rgba(0,0,0,0.4),
0 0 0 4px var(--bg-deep),
0 0 0 5px var(--copper-lo);
background: var(--bg-card);
z-index: 1;
}
.vinyl-stage .vinyl-label::after {
/* Spindle hole */
content: "";
position: absolute;
width: 8%; height: 8%;
top: 46%; left: 46%;
border-radius: 50%;
background: var(--bg-deep);
box-shadow: inset 0 1px 2px rgba(255,255,255,0.1);
z-index: 3;
}
.vinyl-stage #album-art-glow {
position: absolute;
inset: -8%;
width: 116%;
height: 116%;
border-radius: 50%;
filter: blur(20px) saturate(1.4);
opacity: 0.55;
z-index: 0;
object-fit: cover;
}
.vinyl-stage #album-art {
position: relative;
width: 100%;
height: 100%;
object-fit: cover;
display: block;
border-radius: 50%;
z-index: 2;
}
/* ─── Tonearm SVG ──────────────────────────────────────────── */
/* Geometry chosen so the SVG bounding box stays right of the sleeve
(sleeve right edge ≈ 68%). Pivot floats just past the stage's right
edge; needle lands on the visible disc grooves at playing rotation
(0deg) and on the outer rest position at -22deg. Never overlaps
the sleeve cover. */
.vinyl-stage .tonearm {
position: absolute;
top: 26%;
right: -6%;
width: 36%;
height: 36%;
pointer-events: none;
transform-origin: 88% 12%;
transform: rotate(-22deg);
transition: transform 1s var(--ease);
z-index: 3;
filter: drop-shadow(0 4px 12px rgba(0,0,0,0.5));
}
:root[data-playstate="playing"] .vinyl-stage .tonearm {
transform: rotate(0deg);
}
@keyframes sr-vinyl-spin { to { transform: rotate(360deg); } }
/* Spectrogram canvas hidden by default — visualizer toggle reveals it */
.vinyl-stage .spectrogram-canvas {
position: absolute;
bottom: -56px;
left: 7%;
right: 7%;
width: 86%;
height: 38px;
border-radius: 0;
opacity: 0;
transition: opacity 240ms var(--ease);
pointer-events: none;
}
.vinyl-stage.visualizer-active .spectrogram-canvas,
body.visualizer-active .vinyl-stage .spectrogram-canvas {
opacity: 0.6;
}
/* ════════════════════════════════════════════════════════════════
SLEEVE FRAME — production layout
The album cover prints on a cardstock sleeve at left; the disc
sits to the right and peeks out, spinning while the tonearm
rests on it. The vinyl label is now a typographic plate; track
metadata lives in the masthead beside the stage.
════════════════════════════════════════════════════════════════ */
/* Glow: soft ambient halo behind the sleeve.
Performance trick: render the image at 25% × 25% of the stage and
stretch it via transform: scale(4). Filter runs on the smaller
element (16× less area), and scale just upsamples the already-blurred
result. Visually identical to a blur(34px) over the full-size image
but ~10-16× cheaper on track-switch repaints. */
.vinyl-stage > #album-art-glow {
position: absolute;
top: 50%;
left: 50%;
width: 25%;
height: 25%;
border-radius: 0;
object-fit: cover;
filter: blur(9px) saturate(1.6);
opacity: 0.45;
z-index: 0;
pointer-events: none;
transform: translate(-50%, -50%) scale(4.2);
transform-origin: center;
will-change: transform, opacity;
}
:root[data-theme="light"] .vinyl-stage > #album-art-glow {
opacity: 0.26;
filter: blur(10px) saturate(1.8);
}
/* Honour reduced-motion: kill breathing pulse */
@media (prefers-reduced-motion: reduce) {
.vinyl-stage .sleeve #album-art {
animation: none !important;
}
}
/* Stage halo: soft radial bloom that fades to transparent — same
treatment as the fullscreen player so the stage never reads as a
rectangular card pasted into the page. */
.album-art-container.vinyl-stage {
background:
radial-gradient(ellipse at center,
rgba(26, 22, 17, 0.85) 0%,
transparent 70%);
}
:root[data-theme="light"] .album-art-container.vinyl-stage {
background:
radial-gradient(ellipse at center,
rgba(255, 255, 255, 0.55) 0%,
transparent 75%);
}
/* Sleeve: cardstock card with album cover printed on its face.
Geometry mirrors the mockup — sleeve+disc occupy a centered 90%
inner square so the arrangement breathes inside the stage. */
.vinyl-stage .sleeve {
position: absolute;
left: 5%;
top: 10.4%;
width: 63%;
aspect-ratio: 1;
z-index: 3;
background: transparent;
box-shadow:
-2px 8px 24px rgba(0, 0, 0, 0.5),
-4px 18px 44px rgba(0, 0, 0, 0.35);
overflow: hidden;
}
:root[data-theme="light"] .vinyl-stage .sleeve {
background: transparent;
box-shadow:
-2px 8px 22px rgba(0, 0, 0, 0.20),
-4px 18px 36px rgba(0, 0, 0, 0.12);
}
/* Album art is the printed cover tipped onto the cardstock sleeve.
A 5% inset reveals the cardstock as a visible border around the
print; outline + inset shadow define the printed-cover edge.
Explicit width/height (not `inset` shorthand) — img is a replaced
element and would otherwise fall back to its intrinsic pixel
size, blowing out the grid track. */
.vinyl-stage .sleeve #album-art {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 0;
z-index: 1;
box-shadow: none;
margin: 0;
background: transparent;
filter: contrast(0.96) saturate(0.92);
/* No transition on filter: the values are static and a 600ms ease
on a track-switch swap would force a long compositor pass. */
}
/* Crossfade on artwork swap. Class toggled by player.js right before
src assignment so the new cover fades in instead of popping. */
.vinyl-stage .sleeve #album-art.is-swapping {
animation: sr-art-swap 650ms cubic-bezier(0.2, 0.7, 0.2, 1);
}
@keyframes sr-art-swap {
0% { opacity: 0; }
100% { opacity: 1; }
}
/* Cardstock paper grain over the print — multiplies into the image */
.vinyl-stage .sleeve-grain {
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.2' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.10 0 0 0 0 0.08 0 0 0 0 0.06 0 0 0 0.55 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
mix-blend-mode: multiply;
pointer-events: none;
z-index: 2;
opacity: 0.7;
}
:root[data-theme="light"] .vinyl-stage .sleeve-grain { opacity: 0.32; }
/* Worn corner notch — hidden now that the cardstock sleeve is
transparent; the wedge would otherwise read as a stray dark
triangle clipped over the album art. */
.vinyl-stage .sleeve-corner {
display: none;
}
/* Disc wrap — sits behind the sleeve, peeks out the right edge */
.vinyl-stage .vinyl-wrap {
position: absolute;
right: 3.2%;
top: 19.4%;
width: 63%;
aspect-ratio: 1;
z-index: 2;
}
.vinyl-stage .vinyl-wrap .vinyl {
width: 100%;
}
/* Vinyl label = typographic plate (no album art, lives on the sleeve now) */
.vinyl-stage .vinyl-wrap .vinyl-label {
background: linear-gradient(135deg, #2E2820 0%, #1f1a13 100%);
box-shadow:
inset 0 0 18px rgba(0, 0, 0, 0.5),
inset 0 1px 0 rgba(255, 255, 255, 0.04),
0 0 0 3px var(--bg-deep),
0 0 0 4px var(--copper-lo);
display: flex;
align-items: center;
justify-content: center;
}
:root[data-theme="light"] .vinyl-stage .vinyl-wrap .vinyl-label {
background: linear-gradient(135deg, var(--copper) 0%, var(--copper-lo) 100%);
box-shadow:
inset 0 0 18px rgba(0, 0, 0, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.06),
0 0 0 3px var(--bg-paper),
0 0 0 4px var(--copper-lo);
}
.vinyl-label-text {
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.3em;
color: var(--copper);
text-transform: uppercase;
z-index: 2;
font-weight: 500;
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.6);
user-select: none;
}
/* ─── Player details (right column / masthead) ──────────────── */
.player-details,
.track-masthead {
gap: 0;
padding-top: 0;
display: flex;
flex-direction: column;
}
/* Kicker — copper italic mono with hairlines */
.track-masthead > .kicker {
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.32em;
text-transform: uppercase;
color: var(--copper);
display: flex;
align-items: center;
gap: 14px;
margin-bottom: 22px;
}
.track-masthead > .kicker::before,
.track-masthead > .kicker::after {
content: "";
height: 1px;
background: var(--copper);
opacity: 0.6;
flex: 0 0 24px;
}
.track-masthead > .kicker::after { flex: 1 0 auto; }
.track-info {
margin-bottom: 0;
position: relative;
}
#track-title {
font-family: var(--serif);
font-weight: 400;
font-size: clamp(38px, 5vw, 68px);
line-height: 0.96;
letter-spacing: -0.02em;
color: var(--ink);
font-variation-settings: 'opsz' 144;
margin-bottom: 16px;
}
#artist {
font-family: var(--serif);
font-style: italic;
font-weight: 300;
font-size: 22px;
color: var(--copper-hi);
font-variation-settings: 'opsz' 60;
margin-bottom: 4px;
}
#album {
font-family: var(--sans);
font-size: 13px;
color: var(--ink-mute);
letter-spacing: 0.04em;
margin-bottom: 0;
}
/* Editorial metadata grid — 2 cells (State / Source); timecodes flank the timeline */
.meta-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
margin-top: 32px;
border-top: 1px solid var(--rule);
border-bottom: 1px solid var(--rule);
}
.meta-grid.meta-grid-2 {
grid-template-columns: minmax(180px, auto) 1fr;
}
.meta-cell {
padding: 16px 18px 16px 0;
border-right: 1px solid var(--rule);
min-width: 0;
}
.meta-cell:last-child { border-right: 0; padding-right: 0; }
.meta-cell .meta-label {
font-family: var(--mono);
font-size: 9px;
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--ink-faint);
margin-bottom: 8px;
}
.meta-cell .meta-value {
font-family: var(--serif);
font-style: italic;
font-weight: 400;
font-size: 18px;
color: var(--ink);
font-variation-settings: 'opsz' 30;
display: flex;
align-items: center;
gap: 8px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.meta-cell .meta-value.mono {
font-family: var(--mono);
font-style: normal;
font-size: 15px;
color: var(--ink);
font-variant-numeric: tabular-nums;
letter-spacing: 0.02em;
}
.meta-cell .meta-value .state-icon,
.meta-cell .meta-value .source-icon {
width: 14px;
height: 14px;
flex-shrink: 0;
color: var(--copper);
}
.meta-cell .source-value { font-size: 16px; }
@media (max-width: 720px) {
.meta-grid { grid-template-columns: repeat(2, 1fr); }
.meta-cell:nth-child(2) { border-right: 0; }
.meta-cell:nth-child(-n+2) { border-bottom: 1px solid var(--rule); }
}
/* Hide the legacy .playback-state container (its data is now in meta-grid) */
.track-info > .playback-state { display: none; }
/* (Legacy .spectrum + nth-child rules deleted — were capping the
row at max-width: 360px and forcing 3px-fixed bars. The
.now-playing-scoped rules below are the single source of truth.) */
/* Transport — wraps progress + controls */
.transport {
margin-top: 8px;
}
.progress-row {
display: grid;
grid-template-columns: auto 1fr auto;
gap: 18px;
align-items: center;
margin-bottom: 26px;
}
.progress-row .timecode {
font-family: var(--mono);
font-size: 12px;
color: var(--ink-mute);
letter-spacing: 0.06em;
font-variant-numeric: tabular-nums;
}
.progress-row .timecode.elapsed { color: var(--copper); font-weight: 500; }
/* Override the legacy .progress-container layout when inside .transport */
.transport .progress-container {
margin-top: 0;
}
/* VU cluster (replaces freestanding volume container) */
.vu-cluster {
margin-left: auto;
display: flex;
align-items: center;
gap: 14px;
}
.vu-meter {
position: relative;
width: 130px;
height: 56px;
background: linear-gradient(180deg, #1a1610 0%, #0e0c08 100%);
border: 1px solid var(--rule-strong);
border-radius: 4px 4px 0 0;
overflow: hidden;
box-shadow: inset 0 2px 6px rgba(0,0,0,0.5), inset 0 0 30px rgba(var(--copper-rgb), 0.08);
}
.vu-meter::before {
content: "";
position: absolute;
inset: 0;
background: repeating-conic-gradient(from 195deg at 50% 100%,
transparent 0deg 4deg,
rgba(242, 235, 220, 0.08) 4deg 5deg,
transparent 5deg 9deg);
}
.vu-meter::after {
content: "VU";
position: absolute;
bottom: 4px; left: 50%;
transform: translateX(-50%);
font-family: var(--mono);
font-size: 8px;
letter-spacing: 0.3em;
color: var(--ink-faint);
}
.vu-needle {
position: absolute;
bottom: 0; left: 50%;
width: 1.5px;
height: 88%;
background: linear-gradient(to top, var(--copper) 0%, var(--copper-hi) 70%, var(--ink) 100%);
transform-origin: bottom center;
transform: rotate(-22deg);
transition: transform 350ms var(--ease);
box-shadow: 0 0 8px var(--copper-glow);
}
/* Volume container nested inside vu-cluster — strip legacy box & make compact */
.vu-cluster .volume-container {
background: transparent;
border: 0;
border-top: 0;
border-radius: 0;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 6px;
align-items: stretch;
min-width: 0;
width: 110px;
box-shadow: none;
}
.vu-cluster .volume-container > .mute-btn {
align-self: flex-end;
width: 28px;
height: 28px;
border-radius: 50%;
background: transparent;
border: 1px solid var(--rule-strong);
color: var(--ink-soft);
padding: 0;
margin: 0;
}
.vu-cluster .volume-container > .mute-btn:hover {
border-color: var(--copper);
color: var(--copper);
}
.vu-cluster .volume-container > .mute-btn svg {
width: 12px;
height: 12px;
fill: currentColor;
}
.vu-cluster .volume-container > #volume-slider {
width: 100%;
height: 2px;
flex: none;
background: var(--rule-strong);
border-radius: 0;
margin: 0;
padding: 0;
-webkit-appearance: none;
appearance: none;
}
.vu-cluster .volume-container > #volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 10px;
height: 10px;
background: var(--copper);
border-radius: 50%;
box-shadow: 0 0 8px var(--copper-glow);
border: 0;
}
.vu-cluster .volume-container > .volume-display {
text-align: right;
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--ink-mute);
background: transparent;
border: 0;
padding: 0;
margin: 0;
font-variant-numeric: tabular-nums;
width: auto;
min-width: 0;
font-weight: 500;
}
.vu-cluster .volume-container > .volume-display::before {
content: "VOL · ";
color: var(--ink-faint);
font-weight: 400;
}
/* Make the player view source-info row tighter (visualizer toggle row) */
.player-details > .source-info {
margin-top: 18px;
padding-top: 14px;
border-top: 1px solid var(--rule);
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
}
/* ─── Progress (hairline editorial) ─────────────────────────── */
.progress-container {
margin-top: 28px;
margin-bottom: 0; /* override legacy 2rem */
}
.time-display {
font-family: var(--mono);
font-size: 11px;
color: var(--ink-mute);
letter-spacing: 0.04em;
font-variant-numeric: tabular-nums;
margin-bottom: 10px;
}
#current-time { color: var(--copper); font-weight: 500; }
.progress-bar {
height: 2px;
width: 100%;
background: var(--rule-strong);
border-radius: 0;
cursor: pointer;
overflow: visible;
position: relative;
transform: none !important; /* kill legacy :hover scaleY */
transition: background 200ms var(--ease);
min-width: 0; /* let grid shrink it */
}
.progress-bar:hover {
background: var(--rule-strong);
transform: none !important; /* defeat legacy :hover transform */
}
.progress-bar.dragging {
transform: none !important;
background: var(--rule-strong);
}
.progress-fill {
background: var(--copper);
box-shadow: 0 0 12px var(--copper-glow);
border-radius: 0;
height: 100%;
width: 0;
position: relative;
transition: width 0.1s linear;
}
.progress-fill::after {
content: "";
position: absolute;
right: -5px;
top: 50%;
transform: translateY(-50%) scale(1) !important; /* always visible, override legacy scale(0) default */
width: 10px;
height: 10px;
background: var(--copper);
border-radius: 50%;
box-shadow: 0 0 14px var(--copper-glow), 0 0 0 4px rgba(var(--copper-rgb), 0.12);
transition: none;
}
/* Progress row inside transport — grid layout for [time | bar | time] */
.transport .progress-row,
.progress-container.progress-row {
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
gap: 18px;
align-items: center;
margin-top: 0;
margin-bottom: 26px;
}
.progress-row .timecode {
font-family: var(--mono);
font-size: 12px;
color: var(--ink-mute);
letter-spacing: 0.06em;
font-variant-numeric: tabular-nums;
line-height: 1;
}
.progress-row .timecode.elapsed { color: var(--copper); font-weight: 500; }
/* ─── Controls (circular transport) ─────────────────────────── */
.controls {
margin-top: 28px;
gap: 16px;
justify-content: flex-start;
}
.controls button {
background: transparent;
border: 1px solid var(--rule-strong);
color: var(--ink-soft);
width: 48px;
height: 48px;
border-radius: 50%;
transition: all 220ms var(--ease);
}
.controls button:hover {
background: rgba(var(--copper-rgb), 0.06);
border-color: var(--copper);
color: var(--copper);
}
.controls button.primary {
width: 64px;
height: 64px;
background: var(--ink);
color: var(--bg-deep);
border-color: var(--ink);
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45);
}
.controls button.primary:hover {
background: var(--copper);
border-color: var(--copper);
color: var(--bg-deep);
transform: scale(1.04);
box-shadow: 0 8px 28px var(--copper-glow);
}
.controls button svg { width: 20px; height: 20px; }
.controls button.primary svg { width: 24px; height: 24px; }
/* ─── Volume container (VU-meter aesthetic) ─────────────────── */
.volume-container {
margin-top: 28px;
padding-top: 22px;
border-top: 1px solid var(--rule);
gap: 14px;
display: flex;
align-items: center;
}
.mute-btn {
background: transparent;
border: 1px solid var(--rule-strong);
color: var(--ink-soft);
border-radius: 50%;
width: 38px;
height: 38px;
}
.mute-btn:hover {
border-color: var(--copper);
color: var(--copper);
background: rgba(var(--copper-rgb), 0.06);
}
#volume-slider {
height: 2px;
background: var(--rule-strong);
border-radius: 0;
flex: 1;
-webkit-appearance: none;
appearance: none;
cursor: pointer;
}
#volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 14px;
height: 14px;
background: var(--copper);
border-radius: 50%;
box-shadow: 0 0 12px var(--copper-glow), 0 0 0 4px rgba(var(--copper-rgb), 0.12);
border: 0;
cursor: grab;
}
#volume-slider::-moz-range-thumb {
width: 14px;
height: 14px;
background: var(--copper);
border-radius: 50%;
box-shadow: 0 0 12px var(--copper-glow);
border: 0;
}
#volume-slider::-moz-range-track {
height: 2px;
background: var(--rule-strong);
}
.volume-display {
font-family: var(--mono);
font-size: 11px;
color: var(--copper);
letter-spacing: 0.06em;
font-variant-numeric: tabular-nums;
min-width: 44px;
text-align: right;
font-weight: 500;
}
/* ─── Source info ───────────────────────────────────────────── */
.source-info {
margin-top: 24px;
padding-top: 22px;
border-top: 1px solid var(--rule);
}
.source-label {
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--ink-mute);
gap: 10px;
}
.source-icon {
width: 14px;
height: 14px;
color: var(--copper);
}
.player-toggles { gap: 8px; }
.vinyl-toggle-btn {
background: transparent;
border: 1px solid var(--rule-strong);
color: var(--ink-mute);
border-radius: 0;
width: 30px;
height: 30px;
transition: all 180ms var(--ease);
}
.vinyl-toggle-btn:hover,
.vinyl-toggle-btn.active {
border-color: var(--copper);
color: var(--copper);
background: rgba(var(--copper-rgb), 0.06);
}
/* ═══════════════════════════════════════════════════════════════
MINI PLAYER — sticky console strip (3-column grid)
═══════════════════════════════════════════════════════════════ */
.mini-player {
/* Override legacy display: flex with explicit grid */
display: grid !important;
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr) auto;
column-gap: 24px;
row-gap: 0;
align-items: center;
background: linear-gradient(180deg, rgba(14, 13, 11, 0.72) 0%, rgba(14, 13, 11, 0.96) 30%);
backdrop-filter: blur(20px) saturate(160%);
-webkit-backdrop-filter: blur(20px) saturate(160%);
border-top: 1px solid var(--rule-strong);
box-shadow: none;
padding: 12px 32px;
/* Anchor firmly to the bottom of the viewport */
position: fixed !important;
bottom: 0 !important;
left: 0 !important;
right: 0 !important;
z-index: 1000;
}
/* Top edge progress line in copper */
.mini-player::before {
background: var(--copper) !important;
height: 2px !important;
box-shadow: 0 0 8px var(--copper-glow);
}
/* Column 1: track info */
.mini-player .mini-player-info {
grid-column: 1;
grid-row: 1;
min-width: 0;
overflow: hidden;
display: flex;
align-items: center;
gap: 12px;
}
/* Column 2: transport (centered) */
.mini-player .mini-controls {
grid-column: 2;
grid-row: 1;
justify-self: center;
display: flex;
align-items: center;
gap: 12px;
flex-shrink: 0;
}
/* Column 3: progress (timecodes + bar) */
.mini-player .mini-progress-container {
grid-column: 3;
grid-row: 1;
display: flex;
flex-direction: column;
gap: 4px;
justify-self: stretch;
align-items: stretch;
min-width: 180px;
max-width: 360px;
margin: 0 auto;
}
.mini-player .mini-progress-container .mini-time-display {
display: flex;
justify-content: space-between;
}
/* Column 4: volume controls */
.mini-player .mini-volume-container {
grid-column: 4;
grid-row: 1;
justify-self: end;
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
@media (max-width: 880px) {
.mini-player {
grid-template-columns: minmax(0, 1fr) auto auto;
column-gap: 14px;
}
.mini-player .mini-progress-container { display: none; }
.mini-player .mini-volume-container { grid-column: 3; }
}
@media (max-width: 540px) {
.mini-player {
grid-template-columns: minmax(0, 1fr) auto;
padding: 10px 14px;
}
.mini-player .mini-volume-container { display: none; }
}
.mini-player::before {
content: "";
position: absolute;
top: -1px; left: 32px; right: 32px;
height: 2px;
background: linear-gradient(90deg, transparent, var(--copper), transparent);
opacity: 0.55;
}
.mini-album-art {
border-radius: 0;
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
width: 48px;
height: 48px;
}
.mini-track-title {
font-family: var(--serif);
font-style: italic;
font-size: 15px;
color: var(--ink);
font-variation-settings: 'opsz' 60;
letter-spacing: 0;
}
.mini-artist {
font-family: var(--mono);
font-size: 10px;
color: var(--ink-mute);
letter-spacing: 0.1em;
text-transform: uppercase;
margin-top: 4px;
}
.mini-control-btn {
background: transparent;
border: 0;
color: var(--ink-soft);
border-radius: 50%;
width: 32px;
height: 32px;
transition: color 180ms var(--ease);
}
.mini-control-btn:hover { color: var(--copper); background: transparent; }
.mini-control-btn.primary,
#mini-btn-play-pause {
background: var(--ink);
color: var(--bg-deep);
width: 38px;
height: 38px;
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
}
#mini-btn-play-pause:hover {
background: var(--copper);
color: var(--bg-deep);
}
.mini-time-display {
font-family: var(--mono);
font-size: 10px;
color: var(--ink-mute);
letter-spacing: 0.06em;
font-variant-numeric: tabular-nums;
}
#mini-current-time { color: var(--copper); font-weight: 500; }
.mini-player .mini-progress-bar {
flex: 0 0 auto;
height: 3px;
background: var(--rule-strong);
border-radius: 0;
cursor: pointer;
position: relative;
min-width: 0;
}
.mini-player .mini-progress-fill {
height: 100%;
background: var(--copper);
box-shadow: 0 0 8px var(--copper-glow);
border-radius: 0;
}
.mini-volume-slider {
-webkit-appearance: none;
appearance: none;
height: 2px;
background: var(--rule-strong);
}
.mini-volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 10px;
height: 10px;
background: var(--copper);
border-radius: 50%;
box-shadow: 0 0 8px var(--copper-glow);
border: 0;
}
.mini-volume-slider::-moz-range-thumb {
width: 10px; height: 10px;
background: var(--copper);
border-radius: 50%;
border: 0;
}
.mini-volume-display {
font-family: var(--mono);
font-size: 10px;
color: var(--copper);
letter-spacing: 0.06em;
font-variant-numeric: tabular-nums;
font-weight: 500;
}
/* ═══════════════════════════════════════════════════════════════
BROWSER — editorial gallery
═══════════════════════════════════════════════════════════════ */
.browser-container {
background: transparent;
border: 0;
padding: 0;
box-shadow: none;
margin-top: 12px;
}
.breadcrumb {
font-family: var(--serif);
font-style: italic;
font-size: 17px;
color: var(--ink-soft);
font-variation-settings: 'opsz' 60;
background: transparent;
border-bottom: 1px solid var(--rule);
padding: 14px 0;
margin-bottom: 22px;
border-radius: 0;
}
.breadcrumb-item, .breadcrumb-home {
color: var(--ink-soft);
font-style: italic;
}
.breadcrumb-item:hover, .breadcrumb-home:hover { color: var(--copper); }
.breadcrumb-item:last-child {
color: var(--ink);
font-weight: 400;
}
.breadcrumb-separator {
color: var(--ink-faint);
font-style: normal;
margin: 0 8px;
opacity: 1;
}
.browser-toolbar {
background: transparent;
border: 0;
border-bottom: 1px solid var(--rule);
border-radius: 0;
padding: 0 0 14px 0;
margin-bottom: 28px;
gap: 12px;
}
.view-toggle {
background: transparent;
border: 1px solid var(--rule-strong);
border-radius: 0;
padding: 0;
gap: 0;
}
.view-toggle-btn {
background: transparent;
border: 0;
border-right: 1px solid var(--rule-strong);
color: var(--ink-mute);
border-radius: 0;
width: 36px;
height: 36px;
transition: all 180ms var(--ease);
}
.view-toggle-btn:last-child { border-right: 0; }
.view-toggle-btn:hover {
color: var(--copper);
background: rgba(var(--copper-rgb), 0.04);
}
.view-toggle-btn.active {
background: var(--ink);
color: var(--bg-deep);
}
.browser-refresh-btn,
.browser-play-all-btn {
background: transparent;
border: 1px solid var(--rule-strong);
color: var(--ink-soft);
border-radius: 0;
width: 36px;
height: 36px;
transition: all 180ms var(--ease);
}
.browser-refresh-btn:hover,
.browser-play-all-btn:hover {
color: var(--copper);
border-color: var(--copper);
background: transparent;
}
.browser-play-all-btn {
color: var(--copper);
border-color: var(--copper);
}
.browser-search-wrapper {
background: transparent;
border: 0;
border-bottom: 1px solid var(--rule-strong);
border-radius: 0;
padding: 0 0 4px 0;
gap: 8px;
transition: border-color 180ms var(--ease);
}
.browser-search-wrapper:focus-within {
border-bottom-color: var(--copper);
}
.browser-search-input {
background: transparent;
border: 0;
color: var(--ink);
font-family: var(--sans);
font-size: 13px;
padding: 4px 0;
}
.browser-search-input::placeholder { color: var(--ink-mute); }
.browser-search-icon { color: var(--ink-mute); }
.browser-search-clear { color: var(--ink-mute); background: transparent; border: 0; }
.browser-search-clear:hover { color: var(--copper); }
.items-per-page-label {
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--ink-mute);
gap: 8px;
}
.items-per-page-label select {
background: transparent;
border: 1px solid var(--rule-strong);
color: var(--ink);
border-radius: 0;
font-family: var(--mono);
font-size: 11px;
padding: 4px 8px;
}
/* Browser grid items */
.browser-grid {
gap: 28px 22px;
}
.browser-item {
background: transparent;
border: 0;
border-radius: 0;
padding: 0;
transition: transform 280ms var(--ease);
}
.browser-item:hover {
transform: translateY(-3px);
background: transparent;
box-shadow: none;
}
.browser-thumb-wrapper,
.browser-thumbnail {
border-radius: 0;
background: var(--bg-card);
box-shadow: 0 14px 30px -10px rgba(0,0,0,0.5);
transition: box-shadow 280ms var(--ease);
}
.browser-item:hover .browser-thumb-wrapper,
.browser-item:hover .browser-thumbnail {
box-shadow: 0 22px 50px -10px rgba(0,0,0,0.65), 0 0 0 1px var(--copper);
}
.browser-icon {
color: var(--ink-mute);
}
.browser-item-name {
font-family: var(--serif);
font-size: 16px;
color: var(--ink);
font-variation-settings: 'opsz' 18;
letter-spacing: 0;
margin-top: 12px;
}
.browser-item-meta,
.browser-item-type {
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink-mute);
margin-top: 4px;
}
.browser-play-overlay,
.browser-list-play-overlay {
background: rgba(14, 13, 11, 0.5);
}
.browser-play-overlay svg,
.browser-list-play-overlay svg {
fill: var(--ink);
filter: drop-shadow(0 4px 12px rgba(0,0,0,0.6));
}
.browser-empty {
color: var(--ink-mute);
font-family: var(--serif);
font-style: italic;
font-size: 16px;
font-variation-settings: 'opsz' 30;
}
/* List view */
.browser-list {
background: transparent;
border: 1px solid var(--rule);
border-radius: 0;
}
.browser-list-header {
background: var(--bg-paper);
border-bottom: 1px solid var(--rule-strong);
color: var(--ink-mute);
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
}
.browser-list-item {
border-bottom: 1px solid var(--rule);
transition: background 180ms var(--ease);
}
.browser-list-item:hover { background: var(--bg-paper); }
.browser-list-name {
font-family: var(--serif);
font-size: 15px;
color: var(--ink);
font-variation-settings: 'opsz' 18;
}
.browser-list-bitrate,
.browser-list-duration,
.browser-list-size {
font-family: var(--mono);
font-size: 11px;
color: var(--ink-mute);
font-variant-numeric: tabular-nums;
}
/* Pagination */
.pagination {
border-top: 1px solid var(--rule);
margin-top: 32px;
padding-top: 22px;
background: transparent;
}
.pagination button {
background: transparent;
border: 1px solid var(--rule-strong);
color: var(--ink-soft);
border-radius: 0;
font-family: var(--mono);
font-size: 11px;
letter-spacing: 0.12em;
text-transform: uppercase;
padding: 8px 16px;
transition: all 180ms var(--ease);
}
.pagination button:hover:not(:disabled) {
color: var(--copper);
border-color: var(--copper);
}
.pagination button:disabled { opacity: 0.4; }
.page-input {
background: transparent;
border: 1px solid var(--rule-strong);
color: var(--ink);
border-radius: 0;
font-family: var(--mono);
}
.pagination-center,
.pagination-showing {
font-family: var(--mono);
font-size: 11px;
color: var(--ink-mute);
letter-spacing: 0.04em;
}
/* ═══════════════════════════════════════════════════════════════
QUICK ACCESS — console rail
═══════════════════════════════════════════════════════════════ */
.scripts-container {
background: transparent;
border: 0;
padding: 0;
box-shadow: none;
}
.scripts-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 1px;
background: var(--rule-strong);
border: 1px solid var(--rule-strong);
padding: 0;
}
.scripts-empty {
background: var(--bg-card);
padding: 60px 24px;
color: var(--ink-mute);
}
.script-btn,
.link-card {
background: var(--bg-card);
border: 0;
border-radius: 0;
padding: 24px 18px;
color: var(--ink);
transition: all 200ms var(--ease);
text-align: center;
}
.script-btn:hover,
.link-card:hover {
background: var(--bg-card-2);
color: var(--copper);
transform: none;
}
.script-btn .browser-icon,
.link-card .browser-icon {
color: var(--ink-soft);
margin-bottom: 12px;
transition: color 200ms var(--ease);
}
.script-btn:hover .browser-icon,
.link-card:hover .browser-icon { color: var(--copper); transform: translateY(-2px); }
.script-btn .browser-item-name,
.link-card .browser-item-name {
font-family: var(--serif);
font-style: italic;
font-size: 14px;
color: inherit;
font-variation-settings: 'opsz' 18;
}
/* ═══════════════════════════════════════════════════════════════
SETTINGS — numbered editorial sections
═══════════════════════════════════════════════════════════════ */
.settings-container,
.script-management {
background: transparent;
border: 0;
padding: 0;
box-shadow: none;
}
.settings-section {
background: var(--bg-card);
border: 1px solid var(--rule);
border-radius: 0;
padding: 28px 32px;
margin-bottom: 16px;
box-shadow: none;
transition: border-color 200ms var(--ease);
position: relative;
counter-increment: sr-section;
}
.settings-section:hover { border-color: var(--rule-strong); }
.settings-container { counter-reset: sr-section; }
.settings-section summary {
font-family: var(--serif);
font-weight: 400;
font-size: 26px;
letter-spacing: -0.015em;
color: var(--ink);
font-variation-settings: 'opsz' 60;
padding: 0 0 16px 0;
border: 0;
background: transparent;
cursor: pointer;
list-style: none;
position: relative;
}
.settings-section summary::-webkit-details-marker { display: none; }
.settings-section summary::before {
content: "5." counter(sr-section, decimal-leading-zero);
font-family: var(--mono);
font-style: normal;
font-size: 10px;
letter-spacing: 0.22em;
color: var(--copper);
margin-right: 14px;
vertical-align: middle;
}
.settings-section summary::after {
content: "↓";
position: absolute;
right: 0;
top: 0;
font-family: var(--sans);
font-size: 16px;
color: var(--ink-mute);
transition: transform 240ms var(--ease);
}
.settings-section[open] summary::after { transform: rotate(180deg); }
.settings-section-content {
padding-top: 18px;
border-top: 1px solid var(--rule);
}
.settings-section-description {
font-family: var(--serif);
font-style: italic;
font-size: 14px;
color: var(--ink-soft);
line-height: 1.5;
font-variation-settings: 'opsz' 18;
margin-bottom: 18px;
}
.scripts-table {
border: 1px solid var(--rule);
border-collapse: collapse;
width: 100%;
background: transparent;
}
.scripts-table thead {
background: var(--bg-paper);
}
.scripts-table th {
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ink-mute);
padding: 12px 14px;
text-align: left;
border-bottom: 1px solid var(--rule-strong);
font-weight: 400;
}
.scripts-table td {
padding: 12px 14px;
border-bottom: 1px solid var(--rule);
color: var(--ink-soft);
font-family: var(--sans);
font-size: 13px;
vertical-align: middle;
}
.scripts-table tr:last-child td { border-bottom: 0; }
.scripts-table .name-with-icon {
color: var(--ink);
font-family: var(--serif);
font-style: italic;
font-size: 15px;
font-variation-settings: 'opsz' 18;
}
.scripts-table .path-cell,
.scripts-table .mono {
font-family: var(--mono);
font-size: 11px;
color: var(--ink-mute);
}
.add-card {
background: transparent;
border: 1px dashed var(--rule-strong);
border-radius: 0;
padding: 16px;
margin-top: 16px;
transition: all 200ms var(--ease);
color: var(--ink-mute);
cursor: pointer;
}
.add-card:hover {
border-color: var(--copper);
border-style: solid;
color: var(--copper);
background: rgba(var(--copper-rgb), 0.04);
}
.add-card-icon {
font-family: var(--serif);
font-weight: 300;
font-size: 24px;
color: inherit;
}
.add-card-grid {
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
}
.audio-device-selector {
display: flex;
flex-direction: column;
gap: 12px;
}
.audio-device-selector label {
display: flex;
flex-direction: column;
gap: 8px;
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ink-mute);
}
.audio-device-selector select {
background: transparent;
border: 1px solid var(--rule-strong);
color: var(--ink);
border-radius: 0;
font-family: var(--sans);
font-size: 13px;
padding: 10px 12px;
}
.audio-device-status {
font-family: var(--mono);
font-size: 11px;
color: var(--jade);
letter-spacing: 0.1em;
text-transform: uppercase;
}
.folder-disabled-badge,
.folder-unavailable-badge {
background: transparent;
border: 1px solid currentColor;
border-radius: 0;
font-family: var(--mono);
font-size: 9px;
letter-spacing: 0.18em;
text-transform: uppercase;
padding: 3px 8px;
color: var(--amber);
}
.folder-unavailable-badge { color: var(--rust); }
.empty-state-illustration {
color: var(--ink-faint);
}
.empty-state {
color: var(--ink-mute);
font-family: var(--serif);
font-style: italic;
font-size: 14px;
font-variation-settings: 'opsz' 18;
padding: 40px 20px;
text-align: center;
}
/* ═══════════════════════════════════════════════════════════════
DIALOGS — paper card
═══════════════════════════════════════════════════════════════ */
dialog {
background: var(--bg-card);
border: 1px solid var(--rule-strong);
border-radius: 0;
color: var(--ink);
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6);
padding: 0;
max-width: 560px;
width: 90%;
max-height: 90vh;
}
dialog[open] {
display: flex;
flex-direction: column;
}
dialog form {
display: flex;
flex-direction: column;
min-height: 0;
flex: 1 1 auto;
}
.dialog-header { flex: 0 0 auto; }
.dialog-footer { flex: 0 0 auto; }
.dialog-body {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
overscroll-behavior: contain;
}
dialog::backdrop {
background: rgba(14, 13, 11, 0.7);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
}
.dialog-header {
padding: 24px 28px 18px;
border-bottom: 1px solid var(--rule);
background: transparent;
}
.dialog-header h3 {
font-family: var(--serif);
font-weight: 400;
font-style: italic;
font-size: 24px;
color: var(--ink);
font-variation-settings: 'opsz' 60;
letter-spacing: -0.01em;
}
.dialog-body {
padding: 22px 28px;
}
.dialog-body label {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 16px;
}
.dialog-body label > span {
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--ink-mute);
}
.dialog-body input,
.dialog-body textarea,
.dialog-body select {
background: transparent;
border: 0;
border-bottom: 1px solid var(--rule-strong);
border-radius: 0;
color: var(--ink);
font-family: var(--sans);
font-size: 14px;
padding: 8px 0;
transition: border-color 180ms var(--ease);
}
.dialog-body input:focus,
.dialog-body textarea:focus,
.dialog-body select:focus {
outline: 0;
border-bottom-color: var(--copper);
}
.dialog-body textarea {
border: 1px solid var(--rule-strong);
padding: 10px 12px;
resize: vertical;
min-height: 60px;
font-family: var(--mono);
font-size: 12px;
}
.dialog-footer {
padding: 16px 28px 22px;
border-top: 1px solid var(--rule);
background: transparent;
gap: 10px;
}
.btn-primary {
background: var(--ink);
color: var(--bg-deep);
border: 1px solid var(--ink);
border-radius: 0;
font-family: var(--mono);
font-size: 11px;
letter-spacing: 0.18em;
text-transform: uppercase;
padding: 10px 18px;
transition: all 200ms var(--ease);
}
.btn-primary:hover {
background: var(--copper);
border-color: var(--copper);
color: var(--bg-deep);
}
.btn-secondary, .btn-cancel {
background: transparent;
color: var(--ink-soft);
border: 1px solid var(--rule-strong);
border-radius: 0;
font-family: var(--mono);
font-size: 11px;
letter-spacing: 0.18em;
text-transform: uppercase;
padding: 10px 18px;
transition: all 200ms var(--ease);
}
.btn-secondary:hover, .btn-cancel:hover {
color: var(--copper);
border-color: var(--copper);
}
.btn-danger {
background: var(--rust);
color: var(--bg-deep);
border: 1px solid var(--rust);
border-radius: 0;
font-family: var(--mono);
font-size: 11px;
letter-spacing: 0.18em;
text-transform: uppercase;
padding: 10px 18px;
}
.btn-danger:hover { background: transparent; color: var(--rust); }
.btn-small {
background: transparent;
border: 1px solid var(--rule-strong);
color: var(--ink-soft);
border-radius: 0;
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.16em;
text-transform: uppercase;
padding: 6px 12px;
}
.btn-small:hover { border-color: var(--copper); color: var(--copper); }
.confirm-dialog {
padding: 28px;
max-width: 460px;
}
.confirm-dialog p {
font-family: var(--serif);
font-style: italic;
font-size: 17px;
color: var(--ink-soft);
line-height: 1.5;
margin-bottom: 22px;
font-variation-settings: 'opsz' 30;
}
.confirm-dialog-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.execution-status {
font-family: var(--mono);
font-size: 12px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--copper);
padding: 10px 14px;
border: 1px solid var(--rule-strong);
background: var(--bg-paper);
margin-bottom: 14px;
}
.execution-result {
background: var(--bg-paper);
border: 1px solid var(--rule);
border-radius: 0;
}
.execution-result pre {
font-family: var(--mono);
font-size: 11px;
color: var(--ink-soft);
padding: 14px;
}
.result-section h4 {
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ink-mute);
margin-bottom: 8px;
font-weight: 400;
}
.icon-input-wrapper {
display: flex;
align-items: center;
gap: 10px;
}
.icon-preview {
background: var(--bg-paper);
border: 1px solid var(--rule-strong);
border-radius: 0;
width: 36px;
height: 36px;
color: var(--copper);
}
.params-section {
margin-top: 18px;
padding-top: 14px;
border-top: 1px solid var(--rule);
}
.params-header {
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ink-mute);
}
.param-row {
background: var(--bg-paper);
border: 1px solid var(--rule);
border-radius: 0;
padding: 12px;
margin-top: 10px;
}
.param-row-header {
font-family: var(--mono);
font-size: 11px;
color: var(--copper);
letter-spacing: 0.08em;
}
.param-required-label {
color: var(--rust);
font-family: var(--mono);
font-size: 9px;
letter-spacing: 0.18em;
text-transform: uppercase;
}
.param-hint {
font-family: var(--serif);
font-style: italic;
font-size: 12px;
color: var(--ink-mute);
font-variation-settings: 'opsz' 18;
}
.param-remove-btn {
color: var(--rust);
background: transparent;
border: 1px solid var(--rule-strong);
border-radius: 0;
}
.param-remove-btn:hover { border-color: var(--rust); }
/* Icon select popup */
.icon-select-popup {
background: var(--bg-card);
border: 1px solid var(--rule-strong);
border-radius: 0;
box-shadow: 0 14px 40px rgba(0,0,0,0.5);
}
.icon-select-trigger {
background: transparent;
border: 1px solid var(--rule-strong);
border-radius: 0;
color: var(--ink);
font-family: var(--mono);
font-size: 12px;
padding: 8px 12px;
}
.icon-select-trigger:hover { border-color: var(--copper); color: var(--copper); }
.icon-select-cell {
border-radius: 0;
color: var(--ink-soft);
font-family: var(--mono);
font-size: 10px;
}
.icon-select-cell:hover {
background: rgba(var(--copper-rgb), 0.06);
color: var(--copper);
}
/* ═══════════════════════════════════════════════════════════════
AUTH MODAL — paper card
═══════════════════════════════════════════════════════════════ */
#auth-overlay {
background: rgba(14, 13, 11, 0.85);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
}
.auth-modal {
background: var(--bg-card);
border: 1px solid var(--rule-strong);
border-radius: 0;
padding: 40px 36px;
max-width: 440px;
box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6);
position: relative;
}
.auth-modal h2 {
font-family: var(--serif);
font-weight: 400;
font-style: italic;
font-size: 36px;
line-height: 1;
margin-bottom: 12px;
font-variation-settings: 'opsz' 144;
letter-spacing: -0.02em;
color: var(--ink);
}
.auth-modal p {
font-family: var(--serif);
font-style: italic;
font-size: 15px;
color: var(--ink-soft);
line-height: 1.5;
margin-bottom: 18px;
font-variation-settings: 'opsz' 18;
}
.auth-modal input {
background: transparent;
border: 0;
border-bottom: 1px solid var(--rule-strong);
border-radius: 0;
color: var(--ink);
font-family: var(--mono);
font-size: 14px;
padding: 10px 0;
width: 100%;
margin-bottom: 18px;
}
.auth-modal input:focus {
outline: 0;
border-bottom-color: var(--copper);
}
.btn-connect {
background: var(--ink);
color: var(--bg-deep);
border: 1px solid var(--ink);
border-radius: 0;
width: 100%;
padding: 14px;
font-family: var(--mono);
font-size: 11px;
letter-spacing: 0.22em;
text-transform: uppercase;
transition: all 200ms var(--ease);
cursor: pointer;
}
.btn-connect:hover {
background: var(--copper);
border-color: var(--copper);
}
.help-text {
margin-top: 22px;
padding-top: 18px;
border-top: 1px solid var(--rule);
font-family: var(--mono);
font-size: 11px;
color: var(--ink-mute);
letter-spacing: 0.04em;
}
.help-text code {
background: var(--bg-paper);
color: var(--copper);
border: 1px solid var(--rule);
padding: 2px 6px;
font-family: var(--mono);
font-size: 11px;
}
.error-message {
color: var(--rust);
font-family: var(--mono);
font-size: 11px;
letter-spacing: 0.1em;
text-transform: uppercase;
margin-top: 14px;
}
/* ═══════════════════════════════════════════════════════════════
TOAST NOTIFICATIONS
═══════════════════════════════════════════════════════════════ */
.toast {
background: var(--bg-card);
border: 1px solid var(--copper);
border-radius: 0;
color: var(--ink);
font-family: var(--sans);
font-size: 13px;
padding: 14px 18px;
box-shadow: 0 14px 40px rgba(0,0,0,0.5), 0 0 24px var(--copper-glow);
}
.toast.success { border-color: var(--jade); box-shadow: 0 14px 40px rgba(0,0,0,0.5); }
.toast.error { border-color: var(--rust); box-shadow: 0 14px 40px rgba(0,0,0,0.5); }
.toast.info { border-color: var(--rule-strong); box-shadow: 0 14px 40px rgba(0,0,0,0.5); }
/* ═══════════════════════════════════════════════════════════════
FOOTER (colophon)
═══════════════════════════════════════════════════════════════ */
footer {
margin-top: 36px;
padding: 20px 0 0;
border-top: 1px solid var(--rule-strong);
background: transparent;
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--ink-faint);
text-align: center;
}
footer a {
color: var(--copper);
text-decoration: none;
border-bottom: 1px solid transparent;
transition: border-color 200ms var(--ease);
}
footer a:hover { border-bottom-color: var(--copper); }
footer strong {
font-family: var(--serif);
font-style: italic;
font-weight: 400;
font-size: 14px;
color: var(--ink-soft);
letter-spacing: 0.01em;
text-transform: none;
font-variation-settings: 'opsz' 30;
}
footer .separator { color: var(--ink-ghost); margin: 0 8px; }
/* ═══════════════════════════════════════════════════════════════
DISPLAY container (monitors tab)
═══════════════════════════════════════════════════════════════ */
.display-container {
background: transparent;
border: 0;
padding: 0;
box-shadow: none;
}
/* ─── Light theme overrides for SR overrides ──────────────── */
:root[data-theme="light"] .album-art-container {
box-shadow:
0 1px 0 var(--bg-paper),
0 28px 60px -20px rgba(26, 23, 21, 0.18),
0 8px 20px -8px rgba(26, 23, 21, 0.12);
}
:root[data-theme="light"] .mini-player {
background: linear-gradient(180deg, rgba(245, 241, 234, 0.7) 0%, rgba(245, 241, 234, 0.96) 30%);
}
:root[data-theme="light"] dialog::backdrop {
background: rgba(26, 23, 21, 0.5);
}
:root[data-theme="light"] #auth-overlay {
background: rgba(245, 241, 234, 0.85);
}
:root[data-theme="light"] .toast {
box-shadow: 0 14px 40px rgba(26,23,21,0.18), 0 0 24px var(--copper-glow);
}
/* ─── Mobile breakpoint refinements ──────────────────────── */
@media (max-width: 720px) {
#track-title,
.now-playing .track-title { font-size: 38px; }
.player-layout, .now-playing { gap: 28px; grid-template-columns: 1fr; }
.controls { gap: 12px; }
.now-playing .controls .btn-trans { width: 42px; height: 42px; }
.now-playing .controls .btn-trans.primary { width: 56px; height: 56px; }
.mini-player { padding: 12px 16px; gap: 16px; }
.tab-btn { padding: 14px 12px 12px; font-size: 12px; }
.tab-btn.active { font-size: 15px; }
/* Override legacy mobile rule that hid every .tab-btn span (numbers + labels) */
.tab-btn span { display: inline; }
.tab-btn .tab-num { display: none; }
.tab-bar {
flex-wrap: nowrap;
overflow-x: auto;
overscroll-behavior-x: contain;
scrollbar-width: none;
}
.tab-bar::-webkit-scrollbar { display: none; }
.tab-btn { flex: 0 0 auto; }
.breadcrumb { font-size: 14px; }
.browser-toolbar { flex-wrap: wrap; }
.auth-modal { padding: 28px 22px; }
.auth-modal h2 { font-size: 28px; }
.settings-section { padding: 20px; }
.settings-section summary { font-size: 22px; }
footer { font-size: 9px; }
}
/* ════════════════════════════════════════════════════════════════
STUDIO REFERENCE — verbatim mockup snap for the player view.
Scoped to `.now-playing` so other tabs are unaffected. Wins over
earlier overrides through declaration order at equal specificity.
════════════════════════════════════════════════════════════════ */
.now-playing {
display: grid;
grid-template-columns: minmax(320px, 520px) 1fr;
gap: 64px;
align-items: start;
margin-top: 28px;
position: relative;
}
@media (max-width: 980px) {
.now-playing { grid-template-columns: 1fr; gap: 40px; }
.now-playing .track-masthead { padding-right: 0; }
}
/* Vinyl/disc/tonearm rules live in the SLEEVE FRAME section under
.vinyl-stage selectors above. The earlier .now-playing duplicates
were stale clones from the pre-sleeve mockup snap and overrode the
new geometry by source order — removed to let .vinyl-stage rules
take effect. */
/* Spectrogram canvas: hidden — the editorial .spectrum row in the
track-masthead is the visible spectrum. The canvas stays in the
DOM so the visualizer render loop keeps emitting frequencyData
for the dynamic background to consume. */
.now-playing .spectrogram-canvas {
display: none !important;
}
/* ─── Track masthead ──────────────────────────────────────── */
.now-playing .track-masthead {
display: flex;
flex-direction: column;
justify-content: center;
padding-top: 0;
padding-right: clamp(12px, 1.5vw, 24px);
gap: 0;
}
.now-playing .kicker {
display: flex;
align-items: center;
gap: 14px;
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.32em;
text-transform: uppercase;
color: var(--copper);
margin-bottom: 22px;
}
.now-playing .kicker::before,
.now-playing .kicker::after {
content: "";
height: 1px;
background: var(--copper);
opacity: 0.6;
flex: 0 0 24px;
}
.now-playing .kicker::after { flex: 1 0 auto; }
.now-playing .track-title,
.now-playing #track-title {
font-family: var(--serif);
font-weight: 400;
font-size: clamp(34px, 4.4vw, 64px);
line-height: 0.98;
letter-spacing: -0.02em;
font-variation-settings: 'opsz' 144;
margin-bottom: 18px;
color: var(--ink);
margin-top: 0;
/* Long titles get clamped to 3 lines with ellipsis */
display: -webkit-box;
-webkit-line-clamp: 3;
line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
word-break: break-word;
overflow-wrap: anywhere;
hyphens: auto;
}
.now-playing .track-title em {
font-style: italic;
color: var(--copper-hi);
}
.now-playing .track-byline,
.now-playing #artist {
font-family: var(--serif);
font-style: italic;
font-size: 22px;
font-weight: 300;
color: var(--ink-soft);
font-variation-settings: 'opsz' 60;
margin-bottom: 4px;
margin-top: 0;
}
.now-playing .track-album,
.now-playing #album {
font-family: var(--sans);
font-size: 13px;
letter-spacing: 0.04em;
color: var(--ink-mute);
font-style: normal;
font-weight: 400;
margin-top: 0;
}
/* Hide byline/album rows when their content is empty so they
don't leave a stretched blank gap. JS leaves them empty when
the source provides no metadata. */
.now-playing #artist:empty,
.now-playing #album:empty {
display: none;
}
/* ─── 2-cell metadata grid ────────────────────────────────── */
.now-playing .meta-grid {
display: grid;
grid-template-columns: minmax(180px, auto) 1fr;
gap: 0;
margin-top: 36px;
border-top: 1px solid var(--rule);
border-bottom: 1px solid var(--rule);
}
.now-playing .meta-cell {
padding: 16px 24px 16px 0;
border-right: 1px solid var(--rule);
min-width: 0;
}
.now-playing .meta-cell:last-child {
border-right: 0;
padding-left: 24px;
padding-right: 0;
}
.now-playing .meta-cell .label {
font-family: var(--mono);
font-size: 9px;
letter-spacing: 0.22em;
text-transform: uppercase;
/* Match the .kicker (NOW PLAYING) color so the metadata grid reads
as part of the same typographic system. */
color: var(--copper);
margin-bottom: 8px;
}
.now-playing .meta-cell .value {
font-family: var(--serif);
font-style: italic;
font-weight: 400;
font-size: 18px;
color: var(--ink);
font-variation-settings: 'opsz' 30;
display: flex;
align-items: center;
gap: 10px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 1.2;
}
.now-playing .meta-cell .value .state-icon,
.now-playing .meta-cell .value .source-icon {
width: 16px;
height: 16px;
flex-shrink: 0;
color: var(--copper);
fill: currentColor;
}
/* ─── Spectrum bars (JS-injected; real audio from backend FFT) ── */
.now-playing .spectrum {
/* grid-template-columns is set from JS to repeat(N, minmax(0, 1fr))
because CSS repeat() does NOT accept a CSS variable as its count.
Falling back to a 40-column default ensures something sane renders
even if JS hasn't executed yet. */
display: grid !important;
grid-template-columns: repeat(40, minmax(0, 1fr));
align-items: end;
column-gap: 4px;
height: 70px;
margin: 36px 0 24px;
width: 100% !important;
box-sizing: border-box;
min-width: 0;
}
.now-playing .spectrum > span {
display: block;
width: 100%;
min-width: 0;
background: linear-gradient(to top, var(--copper-lo) 0%, var(--copper) 60%, var(--copper-hi) 100%);
opacity: 0.92;
transform-origin: bottom;
border-radius: 99px 99px 0 0;
/* Bars are full-height boxes; the visible height is driven by
transform: scaleY. Transforms are GPU-composited, so per-frame
updates skip layout/paint entirely. */
height: 100%;
transform: scaleY(var(--bar-h-scale, 0.4));
animation: sr-snap-bar 1.1s ease-in-out infinite;
animation-delay: var(--bar-delay, 0s);
animation-play-state: paused;
transition: transform 50ms linear;
will-change: transform;
}
:root[data-playstate="playing"] .now-playing .spectrum span {
animation-play-state: running;
}
/* When real audio data is driving the bars, freeze the synthetic CSS
animation so JS-set transforms aren't overridden by the keyframe. */
body.audio-spectrum-live .now-playing .spectrum span {
animation: none !important;
}
@keyframes sr-snap-bar {
0%, 100% { transform: scaleY(0.4); }
50% { transform: scaleY(1); }
}
/* ─── Transport ───────────────────────────────────────────── */
.now-playing .transport { margin-top: 0; }
.now-playing .progress-row {
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
align-items: center;
gap: 18px;
margin-bottom: 28px;
}
.now-playing .timecode {
font-family: var(--mono);
font-size: 12px;
color: var(--ink-mute);
letter-spacing: 0.06em;
font-variant-numeric: tabular-nums;
line-height: 1;
}
.now-playing .timecode.elapsed { color: var(--copper); font-weight: 500; }
.now-playing .progress-track,
.now-playing .progress-bar {
height: 2px;
width: 100%;
background: var(--rule-strong);
position: relative;
cursor: pointer;
border-radius: 0;
overflow: visible;
transform: none !important;
transition: background 200ms var(--ease);
min-width: 0;
margin: 0;
}
.now-playing .progress-bar:hover,
.now-playing .progress-bar.dragging {
transform: none !important;
background: var(--rule-strong);
}
.now-playing .progress-fill {
position: absolute;
left: 0; top: 0;
height: 100%;
background: var(--copper);
box-shadow: 0 0 12px var(--copper-glow);
border-radius: 0;
width: 0;
transition: width 0.1s linear;
}
.now-playing .progress-fill::after {
content: "";
position: absolute;
right: -5px;
top: 50%;
transform: translateY(-50%) scale(1) !important;
width: 10px; height: 10px;
background: var(--copper);
border-radius: 50%;
box-shadow: 0 0 14px var(--copper-glow), 0 0 0 4px rgba(var(--copper-rgb), 0.12);
transition: none;
}
/* ─── Controls + VU cluster ───────────────────────────────── */
.now-playing .controls {
display: flex;
align-items: center;
gap: 18px;
margin-top: 0;
justify-content: flex-start;
flex-wrap: wrap;
row-gap: 16px;
}
.now-playing .btn-trans {
background: transparent;
border: 1px solid var(--rule-strong);
color: var(--ink-soft);
width: 48px; height: 48px;
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
border-radius: 50%;
padding: 0;
transition: all 200ms var(--ease);
}
.now-playing .btn-trans:hover {
border-color: var(--copper);
color: var(--copper);
background: rgba(var(--copper-rgb), 0.06);
}
.now-playing .btn-trans:disabled {
opacity: 0.35;
cursor: not-allowed;
border-color: var(--rule-strong);
color: var(--ink-mute);
background: transparent;
}
.now-playing .btn-trans.primary {
width: 64px; height: 64px;
background: var(--ink);
color: var(--bg-deep);
border-color: var(--ink);
box-shadow: 0 8px 28px rgba(0,0,0,0.45);
}
.now-playing .btn-trans.primary:hover {
background: var(--copper);
border-color: var(--copper);
color: var(--bg-deep);
transform: scale(1.04);
box-shadow: 0 8px 28px var(--copper-glow);
}
.now-playing .btn-trans svg {
width: 20px; height: 20px;
fill: currentColor;
}
.now-playing .btn-trans.primary svg {
width: 24px; height: 24px;
}
/* VU cluster — volume left, readout center, VU meter right (reversed) */
.now-playing .vu-cluster {
margin-left: auto;
display: flex;
flex-direction: row-reverse;
align-items: center;
gap: 16px;
user-select: none;
}
.now-playing .vu-cluster.muted .vu-needle {
background: linear-gradient(to top, var(--rust) 0%, var(--ink-mute) 100%);
box-shadow: 0 0 8px rgba(194, 85, 63, 0.4);
}
.now-playing .vu-cluster.muted .vu-readout strong { color: var(--rust); }
/* Integrated volume control: tiny mute icon + slim copper slider.
Lives on the LEFT of the cluster (row-reverse), so its divider
sits on its RIGHT to separate it from the readout. */
.now-playing .vu-volume {
display: flex;
align-items: center;
gap: 10px;
padding-right: 12px;
border-right: 1px solid var(--rule);
margin-right: 4px;
}
.now-playing .vu-volume .mute-btn {
background: transparent;
border: 1px solid var(--rule-strong);
color: var(--ink-soft);
width: 30px;
height: 30px;
border-radius: 50%;
padding: 0;
margin: 0;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 180ms var(--ease);
flex-shrink: 0;
}
.now-playing .vu-volume .mute-btn:hover {
border-color: var(--copper);
color: var(--copper);
background: rgba(var(--copper-rgb), 0.06);
}
.now-playing .vu-volume .mute-btn svg {
width: 14px;
height: 14px;
fill: currentColor;
}
.now-playing .vu-volume #volume-slider {
-webkit-appearance: none;
appearance: none;
width: 64px;
height: 2px;
background: var(--rule-strong);
border-radius: 0;
margin: 0;
padding: 0;
cursor: pointer;
flex: none;
}
.now-playing .vu-volume #volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 10px;
height: 10px;
background: var(--copper);
border-radius: 50%;
box-shadow: 0 0 8px var(--copper-glow);
border: 0;
cursor: grab;
}
.now-playing .vu-volume #volume-slider::-moz-range-thumb {
width: 10px;
height: 10px;
background: var(--copper);
border-radius: 50%;
border: 0;
cursor: grab;
}
.now-playing .vu-volume #volume-slider::-moz-range-track {
height: 2px;
background: var(--rule-strong);
border-radius: 0;
}
.now-playing .vu-meter {
position: relative;
width: 120px;
height: 60px;
background: linear-gradient(180deg, #1a1610 0%, #0e0c08 100%);
border: 1px solid var(--rule-strong);
border-radius: 4px 4px 0 0;
overflow: hidden;
box-shadow: inset 0 2px 6px rgba(0,0,0,0.5), inset 0 0 30px rgba(var(--copper-rgb), 0.08);
flex-shrink: 0;
}
.now-playing .vu-meter::before {
content: "";
position: absolute;
inset: 0;
background: repeating-conic-gradient(from 195deg at 50% 100%,
transparent 0deg 4deg,
rgba(242, 235, 220, 0.08) 4deg 5deg,
transparent 5deg 9deg);
}
.now-playing .vu-meter::after {
content: "VU";
position: absolute;
bottom: 4px;
left: 50%;
transform: translateX(-50%);
font-family: var(--mono);
font-size: 8px;
letter-spacing: 0.3em;
color: var(--ink-faint);
}
.now-playing .vu-needle {
position: absolute;
bottom: 0;
left: 50%;
width: 1.5px;
height: 88%;
background: linear-gradient(to top, var(--copper) 0%, var(--copper-hi) 70%, var(--ink) 100%);
transform-origin: bottom center;
transform: rotate(-22deg);
transition: transform 350ms var(--ease);
box-shadow: 0 0 8px var(--copper-glow);
}
.now-playing .vu-readout {
font-family: var(--mono);
font-size: 11px;
color: var(--ink-mute);
letter-spacing: 0.06em;
font-variant-numeric: tabular-nums;
display: flex;
flex-direction: column;
gap: 4px;
line-height: 1.2;
white-space: nowrap;
}
.now-playing .vu-readout strong {
color: var(--copper);
font-weight: 400;
}
/* Light theme — paper-faced VU meter (vintage cream gauge instead of black) */
:root[data-theme="light"] .now-playing .vu-meter {
background: linear-gradient(180deg, #FAF6EE 0%, #E8E0CE 100%);
border-color: var(--rule-strong);
box-shadow:
inset 0 1px 2px rgba(26, 23, 21, 0.08),
inset 0 0 24px rgba(var(--copper-rgb), 0.05);
}
:root[data-theme="light"] .now-playing .vu-meter::before {
background: repeating-conic-gradient(from 195deg at 50% 100%,
transparent 0deg 4deg,
rgba(26, 23, 21, 0.18) 4deg 5deg,
transparent 5deg 9deg);
}
:root[data-theme="light"] .now-playing .vu-meter::after {
color: var(--ink-mute);
}
:root[data-theme="light"] .now-playing .vu-needle {
background: linear-gradient(to top, var(--copper) 0%, var(--copper-lo) 70%, var(--ink) 100%);
box-shadow: 0 0 6px rgba(var(--copper-rgb), 0.25);
}
/* Mobile VU cluster: stack below controls */
@media (max-width: 720px) {
.now-playing .controls { flex-wrap: wrap; }
.now-playing .vu-cluster {
margin-left: 0;
width: 100%;
justify-content: space-between;
margin-top: 12px;
}
.now-playing .vu-meter { width: 110px; height: 50px; }
.now-playing .meta-grid {
grid-template-columns: 1fr;
}
.now-playing .meta-cell {
border-right: 0;
border-bottom: 1px solid var(--rule);
padding: 12px 0;
}
.now-playing .meta-cell:last-child {
border-bottom: 0;
padding-left: 0;
}
}
/* ════════════════════════════════════════════════════════════════
STUDIO REFERENCE — editorial styling for non-player tabs
(Library, Quick Access, Settings, Display)
════════════════════════════════════════════════════════════════ */
/* Common: each tab container is a flat editorial canvas (no card chrome) */
.browser-container,
.scripts-container,
.settings-container,
.display-container,
.script-management {
background: transparent !important;
border: 0 !important;
padding: 0 !important;
box-shadow: none !important;
margin-top: 14px;
}
/* ─── LIBRARY (browser) ───────────────────────────────────── */
/* Breadcrumb — italic serif, copper terminal */
.browser-container .breadcrumb {
background: transparent !important;
border: 0 !important;
border-bottom: 1px solid var(--rule) !important;
border-radius: 0 !important;
padding: 14px 0 !important;
margin-bottom: 24px;
font-family: var(--serif);
font-style: italic;
font-size: 18px;
font-variation-settings: 'opsz' 30;
color: var(--ink-soft);
display: flex;
align-items: center;
gap: 4px;
flex-wrap: wrap;
}
.browser-container .breadcrumb-home,
.browser-container .breadcrumb-item {
color: var(--ink-soft);
text-decoration: none;
cursor: pointer;
transition: color 180ms var(--ease);
background: transparent;
border: 0;
padding: 0;
font-family: inherit;
font-style: italic;
font-size: inherit;
}
.browser-container .breadcrumb-home:hover,
.browser-container .breadcrumb-item:hover { color: var(--copper); }
.browser-container .breadcrumb-item:last-of-type {
color: var(--ink);
font-weight: 500;
}
.browser-container .breadcrumb-separator {
color: var(--ink-faint);
margin: 0 8px;
font-style: normal;
opacity: 1;
font-family: var(--mono);
font-size: 14px;
}
/* Toolbar — hairline groups, no card */
.browser-container .browser-toolbar {
background: transparent !important;
border: 0 !important;
border-bottom: 1px solid var(--rule) !important;
border-radius: 0 !important;
padding: 0 0 16px 0 !important;
margin-bottom: 28px;
gap: 14px;
display: flex;
align-items: center;
flex-wrap: wrap;
}
.browser-container .browser-toolbar-left {
display: flex;
align-items: center;
gap: 10px;
}
.browser-container .browser-toolbar-right {
margin-left: auto;
display: flex;
align-items: center;
gap: 10px;
}
/* View-toggle: square hairline pills with copper active */
.browser-container .view-toggle {
display: flex;
background: transparent !important;
border: 1px solid var(--rule-strong) !important;
border-radius: 0 !important;
padding: 0 !important;
overflow: hidden;
}
.browser-container .view-toggle-btn,
.browser-container .browser-refresh-btn,
.browser-container .browser-play-all-btn {
background: transparent !important;
border: 0 !important;
border-radius: 0 !important;
color: var(--ink-mute) !important;
width: 36px !important;
height: 36px !important;
padding: 0 !important;
display: inline-flex !important;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 180ms var(--ease);
box-sizing: border-box;
}
.browser-container .view-toggle .view-toggle-btn {
border-right: 1px solid var(--rule-strong) !important;
}
.browser-container .view-toggle .view-toggle-btn:last-child {
border-right: 0 !important;
}
.browser-container .view-toggle-btn:hover,
.browser-container .browser-refresh-btn:hover,
.browser-container .browser-play-all-btn:hover {
color: var(--copper) !important;
background: rgba(var(--copper-rgb), 0.06) !important;
}
.browser-container .view-toggle-btn.active {
background: var(--ink) !important;
color: var(--bg-deep) !important;
}
.browser-container .browser-refresh-btn,
.browser-container .browser-play-all-btn {
border: 1px solid var(--rule-strong) !important;
}
.browser-container .browser-play-all-btn {
color: var(--copper) !important;
border-color: var(--copper) !important;
}
/* Search — underline-only editorial input */
.browser-container .browser-search-wrapper {
background: transparent !important;
border: 0 !important;
border-bottom: 1px solid var(--rule-strong) !important;
border-radius: 0 !important;
padding: 4px 0 !important;
/* override legacy position: relative so the absolutely-positioned
icon doesn't overlap the input */
position: static !important;
display: inline-flex !important;
align-items: center;
gap: 10px;
transition: border-color 180ms var(--ease);
min-width: 220px;
max-width: none !important;
}
.browser-container .browser-search-wrapper:focus-within {
border-bottom-color: var(--copper) !important;
}
.browser-container .browser-search-input {
background: transparent !important;
border: 0 !important;
color: var(--ink) !important;
font-family: var(--sans) !important;
font-size: 14px !important;
/* defeat legacy padding: 0.4rem 2rem 0.4rem 2rem */
padding: 4px 0 !important;
outline: none !important;
flex: 1;
min-width: 0;
width: auto !important;
}
.browser-container .browser-search-input::placeholder { color: var(--ink-mute); }
.browser-container .browser-search-icon {
/* defeat legacy position: absolute */
position: static !important;
transform: none !important;
left: auto !important;
top: auto !important;
color: var(--ink-mute);
flex-shrink: 0;
pointer-events: none;
}
.browser-container .browser-search-clear {
/* defeat legacy position: absolute */
position: static !important;
transform: none !important;
right: auto !important;
top: auto !important;
background: transparent !important;
border: 0 !important;
color: var(--ink-mute) !important;
cursor: pointer;
padding: 0 !important;
flex-shrink: 0;
}
.browser-container .browser-search-clear:hover { color: var(--copper) !important; }
/* Items per page label + select */
.browser-container .items-per-page-label {
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ink-mute);
display: inline-flex;
align-items: center;
gap: 8px;
}
.browser-container .items-per-page-label select {
background: var(--bg-card) !important;
border: 1px solid var(--rule-strong) !important;
color: var(--ink) !important;
border-radius: 0 !important;
font-family: var(--mono);
font-size: 11px;
padding: 6px 10px !important;
cursor: pointer;
}
/* Native <select> popup options — paint dark so they don't read
as white-on-dark blocks. Most browsers respect bg/color on <option>. */
select option {
background-color: var(--bg-card);
color: var(--ink);
}
/* Browser grid — editorial cards (default = grid view) */
.browser-container .browser-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 32px 24px;
padding: 0 !important;
}
/* Compact view — denser grid with smaller cards */
.browser-container .browser-grid.browser-grid-compact {
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 18px 14px;
}
.browser-container .browser-grid-compact .browser-thumb-wrapper {
margin-bottom: 8px;
}
.browser-container .browser-grid-compact .browser-item-name {
font-size: 13px;
font-family: var(--sans);
font-weight: 500;
letter-spacing: -0.005em;
line-height: 1.2;
}
.browser-container .browser-grid-compact .browser-item-meta,
.browser-container .browser-grid-compact .browser-item-type {
font-size: 9px;
letter-spacing: 0.1em;
}
.browser-container .browser-grid-compact .browser-icon {
width: 28px;
height: 28px;
}
.browser-container .browser-item {
background: transparent !important;
border: 0 !important;
border-radius: 0 !important;
padding: 0 !important;
cursor: pointer;
transition: transform 280ms var(--ease);
}
.browser-container .browser-item:hover {
transform: translateY(-3px);
background: transparent !important;
box-shadow: none !important;
}
.browser-container .browser-thumb-wrapper {
position: relative;
aspect-ratio: 1;
background: var(--bg-card);
border: 1px solid var(--rule);
border-radius: 0 !important;
overflow: hidden;
box-shadow: 0 14px 30px -10px rgba(0,0,0,0.5);
transition: all 280ms var(--ease);
margin-bottom: 14px;
}
.browser-container .browser-item:hover .browser-thumb-wrapper {
border-color: var(--copper);
box-shadow: 0 20px 40px -10px rgba(0,0,0,0.65);
}
.browser-container .browser-thumbnail {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 0 !important;
}
.browser-container .browser-icon {
color: var(--ink-mute);
}
.browser-container .browser-item-info { padding: 0; }
.browser-container .browser-item-name {
font-family: var(--serif);
font-style: normal;
font-weight: 400;
font-size: 16px;
color: var(--ink);
font-variation-settings: 'opsz' 18;
letter-spacing: 0;
line-height: 1.25;
margin-bottom: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.browser-container .browser-item-meta,
.browser-container .browser-item-type {
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--ink-mute);
}
.browser-container .browser-play-overlay {
background: rgba(14, 13, 11, 0.55);
}
.browser-container .browser-play-overlay svg {
fill: var(--ink);
filter: drop-shadow(0 4px 12px rgba(0,0,0,0.6));
}
.browser-container .browser-empty {
grid-column: 1 / -1;
color: var(--ink-mute);
font-family: var(--serif);
font-style: italic;
font-size: 16px;
text-align: center;
padding: 60px 20px;
font-variation-settings: 'opsz' 30;
}
/* List view — editorial table */
.browser-container .browser-list {
background: transparent !important;
border: 1px solid var(--rule);
border-radius: 0 !important;
}
.browser-container .browser-list-header {
background: var(--bg-paper) !important;
border-bottom: 1px solid var(--rule-strong) !important;
color: var(--ink-mute) !important;
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
padding: 12px 16px;
}
.browser-container .browser-list-item {
border-bottom: 1px solid var(--rule);
transition: background 180ms var(--ease);
padding: 10px 16px;
}
.browser-container .browser-list-item:hover { background: var(--bg-paper); }
.browser-container .browser-list-name {
font-family: var(--serif);
font-style: normal;
font-size: 15px;
color: var(--ink);
font-variation-settings: 'opsz' 18;
}
.browser-container .browser-list-bitrate,
.browser-container .browser-list-duration,
.browser-container .browser-list-size {
font-family: var(--mono);
font-size: 11px;
color: var(--ink-mute);
font-variant-numeric: tabular-nums;
}
/* Pagination — hairline buttons */
.browser-container .pagination {
background: transparent !important;
border: 0 !important;
border-top: 1px solid var(--rule) !important;
margin-top: 36px;
padding-top: 22px !important;
display: flex;
align-items: center;
gap: 18px;
}
.browser-container .pagination button {
background: transparent !important;
border: 1px solid var(--rule-strong) !important;
color: var(--ink-soft) !important;
border-radius: 0 !important;
font-family: var(--mono);
font-size: 11px;
letter-spacing: 0.18em;
text-transform: uppercase;
padding: 8px 16px !important;
transition: all 180ms var(--ease);
cursor: pointer;
}
.browser-container .pagination button:hover:not(:disabled) {
color: var(--copper) !important;
border-color: var(--copper) !important;
}
.browser-container .pagination button:disabled { opacity: 0.35; cursor: not-allowed; }
.browser-container .pagination-center {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--mono);
font-size: 11px;
color: var(--ink-mute);
}
.browser-container .page-input {
background: transparent !important;
border: 1px solid var(--rule-strong) !important;
color: var(--ink) !important;
border-radius: 0 !important;
font-family: var(--mono);
font-size: 11px;
padding: 6px 8px !important;
width: 60px;
text-align: center;
}
.browser-container .pagination-showing {
margin-left: auto;
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--ink-faint);
}
/* ─── QUICK ACCESS — console rail ─────────────────────────── */
.scripts-container .scripts-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 1px;
background: var(--rule);
border: 1px solid var(--rule-strong);
padding: 0;
margin: 0;
}
.scripts-container .script-btn,
.scripts-container .link-card {
background: var(--bg-card) !important;
border: 0 !important;
border-radius: 0 !important;
padding: 26px 18px !important;
color: var(--ink) !important;
transition: all 200ms var(--ease) !important;
text-align: center;
display: flex !important;
flex-direction: column !important;
align-items: center !important;
justify-content: center !important;
cursor: pointer;
min-height: 120px;
}
.scripts-container .script-btn:hover,
.scripts-container .link-card:hover {
background: var(--bg-card-2) !important;
color: var(--copper) !important;
transform: none !important;
}
.scripts-container .script-btn .browser-icon,
.scripts-container .link-card .browser-icon {
color: var(--ink-soft);
margin-bottom: 14px;
font-size: 22px;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
transition: all 200ms var(--ease);
}
.scripts-container .script-btn:hover .browser-icon,
.scripts-container .link-card:hover .browser-icon {
color: var(--copper);
transform: translateY(-2px);
}
.scripts-container .script-btn .browser-item-name,
.scripts-container .link-card .browser-item-name {
font-family: var(--serif);
font-style: italic;
font-weight: 400;
font-size: 15px;
color: inherit;
font-variation-settings: 'opsz' 18;
letter-spacing: 0;
line-height: 1.2;
margin: 0;
}
.scripts-container .scripts-empty {
grid-column: 1 / -1;
background: var(--bg-card);
padding: 80px 24px;
color: var(--ink-mute);
font-family: var(--serif);
font-style: italic;
font-size: 16px;
text-align: center;
font-variation-settings: 'opsz' 30;
border: 1px dashed var(--rule-strong);
}
.scripts-container .scripts-empty p { color: inherit; }
/* ─── SETTINGS — numbered editorial cards ─────────────────── */
.settings-container { counter-reset: sr-section; }
.settings-container .settings-section {
background: var(--bg-card) !important;
border: 1px solid var(--rule) !important;
border-radius: 0 !important;
padding: 28px 32px !important;
margin-bottom: 16px;
box-shadow: none !important;
transition: border-color 200ms var(--ease);
position: relative;
counter-increment: sr-section;
}
.settings-container .settings-section:hover {
border-color: var(--rule-strong) !important;
}
.settings-container .settings-section summary {
font-family: var(--serif);
font-weight: 400;
font-style: normal;
font-size: 24px;
letter-spacing: -0.015em;
color: var(--ink);
font-variation-settings: 'opsz' 60;
padding: 0 0 14px 0 !important;
border: 0 !important;
background: transparent !important;
cursor: pointer;
list-style: none;
position: relative;
display: flex;
align-items: baseline;
gap: 14px;
}
.settings-container .settings-section summary::-webkit-details-marker { display: none; }
.settings-container .settings-section summary::before {
content: "5." counter(sr-section, decimal-leading-zero);
font-family: var(--mono);
font-style: normal;
font-size: 10px;
letter-spacing: 0.22em;
color: var(--copper);
flex-shrink: 0;
line-height: 2.2;
}
.settings-container .settings-section summary::after {
content: "↓";
margin-left: auto;
font-family: var(--sans);
font-size: 16px;
color: var(--ink-mute);
transition: transform 240ms var(--ease);
font-weight: 400;
}
.settings-container .settings-section[open] summary::after {
transform: rotate(180deg);
}
.settings-container .settings-section-content {
padding-top: 18px;
border-top: 1px solid var(--rule);
}
.settings-container .settings-section-description {
font-family: var(--serif);
font-style: italic;
font-size: 14px;
color: var(--ink-soft);
line-height: 1.55;
font-variation-settings: 'opsz' 18;
margin-bottom: 22px;
}
/* Settings tables */
.settings-container .scripts-table {
border: 1px solid var(--rule);
border-collapse: collapse;
width: 100%;
background: transparent;
font-family: var(--sans);
}
.settings-container .scripts-table thead {
background: var(--bg-paper);
}
.settings-container .scripts-table th {
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ink-mute);
padding: 12px 14px;
text-align: left;
border-bottom: 1px solid var(--rule-strong);
font-weight: 400;
}
.settings-container .scripts-table td {
padding: 14px;
border-bottom: 1px solid var(--rule);
color: var(--ink-soft);
font-size: 13px;
vertical-align: middle;
}
.settings-container .scripts-table tr:last-child td { border-bottom: 0; }
.settings-container .scripts-table tr:hover td { background: rgba(255,255,255,0.02); }
.settings-container .scripts-table .name-with-icon {
color: var(--ink);
font-family: var(--serif);
font-style: normal;
font-size: 15px;
font-variation-settings: 'opsz' 18;
}
.settings-container .scripts-table .path-cell,
.settings-container .scripts-table .mono {
font-family: var(--mono);
font-size: 11px;
color: var(--ink-mute);
}
/* Add card — editorial dashed border */
.settings-container .add-card {
background: transparent !important;
border: 1px dashed var(--rule-strong) !important;
border-radius: 0 !important;
padding: 16px !important;
margin-top: 16px;
transition: all 200ms var(--ease);
color: var(--ink-mute) !important;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.settings-container .add-card:hover {
border-color: var(--copper) !important;
border-style: solid !important;
color: var(--copper) !important;
background: rgba(var(--copper-rgb), 0.04) !important;
}
.settings-container .add-card-icon {
font-family: var(--serif);
font-weight: 300;
font-size: 24px;
color: inherit;
line-height: 1;
}
/* Audio device selector inside settings */
.settings-container .audio-device-selector {
display: flex;
flex-direction: column;
gap: 14px;
}
.settings-container .audio-device-selector label {
display: flex;
flex-direction: column;
gap: 8px;
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ink-mute);
}
.settings-container .audio-device-selector select {
background: transparent !important;
border: 1px solid var(--rule-strong) !important;
color: var(--ink) !important;
border-radius: 0 !important;
font-family: var(--sans);
font-size: 13px;
padding: 10px 12px !important;
cursor: pointer;
}
.settings-container .audio-device-status {
font-family: var(--mono);
font-size: 11px;
letter-spacing: 0.12em;
text-transform: uppercase;
padding: 8px 12px;
border: 1px solid var(--rule);
background: var(--bg-paper);
display: inline-flex;
align-items: center;
gap: 8px;
}
.settings-container .audio-device-status.active { color: var(--jade); border-color: var(--jade); }
.settings-container .audio-device-status.available { color: var(--amber); border-color: var(--amber); }
.settings-container .audio-device-status.unavailable { color: var(--rust); border-color: var(--rust); }
.settings-container .audio-device-status::before {
content: "●";
font-size: 10px;
color: currentColor;
}
/* Folder badges */
.settings-container .folder-disabled-badge,
.settings-container .folder-unavailable-badge {
background: transparent !important;
border: 1px solid currentColor !important;
border-radius: 0 !important;
font-family: var(--mono);
font-size: 9px;
letter-spacing: 0.18em;
text-transform: uppercase;
padding: 3px 8px !important;
color: var(--amber);
}
.settings-container .folder-unavailable-badge { color: var(--rust); }
/* Empty state */
.settings-container .empty-state {
color: var(--ink-mute);
font-family: var(--serif);
font-style: italic;
font-size: 14px;
font-variation-settings: 'opsz' 18;
padding: 40px 20px;
text-align: center;
}
.settings-container .empty-state-illustration {
color: var(--ink-faint);
}
.settings-container .empty-state-illustration svg {
width: 48px;
height: 48px;
margin-bottom: 14px;
fill: none;
stroke: currentColor;
stroke-width: 1.5;
}
/* Action buttons (edit/delete inside table rows) */
.settings-container .action-btn,
.settings-container .action-buttons button {
background: transparent !important;
border: 1px solid var(--rule-strong) !important;
color: var(--ink-soft) !important;
border-radius: 0 !important;
width: 30px;
height: 30px;
padding: 0 !important;
display: inline-flex !important;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 180ms var(--ease);
}
.settings-container .action-btn:hover,
.settings-container .action-buttons button:hover {
color: var(--copper) !important;
border-color: var(--copper) !important;
}
.settings-container .action-btn.danger:hover,
.settings-container .action-buttons button.danger:hover {
color: var(--rust) !important;
border-color: var(--rust) !important;
}
/* ─── DISPLAY tab ─────────────────────────────────────────── */
.display-container .display-monitors {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
gap: 22px;
}
/* Full monitor card — header (icon + name + power) + brightness control */
.display-container .display-monitor-card {
background: var(--bg-card) !important;
border: 1px solid var(--rule) !important;
border-radius: 0 !important;
padding: 24px 26px !important;
display: flex !important;
flex-direction: column;
gap: 22px;
transition: border-color 200ms var(--ease);
}
.display-container .display-monitor-card:hover {
border-color: var(--rule-strong) !important;
}
.display-container .display-monitor-header {
display: flex;
align-items: center;
gap: 14px;
}
.display-container .display-monitor-icon {
color: var(--copper);
flex-shrink: 0;
width: 22px !important;
height: 22px !important;
}
.display-container .display-monitor-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 4px;
}
.display-container .display-monitor-name {
font-family: var(--serif);
font-style: normal;
font-weight: 400;
font-size: 17px;
color: var(--ink);
font-variation-settings: 'opsz' 30;
letter-spacing: -0.005em;
line-height: 1.2;
display: inline-flex;
align-items: center;
gap: 10px;
/* Allow text to wrap so we don't ellipsis-truncate the model name */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.display-container .display-monitor-details {
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--ink-mute);
}
.display-container .display-primary-badge {
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--copper);
background: none;
border: 0;
padding: 0;
margin: 0;
line-height: 0;
vertical-align: middle;
filter: drop-shadow(0 0 4px var(--copper-glow));
}
.display-container .display-primary-badge svg {
display: block;
}
/* Power button — visible & state-coloured (on = jade, off = ink-mute) */
.display-container .display-power-btn {
background: transparent !important;
border: 1px solid var(--rule-strong) !important;
color: var(--ink-mute) !important;
border-radius: 50% !important;
width: 38px !important;
height: 38px !important;
padding: 0 !important;
display: inline-flex !important;
align-items: center;
justify-content: center;
cursor: pointer;
flex-shrink: 0;
transition: all 200ms var(--ease);
}
.display-container .display-power-btn svg {
width: 18px;
height: 18px;
fill: currentColor;
}
.display-container .display-power-btn.on {
color: var(--jade) !important;
border-color: var(--jade) !important;
box-shadow: 0 0 12px rgba(122, 178, 148, 0.25);
}
.display-container .display-power-btn.off {
color: var(--ink-faint) !important;
border-color: var(--rule-strong) !important;
}
.display-container .display-power-btn:hover {
color: var(--copper) !important;
border-color: var(--copper) !important;
background: rgba(var(--copper-rgb), 0.06) !important;
}
/* Brightness control row */
.display-container .display-brightness-control {
display: flex;
align-items: center;
gap: 14px;
padding-top: 16px;
border-top: 1px solid var(--rule);
}
.display-container .display-brightness-icon {
color: var(--ink-mute);
width: 18px !important;
height: 18px !important;
flex-shrink: 0;
}
.display-container .display-brightness-slider {
-webkit-appearance: none;
appearance: none;
flex: 1;
height: 2px !important;
background: var(--rule-strong);
border-radius: 0;
cursor: pointer;
padding: 0 !important;
margin: 0 !important;
border: 0 !important;
min-width: 0;
}
.display-container .display-brightness-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 14px;
height: 14px;
background: var(--copper);
border-radius: 50%;
box-shadow: 0 0 12px var(--copper-glow);
border: 0;
cursor: grab;
}
.display-container .display-brightness-slider::-moz-range-thumb {
width: 14px;
height: 14px;
background: var(--copper);
border-radius: 50%;
border: 0;
cursor: grab;
}
.display-container .display-brightness-slider:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.display-container .display-brightness-value {
font-family: var(--mono);
font-size: 12px;
color: var(--copper);
font-variant-numeric: tabular-nums;
font-weight: 500;
min-width: 44px;
text-align: right;
letter-spacing: 0.04em;
}
.display-container .display-monitors > .empty-state-illustration {
grid-column: 1 / -1;
text-align: center;
padding: 80px 24px;
color: var(--ink-mute);
font-family: var(--serif);
font-style: italic;
font-size: 16px;
font-variation-settings: 'opsz' 30;
border: 1px dashed var(--rule);
}
.display-container .display-monitors > .empty-state-illustration svg {
width: 56px;
height: 56px;
margin-bottom: 14px;
color: var(--ink-faint);
}
/* Monitor card (rendered by JS — accept any direct children as cards) */
.display-container .display-monitors > div:not(.empty-state-illustration) {
background: var(--bg-card);
border: 1px solid var(--rule);
padding: 22px 24px;
transition: border-color 200ms var(--ease);
border-radius: 0;
}
.display-container .display-monitors > div:not(.empty-state-illustration):hover {
border-color: var(--rule-strong);
}
/* Headings inside any monitor card */
.display-container .display-monitors h3 {
font-family: var(--serif);
font-style: normal;
font-weight: 400;
font-size: 18px;
color: var(--ink);
font-variation-settings: 'opsz' 30;
letter-spacing: -0.01em;
margin-bottom: 8px;
}
.display-container .display-monitors button {
background: transparent;
border: 1px solid var(--rule-strong);
color: var(--ink-soft);
border-radius: 0;
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
padding: 8px 14px;
cursor: pointer;
transition: all 180ms var(--ease);
}
.display-container .display-monitors button:hover {
color: var(--copper);
border-color: var(--copper);
}
/* ─── ICON SELECT POPUP & TRIGGER (used by settings dialogs) ─── */
.icon-select-trigger {
background: transparent !important;
border: 1px solid var(--rule-strong) !important;
border-radius: 0 !important;
color: var(--ink) !important;
font-family: var(--mono);
font-size: 12px;
padding: 8px 12px !important;
cursor: pointer;
transition: border-color 180ms var(--ease);
}
.icon-select-trigger:hover {
border-color: var(--copper) !important;
color: var(--copper) !important;
}
.icon-select-popup {
background: var(--bg-card) !important;
border: 1px solid var(--rule-strong) !important;
border-radius: 0 !important;
box-shadow: 0 14px 40px rgba(0,0,0,0.5) !important;
}
.icon-select-cell {
border-radius: 0 !important;
color: var(--ink-soft) !important;
font-family: var(--mono);
font-size: 10px;
}
.icon-select-cell:hover {
background: rgba(var(--copper-rgb), 0.06) !important;
color: var(--copper) !important;
}
/* Mobile: tighten padding for non-player tabs */
@media (max-width: 720px) {
.browser-container .browser-grid { gap: 22px 16px; }
.scripts-container .scripts-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
.scripts-container .script-btn,
.scripts-container .link-card { padding: 18px 12px !important; min-height: 100px; }
.settings-container .settings-section { padding: 20px !important; }
.settings-container .settings-section summary { font-size: 20px; }
.browser-container .breadcrumb { font-size: 15px; }
}
/* ════════════════════════════════════════════════════════════════════
STUDIO REFERENCE — POCKET EDITION
Dedicated mobile layout. Bottom-nav, single-column hero player,
compact chrome. Last in the cascade so it overrides all preceding
legacy + editorial rules.
════════════════════════════════════════════════════════════════════ */
@media (max-width: 720px) {
:root {
--pocket-nav-h: 62px;
--pocket-mini-h: 60px;
}
/* ─── Top folio strip ───────────────────────────────────
Collapse the two corner folios into a single hairline strip at
the very top. Right folio (volume / version) is redundant on
phones — tuck it behind the nav into the version label only.
*/
body > .folio.tl {
top: 0 !important;
left: 0 !important;
right: auto !important;
width: auto;
padding: 7px 12px 7px max(12px, env(safe-area-inset-left));
font-size: 8.5px !important;
letter-spacing: 0.18em !important;
background: linear-gradient(180deg, rgba(14, 13, 11, 0.7) 30%, transparent 100%);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
}
:root[data-theme="light"] body > .folio.tl {
background: linear-gradient(180deg, rgba(245, 241, 234, 0.85) 30%, transparent 100%);
}
/* Right folio (volume / version) is redundant on phones — hide it.
Version is still discoverable via Settings / About in the API. */
body > .folio.tr { display: none !important; }
/* ─── Container: leave room for top folio + bottom nav + mini-player ─── */
.container {
padding-top: 40px !important;
padding-bottom: calc(var(--pocket-nav-h) + 24px + env(safe-area-inset-bottom)) !important;
}
body.mini-player-visible .container {
padding-bottom: calc(var(--pocket-nav-h) + var(--pocket-mini-h) + 16px + env(safe-area-inset-bottom)) !important;
}
/* ─── Header: tight 2-row layout ───────────────────────── */
header {
grid-template-columns: 1fr !important;
gap: 8px !important;
margin-bottom: 18px !important;
padding-bottom: 14px !important;
}
header .brand-name { font-size: 22px !important; }
header .brand-sub {
font-size: 8px !important;
letter-spacing: 0.4em !important;
margin-top: 4px !important;
}
.header-toolbar {
grid-column: 1 !important;
justify-self: center !important;
flex-wrap: wrap;
justify-content: center;
gap: 0 !important;
}
.header-btn,
.header-btn-logout,
.header-link {
width: 34px !important;
height: 34px !important;
}
.header-toolbar-sep { display: none !important; }
/* Header links preserve their distinctive look but shrink */
.header-locale {
height: 34px !important;
padding: 0 8px !important;
font-size: 10px !important;
}
/* ─── Bottom Tab Bar (replaces top tab bar) ────────────── */
.tab-bar {
position: fixed !important;
bottom: 0 !important;
left: 0 !important;
right: 0 !important;
margin: 0 !important;
padding: 0 max(8px, env(safe-area-inset-left)) env(safe-area-inset-bottom) max(8px, env(safe-area-inset-right)) !important;
background: rgba(14, 13, 11, 0.94) !important;
-webkit-backdrop-filter: blur(20px) saturate(180%);
backdrop-filter: blur(20px) saturate(180%);
border: 0 !important;
border-top: 1px solid var(--rule-strong) !important;
z-index: 999;
overflow: visible !important;
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 0;
box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.35);
}
:root[data-theme="light"] .tab-bar {
background: rgba(245, 241, 234, 0.92) !important;
box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.10);
}
/* Hairline copper accent strip floating above active tab */
.tab-bar::before {
content: "";
position: absolute;
top: -1px;
left: 16px;
right: 16px;
height: 1px;
background: linear-gradient(90deg,
transparent 0%,
var(--copper) 30%,
var(--copper-hi) 50%,
var(--copper) 70%,
transparent 100%);
opacity: 0.35;
pointer-events: none;
}
/* Re-enable labels (legacy mobile rule hid all spans inside .tab-btn) */
.tab-btn span,
.tab-btn span:not(.tab-num) {
display: inline-block !important;
}
.tab-btn {
height: var(--pocket-nav-h) !important;
padding: 10px 4px !important;
display: flex !important;
flex-direction: column !important;
align-items: center !important;
justify-content: center !important;
gap: 4px !important;
font-family: var(--mono) !important;
font-style: normal !important;
font-size: 9.5px !important;
font-weight: 400 !important;
letter-spacing: 0.12em !important;
text-transform: uppercase;
color: var(--ink-faint) !important;
line-height: 1 !important;
}
.tab-btn .tab-num {
font-size: 11px !important;
letter-spacing: 0.2em !important;
color: var(--ink-mute) !important;
}
.tab-btn span:not(.tab-num) {
font-family: var(--mono) !important;
font-size: 9px !important;
letter-spacing: 0.1em !important;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tab-btn:hover {
color: var(--ink-soft) !important;
}
.tab-btn.active {
color: var(--copper) !important;
font-family: var(--mono) !important;
font-style: normal !important;
font-size: 9.5px !important;
}
.tab-btn.active .tab-num,
.tab-btn.active span:not(.tab-num) {
color: var(--copper) !important;
}
/* Active marker: copper bar at TOP of tab (hairline + glow) */
.tab-btn.active::after {
top: -1px !important;
bottom: auto !important;
left: 14% !important;
right: 14% !important;
height: 2px !important;
background: var(--copper) !important;
box-shadow:
0 0 12px var(--copper-glow),
0 0 28px var(--copper-glow) !important;
}
/* ─── Mini-player: float above bottom nav ─────────────── */
.mini-player {
bottom: calc(var(--pocket-nav-h) + env(safe-area-inset-bottom)) !important;
padding: 9px 14px !important;
gap: 12px !important;
border-top: 1px solid var(--rule);
border-bottom: 1px solid var(--rule);
background: rgba(33, 30, 24, 0.92) !important;
-webkit-backdrop-filter: blur(24px) saturate(160%);
backdrop-filter: blur(24px) saturate(160%);
box-shadow: 0 -4px 14px rgba(0, 0, 0, 0.25);
}
:root[data-theme="light"] .mini-player {
background: rgba(255, 252, 246, 0.94) !important;
}
.mini-player::before {
height: 1px !important;
}
.mini-album-art {
width: 38px !important;
height: 38px !important;
border-radius: 2px;
}
.mini-player-info {
min-width: 0 !important;
flex: 1;
gap: 10px !important;
}
.mini-track-title {
font-family: var(--serif);
font-style: italic;
font-weight: 400;
font-size: 13px !important;
color: var(--ink) !important;
font-variation-settings: 'opsz' 30;
}
.mini-artist {
font-family: var(--mono);
font-size: 9.5px !important;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--ink-mute) !important;
}
.mini-progress-container {
display: none !important; /* progress lives on the ::before line */
}
.mini-volume-container { display: none !important; }
.mini-controls { gap: 4px !important; }
.mini-nav-btn { display: none !important; }
.mini-control-btn {
width: 38px !important;
height: 38px !important;
background: transparent !important;
border: 1px solid var(--rule-strong) !important;
border-radius: 50%;
}
.mini-control-btn:hover {
border-color: var(--copper) !important;
background: rgba(var(--copper-rgb), 0.08) !important;
color: var(--copper) !important;
}
.mini-control-btn svg {
width: 16px !important;
height: 16px !important;
}
/* ─── Player tab — single column hero ─────────────────── */
.player-layout,
.now-playing {
grid-template-columns: 1fr !important;
gap: 28px !important;
margin-top: 4px !important;
}
/* Vinyl stage: centered, generous on tablet/large-phone widths */
.album-art-container.vinyl-stage {
max-width: 460px;
width: 92%;
margin: 0 auto !important;
}
/* Lighter sleeve grain on phones so the printed art reads
cleanly at small size. */
.vinyl-stage .sleeve-grain { opacity: 0.55; }
.vinyl-stage .vinyl-wrap .vinyl-label {
box-shadow:
inset 0 0 16px rgba(0, 0, 0, 0.5),
0 0 0 2px var(--bg-deep),
0 0 0 3px var(--copper-lo);
}
.vinyl-label-text { font-size: 9px; letter-spacing: 0.24em; }
/* Tonearm geometry inherits the desktop .vinyl-stage .tonearm
values — sleeve + disc proportions are identical on mobile,
so the needle still lands on the visible disc grooves. */
/* Track masthead text: centered, condensed cadence */
.track-masthead { text-align: center; }
.track-masthead > .kicker {
margin-bottom: 14px !important;
gap: 10px !important;
justify-content: center;
font-size: 9px !important;
letter-spacing: 0.36em !important;
}
.track-masthead > .kicker::before { flex-basis: 18px !important; }
.track-masthead > .kicker::after { flex-basis: 18px !important; flex-grow: 0 !important; }
.now-playing #track-title,
.player-layout #track-title {
font-size: clamp(28px, 8vw, 38px) !important;
line-height: 1 !important;
margin-bottom: 10px !important;
letter-spacing: -0.02em !important;
}
.now-playing #artist,
.player-layout #artist {
font-size: 17px !important;
margin-bottom: 4px !important;
}
.now-playing #album,
.player-layout #album {
font-size: 10px !important;
letter-spacing: 0.18em !important;
text-transform: uppercase;
color: var(--ink-mute) !important;
}
/* Meta-grid: 2 columns side-by-side with hairline divider */
.now-playing .meta-grid,
.meta-grid.meta-grid-2,
.player-layout .meta-grid {
grid-template-columns: 1fr 1fr !important;
margin-top: 22px !important;
}
.now-playing .meta-cell,
.meta-grid .meta-cell,
.player-layout .meta-cell {
border-right: 1px solid var(--rule) !important;
border-bottom: 0 !important;
padding: 12px 14px !important;
text-align: left;
min-width: 0;
}
.now-playing .meta-cell:nth-child(2n),
.meta-grid .meta-cell:nth-child(2n),
.player-layout .meta-cell:nth-child(2n) {
border-right: 0 !important;
padding-right: 0 !important;
}
.now-playing .meta-cell:first-child,
.meta-grid .meta-cell:first-child,
.player-layout .meta-cell:first-child {
padding-left: 0 !important;
}
.meta-cell .label,
.meta-cell .meta-label {
font-size: 8.5px !important;
letter-spacing: 0.28em !important;
}
.meta-cell .value,
.meta-cell .meta-value {
font-size: 14px !important;
}
/* Spectrum: shrink so it doesn't dominate */
.now-playing .spectrum,
.player-layout .spectrum {
height: 26px !important;
margin: 18px 0 4px 0 !important;
}
/* Transport: bigger touch targets, centered, no wrap */
.transport { margin-top: 14px !important; }
.progress-row {
gap: 12px !important;
margin-bottom: 22px !important;
}
.progress-row .timecode { font-size: 11px !important; }
.now-playing .controls,
.player-layout .controls {
justify-content: center !important;
gap: 28px !important;
flex-wrap: wrap !important;
row-gap: 0 !important;
}
.now-playing .controls .btn-trans,
.player-layout .controls .btn-trans {
width: 50px !important;
height: 50px !important;
}
.now-playing .controls .btn-trans.primary,
.player-layout .controls .btn-trans.primary {
width: 68px !important;
height: 68px !important;
}
.now-playing .controls .btn-trans svg { width: 20px !important; height: 20px !important; }
.now-playing .controls .btn-trans.primary svg { width: 26px !important; height: 26px !important; }
/* VU meter is decorative on a phone — hide it. Volume becomes
its own full-width hairline row beneath the controls.
flex-basis: 100% forces the cluster onto its own line in the
wrapping .controls flex container. */
.now-playing .vu-cluster,
.player-layout .vu-cluster {
display: flex !important;
flex-direction: row !important;
align-items: center;
justify-content: space-between;
gap: 14px !important;
flex-basis: 100% !important;
width: 100% !important;
margin: 18px 0 0 0 !important;
padding-top: 16px !important;
border-top: 1px solid var(--rule);
}
.now-playing .vu-meter,
.player-layout .vu-meter { display: none !important; }
.now-playing .vu-readout,
.player-layout .vu-readout {
flex-direction: row !important;
gap: 14px !important;
font-size: 9.5px !important;
letter-spacing: 0.12em !important;
white-space: nowrap;
}
.now-playing .vu-volume,
.player-layout .vu-volume {
flex: 1 1 auto !important;
min-width: 0 !important;
border-right: 0 !important;
margin-right: 0 !important;
padding-right: 0 !important;
justify-content: flex-end;
gap: 12px !important;
}
.now-playing .vu-volume #volume-slider,
.player-layout .vu-volume #volume-slider {
flex: 1 1 auto !important;
width: 100% !important;
max-width: none !important;
height: 3px !important;
}
.now-playing .vu-volume #volume-slider::-webkit-slider-thumb,
.player-layout .vu-volume #volume-slider::-webkit-slider-thumb {
width: 14px !important;
height: 14px !important;
}
.now-playing .vu-volume .mute-btn,
.player-layout .vu-volume .mute-btn {
width: 36px !important;
height: 36px !important;
}
.now-playing .vu-volume .mute-btn svg,
.player-layout .vu-volume .mute-btn svg {
width: 16px !important;
height: 16px !important;
}
/* ─── Library / Browser polish ────────────────────────── */
.browser-container .browser-toolbar {
flex-wrap: wrap !important;
gap: 10px !important;
}
.browser-container .browser-toolbar-right {
margin-left: 0 !important;
}
.browser-search-wrapper {
flex-basis: 100% !important;
order: 10;
max-width: none !important;
}
/* Pagination: column stack, big tap targets */
.pagination {
flex-direction: column !important;
align-items: stretch !important;
gap: 10px !important;
padding: 18px 0 !important;
border-top: 1px solid var(--rule);
margin-top: 20px;
}
.pagination button {
width: 100% !important;
min-height: 42px;
font-family: var(--mono);
font-size: 11px;
letter-spacing: 0.16em;
text-transform: uppercase;
}
.pagination .pagination-center {
justify-content: center;
order: -1;
}
.pagination-showing {
text-align: center !important;
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.12em;
color: var(--ink-faint);
}
/* ─── Settings: card-like rows for tables ─────────────── */
.settings-container .scripts-table thead { display: none; }
.settings-container .scripts-table,
.settings-container .scripts-table tbody,
.settings-container .scripts-table tr,
.settings-container .scripts-table td {
display: block !important;
width: 100% !important;
}
.settings-container .scripts-table tr {
border: 1px solid var(--rule) !important;
padding: 14px !important;
margin-bottom: 12px !important;
background: rgba(33, 30, 24, 0.32);
}
:root[data-theme="light"] .settings-container .scripts-table tr {
background: rgba(255, 252, 246, 0.6);
}
.settings-container .scripts-table td {
padding: 4px 0 !important;
border: 0 !important;
font-family: var(--sans);
font-size: 13px;
}
.settings-container .scripts-table td:not(:last-child) {
border-bottom: 1px dashed var(--rule) !important;
padding-bottom: 8px !important;
margin-bottom: 8px !important;
}
/* Empty-state row keeps its own block layout */
.settings-container .scripts-table tr:has(.empty-state),
.settings-container .scripts-table tr td.empty-state {
background: transparent;
border: 0 !important;
padding: 0 !important;
}
/* ─── Footer: ultra-compact ───────────────────────────── */
footer {
font-size: 10px !important;
padding: 12px 12px !important;
letter-spacing: 0.04em;
}
footer .separator { margin: 0 4px !important; }
/* Auth modal: full-bleed feel on phones */
.auth-modal {
width: 92% !important;
padding: 28px 22px !important;
}
}
/* ─── Tighter breakpoint for compact phones (<= 420px) ─── */
@media (max-width: 420px) {
.tab-btn span:not(.tab-num) { font-size: 8px !important; }
.tab-btn .tab-num { font-size: 10px !important; }
/* Fewest icons in header — surface only critical chrome */
header .brand-sub { display: none !important; }
header .brand-name { font-size: 20px !important; }
.header-btn[title="API Documentation"],
.header-btn[title="Dynamic background"] {
display: none !important;
}
/* Vinyl stage tighter still on small phones; tonearm inherits
desktop geometry which is proportional to the stage. */
.album-art-container.vinyl-stage { width: 84%; }
.now-playing #track-title,
.player-layout #track-title {
font-size: clamp(24px, 8.5vw, 32px) !important;
}
/* Transport: keep readable but shrink the gap */
.now-playing .controls,
.player-layout .controls { gap: 22px !important; }
.now-playing .controls .btn-trans,
.player-layout .controls .btn-trans { width: 46px !important; height: 46px !important; }
.now-playing .controls .btn-trans.primary,
.player-layout .controls .btn-trans.primary { width: 60px !important; height: 60px !important; }
/* Folio strip text shrinks */
body > .folio.tl,
body > .folio.tr { font-size: 7.5px !important; letter-spacing: 0.14em !important; }
/* Mini-player: shave one icon */
.mini-player { padding: 8px 12px !important; gap: 10px !important; }
.mini-album-art { width: 34px !important; height: 34px !important; }
.mini-control-btn { width: 36px !important; height: 36px !important; }
}
/* ════════════════════════════════════════════════════════════════
FULLSCREEN PLAYER — "Listening Room"
Activated via body.is-fullscreen-player. Uses position:fixed to
take over the viewport (works without native Fullscreen API) and
re-projects the existing player markup at theater scale: oversized
Fraunces italic title, ambient album-art bloom painting the room,
auto-hiding chrome, dramatic vinyl-stage focus.
════════════════════════════════════════════════════════════════ */
/* Fullscreen-only chrome (top strip with edition mark + exit button)
and ambient bloom layer are inert outside fullscreen mode. */
.fs-chrome,
.fs-bloom {
display: none;
pointer-events: none;
}
/* While in fullscreen: take over the viewport, hide everything outside
the player container, and project the player onto a dark stage. */
body.is-fullscreen-player {
overflow: hidden;
}
/* Hide the world */
body.is-fullscreen-player > .container > header,
body.is-fullscreen-player > .container > .tab-bar,
body.is-fullscreen-player > .container > .update-banner,
body.is-fullscreen-player > .container > .connection-banner,
body.is-fullscreen-player > .container > [data-tab-content]:not(#panel-player),
body.is-fullscreen-player > .folio,
body.is-fullscreen-player > #mini-player {
display: none !important;
}
/* Promote the player container to fixed-overlay rank */
body.is-fullscreen-player .player-container {
position: fixed;
inset: 0;
z-index: 9000;
margin: 0;
padding: 0;
background:
radial-gradient(ellipse at 30% 35%, rgba(var(--copper-rgb), 0.05) 0%, transparent 55%),
radial-gradient(ellipse at center, var(--bg-paper) 0%, var(--bg-deep) 75%);
display: grid;
place-items: stretch;
overflow: hidden;
box-shadow: none !important;
border: 0 !important;
/* Soft entry fade — content layers stagger in via .now-playing animations below */
animation: fs-fade-in 320ms var(--ease-out) both;
}
@keyframes fs-fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
/* ─── Ambient bloom: paint the room in the album's color ─────── */
body.is-fullscreen-player .fs-bloom {
display: block;
position: absolute;
inset: -8%;
z-index: 0;
pointer-events: none;
overflow: hidden;
opacity: 0;
animation: fs-bloom-in 1400ms var(--ease-out) 120ms forwards;
}
@keyframes fs-bloom-in {
from { opacity: 0; transform: scale(1.08); }
to { opacity: 0.42; transform: scale(1); }
}
:root[data-theme="light"] body.is-fullscreen-player .fs-bloom {
animation-name: fs-bloom-in-light;
}
@keyframes fs-bloom-in-light {
from { opacity: 0; transform: scale(1.08); }
to { opacity: 0.22; transform: scale(1); }
}
/* Performance trick (matches the vinyl-stage glow): render the bloom
image at 20% of the viewport and stretch it via scale(~6). Blur runs
over a 25× smaller area, so a track-switch repaint of the bloom
collapses from O(viewport-pixels × 110² ) to O(viewport/25 × 18²). */
body.is-fullscreen-player .fs-bloom #fs-bloom-art {
position: absolute;
top: 50%;
left: 50%;
width: 20%;
height: 20%;
object-fit: cover;
filter: blur(18px) saturate(1.6);
transform: translate(-50%, -50%) scale(5.9);
transform-origin: center;
animation: fs-bloom-drift 28s ease-in-out infinite alternate;
will-change: transform;
}
@keyframes fs-bloom-drift {
from { transform: translate(-50%, -50%) scale(5.9) translate3d(-1.5%, -1%, 0); }
to { transform: translate(-50%, -50%) scale(6.1) translate3d(2%, 1.5%, 0); }
}
/* Subtle paper-grain veil over the bloom — keeps it from looking flat. */
body.is-fullscreen-player .player-container::before {
content: "";
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none;
background-image:
radial-gradient(circle at 20% 80%, rgba(0, 0, 0, 0.35) 0%, transparent 40%),
radial-gradient(circle at 80% 20%, rgba(0, 0, 0, 0.30) 0%, transparent 45%);
mix-blend-mode: multiply;
}
/* Vignette + top/bottom edge darkening — frames the listening room */
body.is-fullscreen-player .player-container::after {
content: "";
position: absolute;
inset: 0;
z-index: 2;
pointer-events: none;
background:
linear-gradient(to bottom,
rgba(0, 0, 0, 0.45) 0%,
transparent 12%,
transparent 88%,
rgba(0, 0, 0, 0.55) 100%),
radial-gradient(ellipse at center, transparent 50%, rgba(0, 0, 0, 0.55) 100%);
}
:root[data-theme="light"] body.is-fullscreen-player .player-container::after {
background:
linear-gradient(to bottom,
rgba(0, 0, 0, 0.10) 0%,
transparent 14%,
transparent 86%,
rgba(0, 0, 0, 0.15) 100%),
radial-gradient(ellipse at center, transparent 55%, rgba(0, 0, 0, 0.12) 100%);
}
/* ─── Floating chrome (edition mark + exit) — auto-hides on idle ─ */
body.is-fullscreen-player .fs-chrome {
display: flex;
position: absolute;
top: 0; left: 0; right: 0;
z-index: 50;
align-items: center;
justify-content: space-between;
padding: 22px 36px;
pointer-events: auto;
opacity: 1;
transition: opacity 320ms var(--ease), transform 320ms var(--ease);
transform: translateY(0);
}
body.is-fullscreen-player.fs-chrome-hidden .fs-chrome {
opacity: 0;
transform: translateY(-12px);
pointer-events: none;
}
body.is-fullscreen-player.fs-chrome-hidden { cursor: none; }
.fs-chrome-mark {
display: flex;
align-items: baseline;
gap: 10px;
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.28em;
text-transform: uppercase;
color: var(--ink-mute);
}
.fs-chrome-edition {
color: var(--copper);
font-weight: 500;
}
.fs-chrome-sep { color: var(--ink-faint); }
.fs-chrome-exit {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 8px 14px 8px 12px;
background: rgba(14, 13, 11, 0.55);
border: 1px solid var(--rule-strong);
color: var(--ink-soft);
font-family: var(--mono);
font-size: 11px;
letter-spacing: 0.18em;
text-transform: uppercase;
cursor: pointer;
border-radius: 0;
backdrop-filter: blur(8px) saturate(1.2);
transition: color 160ms var(--ease), border-color 160ms var(--ease), background 160ms var(--ease);
}
.fs-chrome-exit:hover {
color: var(--ink);
border-color: var(--copper);
background: rgba(14, 13, 11, 0.75);
}
.fs-chrome-exit svg { color: var(--copper); }
.fs-chrome-kbd {
font-family: var(--mono);
font-size: 9px;
letter-spacing: 0.16em;
padding: 2px 6px;
border: 1px solid var(--rule-strong);
color: var(--ink-mute);
background: transparent;
border-radius: 0;
}
:root[data-theme="light"] .fs-chrome-exit {
background: rgba(255, 255, 255, 0.7);
}
/* ─── Stage: massive vinyl + masthead at theater scale ─────────── */
body.is-fullscreen-player .now-playing,
body.is-fullscreen-player .player-layout {
position: relative;
z-index: 5;
grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
gap: clamp(40px, 6vw, 96px);
margin: 0 !important;
padding: clamp(80px, 10vh, 140px) clamp(40px, 6vw, 96px) clamp(40px, 6vh, 80px);
align-items: center;
width: 100%;
height: 100%;
box-sizing: border-box;
align-content: center;
}
/* Vinyl stage — pin to comfortable theater size */
body.is-fullscreen-player .album-art-container.vinyl-stage {
width: min(72vh, 100%);
max-width: 720px;
margin-inline: auto;
aspect-ratio: 1;
background:
radial-gradient(ellipse at center,
rgba(26, 22, 17, 0.85) 0%,
transparent 70%);
filter: drop-shadow(0 30px 80px rgba(0, 0, 0, 0.55));
animation: fs-stage-rise 600ms var(--ease-out) both;
}
@keyframes fs-stage-rise {
from { opacity: 0; transform: scale(0.94) translateY(8px); }
to { opacity: 1; transform: scale(1) translateY(0); }
}
:root[data-theme="light"] body.is-fullscreen-player .album-art-container.vinyl-stage {
background:
radial-gradient(ellipse at center,
rgba(255, 255, 255, 0.65) 0%,
transparent 75%);
filter: drop-shadow(0 30px 60px rgba(0, 0, 0, 0.18));
}
/* ─── Masthead: editorial centerfold ───────────────────────────── */
body.is-fullscreen-player .track-masthead {
position: relative;
max-width: 720px;
padding-right: clamp(0px, 2vw, 24px);
/* Stagger ladder: each child slides up with a small delay */
--fs-stagger: 80ms;
}
body.is-fullscreen-player .track-masthead > * {
animation: fs-rise 700ms var(--ease-out) both;
}
body.is-fullscreen-player .track-masthead > .kicker { animation-delay: calc(var(--fs-stagger) * 1); }
body.is-fullscreen-player .track-masthead > .track-title { animation-delay: calc(var(--fs-stagger) * 2); }
body.is-fullscreen-player .track-masthead > .track-byline{ animation-delay: calc(var(--fs-stagger) * 3); }
body.is-fullscreen-player .track-masthead > .track-album { animation-delay: calc(var(--fs-stagger) * 4); }
body.is-fullscreen-player .track-masthead > .meta-grid { animation-delay: calc(var(--fs-stagger) * 5); }
body.is-fullscreen-player .track-masthead > .spectrum { animation-delay: calc(var(--fs-stagger) * 6); }
body.is-fullscreen-player .track-masthead > .transport { animation-delay: calc(var(--fs-stagger) * 7); }
@keyframes fs-rise {
from { opacity: 0; transform: translateY(14px); }
to { opacity: 1; transform: translateY(0); }
}
/* Kicker — editorial small-caps with hairline rules */
body.is-fullscreen-player .now-playing .kicker {
margin-bottom: clamp(18px, 2.4vh, 32px);
font-size: 11px;
letter-spacing: 0.36em;
}
/* The headline — Fraunces italic, oversized, centerfold scale */
body.is-fullscreen-player .now-playing .track-title {
font-family: var(--serif);
font-style: italic;
font-weight: 400;
font-size: clamp(48px, 6.4vw, 112px);
line-height: 0.95;
letter-spacing: -0.015em;
margin: 0 0 clamp(14px, 1.8vh, 22px);
color: var(--ink);
text-wrap: balance;
}
body.is-fullscreen-player .now-playing .track-title em {
font-style: italic;
}
/* Artist + album — refined editorial tier */
body.is-fullscreen-player .now-playing .track-byline {
font-family: var(--sans);
font-weight: 500;
font-size: clamp(16px, 1.4vw, 22px);
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--ink-soft);
margin-bottom: 8px;
}
body.is-fullscreen-player .now-playing .track-album {
font-family: var(--serif);
font-style: italic;
font-weight: 300;
font-size: clamp(15px, 1.2vw, 20px);
color: var(--ink-mute);
margin-bottom: clamp(20px, 2.4vh, 36px);
}
/* Meta grid — promote to a console-readout strip */
body.is-fullscreen-player .now-playing .meta-grid {
grid-template-columns: 1fr 1fr;
gap: clamp(20px, 2vw, 36px);
padding-block: clamp(14px, 1.8vh, 22px);
border-top: 1px solid var(--rule);
border-bottom: 1px solid var(--rule);
margin-bottom: clamp(20px, 2.4vh, 32px);
}
body.is-fullscreen-player .now-playing .meta-cell .label {
font-family: var(--mono);
font-size: 9px;
letter-spacing: 0.32em;
color: var(--copper);
}
body.is-fullscreen-player .now-playing .meta-cell .value {
font-family: var(--mono);
font-size: clamp(13px, 1vw, 16px);
letter-spacing: 0.08em;
color: var(--ink);
}
/* Spectrum — taller, full-width amplitude bars */
body.is-fullscreen-player .now-playing .spectrum {
height: clamp(60px, 9vh, 120px);
margin-bottom: clamp(22px, 2.6vh, 36px);
opacity: 0.95;
}
/* Transport — give it room to breathe */
body.is-fullscreen-player .now-playing .transport {
gap: clamp(20px, 2.6vh, 32px);
}
/* Progress row: huge mono timecodes */
body.is-fullscreen-player .now-playing .progress-row {
gap: clamp(16px, 1.8vw, 28px);
}
body.is-fullscreen-player .now-playing .progress-row .timecode,
body.is-fullscreen-player .now-playing .timecode {
font-family: var(--mono);
font-size: clamp(20px, 2.2vw, 32px);
font-weight: 300;
letter-spacing: 0.04em;
min-width: 6ch;
}
body.is-fullscreen-player .now-playing .progress-track,
body.is-fullscreen-player .now-playing .progress-bar {
height: 4px;
}
body.is-fullscreen-player .now-playing .progress-fill {
box-shadow: 0 0 18px var(--copper-glow);
}
/* Controls — large, generous spacing */
body.is-fullscreen-player .now-playing .controls {
gap: clamp(14px, 1.6vw, 22px);
flex-wrap: wrap;
justify-content: flex-start;
align-items: center;
}
body.is-fullscreen-player .now-playing .btn-trans {
width: clamp(48px, 4.5vw, 64px) !important;
height: clamp(48px, 4.5vw, 64px) !important;
}
body.is-fullscreen-player .now-playing .btn-trans.primary {
width: clamp(64px, 6vw, 88px) !important;
height: clamp(64px, 6vw, 88px) !important;
}
body.is-fullscreen-player .now-playing .btn-trans svg {
width: clamp(20px, 1.8vw, 26px);
height: clamp(20px, 1.8vw, 26px);
}
body.is-fullscreen-player .now-playing .btn-trans.primary svg {
width: clamp(26px, 2.4vw, 34px);
height: clamp(26px, 2.4vw, 34px);
}
/* VU cluster — slightly enlarged, still right-anchored */
body.is-fullscreen-player .now-playing .vu-cluster {
margin-left: auto;
gap: clamp(10px, 1vw, 16px);
}
body.is-fullscreen-player .now-playing .vu-meter {
width: clamp(70px, 6.5vw, 96px);
height: clamp(36px, 3.6vh, 50px);
}
/* Header button: active state when fullscreen is on */
.header-btn#fullscreenToggle.active {
background: var(--bg-card);
color: var(--copper);
border-color: var(--copper);
}
/* ─── Mobile / portrait variant: vertical theater ──────────────── */
@media (max-width: 900px), (orientation: portrait) {
body.is-fullscreen-player .now-playing,
body.is-fullscreen-player .player-layout {
grid-template-columns: 1fr;
grid-template-rows: minmax(0, 1fr) minmax(0, auto);
gap: clamp(20px, 4vh, 40px);
padding: clamp(60px, 8vh, 96px) clamp(20px, 5vw, 40px) clamp(24px, 4vh, 48px);
align-content: start;
}
body.is-fullscreen-player .album-art-container.vinyl-stage {
width: min(70vw, 56vh);
max-width: 480px;
}
body.is-fullscreen-player .now-playing .track-title {
font-size: clamp(36px, 8.5vw, 64px);
}
body.is-fullscreen-player .now-playing .controls {
justify-content: center;
}
body.is-fullscreen-player .now-playing .vu-cluster {
margin-left: 0;
margin-right: auto;
}
body.is-fullscreen-player .fs-chrome {
padding: 14px 18px;
}
}
/* Reduced motion: kill the entry animations and bloom drift */
@media (prefers-reduced-motion: reduce) {
body.is-fullscreen-player .player-container,
body.is-fullscreen-player .fs-bloom,
body.is-fullscreen-player .album-art-container.vinyl-stage,
body.is-fullscreen-player .track-masthead > * {
animation: none !important;
}
body.is-fullscreen-player .fs-bloom { opacity: 0.42; }
body.is-fullscreen-player .fs-bloom #fs-bloom-art { animation: none !important; }
:root[data-theme="light"] body.is-fullscreen-player .fs-bloom { opacity: 0.22; }
}