/* ========================================================= diora — dark radio player theme ========================================================= */ *, *::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; } .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; } /* ========================================================= 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; } /* ===== PODCAST COMPONENTS ===== */ .podcast-seek-bar { display: flex; align-items: center; gap: 6px; padding: 0 12px; flex: 1; min-width: 0; position: relative; } .skip-btn { font-size: 0.72rem; opacity: 0.8; white-space: nowrap; padding: 2px 4px; } .skip-btn:hover { opacity: 1; } .speed-btns { display: flex; gap: 2px; margin-left: 4px; flex-shrink: 0; } .speed-btn { background: none; border: 1px solid var(--border, #444); color: var(--fg, #fff); border-radius: 3px; font-size: 0.68rem; padding: 1px 4px; cursor: pointer; opacity: 0.6; white-space: nowrap; } .speed-btn:hover { opacity: 1; } .speed-btn.active { opacity: 1; border-color: var(--accent, #e63946); color: var(--accent, #e63946); } .seek-slider { flex: 1; accent-color: var(--accent, #e63946); cursor: pointer; height: 4px; } .seek-time { font-size: 0.75rem; color: var(--text-muted, #888); white-space: nowrap; font-variant-numeric: tabular-nums; } .podcast-toolbar { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 0 12px; border-bottom: 1px solid var(--border, #333); margin-bottom: 10px; align-items: center; } .podcast-pane { } .podcast-feed-list { display: flex; flex-direction: column; gap: 6px; } .podcast-feed-item { display: flex; align-items: center; gap: 10px; padding: 6px 0; border-bottom: 1px solid var(--border, #222); } .podcast-feed-info { flex: 1; min-width: 0; } .podcast-feed-title { font-weight: bold; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .podcast-feed-actions { display: flex; gap: 4px; flex-shrink: 0; } .podcast-thumb { width: 48px; height: 48px; object-fit: cover; border-radius: 4px; flex-shrink: 0; } .podcast-thumb-lg { width: 80px; height: 80px; object-fit: cover; border-radius: 6px; flex-shrink: 0; } .podcast-thumb-placeholder { width: 48px; height: 48px; background: var(--surface, #1e1e2e); border-radius: 4px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; } .podcast-unplayed-badge { background: var(--accent, #e63946); color: #fff; border-radius: 99px; padding: 1px 7px; font-size: 0.7rem; margin-left: 6px; } .episode-list { display: flex; flex-direction: column; gap: 4px; } .episode-item { display: flex; align-items: center; gap: 10px; padding: 7px 0; border-bottom: 1px solid var(--border, #222); } .episode-item.episode-played { opacity: 0.5; } .episode-info { flex: 1; min-width: 0; } .episode-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 0.9rem; } .episode-actions { display: flex; gap: 4px; flex-shrink: 0; } .podcast-search-list { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; } .podcast-search-item { display: flex; align-items: center; gap: 10px; padding: 6px 0; border-bottom: 1px solid var(--border, #222); } .podcast-search-info { flex: 1; min-width: 0; } .podcast-queue-ol { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 4px; } .podcast-feed-header { margin-bottom: 12px; } .podcast-feed-header-inner { display: flex; align-items: center; gap: 14px; } .feed-refresh-btn { margin-left: auto; flex-shrink: 0; } /* Clickable episode title (episode list) */ .ep-clickable { cursor: pointer; } .ep-clickable:hover { color: var(--accent, #e63946); text-decoration: underline; } /* Clickable episode title / feed name in the now-playing bar */ .podcast-track-link, .podcast-station-link { cursor: pointer; } .podcast-track-link:hover, .podcast-station-link:hover { color: var(--accent, #e63946); text-decoration: underline; } /* ===== 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; } /* Inbox toolbar + checkboxes */ .inbox-toolbar { display: flex; align-items: center; gap: 8px; padding: 0 0 8px; flex-wrap: wrap; min-height: 32px; } .inbox-select-all-label { display: flex; align-items: center; gap: 4px; font-size: 0.82rem; color: var(--text-muted, #888); cursor: pointer; white-space: nowrap; } .inbox-bulk-actions { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; } .inbox-load-more { display: flex; align-items: center; gap: 6px; margin-left: auto; } .inbox-checkbox-label { display: flex; align-items: center; flex-shrink: 0; padding-right: 4px; cursor: pointer; } .inbox-checkbox-label input[type="checkbox"] { width: 15px; height: 15px; cursor: pointer; accent-color: var(--accent, #e63946); } .episode-item.inbox-selected { background: var(--surface-alt, #2a2a3e); } /* Sleep timer */ .sleep-timer-btn { font-size: 0.68rem; padding: 1px 5px; white-space: nowrap; border: 1px solid var(--border, #444); border-radius: 3px; opacity: 0.7; } .sleep-timer-btn:hover { opacity: 1; } .sleep-timer-menu { position: absolute; background: var(--surface, #1e1e2e); border: 1px solid var(--border, #444); border-radius: 6px; padding: 4px; display: flex; flex-direction: column; gap: 2px; z-index: 200; bottom: calc(100% + 4px); right: 0; } .sleep-timer-option { background: none; border: none; color: var(--fg, #fff); padding: 5px 12px; text-align: left; cursor: pointer; font-size: 0.85rem; border-radius: 4px; } .sleep-timer-option:hover { background: var(--accent, #e63946); color: #fff; } /* Episode filter */ .episode-search-bar { padding: 6px 0 8px; } .episode-search-bar .search-input { width: 100%; box-sizing: border-box; } /* Episode meta line */ .episode-meta { display: flex; gap: 8px; font-size: 0.78rem; color: var(--text-muted, #888); margin-top: 2px; flex-wrap: wrap; } .episode-date { font-weight: 500; color: var(--fg, #ccc); } .episode-feed-link { cursor: pointer; } .episode-feed-link:hover { text-decoration: underline; color: var(--accent, #e63946); } .episode-dur { color: var(--text-muted, #888); } /* Episode progress bar */ .episode-progress-bar { height: 3px; background: var(--border, #333); border-radius: 2px; margin-top: 5px; overflow: hidden; } .episode-progress-fill { height: 100%; background: var(--accent, #e63946); border-radius: 2px; } /* Queue drag-and-drop */ .drag-handle { cursor: grab; color: var(--text-muted, #666); padding: 0 6px; user-select: none; } .drag-handle:active { cursor: grabbing; } #podcast-queue-ol li.dragging { opacity: 0.4; border: 1px dashed var(--accent, #e63946); } /* Feed list toolbar */ .feed-list-toolbar { display: flex; gap: 8px; align-items: center; padding: 0 0 10px; flex-wrap: wrap; } .feed-list-toolbar .search-input { flex: 1; min-width: 140px; } .feed-sort-select { background: var(--surface, #1e1e2e); color: var(--fg, #fff); border: 1px solid var(--border, #444); border-radius: 4px; padding: 4px 6px; font-size: 0.82rem; cursor: pointer; } .reader-marker-btn-mobile { display: none; } @media (max-width: 600px) { .sidebar { width: 100vw; } .podcast-seek-bar { padding: 0 6px; } .podcast-thumb { width: 40px; height: 40px; } .podcast-thumb-lg { width: 60px; height: 60px; } .podcast-feed-actions { flex-direction: column; } .episode-actions { flex-direction: column; } /* 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 shelf (always pinned atop the books view) --- */ .book-recent-shelf { margin-bottom: 14px; } .book-recent-title { font-size: 13px; font-weight: 600; margin: 0 0 6px; color: var(--muted, #888); } .book-recent-row { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 2px; } .book-recent-item { flex: 0 0 auto; display: flex; flex-direction: column; gap: 2px; max-width: 160px; padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius); background: none; text-align: left; cursor: pointer; } .book-recent-item-title { font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .book-recent-item-author { font-size: 11px; color: var(--muted, #888); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* --- 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; cursor: pointer; text-align: center; } .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: Georgia, 'Times New Roman', serif; font-size: 16px; font-weight: 400; } .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: normal !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; } /* 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; }