- static/ambient/index.html: Drift (aus der Session "Ambient-Synthesizer-Webapp",
eine Datei, Web Audio, offline), eingebettet per iframe (?embed=1&bg=#000)
- app.js: Tab 'ambient', iframe lädt beim ersten Öffnen und füllt den Platz bis
zur Leiste; Radio/Podcast und Ambient wechseln sich ab (postMessage
{type:'ambient', cmd|state})
- sw.js: Ambient im Offline-Cache (ignoreSearch für die Shell-Dateien)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1953 lines
46 KiB
CSS
1953 lines
46 KiB
CSS
/* =========================================================
|
|
diora — dark radio player theme
|
|
========================================================= */
|
|
|
|
/* Self-hosted (no external request, works offline via the service worker's
|
|
app-shell cache) — variable font covering weight 200-900 in one file per
|
|
style/subset. Latin + Latin Extended only (covers German/English text). */
|
|
@font-face {
|
|
font-family: 'Source Serif 4';
|
|
font-style: normal;
|
|
font-weight: 200 900;
|
|
font-display: swap;
|
|
src: url('/static/fonts/SourceSerif4-normal-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: 'Source Serif 4';
|
|
font-style: normal;
|
|
font-weight: 200 900;
|
|
font-display: swap;
|
|
src: url('/static/fonts/SourceSerif4-normal-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: 'Source Serif 4';
|
|
font-style: italic;
|
|
font-weight: 200 900;
|
|
font-display: swap;
|
|
src: url('/static/fonts/SourceSerif4-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: 'Source Serif 4';
|
|
font-style: italic;
|
|
font-weight: 200 900;
|
|
font-display: swap;
|
|
src: url('/static/fonts/SourceSerif4-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;
|
|
}
|
|
|
|
*, *::before, *::after {
|
|
box-sizing: border-box;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
html, body {
|
|
height: 100%;
|
|
overflow: hidden;
|
|
scrollbar-color: #333 #000;
|
|
}
|
|
|
|
::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
::-webkit-scrollbar-track { background: #000; }
|
|
::-webkit-scrollbar-thumb { background: #333; border-radius: 4px; }
|
|
::-webkit-scrollbar-thumb:hover { background: #555; }
|
|
|
|
:root {
|
|
--bg: #000;
|
|
--bg-card: transparent;
|
|
--bg-alt: transparent;
|
|
--bg-row: transparent;
|
|
--bg-row-alt: transparent;
|
|
--border: #2a2a2a;
|
|
--accent: #e63946;
|
|
--accent-hover: #ff4d58;
|
|
--green: #2ecc71;
|
|
--yellow: #f1c40f;
|
|
--font: 'Courier New', Courier, monospace;
|
|
--radius: 3px;
|
|
--nav-h: 48px;
|
|
--bar-h: 72px;
|
|
|
|
/* contrast scheme — default: white on dark */
|
|
--fg: #fff;
|
|
--fg-muted: #fff;
|
|
--outline: #000;
|
|
--text: var(--fg);
|
|
--text-muted: var(--fg);
|
|
}
|
|
|
|
/* inverted: black on white */
|
|
body.bright-bg {
|
|
--bg: #fff;
|
|
--bg-card: #f5f5f5;
|
|
--bg-alt: #f0f0f0;
|
|
--bg-row: #fafafa;
|
|
--bg-row-alt: #f5f5f5;
|
|
--fg: #000;
|
|
--fg-muted: #000;
|
|
--outline: #fff;
|
|
--border: #bbb;
|
|
}
|
|
|
|
html, body {
|
|
background: var(--bg);
|
|
color: var(--fg);
|
|
font-family: var(--font);
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
line-height: 1.5;
|
|
min-height: 100vh;
|
|
text-shadow:
|
|
-1px -1px 0 var(--outline),
|
|
1px -1px 0 var(--outline),
|
|
-1px 1px 0 var(--outline),
|
|
1px 1px 0 var(--outline);
|
|
}
|
|
|
|
/* Elements with their own coloured backgrounds — no outline, no override */
|
|
.btn-play,
|
|
.btn-amazon,
|
|
.btn-lastfm,
|
|
.btn-danger,
|
|
.btn-primary,
|
|
.navbar-brand,
|
|
.reader-toast {
|
|
text-shadow: none;
|
|
}
|
|
|
|
/* Accent colour stays red regardless of scheme */
|
|
.navbar-brand { color: var(--accent); }
|
|
.tab-btn.active { color: var(--accent); }
|
|
|
|
a {
|
|
color: var(--accent);
|
|
text-decoration: none;
|
|
}
|
|
a:hover {
|
|
color: var(--accent-hover);
|
|
text-decoration: underline;
|
|
}
|
|
|
|
/* =========================================================
|
|
NAVBAR
|
|
========================================================= */
|
|
|
|
.navbar {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 100;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
height: var(--nav-h);
|
|
padding: 0 1.5rem;
|
|
background: var(--bg);
|
|
border-bottom: 1px solid var(--border);
|
|
/* Window Controls Overlay: extend navbar into the titlebar area */
|
|
padding-left: max(1.5rem, env(titlebar-area-x, 1.5rem));
|
|
padding-top: env(titlebar-area-y, 0);
|
|
height: calc(var(--nav-h) + env(titlebar-area-y, 0px));
|
|
-webkit-app-region: drag;
|
|
}
|
|
.navbar a, .navbar button, .navbar input, .navbar form {
|
|
-webkit-app-region: no-drag;
|
|
}
|
|
|
|
.navbar-brand {
|
|
font-size: 1.4rem;
|
|
font-weight: bold;
|
|
letter-spacing: 0.05em;
|
|
color: var(--accent);
|
|
text-decoration: none;
|
|
}
|
|
.navbar-brand:hover {
|
|
color: var(--accent-hover);
|
|
text-decoration: none;
|
|
}
|
|
|
|
.navbar-links {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.navbar-user {
|
|
color: var(--text-muted);
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
/* =========================================================
|
|
MAIN CONTENT
|
|
========================================================= */
|
|
|
|
.main-content {
|
|
max-width: 1100px;
|
|
margin: 0 auto;
|
|
padding: calc(var(--nav-h) + 3rem) 1.5rem calc(var(--bar-h) + 2rem);
|
|
height: 100%;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
/* =========================================================
|
|
MESSAGES
|
|
========================================================= */
|
|
|
|
.messages {
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.message {
|
|
padding: 0.6rem 1rem;
|
|
border-radius: var(--radius);
|
|
margin-bottom: 0.4rem;
|
|
background: var(--bg-alt);
|
|
border-left: 3px solid var(--text-muted);
|
|
}
|
|
.message-error { border-color: var(--accent); }
|
|
.message-success { border-color: var(--green); }
|
|
.message-warning { border-color: var(--yellow); }
|
|
|
|
/* =========================================================
|
|
NOW PLAYING BAR (fixed at bottom)
|
|
========================================================= */
|
|
|
|
.now-playing-bar {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: var(--bar-h);
|
|
background: var(--bg);
|
|
border-top: 1px solid var(--border);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 1.5rem;
|
|
z-index: 200;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.now-playing-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
flex: 1;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.now-playing-station {
|
|
font-size: 0.75rem;
|
|
color: var(--text-muted);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.now-playing-track {
|
|
font-size: 0.95rem;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
color: var(--text);
|
|
}
|
|
|
|
.now-playing-controls {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.volume-label {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.4rem;
|
|
color: var(--text-muted);
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
.volume-slider {
|
|
width: 80px;
|
|
accent-color: var(--accent);
|
|
cursor: pointer;
|
|
}
|
|
|
|
/* =========================================================
|
|
AFFILIATE SECTION
|
|
========================================================= */
|
|
|
|
.affiliate-section {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1.25rem;
|
|
background: var(--bg-card);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
padding: 1rem;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.affiliate-artwork {
|
|
width: 80px;
|
|
height: 80px;
|
|
object-fit: cover;
|
|
border-radius: var(--radius);
|
|
flex-shrink: 0;
|
|
background: var(--bg-alt);
|
|
}
|
|
|
|
.affiliate-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.2rem;
|
|
min-width: 0;
|
|
}
|
|
|
|
.affiliate-track {
|
|
font-size: 1rem;
|
|
font-weight: bold;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.affiliate-artist {
|
|
font-size: 0.85rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.affiliate-album {
|
|
font-size: 0.8rem;
|
|
color: var(--text-muted);
|
|
font-style: italic;
|
|
}
|
|
|
|
.btn-amazon {
|
|
display: inline-block;
|
|
margin-top: 0.5rem;
|
|
background: #ff9900;
|
|
color: #000;
|
|
padding: 0.3rem 0.8rem;
|
|
border-radius: var(--radius);
|
|
font-weight: bold;
|
|
font-size: 0.8rem;
|
|
}
|
|
.btn-amazon:hover {
|
|
background: #ffad33;
|
|
color: #000;
|
|
text-decoration: none;
|
|
}
|
|
|
|
/* =========================================================
|
|
TABS
|
|
========================================================= */
|
|
|
|
.tabs {
|
|
display: flex;
|
|
border-bottom: 1px solid var(--border);
|
|
margin-bottom: 1.25rem;
|
|
gap: 0;
|
|
}
|
|
|
|
/* Haupttab-Leiste: immer sichtbar unterhalb der Navbar */
|
|
#tabs {
|
|
position: fixed;
|
|
top: var(--nav-h);
|
|
left: 0;
|
|
right: 0;
|
|
z-index: 150;
|
|
background: var(--bg);
|
|
margin-bottom: 0;
|
|
padding: 0 0.5rem;
|
|
}
|
|
|
|
.tab-btn {
|
|
background: none;
|
|
border: none;
|
|
border-bottom: 2px solid transparent;
|
|
color: var(--text-muted);
|
|
cursor: pointer;
|
|
font-family: var(--font);
|
|
font-size: 0.9rem;
|
|
padding: 0.5rem 1.25rem;
|
|
transition: color 0.15s, border-color 0.15s;
|
|
}
|
|
|
|
.tab-btn:hover {
|
|
color: var(--text);
|
|
}
|
|
|
|
.tab-btn.active {
|
|
color: var(--accent);
|
|
border-bottom-color: var(--accent);
|
|
}
|
|
|
|
.tab-panel {
|
|
min-height: 200px;
|
|
}
|
|
|
|
/* Ambient: the synthesizer's frame fills the room down to the player bar (app.js _ambientSize). */
|
|
.ambient-frame {
|
|
display: block;
|
|
width: 100%;
|
|
height: 70vh;
|
|
border: 0;
|
|
background: #000;
|
|
}
|
|
|
|
.sub-tabs {
|
|
border-bottom-color: transparent;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.sub-tabs .tab-btn {
|
|
font-size: 0.8rem;
|
|
padding: 0.3rem 1rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.sub-tabs .tab-btn.active {
|
|
color: var(--accent);
|
|
border-bottom-color: var(--accent);
|
|
}
|
|
|
|
/* =========================================================
|
|
SEARCH
|
|
========================================================= */
|
|
|
|
.search-bar {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.search-input {
|
|
flex: 1;
|
|
background: transparent;
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
color: var(--text);
|
|
font-family: var(--font);
|
|
font-size: 0.9rem;
|
|
padding: 0.4rem 0.7rem;
|
|
outline: none;
|
|
}
|
|
.search-input:focus {
|
|
border-color: var(--accent);
|
|
}
|
|
|
|
.status-msg {
|
|
color: var(--text-muted);
|
|
font-size: 0.85rem;
|
|
min-height: 1.4em;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
/* =========================================================
|
|
TABLES
|
|
========================================================= */
|
|
|
|
.data-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.data-table th {
|
|
background: var(--bg-alt);
|
|
border-bottom: 1px solid var(--border);
|
|
color: var(--text-muted);
|
|
font-weight: normal;
|
|
padding: 0.45rem 0.7rem;
|
|
text-align: left;
|
|
}
|
|
|
|
.data-table td {
|
|
padding: 0.4rem 0.7rem;
|
|
border-bottom: 1px solid var(--border);
|
|
vertical-align: middle;
|
|
max-width: 220px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.data-table tbody tr:nth-child(odd) { background: transparent; }
|
|
.data-table tbody tr:nth-child(even) { background: transparent; }
|
|
.data-table tbody tr:hover { background: rgba(255, 255, 255, 0.05); }
|
|
|
|
.empty-msg {
|
|
color: var(--text-muted);
|
|
text-align: center;
|
|
padding: 1.5rem !important;
|
|
}
|
|
|
|
.history-time {
|
|
color: var(--text-muted);
|
|
font-size: 0.75rem;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* =========================================================
|
|
BUTTONS
|
|
========================================================= */
|
|
|
|
.btn {
|
|
background: transparent;
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
color: var(--text);
|
|
cursor: pointer;
|
|
font-family: var(--font);
|
|
font-size: 0.85rem;
|
|
padding: 0.35rem 0.75rem;
|
|
transition: background 0.1s, border-color 0.1s;
|
|
white-space: nowrap;
|
|
}
|
|
.btn:hover {
|
|
background: rgba(255, 255, 255, 0.07);
|
|
border-color: #444;
|
|
}
|
|
|
|
.btn-play {
|
|
background: var(--accent);
|
|
border-color: var(--accent);
|
|
color: #fff;
|
|
font-size: 0.9rem;
|
|
padding: 0.4rem 1rem;
|
|
}
|
|
.btn-play:hover {
|
|
background: var(--accent-hover);
|
|
border-color: var(--accent-hover);
|
|
}
|
|
.btn-play.playing {
|
|
background: #333;
|
|
border-color: #555;
|
|
color: var(--text);
|
|
}
|
|
|
|
.btn-save {
|
|
color: var(--yellow);
|
|
border-color: #444;
|
|
}
|
|
.btn-save:hover {
|
|
background: #2a2a00;
|
|
border-color: var(--yellow);
|
|
}
|
|
|
|
.btn-sm {
|
|
font-size: 0.78rem;
|
|
padding: 0.25rem 0.5rem;
|
|
}
|
|
|
|
.btn-primary {
|
|
background: var(--accent);
|
|
border-color: var(--accent);
|
|
color: #fff;
|
|
}
|
|
.btn-primary:hover {
|
|
background: var(--accent-hover);
|
|
}
|
|
|
|
.btn-danger {
|
|
background: #300;
|
|
border-color: #700;
|
|
color: #faa;
|
|
}
|
|
.btn-danger:hover {
|
|
background: #500;
|
|
border-color: #a00;
|
|
color: #fff;
|
|
}
|
|
|
|
.btn-lastfm {
|
|
background: #d51007;
|
|
border-color: #d51007;
|
|
color: #fff;
|
|
display: inline-block;
|
|
padding: 0.4rem 1rem;
|
|
border-radius: var(--radius);
|
|
font-family: var(--font);
|
|
font-size: 0.9rem;
|
|
cursor: pointer;
|
|
border: 1px solid transparent;
|
|
}
|
|
.btn-lastfm:hover {
|
|
background: #ff1a0e;
|
|
color: #fff;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.btn-full {
|
|
width: 100%;
|
|
padding: 0.55rem;
|
|
font-size: 0.95rem;
|
|
}
|
|
|
|
.btn-link {
|
|
background: none;
|
|
border: none;
|
|
color: var(--accent);
|
|
cursor: pointer;
|
|
font-family: var(--font);
|
|
font-size: 1rem;
|
|
padding: 0;
|
|
}
|
|
.btn-link:hover {
|
|
color: var(--accent-hover);
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.btn-icon {
|
|
background: none;
|
|
border: none;
|
|
color: var(--text-muted);
|
|
cursor: pointer;
|
|
font-size: 1rem;
|
|
padding: 0;
|
|
line-height: 1;
|
|
}
|
|
.btn-icon.active {
|
|
color: var(--yellow);
|
|
}
|
|
.btn-icon:hover {
|
|
color: var(--yellow);
|
|
}
|
|
|
|
.inline-form {
|
|
display: inline;
|
|
}
|
|
|
|
/* =========================================================
|
|
AUTH FORMS
|
|
========================================================= */
|
|
|
|
.auth-container {
|
|
max-width: 400px;
|
|
margin: 3rem auto;
|
|
background: var(--bg-card);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
padding: 2rem;
|
|
}
|
|
|
|
.auth-title {
|
|
font-size: 1.4rem;
|
|
margin-bottom: 1.5rem;
|
|
color: var(--text);
|
|
}
|
|
|
|
.auth-form {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.form-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.25rem;
|
|
}
|
|
|
|
.form-label {
|
|
font-size: 0.85rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.auth-form input[type="text"],
|
|
.auth-form input[type="password"],
|
|
.auth-form input[type="email"] {
|
|
background: var(--bg-alt);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
color: var(--text);
|
|
font-family: var(--font);
|
|
font-size: 0.9rem;
|
|
padding: 0.45rem 0.7rem;
|
|
outline: none;
|
|
width: 100%;
|
|
}
|
|
.auth-form input:focus {
|
|
border-color: var(--accent);
|
|
}
|
|
|
|
.field-errors {
|
|
list-style: none;
|
|
color: var(--accent);
|
|
font-size: 0.8rem;
|
|
}
|
|
|
|
.form-errors {
|
|
color: var(--accent);
|
|
font-size: 0.85rem;
|
|
background: #1a0000;
|
|
border: 1px solid #500;
|
|
border-radius: var(--radius);
|
|
padding: 0.5rem 0.75rem;
|
|
}
|
|
|
|
.field-help {
|
|
color: var(--text-muted);
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
.auth-switch {
|
|
margin-top: 1.25rem;
|
|
font-size: 0.85rem;
|
|
color: var(--text-muted);
|
|
text-align: center;
|
|
}
|
|
|
|
/* =========================================================
|
|
SETTINGS
|
|
========================================================= */
|
|
|
|
.settings-container {
|
|
max-width: 600px;
|
|
margin: 2rem auto;
|
|
}
|
|
|
|
.settings-title {
|
|
font-size: 1.4rem;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.settings-section {
|
|
background: var(--bg-card);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
padding: 1.25rem;
|
|
margin-bottom: 1.25rem;
|
|
}
|
|
|
|
.settings-section h2 {
|
|
font-size: 1rem;
|
|
color: var(--text-muted);
|
|
margin-bottom: 1rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.08em;
|
|
}
|
|
|
|
.lastfm-description {
|
|
color: var(--text-muted);
|
|
font-size: 0.85rem;
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
|
|
.connected-status {
|
|
margin-bottom: 0.75rem;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.checkbox-label {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
cursor: pointer;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.auth-prompt {
|
|
color: var(--text-muted);
|
|
padding: 1.5rem 0;
|
|
}
|
|
|
|
/* --- WebDAV cloud connections (settings) --- */
|
|
|
|
.webdav-list {
|
|
list-style: none;
|
|
margin: 0 0 1rem;
|
|
padding: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.webdav-item {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 0.75rem;
|
|
flex-wrap: wrap;
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
padding: 0.6rem 0.75rem;
|
|
}
|
|
|
|
.webdav-item-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.15rem;
|
|
min-width: 0;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.webdav-item-actions {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.webdav-form {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.6rem;
|
|
border-top: 1px solid var(--border);
|
|
padding-top: 1rem;
|
|
}
|
|
|
|
.webdav-field {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.25rem;
|
|
font-size: 0.85rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.webdav-field input {
|
|
background: var(--bg-alt);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
color: var(--text);
|
|
font-family: var(--font);
|
|
font-size: 0.9rem;
|
|
padding: 0.45rem 0.7rem;
|
|
outline: none;
|
|
width: 100%;
|
|
}
|
|
|
|
.webdav-field input:focus {
|
|
border-color: var(--accent);
|
|
}
|
|
|
|
.webdav-form .btn {
|
|
align-self: flex-start;
|
|
}
|
|
|
|
/* --- Cloud import browser (books tab) --- */
|
|
|
|
.cloud-browser {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
padding: 0.75rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.cloud-browser-head {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 0.75rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.cloud-browser-head select {
|
|
background: var(--bg-alt);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
color: var(--text);
|
|
font-family: var(--font);
|
|
font-size: 0.85rem;
|
|
padding: 0.3rem 0.5rem;
|
|
outline: none;
|
|
}
|
|
|
|
.cloud-path {
|
|
font-size: 0.8rem;
|
|
color: var(--text-muted);
|
|
word-break: break-all;
|
|
}
|
|
|
|
.cloud-entries {
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
max-height: 320px;
|
|
overflow-y: auto;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.cloud-entry {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 0.75rem;
|
|
padding: 0.4rem 0.25rem;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
|
|
.cloud-entry:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.cloud-entry-name {
|
|
background: none;
|
|
border: none;
|
|
color: inherit;
|
|
font-family: var(--font);
|
|
font-size: 0.9rem;
|
|
text-align: left;
|
|
padding: 0;
|
|
cursor: pointer;
|
|
word-break: break-all;
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.cloud-entry-name:hover {
|
|
color: var(--accent);
|
|
}
|
|
|
|
.cloud-entry-size {
|
|
font-size: 0.75rem;
|
|
color: var(--text-muted);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* =========================================================
|
|
RESPONSIVE
|
|
========================================================= */
|
|
|
|
/* =========================================================
|
|
RECOMMENDATIONS
|
|
========================================================= */
|
|
|
|
.recommendations-section {
|
|
padding: 12px 16px 4px;
|
|
border-bottom: 1px solid #222;
|
|
}
|
|
.recommendations-context {
|
|
font-size: 0.75rem;
|
|
color: var(--fg);
|
|
margin: 0 0 8px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
}
|
|
.recommendations-list {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
}
|
|
.recommendations-list li {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
.muted {
|
|
color: var(--fg);
|
|
font-size: 0.8rem;
|
|
}
|
|
|
|
@media (max-width: 600px) {
|
|
.main-content {
|
|
padding: calc(var(--nav-h) + 3rem) 0.75rem calc(var(--bar-h) + 1.5rem);
|
|
}
|
|
#tabs {
|
|
padding: 0 0.25rem;
|
|
}
|
|
|
|
.now-playing-bar {
|
|
flex-direction: column;
|
|
height: auto;
|
|
padding: 0.5rem 0.75rem;
|
|
gap: 0.4rem;
|
|
}
|
|
|
|
.now-playing-info {
|
|
width: 100%;
|
|
}
|
|
|
|
.now-playing-controls {
|
|
width: 100%;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.volume-label,
|
|
.volume-slider,
|
|
.volume-num {
|
|
display: none;
|
|
}
|
|
|
|
.affiliate-section {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.data-table th:nth-child(3),
|
|
.data-table td:nth-child(3),
|
|
.data-table th:nth-child(4),
|
|
.data-table td:nth-child(4) {
|
|
display: none;
|
|
}
|
|
|
|
.auth-container {
|
|
margin: 1rem;
|
|
padding: 1.25rem;
|
|
}
|
|
}
|
|
|
|
.import-bar {
|
|
padding: 8px 16px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
border-bottom: 1px solid #1a1a1a;
|
|
}
|
|
|
|
/* =========================================================
|
|
TIMER WIDGET
|
|
========================================================= */
|
|
|
|
.timer-widget {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
font-variant-numeric: tabular-nums;
|
|
margin-left: 8px;
|
|
}
|
|
|
|
.timer-phase {
|
|
font-size: 0.7rem;
|
|
color: var(--fg);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
width: 36px;
|
|
text-align: right;
|
|
}
|
|
|
|
.timer-display {
|
|
font-size: 1rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.05em;
|
|
min-width: 48px;
|
|
}
|
|
|
|
/* =========================================================
|
|
DO NOT DISTURB / FOCUS MODE
|
|
========================================================= */
|
|
|
|
body.dnd-mode .navbar,
|
|
body.dnd-mode .tabs,
|
|
body.dnd-mode .tab-panel,
|
|
body.dnd-mode .affiliate-section {
|
|
display: none !important;
|
|
}
|
|
|
|
body.dnd-mode .now-playing-bar {
|
|
position: fixed;
|
|
inset: 0;
|
|
height: 100vh;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
gap: 24px;
|
|
background: transparent;
|
|
z-index: 9999;
|
|
}
|
|
|
|
body.dnd-mode.dnd-dark {
|
|
background: #000 !important;
|
|
background-image: none !important;
|
|
}
|
|
|
|
body.dnd-mode.dnd-dark .now-playing-bar {
|
|
background: #000;
|
|
}
|
|
|
|
.dnd-only {
|
|
display: none;
|
|
}
|
|
|
|
body.dnd-mode .dnd-only {
|
|
display: inline;
|
|
}
|
|
|
|
body.dnd-mode .now-playing-info {
|
|
text-align: center;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
|
|
body.dnd-mode .now-playing-station {
|
|
font-size: 1.6rem;
|
|
}
|
|
|
|
body.dnd-mode .now-playing-track {
|
|
font-size: 1rem;
|
|
color: var(--fg);
|
|
}
|
|
|
|
body.dnd-mode .timer-display {
|
|
font-size: 3.5rem;
|
|
}
|
|
|
|
/* =========================================================
|
|
MOOD CHIPS
|
|
========================================================= */
|
|
|
|
.mood-chips {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 6px;
|
|
padding: 8px 16px;
|
|
border-bottom: 1px solid #1a1a1a;
|
|
}
|
|
|
|
.mood-chip {
|
|
background: transparent;
|
|
border: 1px solid #333;
|
|
color: #ccc;
|
|
border-radius: 999px;
|
|
padding: 3px 12px;
|
|
font-size: 0.78rem;
|
|
cursor: pointer;
|
|
transition: background 0.15s, border-color 0.15s;
|
|
}
|
|
|
|
.mood-chip:hover {
|
|
background: rgba(255, 255, 255, 0.05);
|
|
border-color: #555;
|
|
}
|
|
|
|
.focus-today {
|
|
font-size: 0.72rem;
|
|
color: var(--fg);
|
|
margin-left: 8px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.curated-lists-container {
|
|
padding: 12px 16px 0;
|
|
}
|
|
|
|
.curated-section {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.curated-label {
|
|
font-size: 0.75rem;
|
|
color: var(--fg);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.06em;
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.curated-stations {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
}
|
|
|
|
.curated-stations li {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.curated-name {
|
|
font-size: 0.85rem;
|
|
color: var(--fg);
|
|
}
|
|
|
|
.btn-delete-history {
|
|
background: none;
|
|
border: none;
|
|
color: var(--muted, #888);
|
|
cursor: pointer;
|
|
font-size: 0.75rem;
|
|
padding: 0 4px;
|
|
opacity: 0.5;
|
|
}
|
|
.btn-delete-history:hover { opacity: 1; color: #e55; }
|
|
|
|
#donation-hint {
|
|
position: fixed;
|
|
bottom: 24px;
|
|
right: 24px;
|
|
background: var(--surface, #1e1e2e);
|
|
border: 1px solid var(--border, #333);
|
|
border-radius: 8px;
|
|
padding: 12px 16px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
font-size: 0.85rem;
|
|
box-shadow: 0 4px 16px rgba(0,0,0,0.4);
|
|
z-index: 999;
|
|
animation: hint-in 0.3s ease;
|
|
max-width: 320px;
|
|
}
|
|
|
|
#donation-hint.hiding {
|
|
animation: hint-out 0.4s ease forwards;
|
|
}
|
|
|
|
#donation-hint button {
|
|
background: none;
|
|
border: none;
|
|
cursor: pointer;
|
|
color: var(--muted, #888);
|
|
font-size: 0.8rem;
|
|
padding: 0;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
#donation-hint button:hover { color: var(--fg, #fff); }
|
|
|
|
@keyframes hint-in {
|
|
from { opacity: 0; transform: translateY(12px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
@keyframes hint-out {
|
|
from { opacity: 1; transform: translateY(0); }
|
|
to { opacity: 0; transform: translateY(12px); }
|
|
}
|
|
|
|
.featured-section {
|
|
padding: 8px 0 12px;
|
|
border-bottom: 1px solid var(--border, #333);
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.featured-label {
|
|
font-size: 0.75rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.08em;
|
|
color: var(--muted, #888);
|
|
margin: 0 0 8px;
|
|
}
|
|
|
|
.featured-list {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
}
|
|
|
|
.featured-list li {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.volume-num {
|
|
width: 44px;
|
|
background: var(--surface, #1e1e2e);
|
|
border: 1px solid var(--border, #333);
|
|
border-radius: 4px;
|
|
color: var(--fg, #fff);
|
|
font-size: 0.8rem;
|
|
padding: 2px 4px;
|
|
text-align: center;
|
|
}
|
|
|
|
.volume-num::-webkit-inner-spin-button,
|
|
.volume-num::-webkit-outer-spin-button { display: none; }
|
|
.volume-num { -moz-appearance: textfield; }
|
|
|
|
/* ===== SIDEBAR ===== */
|
|
|
|
.sidebar-overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
background: rgba(0, 0, 0, 0.45);
|
|
z-index: 299;
|
|
}
|
|
|
|
.sidebar {
|
|
position: fixed;
|
|
top: 0;
|
|
right: 0;
|
|
width: 400px;
|
|
max-width: 100vw;
|
|
/* stop above the now-playing bar */
|
|
bottom: 72px;
|
|
background: var(--surface, #111);
|
|
border-left: 1px solid var(--border, #333);
|
|
z-index: 300;
|
|
display: flex;
|
|
flex-direction: column;
|
|
transform: translateX(100%);
|
|
transition: transform 0.22s ease;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.sidebar.open {
|
|
transform: translateX(0);
|
|
}
|
|
|
|
.sidebar-header {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: 10px;
|
|
padding: 14px 16px 10px;
|
|
border-bottom: 1px solid var(--border, #333);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.sidebar-title {
|
|
font-weight: bold;
|
|
font-size: 0.95rem;
|
|
line-height: 1.3;
|
|
}
|
|
|
|
.sidebar-close {
|
|
flex-shrink: 0;
|
|
margin-top: -2px;
|
|
}
|
|
|
|
.sidebar-body {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 16px;
|
|
font-size: 0.88rem;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
/* ===== MODAL DIALOG ===== */
|
|
|
|
.modal-overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
background: rgba(0, 0, 0, 0.55);
|
|
z-index: 500;
|
|
}
|
|
|
|
.modal-dialog {
|
|
position: fixed;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
background: var(--surface, #111);
|
|
border: 1px solid var(--border, #333);
|
|
border-radius: var(--radius);
|
|
padding: 20px;
|
|
width: 360px;
|
|
max-width: calc(100vw - 32px);
|
|
z-index: 501;
|
|
}
|
|
|
|
.modal-message {
|
|
margin: 0 0 16px;
|
|
white-space: pre-wrap;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.modal-input { width: 100%; margin-bottom: 16px; }
|
|
|
|
.modal-actions { display: flex; justify-content: flex-end; gap: 8px; }
|
|
|
|
/* Style links and basic HTML inside shownotes */
|
|
.sidebar-body a { color: var(--accent, #e63946); }
|
|
.sidebar-body p { margin: 0 0 10px; }
|
|
.sidebar-body ul, .sidebar-body ol { margin: 0 0 10px; padding-left: 20px; }
|
|
.sidebar-body h1, .sidebar-body h2, .sidebar-body h3 {
|
|
font-size: 0.9rem;
|
|
margin: 12px 0 4px;
|
|
}
|
|
|
|
.tts-lang-select {
|
|
background: var(--surface, #1e1e2e); color: var(--fg, #fff);
|
|
border: 1px solid var(--border, #444); border-radius: 4px;
|
|
padding: 2px 4px; font-size: 0.8rem; cursor: pointer;
|
|
}
|
|
|
|
.reader-marker-btn-mobile { display: none; }
|
|
|
|
@media (max-width: 600px) {
|
|
.sidebar { width: 100vw; }
|
|
|
|
/* Reader auf Mobile: Header kompakter, Fortschrittsfeld versteckt */
|
|
.reader-header { padding: 6px 8px; gap: 4px; }
|
|
.reader-title { font-size: 13px; }
|
|
.reader-header-actions { gap: 4px; }
|
|
.reader-progress-wrap { display: none; }
|
|
.reader-content { padding: 16px 10px; }
|
|
|
|
/* On a phone, opening the 150px margin just to reach the marker-mode toggle
|
|
eats too much of an already-narrow reading column — surface it directly
|
|
in the main header instead (desktop keeps it only in the margin header,
|
|
grouped with "Notes", per earlier feedback against back-and-forth). */
|
|
.reader-marker-btn-mobile { display: inline-block; }
|
|
}
|
|
|
|
/* =========================================================
|
|
Ebook reader + book list + focus station
|
|
========================================================= */
|
|
|
|
/* --- Drop zone --- */
|
|
.book-drop-zone {
|
|
border: 2px dashed var(--border);
|
|
border-radius: var(--radius);
|
|
padding: 24px 16px;
|
|
text-align: center;
|
|
margin: 12px 0;
|
|
cursor: pointer;
|
|
transition: border-color 0.15s, background 0.15s;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
align-items: center;
|
|
}
|
|
.book-drop-zone:hover,
|
|
.book-drop-zone.drag-over {
|
|
border-color: var(--accent);
|
|
background: rgba(255,255,255,0.04);
|
|
}
|
|
.book-drop-zone label {
|
|
color: var(--accent);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.enc-key-prompt {
|
|
padding: 24px 0 16px;
|
|
}
|
|
|
|
.offline-notice {
|
|
font-size: 13px;
|
|
color: var(--accent);
|
|
padding: 4px 0 8px;
|
|
}
|
|
|
|
.book-key-bar {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
margin-top: 1rem;
|
|
padding-top: 0.75rem;
|
|
border-top: 1px solid var(--border);
|
|
}
|
|
|
|
/* --- Book list --- */
|
|
.book-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
margin-top: 8px;
|
|
}
|
|
.book-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 8px 10px;
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
gap: 12px;
|
|
}
|
|
.book-item-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
min-width: 0;
|
|
}
|
|
.book-title {
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.book-author,
|
|
.book-progress {
|
|
font-size: 12px;
|
|
}
|
|
.book-item-meta-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
.book-item-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
flex-shrink: 0;
|
|
}
|
|
.book-read-badge {
|
|
font-size: 12px;
|
|
font-weight: normal;
|
|
}
|
|
.book-shelf-badge {
|
|
display: inline-block;
|
|
font-size: 11px;
|
|
padding: 1px 7px;
|
|
border: 1px solid var(--border);
|
|
border-radius: 10px;
|
|
color: var(--muted, #888);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* --- Per-book "⋮" menu (everything except Open) --- */
|
|
.book-item-menu {
|
|
position: relative;
|
|
}
|
|
.book-item-menu-list {
|
|
display: none;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
position: absolute;
|
|
right: 0;
|
|
top: calc(100% + 4px);
|
|
z-index: 20;
|
|
min-width: 220px;
|
|
background: var(--surface, #111);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
padding: 4px;
|
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
|
}
|
|
.book-item-menu-list.open {
|
|
display: flex;
|
|
}
|
|
.book-menu-item {
|
|
background: none;
|
|
border: none;
|
|
color: var(--fg);
|
|
text-align: left;
|
|
padding: 8px 10px;
|
|
border-radius: var(--radius);
|
|
cursor: pointer;
|
|
font: inherit;
|
|
font-size: 13px;
|
|
white-space: nowrap;
|
|
}
|
|
.book-menu-item:hover {
|
|
background: var(--bg-row, rgba(255, 255, 255, 0.08));
|
|
}
|
|
.book-menu-item--danger {
|
|
color: var(--accent, #e63946);
|
|
}
|
|
.book-list-filter {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
font-size: 13px;
|
|
color: var(--muted, #888);
|
|
margin-top: 10px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
/* --- Recently-read section (always pinned atop the books view, plain list style) --- */
|
|
.book-recent-section {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
margin-bottom: 14px;
|
|
padding-bottom: 14px;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.book-recent-title {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
margin: 0;
|
|
color: var(--muted, #888);
|
|
}
|
|
|
|
/* --- Book folders (single level) --- */
|
|
.book-folder-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
gap: 8px;
|
|
margin-bottom: 10px;
|
|
}
|
|
.book-folder-tile {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 4px;
|
|
padding: 12px 8px;
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
background: none;
|
|
color: var(--fg);
|
|
cursor: pointer;
|
|
text-align: center;
|
|
font: inherit;
|
|
}
|
|
.book-folder-tile-icon {
|
|
font-size: 22px;
|
|
}
|
|
.book-folder-tile-name {
|
|
font-size: 13px;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
max-width: 100%;
|
|
}
|
|
.book-folder-tile-count {
|
|
font-size: 11px;
|
|
color: var(--muted, #888);
|
|
}
|
|
.book-folder-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
/* --- PDF pages --- */
|
|
.pdf-page-wrapper {
|
|
margin: 0 auto 1rem;
|
|
display: flex;
|
|
justify-content: center;
|
|
max-width: none;
|
|
}
|
|
|
|
.pdf-page {
|
|
display: block;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.25);
|
|
}
|
|
|
|
/* --- Reader overlay --- */
|
|
.reader-overlay {
|
|
position: fixed;
|
|
top: var(--nav-h);
|
|
left: 0;
|
|
right: 0;
|
|
bottom: var(--bar-h);
|
|
background: var(--bg);
|
|
z-index: 200;
|
|
display: flex;
|
|
flex-direction: column;
|
|
/* The global white text-outline (for readability over a custom background
|
|
image elsewhere in the app) is never needed here — the reader always
|
|
sits on its own opaque background — and it muddies highlighted text
|
|
badly when combined with a colored highlight background. */
|
|
text-shadow: none;
|
|
}
|
|
.reader-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 8px 16px;
|
|
border-bottom: 1px solid var(--border);
|
|
flex-shrink: 0;
|
|
gap: 12px;
|
|
}
|
|
.reader-title {
|
|
font-weight: 700;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
flex: 1;
|
|
}
|
|
.reader-header-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.reader-progress-wrap {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
}
|
|
.reader-content {
|
|
flex: 1;
|
|
overflow-y: scroll;
|
|
overflow-x: auto;
|
|
position: relative;
|
|
padding: 24px 16px;
|
|
line-height: 1.8;
|
|
font-family: 'Source Serif 4', Georgia, 'Times New Roman', serif;
|
|
font-size: 16px;
|
|
font-weight: 380;
|
|
}
|
|
.reader-content > * {
|
|
max-width: 65ch;
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
}
|
|
.reader-content p {
|
|
margin-bottom: 1em;
|
|
}
|
|
.reader-content h1, .reader-content h2, .reader-content h3 {
|
|
margin: 1.4em 0 0.6em;
|
|
font-family: var(--font);
|
|
}
|
|
|
|
/* --- Focus station sidebar --- */
|
|
/* --- Radio sidebar --- */
|
|
.rsb-nowplaying { margin-bottom: 12px; }
|
|
.rsb-station-name { font-weight: 600; }
|
|
.rsb-track { font-size: 0.85rem; margin-top: 2px; }
|
|
.rsb-controls { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; flex-wrap: wrap; }
|
|
.rsb-vol { display: flex; align-items: center; gap: 6px; font-size: 0.85rem; color: var(--muted, #888); }
|
|
.rsb-station-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 6px; }
|
|
|
|
/* --- Table of contents sidebar --- */
|
|
.toc-list {
|
|
list-style: none;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
}
|
|
.toc-entry {
|
|
display: block;
|
|
width: 100%;
|
|
text-align: left;
|
|
padding: 4px 0;
|
|
font-size: 13px;
|
|
white-space: normal;
|
|
word-break: break-word;
|
|
}
|
|
.toc-entry:hover {
|
|
color: var(--accent);
|
|
}
|
|
|
|
/* Focus station pending (playing interrupted) */
|
|
#focus-station-btn.focus-pending {
|
|
color: var(--accent);
|
|
animation: focus-pulse 1.4s ease-in-out infinite;
|
|
}
|
|
@keyframes focus-pulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.4; }
|
|
}
|
|
|
|
/* =========================================================
|
|
Reader feature additions
|
|
========================================================= */
|
|
|
|
/* Reader themes */
|
|
.reader-theme-sepia .reader-content { background:#f5e6c8; color:#3b2a1a; }
|
|
.reader-theme-bright .reader-content { background:#fff; color:#111; }
|
|
.reader-content > * { max-width:var(--reader-max-width,65ch); margin-left:auto; margin-right:auto; }
|
|
.reader-content > .pdf-page-wrapper { max-width: none; margin: 0 auto 1rem; }
|
|
|
|
/* Inline panels */
|
|
.reader-settings-panel, .reader-search-bar {
|
|
display:flex; align-items:center; gap:12px; padding:8px 16px;
|
|
border-bottom:1px solid var(--border); flex-shrink:0; flex-wrap:wrap; background:var(--bg);
|
|
}
|
|
.reader-settings-panel input[type="range"] { width:80px; }
|
|
|
|
/* PDF viewport wrapper — CSS zoom applied here for smooth zoom without re-render */
|
|
#pdf-viewport { transform-origin: top left; }
|
|
|
|
/* PDF inner container — shares origin with canvas so text layer aligns exactly */
|
|
.pdf-page-inner { position:relative; display:inline-block; line-height:0; }
|
|
|
|
/* PDF text layer — sits flush over the canvas inside pdf-page-inner */
|
|
.pdf-text-layer { position:absolute; top:0; left:0; width:100%; height:100%; overflow:hidden; pointer-events:none; }
|
|
.pdf-text-layer span { position:absolute; color:transparent !important; background:transparent; white-space:pre; cursor:text; pointer-events:auto; user-select:text; -webkit-user-select:text; line-height:1; }
|
|
.pdf-text-layer span::selection { background:rgba(0,120,255,0.3); }
|
|
.pdf-text-layer span.reader-search-match { background:rgba(241,196,15,.5); }
|
|
.pdf-text-layer span.reader-search-match.active { background:rgba(230,57,70,.6); }
|
|
|
|
/* PDF invert */
|
|
#reader-overlay.pdf-inverted .pdf-page { filter:invert(1); }
|
|
|
|
/* ---- Immersive reader mode ---- */
|
|
.navbar { transition: transform 0.35s ease; }
|
|
.now-playing-bar{ transition: transform 0.35s ease; }
|
|
.reader-overlay { transition: top 0.35s ease, bottom 0.35s ease; }
|
|
.reader-header { overflow: hidden; max-height: 60px;
|
|
transition: max-height 0.35s ease, padding-top 0.35s ease, padding-bottom 0.35s ease; }
|
|
|
|
body.reader-immersive .navbar { transform: translateY(-100%); }
|
|
body.reader-immersive .now-playing-bar { transform: translateY(100%); }
|
|
body.reader-immersive .reader-overlay { top: 0; bottom: 0; }
|
|
body.reader-immersive .reader-header { max-height: 0; padding-top: 0; padding-bottom: 0; }
|
|
|
|
body.reader-immersive.reader-show-top .navbar { transform: none; }
|
|
body.reader-immersive.reader-show-top .reader-overlay { top: var(--nav-h); }
|
|
body.reader-immersive.reader-show-top .reader-header { max-height: 60px; padding-top: 8px; padding-bottom: 8px; }
|
|
|
|
body.reader-immersive.reader-show-bottom .now-playing-bar { transform: none; }
|
|
body.reader-immersive.reader-show-bottom .reader-overlay { bottom: var(--bar-h); }
|
|
|
|
/* PDF paginated */
|
|
.reader-content.pdf-paginated { overflow:hidden !important; display:flex; align-items:center; justify-content:center; }
|
|
.pdf-paginated .pdf-page-wrapper { margin:0; }
|
|
|
|
/* PDF loading overlay */
|
|
.pdf-loading-overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: rgba(20, 20, 20, 0.85);
|
|
z-index: 200;
|
|
}
|
|
.pdf-loading-spinner {
|
|
width: 36px;
|
|
height: 36px;
|
|
border: 3px solid #333;
|
|
border-top-color: var(--accent, #e63946);
|
|
border-radius: 50%;
|
|
animation: pdf-spin 0.7s linear infinite;
|
|
}
|
|
@keyframes pdf-spin { to { transform: rotate(360deg); } }
|
|
|
|
/* PDF two-page spread */
|
|
.pdf-spread-wrapper { display:flex; flex-direction:row; gap:8px; justify-content:center; margin-bottom:1rem; }
|
|
.pdf-spread-wrapper .pdf-page-wrapper { margin:0; }
|
|
.pdf-spread-cover { margin-bottom:1rem; }
|
|
/* Disable text selection during pinch */
|
|
#reader-content.pinch-active { user-select:none; -webkit-user-select:none; }
|
|
|
|
/* Highlight popover */
|
|
.highlight-popover { position:fixed; z-index:500; display:flex; gap:6px; background:var(--bg-card,#1a1a1a); border:1px solid var(--border); border-radius:var(--radius); padding:6px 8px; box-shadow:0 4px 16px rgba(0,0,0,.5); }
|
|
.hl-color-btn { width:24px; height:24px; border-radius:50%; border:2px solid transparent; cursor:pointer; font-weight:700; font-size:12px; color:#000; line-height:1; }
|
|
.hl-color-btn:hover { border-color:#fff; }
|
|
.hl-note-btn { background:none; border:1px solid var(--border); color:var(--fg); padding:2px 6px; border-radius:var(--radius); cursor:pointer; }
|
|
|
|
/* Footnote popover */
|
|
.footnote-popover { position:fixed; z-index:500; max-width:min(400px,90vw); max-height:220px; overflow-y:auto; background:#1a1a1a; color:#e8e8e8; border:1px solid #444; border-radius:var(--radius); padding:10px 28px 10px 12px; box-shadow:0 4px 20px rgba(0,0,0,.6); font-size:0.88em; line-height:1.55; pointer-events:auto; }
|
|
.footnote-popover.reader-theme-sepia { background:#ede0c4; color:#3b2a1a; border-color:#c8b89a; }
|
|
.footnote-popover.reader-theme-bright { background:#f0f0f0; color:#111; border-color:#bbb; }
|
|
.footnote-popover-close { position:absolute; top:4px; right:6px; background:none; border:none; color:inherit; opacity:0.5; cursor:pointer; font-size:14px; padding:2px 4px; line-height:1; }
|
|
|
|
/* Highlight marks */
|
|
.reader-no-bold * { font-weight: inherit !important; }
|
|
.reader-content img { max-width: 100%; height: auto; display: block; }
|
|
/* color:inherit overrides the browser's UA-default `mark { color: black }` —
|
|
without it, highlighted text abruptly flips to black regardless of the
|
|
reader theme's own text color (jarring on the default dark theme, whose
|
|
body text is white). Inheriting keeps highlighted text the same color as
|
|
the text around it in every theme; the background tones below are chosen
|
|
dark/saturated enough to stay readable specifically against white text. */
|
|
.epub-highlight { border-radius:2px; cursor:pointer; color:inherit; }
|
|
.epub-highlight[data-color="yellow"] { background:rgba(184,134,11,.55); }
|
|
.epub-highlight[data-color="green"] { background:rgba(30,140,90,.55); }
|
|
.epub-highlight[data-color="blue"] { background:rgba(41,110,180,.55); }
|
|
.epub-highlight[data-color="red"] { background:rgba(190,50,60,.55); }
|
|
|
|
/* Margin ("page margin") panel — left of the reader content. Highlights without
|
|
a note show as a small color dot; anything with note text (freeform notes or
|
|
a highlight+note) shows as a readable little "post-it" card, positioned next
|
|
to the text height it belongs to. Click empty space to place a new freeform
|
|
note anchored to that line. Freeform notes have NO in-text mark — the anchor
|
|
is purely positional, only ever visible here in the margin. */
|
|
.reader-body-row { display:flex; flex:1; min-height:0; }
|
|
.reader-margin {
|
|
width:0; flex-shrink:0; overflow:hidden;
|
|
display:flex; flex-direction:column;
|
|
border-right:1px solid var(--border); background:var(--bg);
|
|
transition:width 0.25s ease;
|
|
}
|
|
.reader-margin.open { width:220px; }
|
|
.reader-margin-header {
|
|
display:none; align-items:center; justify-content:space-between;
|
|
padding:6px 8px; border-bottom:1px solid var(--border); white-space:nowrap; flex-shrink:0;
|
|
}
|
|
.reader-margin.open .reader-margin-header { display:flex; }
|
|
.reader-margin-title { font-size:11px; text-transform:uppercase; letter-spacing:.04em; color:var(--muted,#888); }
|
|
.reader-margin-header-actions { display:flex; align-items:center; gap:6px; }
|
|
.reader-margin-markers { position:relative; flex:1; overflow-y:auto; overflow-x:hidden; cursor:crosshair; }
|
|
|
|
.margin-note { position:absolute; left:8px; right:8px; cursor:pointer; }
|
|
|
|
.margin-note-dot { left:8px; right:auto; width:12px; height:12px; border-radius:50%; }
|
|
.margin-note-dot[data-color="yellow"] { background:rgba(241,196,15,.85); }
|
|
.margin-note-dot[data-color="green"] { background:rgba(46,204,113,.85); }
|
|
.margin-note-dot[data-color="blue"] { background:rgba(52,152,219,.85); }
|
|
.margin-note-dot[data-color="red"] { background:rgba(230,57,70,.85); }
|
|
|
|
.margin-note-text {
|
|
font-size:11px; line-height:1.4; padding:5px 7px; border-radius:3px;
|
|
background:var(--bg-card,#1a1a1a); border-left:3px solid var(--muted,#888);
|
|
box-shadow:1px 2px 5px rgba(0,0,0,.3);
|
|
white-space:pre-wrap; word-break:break-word;
|
|
}
|
|
.margin-note-text[data-color="yellow"] { border-left-color:#f1c40f; }
|
|
.margin-note-text[data-color="green"] { border-left-color:#2ecc71; }
|
|
.margin-note-text[data-color="blue"] { border-left-color:#3498db; }
|
|
.margin-note-text[data-color="red"] { border-left-color:#e63946; }
|
|
/* Freeform notes get a paper-like tint, like a post-it note pinned to the text */
|
|
.margin-note-text.margin-note-freeform {
|
|
border-left-color:#e6c229; background:rgba(241,196,15,.13);
|
|
}
|
|
|
|
/* Inline note editor — appears right where the note lives in the margin, so
|
|
setting/editing a note never means jumping to a separate panel */
|
|
.margin-note-editor { position:absolute; left:8px; right:8px; z-index:1; }
|
|
.margin-note-textarea {
|
|
width:100%; min-height:52px; font-size:11px; line-height:1.4; padding:5px 7px;
|
|
border-radius:3px; border:1px solid var(--accent,#e63946);
|
|
background:var(--bg-card,#1a1a1a); color:var(--fg,#eee);
|
|
box-shadow:1px 2px 5px rgba(0,0,0,.4); resize:vertical;
|
|
}
|
|
|
|
/* Note bottom sheet — the mobile alternative to the inline margin editor.
|
|
Typing a note into a 150px-wide margin column is unpleasant on a phone
|
|
keyboard, and getting there first requires opening that column at all, so
|
|
on narrow viewports note editing uses this full-width sheet instead (see
|
|
_isMobileReader() / _openNoteEditor() in app.js). Existing notes staying
|
|
less discoverable on mobile is an accepted tradeoff — reviewing them in
|
|
depth is expected to happen on desktop; setting them fast is what matters
|
|
here. */
|
|
.note-bottom-sheet { position:fixed; inset:0; z-index:650; }
|
|
.note-bottom-sheet-backdrop {
|
|
position:absolute; inset:0; background:rgba(0,0,0,.5);
|
|
opacity:0; transition:opacity .2s ease; pointer-events:none;
|
|
}
|
|
.note-bottom-sheet.open .note-bottom-sheet-backdrop { opacity:1; pointer-events:auto; }
|
|
.note-bottom-sheet-panel {
|
|
position:absolute; left:0; right:0; bottom:0;
|
|
background:var(--surface,#111); border-top:1px solid var(--border,#333);
|
|
border-radius:12px 12px 0 0; padding:12px 14px calc(14px + env(safe-area-inset-bottom));
|
|
transform:translateY(100%); transition:transform .25s ease;
|
|
display:flex; flex-direction:column; gap:10px;
|
|
}
|
|
.note-bottom-sheet.open .note-bottom-sheet-panel { transform:translateY(0); }
|
|
.note-bottom-sheet-textarea {
|
|
width:100%; min-height:110px; font-size:15px; line-height:1.4; padding:8px 10px;
|
|
border-radius:var(--radius); border:1px solid var(--border,#333);
|
|
background:var(--bg-card,#1a1a1a); color:var(--fg,#eee); resize:vertical;
|
|
}
|
|
.note-bottom-sheet-actions { display:flex; justify-content:flex-end; gap:8px; }
|
|
|
|
@media (max-width: 600px) {
|
|
.reader-margin.open { width:150px; }
|
|
.reader-margin-title { display:none; }
|
|
}
|
|
|
|
/* Search matches */
|
|
mark.reader-search-match { background:rgba(241,196,15,.6); color:inherit; border-radius:2px; }
|
|
mark.reader-search-match.active { background:rgba(230,57,70,.7); }
|
|
#rs-search-count { font-size:12px; min-width:50px; }
|
|
|
|
/* Read-aloud (TTS) */
|
|
mark.tts-current { background:rgba(230,57,70,.55); color:inherit; border-radius:2px; }
|
|
.tts-bar { position:fixed; bottom:calc(var(--bar-h) + 16px); left:50%; transform:translateX(-50%); display:flex; align-items:center; gap:14px; background:var(--bg-card,#1a1a1a); border:1px solid var(--border); border-radius:var(--radius); padding:8px 16px; box-shadow:0 4px 16px rgba(0,0,0,.5); z-index:600; }
|
|
.tts-bar button { background:none; border:none; color:inherit; font-size:16px; cursor:pointer; padding:2px 4px; line-height:1; }
|
|
.tts-bar button:hover { opacity:0.7; }
|
|
|
|
/* Bookmarks sidebar */
|
|
.bookmark-entry { display:flex; width:100%; padding:6px 0; font-size:13px; justify-content:space-between; border-bottom:1px solid var(--border); }
|
|
|
|
/* Toast */
|
|
.reader-toast { position:fixed; bottom:calc(var(--bar-h) + 16px); left:50%; transform:translateX(-50%); background:var(--fg); color:var(--bg); padding:6px 14px; border-radius:var(--radius); font-size:13px; z-index:600; animation:toast-fade 2s ease forwards; pointer-events:none; }
|
|
@keyframes toast-fade { 0%,70%{opacity:1} 100%{opacity:0} }
|
|
|
|
.reader-toast-action { display:flex; align-items:center; gap:10px; padding:8px 8px 8px 14px; animation:toast-slide-up 0.2s ease forwards; pointer-events:auto; }
|
|
.reader-toast-btn { background:var(--accent,#e63946); color:#fff; border:none; border-radius:calc(var(--radius) - 2px); padding:5px 12px; font-family:var(--font); font-size:13px; font-weight:600; cursor:pointer; white-space:nowrap; }
|
|
.reader-toast-btn:hover { filter:brightness(1.1); }
|
|
@keyframes toast-slide-up { 0%{opacity:0; transform:translate(-50%, 8px);} 100%{opacity:1; transform:translate(-50%, 0);} }
|
|
|
|
.build-time {
|
|
position: fixed;
|
|
bottom: 4px;
|
|
right: 6px;
|
|
font-size: 0.65rem;
|
|
color: #444;
|
|
pointer-events: none;
|
|
z-index: 1;
|
|
}
|