Der Lesestand wird gleich beim Öffnen erfragt (parallel zur Datei), ein PDF zeichnet zuerst die Seite, an der es weitergeht, statt Seite 1; Titel, Inhaltsverzeichnis und Seitenzahlen kommen nebenher und halten das Öffnen nicht mehr auf. Eine Zeichnung, die schon läuft, wird abgewartet statt neu begonnen. „Leserfarben“ (neben Originalfarben und Invertieren): die Seite in den Farben des Lesers (Dunkel, Sepia) per SVG-Filter, Bilder darüber wie gedruckt (ihre Lage aus den Zeichenbefehlen); ein Bild, das die halbe Seite füllt, ist ein Scan und wird mit umgefärbt. pdf.js' eigene pageColors färben die Bilder mit um. Ein anderes Design ändert nur die Farben des Filters. Seitenzahlen des Buchs (PageLabels: „iv“, „A-3“) im Kopf mit der Seite der Datei daneben, eingeben lässt sich beides; Lesezeichen und der Zurück-Knopf nennen sie. Das Test-PDF hat dafür Seitenzahlen und ein farbiges Bild bekommen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2097 lines
55 KiB
CSS
2097 lines
55 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: var(--raised) var(--bg);
|
|
}
|
|
|
|
::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
::-webkit-scrollbar-track { background: var(--bg); }
|
|
::-webkit-scrollbar-thumb { background: var(--raised); border-radius: 4px; }
|
|
::-webkit-scrollbar-thumb:hover { background: var(--border-strong); }
|
|
|
|
: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);
|
|
|
|
/* The colour scheme (docs/theme.md): dark here, light below. Use only these. */
|
|
--muted: #9a9a9a;
|
|
--card: #0b0b0b;
|
|
--panel: #111;
|
|
--hover: #141414;
|
|
--raised: #1a1a1a;
|
|
--border-strong: #444;
|
|
--on-accent: #fff;
|
|
--scrim: rgba(0, 0, 0, 0.6);
|
|
--shadow: none;
|
|
--surface: var(--panel);
|
|
color-scheme: dark;
|
|
}
|
|
|
|
html[data-theme="light"] {
|
|
--bg: #f6f5f1;
|
|
--fg: #17171a;
|
|
--fg-muted: #17171a;
|
|
--muted: #66666b;
|
|
--card: #fff;
|
|
--panel: #fff;
|
|
--hover: #ecebe6;
|
|
--raised: #e4e2dc;
|
|
--border: #d9d6ce;
|
|
--border-strong: #b4b0a6;
|
|
--accent: #d0263a;
|
|
--accent-hover: #b51f31;
|
|
--on-accent: #fff;
|
|
--scrim: rgba(20, 20, 20, 0.35);
|
|
--shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
|
--bg-card: var(--card);
|
|
--bg-alt: var(--hover);
|
|
/* Outlined text is for wallpapers (body.bright-bg / a dark picture); on plain light, none. */
|
|
--outline: transparent;
|
|
color-scheme: light;
|
|
}
|
|
|
|
/* 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: var(--bg);
|
|
}
|
|
|
|
.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: var(--hover); }
|
|
|
|
.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: var(--hover);
|
|
border-color: var(--border-strong);
|
|
}
|
|
|
|
.btn-play {
|
|
background: var(--accent);
|
|
border-color: var(--accent);
|
|
color: var(--on-accent);
|
|
font-size: 0.9rem;
|
|
padding: 0.4rem 1rem;
|
|
}
|
|
.btn-play:hover {
|
|
background: var(--accent-hover);
|
|
border-color: var(--accent-hover);
|
|
}
|
|
.btn-play.playing {
|
|
background: var(--raised);
|
|
border-color: var(--border-strong);
|
|
color: var(--text);
|
|
}
|
|
|
|
.btn-save {
|
|
color: var(--yellow);
|
|
border-color: var(--border-strong);
|
|
}
|
|
.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: var(--on-accent);
|
|
}
|
|
.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;
|
|
}
|
|
html[data-theme="light"] .btn-danger { background: #fdecee; border-color: #e7a3aa; color: #a3172a; }
|
|
html[data-theme="light"] .btn-danger:hover { background: #a3172a; border-color: #a3172a; color: #fff; }
|
|
html[data-theme="light"] .btn-save:hover { background: #fbf3d3; }
|
|
|
|
.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;
|
|
}
|
|
html[data-theme="light"] .form-errors { background: #fdecee; border-color: #e7a3aa; }
|
|
|
|
.field-help {
|
|
color: var(--text-muted);
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
.auth-alt { margin-top: 1rem; }
|
|
.auth-or {
|
|
display: flex; align-items: center; gap: 0.6rem;
|
|
margin: 0 0 1rem; font-size: 0.8rem; color: var(--text-muted);
|
|
}
|
|
.auth-or::before, .auth-or::after { content: ''; flex: 1; border-top: 1px solid var(--border, #333); }
|
|
.auth-alt .btn-full { display: block; text-align: center; text-decoration: none; }
|
|
|
|
/* Unlocking the books on a device without the key (js/app/33-unlock.js) */
|
|
.unlock-code { font: 600 1.6rem/1.2 ui-monospace, monospace; letter-spacing: 0.15em; text-align: center; margin: 4px 0 14px; }
|
|
.unlock-or { font-size: 0.85rem; color: var(--text-muted); margin: 0 0 8px; }
|
|
.unlock-status { font-size: 0.85rem; color: var(--text-muted); min-height: 1.2em; margin: 0 0 10px; }
|
|
|
|
.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;
|
|
}
|
|
|
|
/* Editing a saved connection: a full row under its name and buttons */
|
|
.webdav-edit {
|
|
flex-basis: 100%;
|
|
padding-top: 0.75rem;
|
|
}
|
|
.webdav-edit[hidden] {
|
|
display: none;
|
|
}
|
|
|
|
/* --- 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 var(--border);
|
|
}
|
|
.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 var(--border);
|
|
}
|
|
|
|
/* =========================================================
|
|
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 var(--border);
|
|
}
|
|
|
|
.mood-chip {
|
|
background: transparent;
|
|
border: 1px solid var(--border);
|
|
color: var(--fg);
|
|
border-radius: 999px;
|
|
padding: 3px 12px;
|
|
font-size: 0.78rem;
|
|
cursor: pointer;
|
|
transition: background 0.15s, border-color 0.15s;
|
|
}
|
|
|
|
.mood-chip:hover {
|
|
background: var(--hover);
|
|
border-color: var(--border-strong);
|
|
}
|
|
|
|
.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: var(--scrim);
|
|
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: var(--scrim);
|
|
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: var(--hover);
|
|
}
|
|
.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; line-height:1; -webkit-text-size-adjust:none; text-size-adjust:none; z-index:1; }
|
|
.pdf-text-layer span, .pdf-text-layer br { 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; transform-origin:0 0; }
|
|
.pdf-text-layer span::selection, .pdf-text-layer mark::selection { background:rgba(0,120,255,0.3); }
|
|
.pdf-text-layer br::selection { background:transparent; }
|
|
.pdf-text-layer mark.reader-search-match { color:transparent; background:rgba(241,196,15,.5); padding:0; border-radius:2px; }
|
|
.pdf-text-layer mark.reader-search-match.active { background:rgba(230,57,70,.6); }
|
|
/* PDF links — over the text, so a tap on one follows it */
|
|
.pdf-link-layer { position:absolute; top:0; left:0; width:100%; height:100%; pointer-events:none; z-index:2; }
|
|
.pdf-link { position:absolute; display:block; pointer-events:auto; border-radius:2px; }
|
|
.pdf-link:hover, .pdf-link:focus-visible { background:rgba(0,120,255,0.15); outline:none; }
|
|
.pdf-back-btn { position:absolute; left:50%; bottom:calc(var(--bar-h, 0px) + 24px); transform:translateX(-50%); z-index:20; box-shadow:0 2px 8px rgba(0,0,0,.25); }
|
|
|
|
/* PDF invert */
|
|
/* Dark pages: inverted, and the hues turned back, so photos and coloured figures aren't negatives. */
|
|
#reader-overlay.pdf-inverted .pdf-page { filter:invert(1) hue-rotate(180deg); }
|
|
/* Leserfarben (58-pdf.js _pdfColorFilter): the page recoloured, its pictures over it as printed */
|
|
.pdf-page.pdf-recolored { filter:url(#pdf-reader-colors); }
|
|
.pdf-image-layer { position:absolute; top:0; left:0; width:100%; height:100%; pointer-events:none; }
|
|
.pdf-image-layer canvas { position:absolute; display:block; }
|
|
|
|
/* ---- 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); }
|
|
/* Reading aloud: the player bar is its remote control and stays, as in the apps. */
|
|
html.nui-tts-on body.reader-immersive .now-playing-bar { transform: none; }
|
|
html.nui-tts-on body.reader-immersive .reader-overlay { bottom: var(--bar-h); }
|
|
|
|
/* PDF paginated */
|
|
/* Page by page the page fills the whole reading area, without its padding: 66-pdf-paginated.js
|
|
measures the area and places the page by its text box itself. */
|
|
.reader-content.pdf-paginated { overflow:hidden !important; display:block; padding:0 !important; }
|
|
.reader-content.pdf-paginated > #pdf-viewport { width:100%; height:100%; max-width:none; margin:0; }
|
|
.pdf-paginated .pdf-page-wrapper { margin:0; }
|
|
.pdf-paginated .pdf-page-wrapper { position:relative; }
|
|
.pdf-paginated .pdf-page-inner { position:absolute; left:0; top:0; }
|
|
|
|
/* PDF loading overlay */
|
|
.pdf-loading-overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: var(--scrim);
|
|
z-index: 200;
|
|
}
|
|
.pdf-loading-spinner {
|
|
width: 36px;
|
|
height: 36px;
|
|
border: 3px solid var(--raised);
|
|
border-top-color: var(--accent, #e63946);
|
|
border-radius: 50%;
|
|
animation: pdf-spin 0.7s linear infinite;
|
|
}
|
|
@keyframes pdf-spin { to { transform: rotate(360deg); } }
|
|
.reader-opening { display: flex; flex-direction: column; align-items: center; gap: 16px; }
|
|
|
|
/* 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:var(--fg); }
|
|
.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; }
|
|
.footnote-popover > :first-child { margin-top:0; }
|
|
.footnote-popover > :last-child { margin-bottom:0; }
|
|
|
|
/* ---- Reading in pages (63-pages.js) ----
|
|
Only the current chapter is laid out; the others stay in the DOM (block
|
|
indices, search, notes, anchors). No free scrolling: the pager moves. */
|
|
#reader-content.nui-window > [data-epub-src]:not(.nui-ch-current) { display:none; }
|
|
/* Room below the chapter, so its last page can show just the remaining text
|
|
(then the page's colour) instead of scrolling back over lines already read. */
|
|
#reader-content.nui-window::after { content:""; display:block; height:100vh; }
|
|
#reader-content.nui-window { overflow-y:hidden; -ms-overflow-style:none; scrollbar-width:none; }
|
|
#reader-content.nui-window::-webkit-scrollbar { display:none; }
|
|
/* Masks: the line cut at the page's bottom and whatever sits in the top gap. */
|
|
.nui-page-mask { position:fixed; z-index:250; pointer-events:none; display:none; }
|
|
/* Footnotes at the foot of the page, like a printed book. */
|
|
.nui-page-notes { position:fixed; z-index:255; overflow:hidden; padding:0 0 4px; box-sizing:border-box;
|
|
line-height:1.4; font-weight:400; cursor:pointer; }
|
|
.nui-pn-rule { width:30%; border-top:1px solid currentColor; margin:2px 0 6px; }
|
|
.nui-pn-item { margin:0 0 4px; padding-left:1.6em; text-indent:-1.6em; }
|
|
.nui-pn-item p, .nui-pn-item div, .nui-pn-item aside, .nui-pn-item li { display:inline; margin:0; padding:0; text-indent:0; }
|
|
.nui-pn-item p + p::before { content:" "; }
|
|
.nui-pn-label { display:inline-block; min-width:1.6em; text-indent:0; font-weight:700; }
|
|
.nui-pn-item a { color:inherit; text-decoration:none; }
|
|
.nui-pn-item img { display:none; }
|
|
/* Cut off: say so, a tap shows the whole note. */
|
|
.nui-page-notes.nui-pn-cut::after { content:"… mehr"; position:absolute; right:0; bottom:2px; padding-left:8px;
|
|
background:inherit; font-weight:700; }
|
|
/* Two pages side by side (a spread, 63-pages.js): the right one is a copy of
|
|
the chapter, scrolled to the page after the left one. */
|
|
#reader-spread-page { display:none; }
|
|
#reader-overlay.reader-spread #reader-content,
|
|
#reader-overlay.reader-spread #reader-spread-page { flex:1 1 0; min-width:0; }
|
|
#reader-overlay.reader-spread #reader-spread-page { display:block; overflow:hidden; box-shadow:inset 1px 0 0 var(--border);
|
|
user-select:none; -webkit-user-select:none; cursor:default; }
|
|
#reader-spread-page::after { content:""; display:block; height:100vh; }
|
|
/* An article in the reader (65-article.js): what only books have stays out. */
|
|
#reader-overlay.reader-article-mode .reader-progress-wrap,
|
|
#reader-overlay.reader-article-mode #reader-bookmark-btn,
|
|
#reader-overlay.reader-article-mode #reader-bm-list-btn,
|
|
#reader-overlay.reader-article-mode #reader-toc-btn,
|
|
#reader-overlay.reader-article-mode #reader-margin-btn,
|
|
#reader-overlay.reader-article-mode #reader-reset-pos-btn,
|
|
#reader-overlay.reader-article-mode .marker-mode-btn,
|
|
#reader-overlay.reader-article-mode #reader-margin,
|
|
#reader-overlay.reader-article-mode .nui-toc { display:none !important; }
|
|
.reader-article-actions { display:inline-flex; gap:10px; align-items:center; }
|
|
/* The browser's line under the page (the apps have their own footer). */
|
|
#reader-page-footer { display:flex; justify-content:space-between; gap:12px; flex-shrink:0; padding:4px 16px 6px;
|
|
font-size:12px; color:var(--muted,#888); font-variant-numeric:tabular-nums; }
|
|
.reader-theme-sepia #reader-page-footer { background:#f5e6c8; color:#7a6650; }
|
|
.reader-theme-bright #reader-page-footer { background:#fff; color:#777; }
|
|
|
|
/* Page numbers of the printed edition (epub:type="pagebreak") */
|
|
.reader-content .epub-pagenum { display:inline-block; font-size:0.62em; line-height:1.3; font-weight:400; font-style:normal; letter-spacing:0; text-transform:none; vertical-align:0.35em; padding:0 0.35em; margin-right:0.4em; border:1px solid currentColor; border-radius:3px; opacity:0.45; user-select:none; }
|
|
.reader-content .epub-pagenum:empty { display:none; }
|
|
|
|
/* 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 {
|
|
--reader-margin-w:220px; /* read by app.js _centerReaderColumn */
|
|
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:var(--reader-margin-w); }
|
|
/* The text column keeps its place while the margin opens (app.js _centerReaderColumn). */
|
|
.reader-content { transition: padding-right 0.25s ease; }
|
|
.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:hidden; cursor:crosshair; contain:layout paint; }
|
|
/* The cards in book coordinates; scrolling moves this layer (app.js _repositionMarginMarkers). */
|
|
.margin-layer { position:absolute; top:0; left:0; right:0; height:0; will-change:transform; }
|
|
|
|
.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:var(--scrim);
|
|
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 { --reader-margin-w: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; }
|
|
/* The apps: the player bar sits in the reader, above its footer; notices go above both. */
|
|
html.nui-reading .reader-toast { bottom:calc(var(--nui-dock-h, 0px) + 40px); }
|
|
.reader-toast-btn { background:var(--accent,#e63946); color:var(--on-accent); 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: var(--border-strong);
|
|
pointer-events: none;
|
|
z-index: 1;
|
|
}
|