14e9f2294e
Restructures the player tab DOM to actually look like the editorial mockup, not just inherit new fonts. The previous commit only swapped tokens & typography on the legacy Spotify-clone layout. DOM additions (all preserve existing JS-touched IDs): - Vinyl stage: rotating vinyl wrapping the existing #album-art as a circular center label; spins only when state=playing via CSS hook - SVG tonearm: pivots in/out based on data-playstate - Kicker line: copper italic mono header above the track title - Editorial 4-cell metadata grid: State / Source / Elapsed / Length - Decorative spectrum bars (30, CSS-only animation, paused when idle) - VU meter cluster: needle visual driven by volume %, alongside the preserved volume slider for a11y - Folio marks: top-left and top-right of the player container JS hooks (small, additive): - updatePlaybackState now sets :root[data-playstate] for CSS - progress tick mirrors timecode into meta-grid cells - volume update rotates the VU needle - folio-version mirrors the version label i18n: - new keys: player.kicker, player.modes, player.folio_*, meta.* - added to both en.json and ru.json Restored: media_server/static/redesign-mockup.html (Studio Reference visual reference; deleting it in the prior commit was a mistake).
1672 lines
52 KiB
HTML
1672 lines
52 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Media Server — Studio Reference (Redesign Mockup)</title>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300..900;1,9..144,300..900&family=Geist:wght@300..700&family=Geist+Mono:wght@300..600&display=swap" rel="stylesheet">
|
||
<style>
|
||
/* ────────────────────────────────────────────────────────────
|
||
STUDIO REFERENCE — Editorial hi-fi mockup
|
||
Palette: warm charcoal · paper cream · copper accent
|
||
──────────────────────────────────────────────────────────── */
|
||
:root {
|
||
--bg-deep: #0E0D0B;
|
||
--bg-paper: #18150F;
|
||
--bg-card: #1F1B14;
|
||
--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-glow: rgba(224, 128, 56, 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', 'Times New Roman', serif;
|
||
--sans: 'Geist', 'Helvetica Neue', system-ui, sans-serif;
|
||
--mono: 'Geist Mono', 'JetBrains Mono', ui-monospace, monospace;
|
||
|
||
--ease: cubic-bezier(.2, .7, .2, 1);
|
||
--ease-out: cubic-bezier(.16, 1, .3, 1);
|
||
}
|
||
|
||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
html, body {
|
||
background: var(--bg-deep);
|
||
color: var(--ink);
|
||
font-family: var(--sans);
|
||
font-feature-settings: 'ss01', 'cv11';
|
||
-webkit-font-smoothing: antialiased;
|
||
text-rendering: optimizeLegibility;
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
/* Film-grain overlay — adds analog warmth */
|
||
body::before {
|
||
content: "";
|
||
position: fixed; inset: 0;
|
||
pointer-events: none;
|
||
z-index: 9999;
|
||
opacity: 0.06;
|
||
mix-blend-mode: overlay;
|
||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix 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'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
|
||
}
|
||
|
||
/* Vignette */
|
||
body::after {
|
||
content: "";
|
||
position: fixed; inset: 0;
|
||
pointer-events: none;
|
||
z-index: 9998;
|
||
background: radial-gradient(ellipse at center, transparent 50%, rgba(0,0,0,0.45) 100%);
|
||
}
|
||
|
||
/* Selection */
|
||
::selection { background: var(--copper); color: var(--bg-deep); }
|
||
|
||
/* Scrollbar */
|
||
::-webkit-scrollbar { width: 10px; height: 10px; }
|
||
::-webkit-scrollbar-track { background: transparent; }
|
||
::-webkit-scrollbar-thumb { background: var(--ink-ghost); border-radius: 0; }
|
||
::-webkit-scrollbar-thumb:hover { background: var(--ink-faint); }
|
||
|
||
/* ─── Layout shell ─────────────────────────────────────────── */
|
||
.shell {
|
||
max-width: 1380px;
|
||
margin: 0 auto;
|
||
padding: 32px 56px 160px;
|
||
position: relative;
|
||
}
|
||
|
||
@media (max-width: 900px) {
|
||
.shell { padding: 20px 20px 160px; }
|
||
}
|
||
|
||
/* ─── Masthead ─────────────────────────────────────────────── */
|
||
.masthead {
|
||
display: grid;
|
||
grid-template-columns: 1fr auto 1fr;
|
||
align-items: center;
|
||
padding-bottom: 22px;
|
||
border-bottom: 1px solid var(--rule-strong);
|
||
position: relative;
|
||
animation: fadeUp 0.7s var(--ease-out) both;
|
||
}
|
||
|
||
.masthead .left {
|
||
display: flex; align-items: center; gap: 18px;
|
||
font-family: var(--mono);
|
||
font-size: 11px;
|
||
letter-spacing: 0.14em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-mute);
|
||
}
|
||
|
||
.dot-live {
|
||
width: 8px; height: 8px; border-radius: 50%;
|
||
background: var(--jade);
|
||
box-shadow: 0 0 0 0 var(--jade);
|
||
animation: pulse 2.4s ease-in-out infinite;
|
||
}
|
||
|
||
@keyframes 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); }
|
||
}
|
||
|
||
.brand {
|
||
text-align: center;
|
||
font-family: var(--serif);
|
||
font-weight: 400;
|
||
font-style: italic;
|
||
font-size: 28px;
|
||
line-height: 1;
|
||
letter-spacing: -0.01em;
|
||
font-variation-settings: 'opsz' 144, 'SOFT' 50;
|
||
}
|
||
.brand span {
|
||
display: block;
|
||
font-family: var(--mono);
|
||
font-style: normal;
|
||
font-weight: 400;
|
||
font-size: 9px;
|
||
letter-spacing: 0.32em;
|
||
margin-top: 6px;
|
||
color: var(--ink-mute);
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.toolbar {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
align-items: center;
|
||
gap: 6px;
|
||
}
|
||
.toolbar button, .toolbar select {
|
||
background: transparent;
|
||
border: 1px solid transparent;
|
||
color: var(--ink-soft);
|
||
width: 36px; height: 36px;
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
cursor: pointer;
|
||
border-radius: 0;
|
||
transition: all 180ms var(--ease);
|
||
font-family: var(--mono);
|
||
font-size: 11px;
|
||
letter-spacing: 0.1em;
|
||
}
|
||
.toolbar button:hover, .toolbar select:hover {
|
||
color: var(--copper);
|
||
border-color: var(--rule-strong);
|
||
}
|
||
.toolbar select {
|
||
width: auto; padding: 0 10px;
|
||
text-transform: uppercase;
|
||
-webkit-appearance: none; appearance: none;
|
||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M1 1l4 4 4-4' stroke='%239C937F' fill='none' stroke-width='1.4'/></svg>");
|
||
background-repeat: no-repeat;
|
||
background-position: right 4px center;
|
||
padding-right: 18px;
|
||
}
|
||
.toolbar .accent-swatch {
|
||
width: 14px; height: 14px; border-radius: 50%;
|
||
background: var(--copper);
|
||
box-shadow: 0 0 0 1px var(--rule-strong), 0 0 14px var(--copper-glow);
|
||
}
|
||
.toolbar .sep {
|
||
width: 1px; height: 20px;
|
||
background: var(--rule-strong);
|
||
margin: 0 6px;
|
||
}
|
||
|
||
/* Folio marks at corners */
|
||
.folio {
|
||
position: absolute;
|
||
font-family: var(--mono);
|
||
font-size: 10px;
|
||
letter-spacing: 0.2em;
|
||
color: var(--ink-faint);
|
||
text-transform: uppercase;
|
||
}
|
||
.folio.tl { top: 14px; left: 14px; }
|
||
.folio.tr { top: 14px; right: 14px; }
|
||
.folio.bl { bottom: 14px; left: 14px; }
|
||
.folio.br { bottom: 14px; right: 14px; }
|
||
|
||
/* ─── Tab strip ────────────────────────────────────────────── */
|
||
.tabstrip {
|
||
display: flex;
|
||
gap: 0;
|
||
margin-top: 14px;
|
||
border-bottom: 1px solid var(--rule);
|
||
position: relative;
|
||
overflow-x: auto;
|
||
scrollbar-width: none;
|
||
animation: fadeUp 0.8s 0.1s var(--ease-out) both;
|
||
}
|
||
.tabstrip::-webkit-scrollbar { display: none; }
|
||
.tab {
|
||
background: transparent;
|
||
border: 0;
|
||
color: var(--ink-mute);
|
||
padding: 18px 26px 16px;
|
||
cursor: pointer;
|
||
font-family: var(--sans);
|
||
font-size: 13px;
|
||
font-weight: 400;
|
||
letter-spacing: 0.04em;
|
||
position: relative;
|
||
display: inline-flex;
|
||
align-items: baseline;
|
||
gap: 10px;
|
||
transition: color 180ms var(--ease);
|
||
white-space: nowrap;
|
||
}
|
||
.tab .num {
|
||
font-family: var(--mono);
|
||
font-size: 10px;
|
||
color: var(--ink-faint);
|
||
letter-spacing: 0.15em;
|
||
}
|
||
.tab:hover { color: var(--ink-soft); }
|
||
.tab.active {
|
||
color: var(--ink);
|
||
font-family: var(--serif);
|
||
font-style: italic;
|
||
font-size: 17px;
|
||
font-variation-settings: 'opsz' 144;
|
||
}
|
||
.tab.active::after {
|
||
content: "";
|
||
position: absolute;
|
||
bottom: -1px; left: 0; right: 0;
|
||
height: 2px;
|
||
background: var(--copper);
|
||
box-shadow: 0 0 12px var(--copper-glow);
|
||
}
|
||
|
||
/* ─── Hero — Now Playing editorial spread ──────────────────── */
|
||
.now-playing {
|
||
display: grid;
|
||
grid-template-columns: minmax(320px, 520px) 1fr;
|
||
gap: 64px;
|
||
margin-top: 48px;
|
||
position: relative;
|
||
animation: fadeUp 0.9s 0.2s var(--ease-out) both;
|
||
}
|
||
|
||
@media (max-width: 980px) {
|
||
.now-playing { grid-template-columns: 1fr; gap: 40px; }
|
||
}
|
||
|
||
/* ─── Vinyl + Tonearm ──────────────────────────────────────── */
|
||
.vinyl-stage {
|
||
position: relative;
|
||
aspect-ratio: 1;
|
||
width: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.vinyl {
|
||
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);
|
||
position: relative;
|
||
animation: spin 14s linear infinite;
|
||
}
|
||
.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;
|
||
}
|
||
|
||
/* Album art label in vinyl center */
|
||
.vinyl-label {
|
||
position: absolute;
|
||
inset: 32%;
|
||
border-radius: 50%;
|
||
background:
|
||
radial-gradient(circle at 30% 30%, #C26B3D, #8B3F1A 60%, #5C2510 100%);
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
text-align: center;
|
||
color: var(--ink);
|
||
box-shadow: inset 0 0 24px rgba(0,0,0,0.4);
|
||
overflow: hidden;
|
||
}
|
||
.vinyl-label::before {
|
||
content: "";
|
||
position: absolute;
|
||
width: 12%; height: 12%;
|
||
border-radius: 50%;
|
||
background: var(--bg-deep);
|
||
box-shadow: inset 0 1px 2px rgba(255,255,255,0.1);
|
||
}
|
||
.vinyl-label-text {
|
||
position: relative;
|
||
font-family: var(--serif);
|
||
font-style: italic;
|
||
font-size: 11px;
|
||
font-variation-settings: 'opsz' 144;
|
||
letter-spacing: 0.03em;
|
||
margin-top: 36%;
|
||
padding: 0 14%;
|
||
opacity: 0.85;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
@keyframes spin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
/* Tonearm */
|
||
.tonearm {
|
||
position: absolute;
|
||
top: -8%; right: -4%;
|
||
width: 58%; height: 58%;
|
||
pointer-events: none;
|
||
transform-origin: 88% 12%;
|
||
transform: rotate(-22deg);
|
||
transition: transform 1s var(--ease);
|
||
z-index: 2;
|
||
}
|
||
.now-playing.is-playing .tonearm { transform: rotate(0deg); }
|
||
|
||
/* ─── Track masthead ───────────────────────────────────────── */
|
||
.track-masthead {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
}
|
||
|
||
.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: 20px;
|
||
}
|
||
.kicker::before, .kicker::after {
|
||
content: ""; flex: 0 0 24px; height: 1px;
|
||
background: var(--copper);
|
||
opacity: 0.6;
|
||
}
|
||
.kicker::after { flex: 1 0 auto; }
|
||
|
||
.track-title {
|
||
font-family: var(--serif);
|
||
font-weight: 400;
|
||
font-size: clamp(42px, 5.6vw, 78px);
|
||
line-height: 0.96;
|
||
letter-spacing: -0.02em;
|
||
font-variation-settings: 'opsz' 144, 'SOFT' 30;
|
||
margin-bottom: 18px;
|
||
color: var(--ink);
|
||
}
|
||
.track-title em {
|
||
font-style: italic;
|
||
color: var(--copper-hi);
|
||
font-variation-settings: 'opsz' 144, 'SOFT' 60;
|
||
}
|
||
|
||
.track-byline {
|
||
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;
|
||
}
|
||
.track-album {
|
||
font-family: var(--sans);
|
||
font-size: 13px;
|
||
letter-spacing: 0.04em;
|
||
color: var(--ink-mute);
|
||
}
|
||
|
||
/* ─── Metadata grid (magazine sidebar) ─────────────────────── */
|
||
.meta-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 0;
|
||
margin-top: 36px;
|
||
border-top: 1px solid var(--rule);
|
||
border-bottom: 1px solid var(--rule);
|
||
}
|
||
.meta-cell {
|
||
padding: 16px 18px 16px 0;
|
||
border-right: 1px solid var(--rule);
|
||
}
|
||
.meta-cell:last-child { border-right: 0; padding-right: 0; }
|
||
.meta-cell .label {
|
||
font-family: var(--mono);
|
||
font-size: 9px;
|
||
letter-spacing: 0.22em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-faint);
|
||
margin-bottom: 8px;
|
||
}
|
||
.meta-cell .value {
|
||
font-family: var(--mono);
|
||
font-size: 13px;
|
||
color: var(--ink);
|
||
letter-spacing: 0.02em;
|
||
}
|
||
.meta-cell .value.serif {
|
||
font-family: var(--serif);
|
||
font-style: italic;
|
||
font-size: 18px;
|
||
font-variation-settings: 'opsz' 60;
|
||
}
|
||
|
||
/* ─── Progress + transport ─────────────────────────────────── */
|
||
.transport {
|
||
margin-top: 36px;
|
||
}
|
||
|
||
.progress-row {
|
||
display: grid;
|
||
grid-template-columns: auto 1fr auto;
|
||
align-items: center;
|
||
gap: 18px;
|
||
margin-bottom: 28px;
|
||
}
|
||
.timecode {
|
||
font-family: var(--mono);
|
||
font-size: 12px;
|
||
color: var(--ink-mute);
|
||
letter-spacing: 0.06em;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.timecode.elapsed { color: var(--copper); }
|
||
.progress-track {
|
||
height: 2px;
|
||
background: var(--rule-strong);
|
||
position: relative;
|
||
cursor: pointer;
|
||
}
|
||
.progress-fill {
|
||
position: absolute; left: 0; top: 0;
|
||
height: 100%;
|
||
width: 38%;
|
||
background: var(--copper);
|
||
box-shadow: 0 0 12px var(--copper-glow);
|
||
transition: width 200ms linear;
|
||
}
|
||
.progress-fill::after {
|
||
content: "";
|
||
position: absolute; right: -5px; top: -4px;
|
||
width: 10px; height: 10px;
|
||
background: var(--copper);
|
||
border-radius: 50%;
|
||
box-shadow: 0 0 14px var(--copper-glow), 0 0 0 4px rgba(224, 128, 56, 0.12);
|
||
}
|
||
|
||
/* Spectrum — visual signature of the player */
|
||
.spectrum {
|
||
display: flex;
|
||
align-items: flex-end;
|
||
justify-content: center;
|
||
gap: 3px;
|
||
height: 38px;
|
||
margin-bottom: 24px;
|
||
}
|
||
.spectrum span {
|
||
display: block;
|
||
width: 3px;
|
||
background: linear-gradient(to top, var(--copper-lo), var(--copper-hi));
|
||
opacity: 0.85;
|
||
animation: bar 1.1s ease-in-out infinite;
|
||
}
|
||
.spectrum span:nth-child(1) { animation-delay: -0.10s; height: 30%; }
|
||
.spectrum span:nth-child(2) { animation-delay: -0.45s; height: 60%; }
|
||
.spectrum span:nth-child(3) { animation-delay: -0.20s; height: 80%; }
|
||
.spectrum span:nth-child(4) { animation-delay: -0.55s; height: 50%; }
|
||
.spectrum span:nth-child(5) { animation-delay: -0.30s; height: 95%; }
|
||
.spectrum span:nth-child(6) { animation-delay: -0.05s; height: 70%; }
|
||
.spectrum span:nth-child(7) { animation-delay: -0.65s; height: 40%; }
|
||
.spectrum span:nth-child(8) { animation-delay: -0.25s; height: 85%; }
|
||
.spectrum span:nth-child(9) { animation-delay: -0.40s; height: 55%; }
|
||
.spectrum span:nth-child(10) { animation-delay: -0.10s; height: 75%; }
|
||
.spectrum span:nth-child(11) { animation-delay: -0.50s; height: 35%; }
|
||
.spectrum span:nth-child(12) { animation-delay: -0.15s; height: 90%; }
|
||
.spectrum span:nth-child(13) { animation-delay: -0.60s; height: 45%; }
|
||
.spectrum span:nth-child(14) { animation-delay: -0.30s; height: 65%; }
|
||
.spectrum span:nth-child(15) { animation-delay: -0.45s; height: 85%; }
|
||
.spectrum span:nth-child(16) { animation-delay: -0.20s; height: 55%; }
|
||
.spectrum span:nth-child(17) { animation-delay: -0.55s; height: 70%; }
|
||
.spectrum span:nth-child(18) { animation-delay: -0.10s; height: 30%; }
|
||
.spectrum span:nth-child(19) { animation-delay: -0.40s; height: 80%; }
|
||
.spectrum span:nth-child(20) { animation-delay: -0.25s; height: 60%; }
|
||
.spectrum span:nth-child(21) { animation-delay: -0.50s; height: 90%; }
|
||
.spectrum span:nth-child(22) { animation-delay: -0.15s; height: 50%; }
|
||
.spectrum span:nth-child(23) { animation-delay: -0.60s; height: 70%; }
|
||
.spectrum span:nth-child(24) { animation-delay: -0.30s; height: 40%; }
|
||
.spectrum span:nth-child(25) { animation-delay: -0.45s; height: 85%; }
|
||
.spectrum span:nth-child(26) { animation-delay: -0.20s; height: 55%; }
|
||
.spectrum span:nth-child(27) { animation-delay: -0.55s; height: 75%; }
|
||
.spectrum span:nth-child(28) { animation-delay: -0.10s; height: 35%; }
|
||
.spectrum span:nth-child(29) { animation-delay: -0.40s; height: 65%; }
|
||
.spectrum span:nth-child(30) { animation-delay: -0.25s; height: 95%; }
|
||
|
||
@keyframes bar {
|
||
0%, 100% { transform: scaleY(0.4); }
|
||
50% { transform: scaleY(1); }
|
||
}
|
||
|
||
/* Transport controls */
|
||
.controls {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 18px;
|
||
}
|
||
.btn-trans {
|
||
background: transparent;
|
||
border: 1px solid var(--rule-strong);
|
||
color: var(--ink-soft);
|
||
width: 48px; height: 48px;
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
cursor: pointer;
|
||
border-radius: 50%;
|
||
transition: all 200ms var(--ease);
|
||
}
|
||
.btn-trans:hover {
|
||
border-color: var(--copper);
|
||
color: var(--copper);
|
||
background: rgba(224, 128, 56, 0.06);
|
||
}
|
||
.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), inset 0 -6px 16px rgba(0,0,0,0.05);
|
||
}
|
||
.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);
|
||
}
|
||
.btn-trans svg { width: 20px; height: 20px; fill: currentColor; }
|
||
.btn-trans.primary svg { width: 24px; height: 24px; }
|
||
|
||
/* VU meter style volume */
|
||
.vu-cluster {
|
||
margin-left: auto;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
}
|
||
.vu-meter {
|
||
position: relative;
|
||
width: 140px; 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(224,128,56,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-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);
|
||
}
|
||
.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-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;
|
||
}
|
||
.vu-readout strong { color: var(--copper); font-weight: 400; }
|
||
|
||
/* ─── Section divider ──────────────────────────────────────── */
|
||
.section-divider {
|
||
display: grid;
|
||
grid-template-columns: auto 1fr auto;
|
||
align-items: center;
|
||
gap: 24px;
|
||
margin: 96px 0 32px;
|
||
animation: fadeUp 1s 0.4s var(--ease-out) both;
|
||
}
|
||
.section-divider .label {
|
||
font-family: var(--serif);
|
||
font-style: italic;
|
||
font-size: 22px;
|
||
font-variation-settings: 'opsz' 144;
|
||
}
|
||
.section-divider .label .num {
|
||
font-family: var(--mono);
|
||
font-style: normal;
|
||
font-size: 10px;
|
||
letter-spacing: 0.3em;
|
||
color: var(--copper);
|
||
margin-right: 12px;
|
||
vertical-align: middle;
|
||
}
|
||
.section-divider .rule {
|
||
height: 1px;
|
||
background: var(--rule-strong);
|
||
position: relative;
|
||
}
|
||
.section-divider .rule::before {
|
||
content: "";
|
||
position: absolute;
|
||
left: 0; top: 50%; transform: translateY(-50%);
|
||
width: 6px; height: 6px;
|
||
background: var(--copper);
|
||
border-radius: 50%;
|
||
}
|
||
.section-divider .meta {
|
||
font-family: var(--mono);
|
||
font-size: 10px;
|
||
letter-spacing: 0.2em;
|
||
color: var(--ink-faint);
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
/* ─── Browser preview ──────────────────────────────────────── */
|
||
.browser-toolbar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 14px 0;
|
||
border-bottom: 1px solid var(--rule);
|
||
margin-bottom: 28px;
|
||
}
|
||
.browser-toolbar .crumb {
|
||
font-family: var(--serif);
|
||
font-style: italic;
|
||
font-size: 16px;
|
||
color: var(--ink-soft);
|
||
font-variation-settings: 'opsz' 60;
|
||
}
|
||
.browser-toolbar .crumb b {
|
||
color: var(--ink);
|
||
font-weight: 400;
|
||
}
|
||
.browser-toolbar .crumb .sep {
|
||
color: var(--ink-faint);
|
||
margin: 0 8px;
|
||
font-style: normal;
|
||
}
|
||
.browser-toolbar .spacer { flex: 1; }
|
||
.browser-toolbar .pill {
|
||
font-family: var(--mono);
|
||
font-size: 10px;
|
||
letter-spacing: 0.2em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-mute);
|
||
padding: 6px 12px;
|
||
border: 1px solid var(--rule-strong);
|
||
cursor: pointer;
|
||
background: transparent;
|
||
transition: all 180ms var(--ease);
|
||
}
|
||
.browser-toolbar .pill:hover { color: var(--copper); border-color: var(--copper); }
|
||
.browser-toolbar .pill.active {
|
||
background: var(--copper);
|
||
color: var(--bg-deep);
|
||
border-color: var(--copper);
|
||
}
|
||
|
||
.browser-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||
gap: 28px 24px;
|
||
}
|
||
.media-card {
|
||
cursor: pointer;
|
||
transition: transform 280ms var(--ease);
|
||
}
|
||
.media-card:hover { transform: translateY(-4px); }
|
||
.media-cover {
|
||
aspect-ratio: 1;
|
||
background: var(--bg-card);
|
||
position: relative;
|
||
overflow: hidden;
|
||
margin-bottom: 14px;
|
||
box-shadow: 0 14px 40px rgba(0,0,0,0.4);
|
||
transition: box-shadow 280ms var(--ease);
|
||
}
|
||
.media-card:hover .media-cover {
|
||
box-shadow: 0 20px 50px rgba(0,0,0,0.55), 0 0 0 1px var(--copper);
|
||
}
|
||
.media-cover::after {
|
||
content: "";
|
||
position: absolute; inset: 0;
|
||
background: radial-gradient(circle at 70% 30%, rgba(255,255,255,0.08), transparent 60%);
|
||
}
|
||
.media-cover .play-overlay {
|
||
position: absolute; inset: 0;
|
||
display: flex; align-items: center; justify-content: center;
|
||
background: rgba(14, 13, 11, 0.4);
|
||
opacity: 0;
|
||
transition: opacity 200ms var(--ease);
|
||
}
|
||
.media-card:hover .play-overlay { opacity: 1; }
|
||
.media-cover .play-overlay svg {
|
||
width: 44px; height: 44px;
|
||
fill: var(--ink);
|
||
filter: drop-shadow(0 4px 12px rgba(0,0,0,0.6));
|
||
}
|
||
.media-cover .index {
|
||
position: absolute;
|
||
top: 10px; left: 12px;
|
||
font-family: var(--mono);
|
||
font-size: 10px;
|
||
color: var(--ink);
|
||
letter-spacing: 0.15em;
|
||
opacity: 0.7;
|
||
}
|
||
.media-meta .title {
|
||
font-family: var(--serif);
|
||
font-size: 16px;
|
||
color: var(--ink);
|
||
line-height: 1.25;
|
||
margin-bottom: 4px;
|
||
font-variation-settings: 'opsz' 60;
|
||
}
|
||
.media-meta .sub {
|
||
font-family: var(--mono);
|
||
font-size: 10px;
|
||
color: var(--ink-mute);
|
||
letter-spacing: 0.1em;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
/* Cover swatches */
|
||
.cover-1 { background: linear-gradient(135deg, #1F2933 0%, #4D5C6E 100%); }
|
||
.cover-2 { background: linear-gradient(135deg, #6B2B1F 0%, #C26B3D 70%, #F4A064 100%); }
|
||
.cover-3 { background: linear-gradient(135deg, #2A2A2A 0%, #1A1A1A 100%); }
|
||
.cover-4 { background: radial-gradient(circle at 30% 30%, #7AB294 0%, #2C4A3A 100%); }
|
||
.cover-5 { background: linear-gradient(135deg, #F2EBDC 0%, #C8B89A 100%); }
|
||
.cover-6 { background: conic-gradient(from 0deg at 50% 50%, #E08038, #C2553F, #6B2B1F, #E08038); }
|
||
.cover-7 { background: linear-gradient(180deg, #0E0D0B 0%, #C26B3D 100%); }
|
||
.cover-8 { background: repeating-linear-gradient(45deg, #2E2820, #2E2820 4px, #1F1B14 4px, #1F1B14 12px); }
|
||
|
||
/* ─── Settings preview (compact card grid) ─────────────────── */
|
||
.settings-stack {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||
gap: 24px;
|
||
}
|
||
.settings-card {
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--rule);
|
||
padding: 26px 26px 24px;
|
||
position: relative;
|
||
transition: border-color 240ms var(--ease);
|
||
}
|
||
.settings-card:hover { border-color: var(--rule-strong); }
|
||
.settings-card .card-num {
|
||
position: absolute;
|
||
top: 18px; right: 22px;
|
||
font-family: var(--mono);
|
||
font-size: 10px;
|
||
letter-spacing: 0.2em;
|
||
color: var(--ink-faint);
|
||
}
|
||
.settings-card h3 {
|
||
font-family: var(--serif);
|
||
font-weight: 400;
|
||
font-size: 24px;
|
||
color: var(--ink);
|
||
margin-bottom: 4px;
|
||
font-variation-settings: 'opsz' 144;
|
||
}
|
||
.settings-card h3 em {
|
||
font-style: italic;
|
||
color: var(--copper);
|
||
}
|
||
.settings-card .desc {
|
||
font-family: var(--sans);
|
||
font-size: 13px;
|
||
color: var(--ink-mute);
|
||
line-height: 1.5;
|
||
margin-bottom: 22px;
|
||
}
|
||
.settings-card .stat-line {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 12px 0;
|
||
border-top: 1px solid var(--rule);
|
||
font-family: var(--mono);
|
||
font-size: 11px;
|
||
}
|
||
.settings-card .stat-line .k { color: var(--ink-mute); letter-spacing: 0.1em; text-transform: uppercase; }
|
||
.settings-card .stat-line .v { color: var(--ink); font-variant-numeric: tabular-nums; }
|
||
.settings-card .stat-line .v.ok { color: var(--jade); }
|
||
.settings-card .stat-line .v.warn { color: var(--amber); }
|
||
.settings-card .stat-line .v.err { color: var(--rust); }
|
||
|
||
.settings-card .cta {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
margin-top: 22px;
|
||
font-family: var(--mono);
|
||
font-size: 11px;
|
||
letter-spacing: 0.18em;
|
||
text-transform: uppercase;
|
||
color: var(--copper);
|
||
cursor: pointer;
|
||
background: transparent;
|
||
border: 0;
|
||
padding: 0;
|
||
transition: gap 200ms var(--ease);
|
||
}
|
||
.settings-card .cta:hover { gap: 14px; }
|
||
.settings-card .cta::after {
|
||
content: "→";
|
||
font-family: var(--sans);
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* ─── Quick actions strip ──────────────────────────────────── */
|
||
.actions-rail {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||
gap: 1px;
|
||
background: var(--rule-strong);
|
||
border: 1px solid var(--rule-strong);
|
||
}
|
||
.action {
|
||
background: var(--bg-card);
|
||
padding: 24px 18px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
transition: all 200ms var(--ease);
|
||
position: relative;
|
||
}
|
||
.action:hover {
|
||
background: var(--bg-card-2);
|
||
color: var(--copper);
|
||
}
|
||
.action:hover .action-icon { color: var(--copper); transform: translateY(-2px); }
|
||
.action-icon {
|
||
font-size: 22px;
|
||
color: var(--ink-soft);
|
||
margin-bottom: 12px;
|
||
width: 32px; height: 32px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
transition: all 200ms var(--ease);
|
||
}
|
||
.action-label {
|
||
font-family: var(--serif);
|
||
font-style: italic;
|
||
font-size: 14px;
|
||
color: var(--ink);
|
||
font-variation-settings: 'opsz' 60;
|
||
margin-bottom: 4px;
|
||
}
|
||
.action-key {
|
||
font-family: var(--mono);
|
||
font-size: 9px;
|
||
letter-spacing: 0.18em;
|
||
color: var(--ink-faint);
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
/* ─── Mini player (sticky console strip) ───────────────────── */
|
||
.mini-player {
|
||
position: fixed;
|
||
bottom: 0; left: 0; right: 0;
|
||
background: linear-gradient(180deg, rgba(14,13,11,0.6) 0%, rgba(14,13,11,0.95) 30%);
|
||
backdrop-filter: blur(20px) saturate(140%);
|
||
-webkit-backdrop-filter: blur(20px) saturate(140%);
|
||
border-top: 1px solid var(--rule-strong);
|
||
padding: 14px 32px;
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr 1fr;
|
||
align-items: center;
|
||
gap: 32px;
|
||
z-index: 100;
|
||
animation: slideUp 0.9s 0.5s var(--ease-out) both;
|
||
}
|
||
.mini-player::before {
|
||
content: "";
|
||
position: absolute;
|
||
top: -1px; left: 32px; right: 32px;
|
||
height: 2px;
|
||
background: linear-gradient(90deg, transparent, var(--copper), transparent);
|
||
opacity: 0.5;
|
||
}
|
||
.mp-track {
|
||
display: flex; align-items: center; gap: 16px;
|
||
min-width: 0;
|
||
}
|
||
.mp-cover {
|
||
width: 48px; height: 48px;
|
||
flex-shrink: 0;
|
||
background: linear-gradient(135deg, #6B2B1F, #C26B3D);
|
||
position: relative;
|
||
box-shadow: 0 4px 14px rgba(0,0,0,0.4);
|
||
}
|
||
.mp-cover::after {
|
||
content: "";
|
||
position: absolute; inset: 0;
|
||
background: radial-gradient(circle at 70% 30%, rgba(255,255,255,0.15), transparent 60%);
|
||
}
|
||
.mp-info { min-width: 0; }
|
||
.mp-info .t {
|
||
font-family: var(--serif);
|
||
font-style: italic;
|
||
font-size: 15px;
|
||
color: var(--ink);
|
||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||
font-variation-settings: 'opsz' 60;
|
||
}
|
||
.mp-info .a {
|
||
font-family: var(--mono);
|
||
font-size: 10px;
|
||
color: var(--ink-mute);
|
||
letter-spacing: 0.1em;
|
||
text-transform: uppercase;
|
||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||
margin-top: 4px;
|
||
}
|
||
.mp-controls {
|
||
display: flex; justify-content: center; align-items: center; gap: 18px;
|
||
}
|
||
.mp-btn {
|
||
background: transparent;
|
||
border: 0;
|
||
color: var(--ink-soft);
|
||
width: 32px; height: 32px;
|
||
cursor: pointer;
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
transition: color 180ms var(--ease);
|
||
}
|
||
.mp-btn:hover { color: var(--copper); }
|
||
.mp-btn.primary {
|
||
background: var(--ink);
|
||
color: var(--bg-deep);
|
||
width: 38px; height: 38px;
|
||
border-radius: 50%;
|
||
box-shadow: 0 4px 14px rgba(0,0,0,0.4);
|
||
}
|
||
.mp-btn.primary:hover { background: var(--copper); }
|
||
.mp-btn svg { width: 16px; height: 16px; fill: currentColor; }
|
||
.mp-btn.primary svg { width: 14px; height: 14px; }
|
||
|
||
.mp-end {
|
||
display: flex; align-items: center; gap: 14px; justify-content: flex-end;
|
||
}
|
||
.mp-time {
|
||
font-family: var(--mono);
|
||
font-size: 10px;
|
||
color: var(--ink-mute);
|
||
letter-spacing: 0.06em;
|
||
font-variant-numeric: tabular-nums;
|
||
min-width: 80px; text-align: right;
|
||
}
|
||
.mp-time strong { color: var(--copper); font-weight: 400; }
|
||
.mp-vol {
|
||
display: flex; align-items: center; gap: 8px;
|
||
}
|
||
.mp-vol-bar {
|
||
width: 80px; height: 2px;
|
||
background: var(--rule-strong);
|
||
position: relative;
|
||
}
|
||
.mp-vol-bar::before {
|
||
content: "";
|
||
position: absolute; left: 0; top: 0; bottom: 0;
|
||
width: 60%;
|
||
background: var(--copper);
|
||
}
|
||
|
||
/* ─── Footer ───────────────────────────────────────────────── */
|
||
footer.colophon {
|
||
margin-top: 120px;
|
||
padding-top: 28px;
|
||
border-top: 1px solid var(--rule-strong);
|
||
display: grid;
|
||
grid-template-columns: 1fr auto 1fr;
|
||
gap: 24px;
|
||
font-family: var(--mono);
|
||
font-size: 10px;
|
||
letter-spacing: 0.18em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-faint);
|
||
}
|
||
footer.colophon .center {
|
||
font-family: var(--serif);
|
||
font-style: italic;
|
||
font-size: 14px;
|
||
letter-spacing: 0.02em;
|
||
text-transform: none;
|
||
color: var(--ink-mute);
|
||
}
|
||
footer.colophon a { color: var(--copper); text-decoration: none; }
|
||
footer.colophon a:hover { text-decoration: underline; }
|
||
footer.colophon .right { text-align: right; }
|
||
|
||
/* ─── Animations ───────────────────────────────────────────── */
|
||
@keyframes fadeUp {
|
||
from { opacity: 0; transform: translateY(20px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
@keyframes slideUp {
|
||
from { opacity: 0; transform: translateY(100%); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
/* ─── Responsive ───────────────────────────────────────────── */
|
||
@media (max-width: 720px) {
|
||
.masthead { grid-template-columns: 1fr; gap: 14px; }
|
||
.masthead .left, .toolbar { justify-content: center; }
|
||
.meta-grid { grid-template-columns: repeat(2, 1fr); }
|
||
.meta-cell:nth-child(2) { border-right: 0; }
|
||
.progress-row { grid-template-columns: 1fr; gap: 10px; }
|
||
.controls { flex-wrap: wrap; }
|
||
.vu-cluster { width: 100%; justify-content: space-between; margin-left: 0; margin-top: 16px; }
|
||
.mini-player { grid-template-columns: 1fr; gap: 12px; padding: 12px 16px; }
|
||
.mp-end { display: none; }
|
||
.section-divider { grid-template-columns: 1fr; }
|
||
.section-divider .meta { display: none; }
|
||
}
|
||
|
||
/* Banner — design notes panel for the user */
|
||
.notes-panel {
|
||
position: fixed;
|
||
top: 14px; right: 14px;
|
||
width: 280px;
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--copper);
|
||
padding: 18px 18px 16px;
|
||
font-family: var(--mono);
|
||
font-size: 11px;
|
||
color: var(--ink-soft);
|
||
z-index: 200;
|
||
box-shadow: 0 14px 40px rgba(0,0,0,0.5), 0 0 30px var(--copper-glow);
|
||
}
|
||
.notes-panel h4 {
|
||
font-family: var(--serif);
|
||
font-style: italic;
|
||
font-size: 16px;
|
||
color: var(--copper);
|
||
margin-bottom: 10px;
|
||
font-weight: 400;
|
||
}
|
||
.notes-panel ul {
|
||
list-style: none;
|
||
display: flex; flex-direction: column; gap: 6px;
|
||
letter-spacing: 0.04em;
|
||
}
|
||
.notes-panel li::before { content: "→ "; color: var(--copper); }
|
||
.notes-panel .close {
|
||
position: absolute; top: 10px; right: 12px;
|
||
background: transparent; border: 0;
|
||
color: var(--ink-mute); cursor: pointer;
|
||
font-size: 16px; line-height: 1;
|
||
}
|
||
.notes-panel.hidden { display: none; }
|
||
|
||
@media (max-width: 980px) { .notes-panel { display: none; } }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- Design notes panel (dismissible) -->
|
||
<aside class="notes-panel" id="notesPanel">
|
||
<button class="close" onclick="document.getElementById('notesPanel').classList.add('hidden')">×</button>
|
||
<h4>Studio Reference</h4>
|
||
<ul>
|
||
<li>Editorial hi-fi mockup</li>
|
||
<li>Fraunces · Geist · Geist Mono</li>
|
||
<li>Copper-on-charcoal · grain</li>
|
||
<li>Asymmetric magazine grid</li>
|
||
<li>Vinyl + tonearm + VU meter</li>
|
||
<li>Hover over cards & buttons</li>
|
||
</ul>
|
||
</aside>
|
||
|
||
<div class="shell">
|
||
|
||
<!-- Folio marks -->
|
||
<span class="folio tl">№ 0008 · v0.2.0</span>
|
||
<span class="folio tr">Vol. I — APR · MMXXVI</span>
|
||
|
||
<!-- Masthead -->
|
||
<header class="masthead">
|
||
<div class="left">
|
||
<span class="dot-live"></span>
|
||
<span>Connected · Local 8765</span>
|
||
</div>
|
||
<div class="brand">
|
||
Media Server
|
||
<span>Studio Reference Edition</span>
|
||
</div>
|
||
<div class="toolbar">
|
||
<button title="API Docs">
|
||
<svg width="14" height="14" viewBox="0 0 24 24"><path fill="currentColor" d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm-1 7V3.5L18.5 9H13z"/></svg>
|
||
</button>
|
||
<button title="Accent">
|
||
<span class="accent-swatch"></span>
|
||
</button>
|
||
<button title="Background">
|
||
<svg width="14" height="14" viewBox="0 0 24 24"><path fill="currentColor" d="M12 3v10.55A4 4 0 1 0 14 17V7h4V3h-6z"/></svg>
|
||
</button>
|
||
<button title="Theme">
|
||
<svg width="14" height="14" viewBox="0 0 24 24"><path fill="currentColor" d="M9 2c-1.05 0-2.05.16-3 .46 4.06 1.27 7 5.06 7 9.54 0 4.48-2.94 8.27-7 9.54.95.3 1.95.46 3 .46 5.52 0 10-4.48 10-10S14.52 2 9 2z"/></svg>
|
||
</button>
|
||
<select>
|
||
<option>EN</option>
|
||
<option>RU</option>
|
||
</select>
|
||
<span class="sep"></span>
|
||
<button title="Logout">
|
||
<svg width="14" height="14" viewBox="0 0 24 24"><path fill="currentColor" d="M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z"/></svg>
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- Tab strip -->
|
||
<nav class="tabstrip">
|
||
<button class="tab active"><span class="num">01</span>Now Spinning</button>
|
||
<button class="tab"><span class="num">02</span>Display</button>
|
||
<button class="tab"><span class="num">03</span>Library</button>
|
||
<button class="tab"><span class="num">04</span>Quick Access</button>
|
||
<button class="tab"><span class="num">05</span>Settings</button>
|
||
</nav>
|
||
|
||
<!-- ═══════════════════════════════════════════════════════
|
||
HERO — Now Playing editorial spread
|
||
═══════════════════════════════════════════════════════ -->
|
||
<section class="now-playing is-playing">
|
||
|
||
<!-- Vinyl with tonearm -->
|
||
<div class="vinyl-stage">
|
||
<div class="vinyl">
|
||
<div class="vinyl-label">
|
||
<div class="vinyl-label-text">
|
||
Kind of<br>Blue
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<svg class="tonearm" viewBox="0 0 200 200">
|
||
<defs>
|
||
<linearGradient id="armGrad" x1="0" x2="1">
|
||
<stop offset="0" stop-color="#3a3528"/>
|
||
<stop offset="0.5" stop-color="#9C937F"/>
|
||
<stop offset="1" stop-color="#5C5447"/>
|
||
</linearGradient>
|
||
</defs>
|
||
<!-- arm pivot base -->
|
||
<circle cx="176" cy="24" r="14" fill="#1a1611" stroke="#3A3528" stroke-width="1"/>
|
||
<circle cx="176" cy="24" r="6" fill="#3A3528"/>
|
||
<circle cx="176" cy="24" r="2" fill="#E08038"/>
|
||
<!-- arm -->
|
||
<line x1="176" y1="24" x2="64" y2="136" stroke="url(#armGrad)" stroke-width="3.5" stroke-linecap="round"/>
|
||
<!-- counterweight at pivot end -->
|
||
<rect x="180" y="14" width="14" height="20" fill="#26211A" stroke="#3A3528"/>
|
||
<!-- headshell -->
|
||
<rect x="56" y="128" width="22" height="18" rx="2" fill="#26211A" stroke="#3A3528" transform="rotate(-45 67 137)"/>
|
||
<!-- needle tip glow -->
|
||
<circle cx="62" cy="138" r="3" fill="#E08038" opacity="0.8"/>
|
||
<circle cx="62" cy="138" r="6" fill="none" stroke="#E08038" stroke-width="0.5" opacity="0.4"/>
|
||
</svg>
|
||
</div>
|
||
|
||
<!-- Track masthead -->
|
||
<div class="track-masthead">
|
||
<div class="kicker">Now Spinning · Track 03 of 05 · Spotify</div>
|
||
|
||
<h1 class="track-title">So What — <em>Take One</em></h1>
|
||
<div class="track-byline">Miles Davis Sextet</div>
|
||
<div class="track-album">Kind of Blue · Columbia · 1959 · Remastered</div>
|
||
|
||
<!-- Metadata grid -->
|
||
<div class="meta-grid">
|
||
<div class="meta-cell">
|
||
<div class="label">Bitrate</div>
|
||
<div class="value">320 kbps</div>
|
||
</div>
|
||
<div class="meta-cell">
|
||
<div class="label">Format</div>
|
||
<div class="value">FLAC · 24/96</div>
|
||
</div>
|
||
<div class="meta-cell">
|
||
<div class="label">Output</div>
|
||
<div class="value">Studio Mon.</div>
|
||
</div>
|
||
<div class="meta-cell">
|
||
<div class="label">Genre</div>
|
||
<div class="value serif">Modal Jazz</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Spectrum -->
|
||
<div class="spectrum" style="margin-top: 36px;">
|
||
<span></span><span></span><span></span><span></span><span></span>
|
||
<span></span><span></span><span></span><span></span><span></span>
|
||
<span></span><span></span><span></span><span></span><span></span>
|
||
<span></span><span></span><span></span><span></span><span></span>
|
||
<span></span><span></span><span></span><span></span><span></span>
|
||
<span></span><span></span><span></span><span></span><span></span>
|
||
</div>
|
||
|
||
<!-- Transport -->
|
||
<div class="transport">
|
||
|
||
<div class="progress-row">
|
||
<span class="timecode elapsed">03:42</span>
|
||
<div class="progress-track">
|
||
<div class="progress-fill"></div>
|
||
</div>
|
||
<span class="timecode">09:22</span>
|
||
</div>
|
||
|
||
<div class="controls">
|
||
<button class="btn-trans" title="Previous">
|
||
<svg viewBox="0 0 24 24"><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"/></svg>
|
||
</button>
|
||
<button class="btn-trans primary" title="Pause">
|
||
<svg viewBox="0 0 24 24"><path d="M6 5h4v14H6zm8 0h4v14h-4z"/></svg>
|
||
</button>
|
||
<button class="btn-trans" title="Next">
|
||
<svg viewBox="0 0 24 24"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/></svg>
|
||
</button>
|
||
|
||
<div class="vu-cluster">
|
||
<div class="vu-meter">
|
||
<div class="vu-needle" id="vuNeedle"></div>
|
||
</div>
|
||
<div class="vu-readout">
|
||
<span>OUT <strong>−6 dB</strong></span>
|
||
<span>VOL <strong>72%</strong></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ═══════════════════════════════════════════════════════
|
||
LIBRARY preview
|
||
═══════════════════════════════════════════════════════ -->
|
||
<div class="section-divider">
|
||
<div class="label"><span class="num">§ 03</span><em>The Library</em></div>
|
||
<div class="rule"></div>
|
||
<div class="meta">14 folders · 2,148 items</div>
|
||
</div>
|
||
|
||
<div class="browser-toolbar">
|
||
<div class="crumb">
|
||
<em>Music</em>
|
||
<span class="sep">/</span>
|
||
<em>Jazz</em>
|
||
<span class="sep">/</span>
|
||
<b>Miles Davis</b>
|
||
</div>
|
||
<div class="spacer"></div>
|
||
<button class="pill active">Grid</button>
|
||
<button class="pill">Compact</button>
|
||
<button class="pill">List</button>
|
||
<button class="pill">↻</button>
|
||
</div>
|
||
|
||
<div class="browser-grid">
|
||
<div class="media-card">
|
||
<div class="media-cover cover-2">
|
||
<span class="index">A1</span>
|
||
<div class="play-overlay"><svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg></div>
|
||
</div>
|
||
<div class="media-meta">
|
||
<div class="title">Kind of Blue</div>
|
||
<div class="sub">1959 · 5 tracks</div>
|
||
</div>
|
||
</div>
|
||
<div class="media-card">
|
||
<div class="media-cover cover-1">
|
||
<span class="index">A2</span>
|
||
<div class="play-overlay"><svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg></div>
|
||
</div>
|
||
<div class="media-meta">
|
||
<div class="title">Sketches of Spain</div>
|
||
<div class="sub">1960 · 5 tracks</div>
|
||
</div>
|
||
</div>
|
||
<div class="media-card">
|
||
<div class="media-cover cover-6">
|
||
<span class="index">A3</span>
|
||
<div class="play-overlay"><svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg></div>
|
||
</div>
|
||
<div class="media-meta">
|
||
<div class="title">Bitches Brew</div>
|
||
<div class="sub">1970 · 7 tracks</div>
|
||
</div>
|
||
</div>
|
||
<div class="media-card">
|
||
<div class="media-cover cover-4">
|
||
<span class="index">A4</span>
|
||
<div class="play-overlay"><svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg></div>
|
||
</div>
|
||
<div class="media-meta">
|
||
<div class="title">In a Silent Way</div>
|
||
<div class="sub">1969 · 4 tracks</div>
|
||
</div>
|
||
</div>
|
||
<div class="media-card">
|
||
<div class="media-cover cover-3">
|
||
<span class="index">A5</span>
|
||
<div class="play-overlay"><svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg></div>
|
||
</div>
|
||
<div class="media-meta">
|
||
<div class="title">Birth of the Cool</div>
|
||
<div class="sub">1957 · 11 tracks</div>
|
||
</div>
|
||
</div>
|
||
<div class="media-card">
|
||
<div class="media-cover cover-5">
|
||
<span class="index">A6</span>
|
||
<div class="play-overlay"><svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg></div>
|
||
</div>
|
||
<div class="media-meta">
|
||
<div class="title">Porgy and Bess</div>
|
||
<div class="sub">1959 · 13 tracks</div>
|
||
</div>
|
||
</div>
|
||
<div class="media-card">
|
||
<div class="media-cover cover-7">
|
||
<span class="index">A7</span>
|
||
<div class="play-overlay"><svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg></div>
|
||
</div>
|
||
<div class="media-meta">
|
||
<div class="title">Milestones</div>
|
||
<div class="sub">1958 · 7 tracks</div>
|
||
</div>
|
||
</div>
|
||
<div class="media-card">
|
||
<div class="media-cover cover-8">
|
||
<span class="index">A8</span>
|
||
<div class="play-overlay"><svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg></div>
|
||
</div>
|
||
<div class="media-meta">
|
||
<div class="title">'Round About Midnight</div>
|
||
<div class="sub">1957 · 6 tracks</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════════════════════════════════════════════
|
||
QUICK ACCESS
|
||
═══════════════════════════════════════════════════════ -->
|
||
<div class="section-divider">
|
||
<div class="label"><span class="num">§ 04</span><em>Quick Access</em></div>
|
||
<div class="rule"></div>
|
||
<div class="meta">Scripts · Shortcuts · Links</div>
|
||
</div>
|
||
|
||
<div class="actions-rail">
|
||
<button class="action">
|
||
<div class="action-icon">⏻</div>
|
||
<div class="action-label">Shutdown</div>
|
||
<div class="action-key">PWR · 01</div>
|
||
</button>
|
||
<button class="action">
|
||
<div class="action-icon">↻</div>
|
||
<div class="action-label">Restart</div>
|
||
<div class="action-key">PWR · 02</div>
|
||
</button>
|
||
<button class="action">
|
||
<div class="action-icon">⌚</div>
|
||
<div class="action-label">Sleep</div>
|
||
<div class="action-key">PWR · 03</div>
|
||
</button>
|
||
<button class="action">
|
||
<div class="action-icon">▣</div>
|
||
<div class="action-label">Lock</div>
|
||
<div class="action-key">SEC · 01</div>
|
||
</button>
|
||
<button class="action">
|
||
<div class="action-icon">♪</div>
|
||
<div class="action-label">Spotify</div>
|
||
<div class="action-key">APP · 01</div>
|
||
</button>
|
||
<button class="action">
|
||
<div class="action-icon">▶</div>
|
||
<div class="action-label">VLC</div>
|
||
<div class="action-key">APP · 02</div>
|
||
</button>
|
||
<button class="action">
|
||
<div class="action-icon">⌂</div>
|
||
<div class="action-label">Home Asst.</div>
|
||
<div class="action-key">LNK · 01</div>
|
||
</button>
|
||
<button class="action">
|
||
<div class="action-icon">⚙</div>
|
||
<div class="action-label">Router</div>
|
||
<div class="action-key">LNK · 02</div>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- ═══════════════════════════════════════════════════════
|
||
SETTINGS preview
|
||
═══════════════════════════════════════════════════════ -->
|
||
<div class="section-divider">
|
||
<div class="label"><span class="num">§ 05</span><em>Settings</em></div>
|
||
<div class="rule"></div>
|
||
<div class="meta">System · Audio · Folders · Callbacks</div>
|
||
</div>
|
||
|
||
<div class="settings-stack">
|
||
|
||
<article class="settings-card">
|
||
<span class="card-num">5.01</span>
|
||
<h3><em>Audio</em> Output</h3>
|
||
<p class="desc">Loopback device captured by the visualizer & VU meter. Auto-detection picks the system default.</p>
|
||
<div class="stat-line">
|
||
<span class="k">Device</span>
|
||
<span class="v">Realtek HD</span>
|
||
</div>
|
||
<div class="stat-line">
|
||
<span class="k">Status</span>
|
||
<span class="v ok">CONNECTED</span>
|
||
</div>
|
||
<div class="stat-line">
|
||
<span class="k">Latency</span>
|
||
<span class="v">12 ms</span>
|
||
</div>
|
||
<button class="cta">Configure</button>
|
||
</article>
|
||
|
||
<article class="settings-card">
|
||
<span class="card-num">5.02</span>
|
||
<h3><em>Media</em> Folders</h3>
|
||
<p class="desc">Library roots scanned for browsing. Network shares show availability status.</p>
|
||
<div class="stat-line">
|
||
<span class="k">Music</span>
|
||
<span class="v ok">D:\Music</span>
|
||
</div>
|
||
<div class="stat-line">
|
||
<span class="k">Movies</span>
|
||
<span class="v ok">\\NAS\Films</span>
|
||
</div>
|
||
<div class="stat-line">
|
||
<span class="k">Podcasts</span>
|
||
<span class="v warn">UNREACHABLE</span>
|
||
</div>
|
||
<button class="cta">Manage 4 folders</button>
|
||
</article>
|
||
|
||
<article class="settings-card">
|
||
<span class="card-num">5.03</span>
|
||
<h3><em>Scripts</em> & Hooks</h3>
|
||
<p class="desc">Custom commands that run on demand or trigger on playback events.</p>
|
||
<div class="stat-line">
|
||
<span class="k">Scripts</span>
|
||
<span class="v">7 active</span>
|
||
</div>
|
||
<div class="stat-line">
|
||
<span class="k">Callbacks</span>
|
||
<span class="v">3 wired</span>
|
||
</div>
|
||
<div class="stat-line">
|
||
<span class="k">Last run</span>
|
||
<span class="v ok">OK · 14:22</span>
|
||
</div>
|
||
<button class="cta">Edit scripts</button>
|
||
</article>
|
||
|
||
<article class="settings-card">
|
||
<span class="card-num">5.04</span>
|
||
<h3><em>System</em> Health</h3>
|
||
<p class="desc">Server diagnostics, websocket state, and update channel.</p>
|
||
<div class="stat-line">
|
||
<span class="k">Version</span>
|
||
<span class="v">v0.2.0</span>
|
||
</div>
|
||
<div class="stat-line">
|
||
<span class="k">Uptime</span>
|
||
<span class="v">04 d · 11 h</span>
|
||
</div>
|
||
<div class="stat-line">
|
||
<span class="k">Update</span>
|
||
<span class="v ok">UP TO DATE</span>
|
||
</div>
|
||
<button class="cta">Open diagnostics</button>
|
||
</article>
|
||
|
||
</div>
|
||
|
||
<!-- Colophon -->
|
||
<footer class="colophon">
|
||
<div>Built · Anno 2026</div>
|
||
<div class="center">A. Dolgolyov · <a href="mailto:dolgolyov.alexei@gmail.com">dolgolyov.alexei@gmail.com</a></div>
|
||
<div class="right">Source on Gitea</div>
|
||
</footer>
|
||
|
||
</div>
|
||
|
||
<!-- ═══════════════════════════════════════════════════════
|
||
STICKY MINI PLAYER — console strip
|
||
═══════════════════════════════════════════════════════ -->
|
||
<div class="mini-player">
|
||
<div class="mp-track">
|
||
<div class="mp-cover"></div>
|
||
<div class="mp-info">
|
||
<div class="t">So What — Take One</div>
|
||
<div class="a">Miles Davis · Kind of Blue</div>
|
||
</div>
|
||
</div>
|
||
<div class="mp-controls">
|
||
<button class="mp-btn" title="Previous">
|
||
<svg viewBox="0 0 24 24"><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"/></svg>
|
||
</button>
|
||
<button class="mp-btn primary" title="Pause">
|
||
<svg viewBox="0 0 24 24"><path d="M6 5h4v14H6zm8 0h4v14h-4z"/></svg>
|
||
</button>
|
||
<button class="mp-btn" title="Next">
|
||
<svg viewBox="0 0 24 24"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/></svg>
|
||
</button>
|
||
</div>
|
||
<div class="mp-end">
|
||
<span class="mp-time"><strong>03:42</strong> / 09:22</span>
|
||
<div class="mp-vol">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="#9C937F"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z"/></svg>
|
||
<div class="mp-vol-bar"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// ─── Tab switcher (visual only) ─────────────────────────
|
||
document.querySelectorAll('.tab').forEach(tab => {
|
||
tab.addEventListener('click', () => {
|
||
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
||
tab.classList.add('active');
|
||
});
|
||
});
|
||
|
||
// ─── Browser view toggle ────────────────────────────────
|
||
document.querySelectorAll('.browser-toolbar .pill').forEach(pill => {
|
||
pill.addEventListener('click', () => {
|
||
if (pill.textContent === '↻') return;
|
||
document.querySelectorAll('.browser-toolbar .pill').forEach(p => {
|
||
if (p.textContent !== '↻') p.classList.remove('active');
|
||
});
|
||
pill.classList.add('active');
|
||
});
|
||
});
|
||
|
||
// ─── Animate the VU needle and progress for life ────────
|
||
const needle = document.getElementById('vuNeedle');
|
||
const fill = document.querySelector('.progress-fill');
|
||
let pct = 38;
|
||
setInterval(() => {
|
||
const swing = -22 + Math.random() * 44;
|
||
if (needle) needle.style.transform = `rotate(${swing}deg)`;
|
||
}, 220);
|
||
setInterval(() => {
|
||
pct = (pct + 0.08) % 100;
|
||
if (fill) fill.style.width = pct + '%';
|
||
}, 250);
|
||
|
||
// ─── Progress bar click-to-seek ─────────────────────────
|
||
document.querySelector('.progress-track')?.addEventListener('click', (e) => {
|
||
const rect = e.currentTarget.getBoundingClientRect();
|
||
pct = ((e.clientX - rect.left) / rect.width) * 100;
|
||
if (fill) fill.style.width = pct + '%';
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|