/* ============================================================ 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; } } /* ======================================== 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)); } .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 ────────────────────────────────────── */ /* The footer was removed in favour of an About dialog, so the page bottom is now whatever the active tab content ends with. A 64px bottom pad left a visible dead band under the player view; 24px keeps a breath of breathing room without painting an empty page. */ .container { max-width: 1280px; padding: 56px 48px 24px; } @media (max-width: 720px) { .container { padding: 48px 18px 24px; } } /* ─── 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 721–1240 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; /* Subtle counterclockwise tilt — sleeve rests on the disc as if casually placed, breaking up the otherwise rigid rectilinear grid. The shadow above carries the same diagonal so the lean reads as physical rather than transformed. */ transform: rotate(-2deg); transform-origin: 50% 60%; } :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); } /* ═══════════════════════════════════════════════════════════════ ABOUT DIALOG (colophon) ═══════════════════════════════════════════════════════════════ */ .about-dialog { max-width: 520px; } .about-credit { font-family: var(--serif); font-style: italic; font-size: 17px; line-height: 1.5; color: var(--ink-soft); margin: 0 0 22px; font-variation-settings: 'opsz' 30; } .about-credit strong { font-style: italic; font-weight: 400; font-size: 22px; color: var(--ink); letter-spacing: 0.01em; font-variation-settings: 'opsz' 60; display: block; margin-top: 2px; } .about-links { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule); } .about-links li { display: flex; flex-direction: column; gap: 4px; padding: 14px 0; border-bottom: 1px solid var(--rule); } .about-links-label { font-family: var(--mono); font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-faint); } .about-links a { font-family: var(--mono); font-size: 12px; color: var(--copper); text-decoration: none; border-bottom: 1px solid transparent; transition: border-color 200ms var(--ease); word-break: break-all; } .about-links a:hover { border-bottom-color: var(--copper); } /* ═══════════════════════════════════════════════════════════════ 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; } } /* ════════════════════════════════════════════════════════════════ 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