Current shipping vinyl: pressed grooves, copper-bordered label rim, full album art on the label. Reference baseline for everything below.
+
+
+
+
+
+ CSS only
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 01
+ Sleeve Frame
+ CSS only
+
+
Vinyl peeks out of a square cardstock sleeve — paper grain, ring-wear circle, worn-corner notch. The album art lives on the sleeve; the disc gets a plain typographic label. Reads instantly as "record on a turntable", not "spinning disc."
+
+
+
+
+
+ CSS only · highest ROI
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 02
+ Sheen, Grain & Dead-Wax
+ CSS only
+
+
Three layers added to the existing vinyl: a fixed reflection sweep (doesn't rotate with the disc — the studio-light look), paper grain on the label so the print sits in cardstock, and a dead-wax engraving of the master‑lacquer code spinning with the disc. Off-center spindle by 1.5%. Highest visual ROI for the smallest amount of new code.
+
+
+
+
+
+ CSS only
+
+
+
+
+
+
+
+
+
+ 03
+ Tone-Graded Cover
+ CSS only
+
+
Same disc, but the album art on the label is color-graded — duotone copper/emerald, deeper saturation drop, vignette around the label rim. Effect: every album cover ends up looking like it came from the same pressing plant, matching the Studio Reference chrome.
+
+
+
+
+
+ CSS hover · JS in production
+
+
+
+
+
+
+
+
+
+
+
+ Hover to play
+
+
+
+
+ 04
+ Sleeve-to-Disc Reveal
+ needs JS
+
+
Hover this card — the disc slides out of the sleeve and starts spinning. In production, this would be wired to the play/pause state: paused = tucked-in sleeve view, playing = disc revealed and spinning. Most evocative, also the most code (animation choreography + state coupling).
+
+
+
+
+
diff --git a/media_server/static/js/app.js b/media_server/static/js/app.js
index beafb85..70ffc81 100644
--- a/media_server/static/js/app.js
+++ b/media_server/static/js/app.js
@@ -14,6 +14,7 @@ import {
VOLUME_THROTTLE_MS, VOLUME_RELEASE_DELAY_MS,
changeLocale, t,
setAuthRequired,
+ showAboutDialog, closeAboutDialog,
} from './core.js';
// Layer 1: Player (tabs, theme, accent, vinyl, visualizer, UI)
@@ -129,6 +130,8 @@ Object.assign(window, {
toggleDisplayPower,
// Audio device
onAudioDeviceChanged,
+ // About
+ showAboutDialog, closeAboutDialog,
});
// ============================================================
@@ -399,6 +402,16 @@ window.addEventListener('DOMContentLoaded', async () => {
}
});
+ // About dialog backdrop click to close
+ const aboutDialog = document.getElementById('aboutDialog');
+ if (aboutDialog) {
+ aboutDialog.addEventListener('click', (e) => {
+ if (e.target === aboutDialog) {
+ closeAboutDialog();
+ }
+ });
+ }
+
// Delegated click handlers for link table actions (XSS-safe)
document.getElementById('linksTableBody').addEventListener('click', (e) => {
const btn = e.target.closest('[data-action]');
diff --git a/media_server/static/js/core.js b/media_server/static/js/core.js
index 6b7959c..2215702 100644
--- a/media_server/static/js/core.js
+++ b/media_server/static/js/core.js
@@ -397,6 +397,16 @@ export function closeDialog(dialog) {
}, { once: true });
}
+export function showAboutDialog() {
+ const dialog = document.getElementById('aboutDialog');
+ if (dialog) dialog.showModal();
+}
+
+export function closeAboutDialog() {
+ const dialog = document.getElementById('aboutDialog');
+ if (dialog) closeDialog(dialog);
+}
+
export function showConfirm(message) {
return new Promise((resolve) => {
const dialog = document.getElementById('confirmDialog');
diff --git a/media_server/static/locales/en.json b/media_server/static/locales/en.json
index 15cefe4..172edc5 100644
--- a/media_server/static/locales/en.json
+++ b/media_server/static/locales/en.json
@@ -259,8 +259,13 @@
"links.msg.load_failed": "Failed to load link details",
"links.confirm.delete": "Are you sure you want to delete the link \"{name}\"?",
"links.confirm.unsaved": "You have unsaved changes. Are you sure you want to discard them?",
- "footer.created_by": "Created by",
- "footer.source_code": "Source Code",
+ "about.button_title": "About",
+ "about.title": "About",
+ "about.created_by": "Created by",
+ "about.email": "Email",
+ "about.repository": "Repository",
+ "about.source_code": "Source Code",
+ "dialog.close": "Close",
"update.available": "Update available: v{version}",
"update.view_release": "View Release"
}
diff --git a/media_server/static/locales/ru.json b/media_server/static/locales/ru.json
index 340a5cd..7b7d3f7 100644
--- a/media_server/static/locales/ru.json
+++ b/media_server/static/locales/ru.json
@@ -259,8 +259,13 @@
"links.msg.load_failed": "Не удалось загрузить данные ссылки",
"links.confirm.delete": "Вы уверены, что хотите удалить ссылку \"{name}\"?",
"links.confirm.unsaved": "У вас есть несохраненные изменения. Вы уверены, что хотите отменить их?",
- "footer.created_by": "Создано",
- "footer.source_code": "Исходный код",
+ "about.button_title": "О программе",
+ "about.title": "О программе",
+ "about.created_by": "Создано",
+ "about.email": "Эл. почта",
+ "about.repository": "Репозиторий",
+ "about.source_code": "Исходный код",
+ "dialog.close": "Закрыть",
"update.available": "Доступно обновление: v{version}",
"update.view_release": "Перейти к релизу"
}
diff --git a/media_server/static/vinyl-variants-mockup.html b/media_server/static/vinyl-variants-mockup.html
new file mode 100644
index 0000000..c7b6034
--- /dev/null
+++ b/media_server/static/vinyl-variants-mockup.html
@@ -0,0 +1,911 @@
+
+
+