diff --git a/media_server/static/css/styles.css b/media_server/static/css/styles.css index fa479d7..858b2f0 100644 --- a/media_server/static/css/styles.css +++ b/media_server/static/css/styles.css @@ -4112,52 +4112,121 @@ body.mini-player-visible footer { /* ─── Container & header ────────────────────────────────────── */ .container { max-width: 1280px; - padding: 28px 48px 140px; + padding: 56px 48px 140px; } @media (max-width: 720px) { - .container { padding: 18px 18px 140px; } + .container { padding: 48px 18px 140px; } } -header { - padding: 0 0 22px 0; - border-bottom: 1px solid var(--rule-strong); - margin-bottom: 18px; - position: relative; -} - -/* Folio mark on left side of header */ -header > div:first-child { +/* ─── Folio marks (page corners, all tabs) ────────────────── */ +body > .folio { + position: fixed; + top: 16px; + z-index: 50; font-family: var(--mono); - font-size: 11px; - letter-spacing: 0.14em; + font-size: 10px; + letter-spacing: 0.2em; text-transform: uppercase; - color: var(--ink-mute); + 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; } } -.status-dot { - width: 8px; - height: 8px; +/* 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 8px rgba(122, 178, 148, 0); } + 50% { box-shadow: 0 0 0 6px rgba(122, 178, 148, 0); } } -.version-label { - font-family: var(--mono); +/* 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; - color: var(--ink-mute); - border: 1px solid var(--rule-strong); - border-radius: 0; - padding: 2px 10px; + 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; +} + +@media (max-width: 720px) { + .header-toolbar { grid-column: 1; } } .header-toolbar { @@ -4478,15 +4547,22 @@ header > div:first-child { @keyframes sr-vinyl-spin { to { transform: rotate(360deg); } } +/* Spectrogram canvas hidden by default — visualizer toggle reveals it */ .vinyl-stage .spectrogram-canvas { position: absolute; - bottom: -52px; - left: 0; - right: 0; - width: 100%; - height: 44px; + bottom: -56px; + left: 7%; + right: 7%; + width: 86%; + height: 38px; border-radius: 0; - opacity: 0.7; + 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; } /* ─── Player details (right column / masthead) ──────────────── */ @@ -4616,17 +4692,20 @@ header > div:first-child { /* Hide the legacy .playback-state container (its data is now in meta-grid) */ .track-info > .playback-state { display: none; } -/* Spectrum decorative bars */ +/* Spectrum decorative bars (centered, compact) */ .spectrum { display: flex; align-items: flex-end; + justify-content: center; gap: 3px; - height: 38px; - margin-top: 28px; - margin-bottom: 8px; + height: 32px; + margin: 28px auto 8px; + max-width: 360px; } .spectrum span { - flex: 1; + display: block; + width: 3px; + flex: 0 0 3px; background: linear-gradient(to top, var(--copper-lo), var(--copper-hi)); opacity: 0.85; border-radius: 99px 99px 0 0; @@ -4747,32 +4826,94 @@ header > div:first-child { box-shadow: 0 0 8px var(--copper-glow); } -/* Volume container nested inside vu-cluster — compact stacked controls */ +/* Volume container nested inside vu-cluster — strip legacy box & make compact */ .vu-cluster .volume-container { - margin-top: 0; - padding-top: 0; + background: transparent; + border: 0; border-top: 0; + border-radius: 0; + padding: 0; + margin: 0; + display: flex; flex-direction: column; - gap: 8px; + gap: 6px; align-items: stretch; - min-width: 140px; + min-width: 0; + width: 110px; + box-shadow: none; } .vu-cluster .volume-container > .mute-btn { - align-self: flex-start; + 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) ─────────────────────────── */ diff --git a/media_server/static/index.html b/media_server/static/index.html index 85984cb..74fc358 100644 --- a/media_server/static/index.html +++ b/media_server/static/index.html @@ -75,11 +75,15 @@ + + Connected · Local 8765 + Vol. IStudio Reference · +
-
- - +
+ Media Server + Studio Reference Edition
@@ -153,9 +157,6 @@
- Now Spinning · v— - Vol. I — Studio Reference -
@@ -280,15 +281,12 @@
- +
Modes
- diff --git a/media_server/static/js/app.js b/media_server/static/js/app.js index ecf4c76..410e5ed 100644 --- a/media_server/static/js/app.js +++ b/media_server/static/js/app.js @@ -163,8 +163,8 @@ window.addEventListener('DOMContentLoaded', async () => { navigator.serviceWorker.register('/sw.js').catch(() => {}); } - // Initialize vinyl mode - applyVinylMode(); + // Vinyl is now structural / always-on via CSS — no init call needed. + // applyVinylMode(); // Initialize audio visualizer checkVisualizerAvailability().then(() => { diff --git a/media_server/static/js/player.js b/media_server/static/js/player.js index 7b0e9f6..99a0694 100644 --- a/media_server/static/js/player.js +++ b/media_server/static/js/player.js @@ -19,6 +19,8 @@ import { IconSelect } from './icon-select.js'; export let activeTab = 'player'; export function setMiniPlayerVisible(visible) { + // On any non-player tab the mini player must stay visible regardless of scroll. + if (activeTab !== 'player') visible = true; const miniPlayer = document.getElementById('mini-player'); if (visible) { miniPlayer.classList.remove('hidden'); diff --git a/media_server/static/locales/en.json b/media_server/static/locales/en.json index b13d667..6700727 100644 --- a/media_server/static/locales/en.json +++ b/media_server/static/locales/en.json @@ -21,8 +21,10 @@ "player.no_media": "No media playing", "player.kicker": "Now Playing", "player.modes": "Modes", - "player.folio_left": "Now Spinning", - "player.folio_right": "Vol. I — Studio Reference", + "header.connected": "Connected", + "header.volume": "Vol. I", + "header.edition": "Studio Reference", + "header.edition_sub": "Studio Reference Edition", "meta.state": "State", "meta.source": "Source", "meta.elapsed": "Elapsed", diff --git a/media_server/static/locales/ru.json b/media_server/static/locales/ru.json index 624f4c2..1dc7825 100644 --- a/media_server/static/locales/ru.json +++ b/media_server/static/locales/ru.json @@ -21,8 +21,10 @@ "player.no_media": "Медиа не воспроизводится", "player.kicker": "Сейчас играет", "player.modes": "Режимы", - "player.folio_left": "Сейчас играет", - "player.folio_right": "Том I — Studio Reference", + "header.connected": "Подключено", + "header.volume": "Том I", + "header.edition": "Studio Reference", + "header.edition_sub": "Studio Reference Edition", "meta.state": "Состояние", "meta.source": "Источник", "meta.elapsed": "Прошло",