﻿/* Must stay at the very top — CSS requires @import to precede all other
   rules. Two fonts, site-wide: Cinzel for the wordmark + structural
   section/page headings, Inter for everything else (body copy, buttons,
   form fields, list content). */
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@600;700&family=Inter:wght@400;500;600;700&display=swap');

/* =========================================================
   Dracarium — style.css (FULL FILE)
   Consolidated + long-term fixes:
   - Dark mode is driven by html.dark-mode (not body.dark-mode)
   - Fix “flashbang” by allowing theme to apply early (see note in JS)
   - Fix Manage modal being “messed up” by scoping Bulk modal styles
     (no more .modal/.modal-header collisions)
   - Clean hover behavior in list + grid

   BUGFIX #2 APPLIED:
   - Bulk modal CSS selectors now match your actual markup:
     #bulkModalBackdrop .bulk-modal (instead of .modal)
   ========================================================= */


/* =========================================================
   BASE + THEME TOKENS
   ========================================================= */

/* No height:100% here on purpose — body already has min-height:100vh
   below, which is the "at least a full viewport, grow if taller" behavior
   we want. A hard height:100% caps body (and its flex children, via the
   default align-items:stretch) at exactly one viewport tall regardless of
   how much #book-list actually contains, which clips .content's painted
   background short whenever the list overflows past the fold. */

:root {
    --bg: #fffaf0;
    --text: #333;
    --content-bg: #f6efe0;
    --card: rgba(255,255,255,0.95);
    --card-border: rgba(0,0,0,0.10);
    --card-shadow: 0 3px 6px rgba(0,0,0,0.15);
    --tan: #c9974d;
    --tan-2: rgba(201, 151, 77, 0.12);
    --tan-3: rgba(201, 151, 77, 0.18);
    /* --tan itself is only 2.5:1 against this page's cream background —
       fine for decorative fills/borders, but fails WCAG AA (4.5:1) as a
       text color. --tan-text is a darkened gold for the few places that
       use the accent color AS text (links, focus ring): 5.3:1 here. */
    --tan-text: #8a6216;
    --sidebar: rgba(255, 255, 255, 0.95);
    --sidebar-border: #ccc;
    --topbar: rgba(255,255,255,0.9);
    --ink: #4b2e00;
    --ink-2: #5c4523;
    --focus: var(--tan-text);
    --btn-bg: #4B2E00;
    --btn-bg-hover: #3A2300;
    --btn-text: #ffffff;
}

/* Dark mode tokens (driven by html.dark-mode) */
html.dark-mode {
    --bg: #1e1e1e;
    --text: #ddd;
    --content-bg: #2b2b2b;
    --card: rgba(50,50,50,0.95);
    --card-border: rgba(255,255,255,0.10);
    --card-shadow: 0 4px 12px rgba(0,0,0,0.40);
    --sidebar: rgba(30, 30, 30, 0.95);
    --sidebar-border: #444;
    --topbar: rgba(50,50,50,0.85);
    --ink: #f0f0f0;
    --ink-2: #ccc;
    /* Unlike light mode, plain --tan (#c9974d) already passes 6.4:1
       against this dark background as text, so no darkening needed here. */
    --tan-text: #c9974d;
    --btn-bg: #3a3a3a;
    --btn-bg-hover: #4a4a4a;
    --btn-text: #f0f0f0;
}

/* High-Contrast tokens (driven by html.contrast-mode) — an independent
   3rd theme, not a booster on top of light/dark. Pure black/white, solid
   borders instead of soft rgba tints, no gold accent (even the corrected
   --tan-text gold is harder to guarantee AAA-safe than just using black),
   so every value below is redefined rather than inherited. Mutually
   exclusive with html.dark-mode at the JS level (theme.js). */
html.contrast-mode {
    --bg: #ffffff;
    --text: #000000;
    --content-bg: #ffffff;
    --card: #ffffff;
    --card-border: #000000;
    --card-shadow: none;
    --tan: #000000;
    --tan-2: rgba(0,0,0,0.12);
    --tan-3: rgba(0,0,0,0.18);
    --tan-text: #000000;
    --sidebar: #ffffff;
    --sidebar-border: #000000;
    --topbar: #ffffff;
    --ink: #000000;
    --ink-2: #000000;
    --btn-bg: #000000;
    --btn-bg-hover: #1a1a1a;
    --btn-text: #ffffff;
}

/* Bolder focus ring in High-Contrast — the base 2px rule above still
   supplies color (var(--focus), already black in this theme). */
html.contrast-mode button:focus-visible,
html.contrast-mode select:focus-visible,
html.contrast-mode input[type="range"]:focus-visible,
html.contrast-mode input:focus-visible,
html.contrast-mode textarea:focus-visible {
    outline-width: 3px;
}

/* Optional: helps avoid a “flash” if theme is applied early.
   NOTE: For best results, your JS should toggle html.dark-mode
   as early as possible (inline in <head>), but this still helps. */
html {
    font-size: 100%;
    background-color: var(--bg);
    color: var(--text);
}

/* Theme-switch transition baseline. Without this, surfaces like .content/
   .sidebar/.site-header (which fully cover body's own background, so
   body's transition below is never actually visible) snap instantly
   between themes while nothing visibly fades — that mismatch is the
   "flash" on switching to/from dark mode: light and contrast are both
   near-white, so an instant snap between them is imperceptible, but
   light-to-dark is a big enough swing that snapping instead of fading
   reads as jarring. A few elements below declare their own more specific
   `transition` (which fully overrides this universal one, not merges with
   it) without including background-color/color — those are patched
   individually where they're defined. Neutralized for prefers-reduced-
   motion by the media query right below, same as everything else here. */
*, *::before, *::after {
    transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

/* Font-size control (Accessibility panel) — scales the root, which
   cascades through the site's rem-based sizing (px-based dimensions like
   cover images/avatars are unaffected, as intended). */
html.font-lg {
    font-size: 112.5%;
}

html.font-xl {
    font-size: 125%;
}

/* Respect the OS-level reduced-motion preference: keep the small hover/
   press micro-interactions functional (so state changes are still
   visible) but make them effectively instant instead of animated. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        transition-duration: 0.001ms !important;
        animation-duration: 0.001ms !important;
    }
}

body {
    font-family: 'Inter', system-ui, sans-serif;
    line-height: 1.5;
    background-color: var(--bg);
    color: var(--text);
    margin: 0;
    padding: 0;
    display: flex; /* sidebar + content */
    min-height: 100vh;
    /* background-color/color transition now comes from the universal rule
       above (same 0.2s ease timing) — no need to redeclare it here. */
}

.hidden {
    display: none !important;
}

button:focus-visible,
select:focus-visible,
input[type="range"]:focus-visible,
input:focus-visible,
textarea:focus-visible {
    outline: 2px solid var(--focus);
    outline-offset: 2px;
}


/* =========================================================
   SITE HEADER (persistent global bar — header.js)
   ========================================================= */

body.has-site-header {
    padding-top: 64px;
}

.site-header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 3000;
    background: var(--topbar);
    border-bottom: 1px solid var(--card-border);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    transition: background-color 0.2s ease, border-color 0.2s ease;
}

.site-header-inner {
    max-width: 1400px;
    margin: 0 auto;
    padding: 10px 16px;
    display: flex;
    align-items: center;
    gap: 20px;
}

.site-logo {
    font-family: 'Cinzel', Georgia, serif;
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--ink);
    text-decoration: none;
    letter-spacing: 0.03em;
    white-space: nowrap;
}

/* login.html's page title — shares the wordmark's font (it IS the
   wordmark, shown pre-auth where .site-header isn't rendered). */
.login-title {
    font-family: 'Cinzel', Georgia, serif;
}

.site-nav {
    display: flex;
    gap: 6px;
    margin-right: auto;
}

.site-nav-link {
    padding: 8px 12px;
    border-radius: 6px;
    color: var(--ink);
    text-decoration: none;
    font-size: 0.92rem;
    white-space: nowrap;
    transition: background 0.15s ease, color 0.2s ease;
}

.site-nav-link:hover,
.site-nav-link.is-active {
    background: var(--tan-2);
}

.site-header-right {
    display: flex;
    align-items: center;
    gap: 10px;
}

.profile-menu {
    position: relative;
}

.profile-menu-trigger {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 12px 5px 5px;
    border-radius: 20px;
    border: 1px solid var(--card-border);
    background: var(--card);
    color: var(--ink);
    cursor: pointer;
    font-family: inherit;
    font-size: 0.9rem;
}

.profile-menu-trigger:hover {
    background: var(--tan-2);
}

.profile-avatar {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    object-fit: cover;
    display: block;
    background: var(--tan-2);
}

.profile-caret {
    font-size: 0.75rem;
    opacity: 0.7;
}

/* Flush against the trigger (no gap) so a mouse moving from the trigger
   into the dropdown never crosses a dead zone that would drop :hover. */
.profile-dropdown {
    position: absolute;
    top: 100%;
    right: 0;
    min-width: 190px;
    background: var(--card);
    border: 1px solid var(--card-border);
    border-radius: 10px;
    box-shadow: var(--card-shadow);
    padding: 6px;
    margin-top: 8px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px);
    transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s, background-color 0.2s ease, border-color 0.2s ease;
}

/* The invisible bridge that actually eliminates the hover dead zone. */
.profile-dropdown::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: -8px;
    height: 8px;
}

.profile-menu:hover .profile-dropdown,
.profile-menu:focus-within .profile-dropdown,
.profile-menu.open .profile-dropdown {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.profile-dropdown-link {
    padding: 8px 10px;
    border-radius: 6px;
    color: var(--ink);
    text-decoration: none;
    font-size: 0.88rem;
    white-space: nowrap;
}

.profile-dropdown-link:hover {
    background: var(--tan-2);
}

/* Accessibility panel — same open/close pattern as .profile-menu above
   (hover/focus-within + .open fallback, wired by header.js's wireMenu). */
.a11y-menu {
    position: relative;
}

.a11y-menu-trigger {
    font-family: inherit;
}

.a11y-panel {
    position: absolute;
    top: 100%;
    right: 0;
    width: 240px;
    background: var(--card);
    border: 1px solid var(--card-border);
    border-radius: 10px;
    box-shadow: var(--card-shadow);
    padding: 12px;
    margin-top: 8px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px);
    transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s, background-color 0.2s ease, border-color 0.2s ease;
    z-index: 10;
}

.a11y-panel::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: -8px;
    height: 8px;
}

.a11y-menu:hover .a11y-panel,
.a11y-menu:focus-within .a11y-panel,
.a11y-menu.open .a11y-panel {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.a11y-panel-label {
    font-weight: 700;
    font-size: 0.82rem;
    margin-bottom: 6px;
    color: var(--ink);
}

.a11y-radio-row {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.a11y-radio {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.88rem;
    color: var(--text);
    cursor: pointer;
    padding: 4px 6px;
    border-radius: 6px;
}

.a11y-radio:hover {
    background: var(--tan-2);
}

@media (max-width: 720px) {
    .site-header-inner {
        gap: 10px;
        padding: 8px 10px;
    }
    .profile-name {
        display: none;
    }
}


/* =========================================================
   LAYOUT
   ========================================================= */

/* Sidebar */
.sidebar {
    width: 250px;
    background-color: var(--sidebar);
    padding: 20px;
    border-right: 1px solid var(--sidebar-border);
    overflow-y: auto;
    transition: width 0.25s ease, padding 0.25s ease, border 0.25s ease, background-color 0.2s ease;
}

/* When filters are hidden */
body.filters-collapsed .sidebar {
    width: 0 !important;
    padding: 0 !important;
    border-right: none !important;
    overflow: hidden !important;
    margin: 0 !important;
}

/* Main content column */
.content {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    min-width: 0; /* important for flex layouts */
    flex: 1;
    padding: 20px;
    background-color: var(--content-bg);
    background-size: cover;
    background-attachment: fixed;
}


/* =========================================================
   TOP BAR
   ========================================================= */

.top-bar {
    display: flex;
    flex-direction: column; /* ✅ 2-row layout */
    gap: 10px;
    margin-bottom: 20px;
    background-color: var(--topbar);
    padding: 10px;
    border-radius: 8px;
}

/* 2-row top bar layout */
.topbar-row {
    display: flex;
    align-items: center;
    gap: 12px;
}

.topbar-row-1 {
    justify-content: space-between;
}

/* Right side of row 1 */
.topbar-right {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
}

/* Right cluster on row 2 */
.topbar-row-2-right {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

/* Keep your left cluster consistent */
.topbar-left {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

/* Title */
h1 {
    font-family: 'Cinzel', Georgia, serif;
    text-align: center;
    font-size: 2rem;
    color: var(--ink);
    text-shadow: 1px 1px rgba(255,255,255,0.85);
    margin: 0;
}

.topbar-result-count {
    flex: 1;
    text-align: center;
    margin: 0;
    font-weight: 700;
    font-size: 0.95rem;
    color: var(--ink);
    opacity: 0.7;
}

html.dark-mode .topbar-result-count {
    color: var(--text);
}

/* remove default margins that push it down/right */
.top-bar button,
.top-bar select {
    margin: 0;
}

select,
input[type="range"],
input[type="text"],
input[type="number"],
textarea {
    padding: 6px 8px;
    border-radius: 6px;
    border: 1px solid #aaa;
    font-family: inherit;
}

/* Base button */
button {
    padding: 10px;
    background-color: var(--btn-bg);
    color: var(--btn-text);
    border: none;
    border-radius: 6px;
    cursor: pointer;
    transition: background-color 0.15s ease, transform 0.05s ease, color 0.2s ease, border-color 0.2s ease;
}

    button:hover {
        background-color: var(--btn-bg-hover);
    }

    button:active {
        transform: translateY(1px);
    }

.topbar-btn {
    display: inline-block;
    padding: 8px 12px;
    border-radius: 6px;
    background-color: var(--btn-bg);
    color: var(--btn-text);
    cursor: pointer;
    border: none;
    white-space: nowrap;
    text-decoration: none;
    transition: background-color 0.15s ease, transform 0.05s ease, color 0.2s ease, border-color 0.2s ease;
}

    .topbar-btn:hover {
        background-color: var(--btn-bg-hover);
    }

    .topbar-btn:active {
        transform: translateY(1px);
    }

body.filters-collapsed #filtersToggle {
    background-color: var(--btn-bg);
}

/* Styled <select> — matches the site's palette/border-radius so dropdowns
   (Sort by, Min rating, per-page) don't fall back to bare browser-default
   chrome next to the solid .topbar-btn buttons they sit alongside. */
.styled-select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    padding: 8px 30px 8px 12px;
    border-radius: 6px;
    border: 1px solid rgba(75, 46, 0, 0.35);
    background-color: var(--card);
    color: var(--ink);
    font-family: inherit;
    font-size: 0.95rem;
    cursor: pointer;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5l3.5 3.5 3.5-3.5' fill='none' stroke='%234b2e00' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
    background-size: 11px;
    transition: border-color 0.15s ease, background-color 0.2s ease, color 0.2s ease;
}

.styled-select:hover {
    border-color: rgba(75, 46, 0, 0.6);
}

html.dark-mode .styled-select {
    background-color: rgba(255, 255, 255, 0.08);
    border-color: rgba(255, 255, 255, 0.25);
    color: var(--text);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5l3.5 3.5 3.5-3.5' fill='none' stroke='%23eeeeee' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

html.dark-mode .styled-select:hover {
    border-color: rgba(255, 255, 255, 0.45);
}

html.contrast-mode .styled-select {
    border-color: var(--card-border);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5l3.5 3.5 3.5-3.5' fill='none' stroke='%23000000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

    body.filters-collapsed #filtersToggle:hover {
        background-color: var(--btn-bg-hover);
    }

/* Pagination — appears above and below #book-list, identical markup both
   places (rendered from the same paginationHTML() call). */
.pagination-slot:empty {
    display: none;
}

.pagination {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 6px;
    margin: 18px 0;
}

.page-btn {
    appearance: none;
    min-width: 36px;
    height: 36px;
    padding: 0 10px;
    border-radius: 6px;
    border: 1px solid rgba(75, 46, 0, 0.25);
    background-color: var(--card);
    color: var(--ink);
    font-family: inherit;
    font-size: 0.9rem;
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.05s ease, color 0.2s ease;
}

.page-btn:hover:not(:disabled) {
    border-color: rgba(75, 46, 0, 0.5);
    background-color: var(--tan-2);
}

.page-btn:active:not(:disabled) {
    transform: translateY(1px);
}

.page-btn.is-active {
    background-color: var(--btn-bg);
    border-color: var(--btn-bg);
    color: var(--btn-text);
    cursor: default;
}

.page-btn:disabled {
    opacity: 0.4;
    cursor: default;
}

.page-ellipsis {
    padding: 0 4px;
    color: var(--ink-2);
}

html.dark-mode .page-btn {
    background-color: rgba(255, 255, 255, 0.08);
    border-color: rgba(255, 255, 255, 0.25);
    color: var(--text);
}

html.dark-mode .page-btn:hover:not(:disabled) {
    border-color: rgba(255, 255, 255, 0.45);
    background-color: rgba(255, 255, 255, 0.14);
}

html.dark-mode .page-btn.is-active {
    background-color: var(--btn-bg);
    border-color: var(--btn-bg);
    color: var(--btn-text);
}

html.contrast-mode .page-btn {
    border-color: var(--card-border);
}

html.contrast-mode .page-btn:hover:not(:disabled) {
    border-color: var(--card-border);
    background-color: var(--tan-2);
}

/* =========================================================
   BOOK LIST + CARDS
   ========================================================= */

#book-list {
    margin-top: 40px;
    position: relative;
    z-index: 0;
    transition: opacity 0.15s ease;
}

/* Set/cleared by loadCurrentPage() (main.js) around each fetch — every
   page turn/sort/filter-apply is now a real round trip instead of a
   synchronous array slice, so the grid needs a visible in-flight state. */
#book-list.is-loading {
    opacity: 0.5;
    pointer-events: none;
}

.book {
    background: var(--card);
    padding: 15px;
    border-radius: 10px;
    margin-bottom: 15px;
    box-shadow: var(--card-shadow);
    border: 1px solid var(--card-border);
    border-left: 4px solid #bfa58a;
    cursor: pointer;
    transition: background-color 0.15s ease, box-shadow 0.15s ease, transform 0.1s ease, border-color 0.2s ease;
}

    /* Hover highlight in BOTH list + grid */
    .book:hover {
        background-color: var(--tan-2);
        box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08);
        transform: translateY(-1px);
    }

html.dark-mode .book:hover {
    background-color: var(--tan-3);
}

/* Title + meta */
.book-title {
    font-size: 1.3rem;
    font-weight: bold;
    color: var(--ink);
}

.book-meta {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}

.book-author {
    font-size: 0.9rem;
    color: var(--ink-2);
}

/* List-view-only extras (synopsis + your reading status) — grid mode never
   renders this markup at all (see bookCardHTML in main.js), so no display:none
   scoping is needed here. */
.book-synopsis {
    margin: 4px 0 0;
    font-size: 0.88rem;
    line-height: 1.4;
    color: var(--ink-2);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.book-status-line {
    margin-top: 4px;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--tan-text);
}

/* =========================================================
   INDEX — Rating/Readers badges (for .book-badges)
   ========================================================= */

.book-badges {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    margin-top: 8px;
}

.badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    border-radius: 999px;
    font-size: 0.85rem;
    line-height: 1.1;
    font-weight: 700;
    white-space: nowrap;
    border: 1px solid rgba(75,46,0,0.22);
    background: rgba(255,250,240,0.85);
    color: var(--ink);
}

    .badge.is-empty {
        font-weight: 600;
        opacity: 0.7;
    }

.badge--rating {
    border-color: rgba(201,151,77,0.45);
    background: rgba(201,151,77,0.18);
}

.badge--status {
    border-color: rgba(75,46,0,0.22);
    background: rgba(75,46,0,0.08);
}

/* Dark mode */
html.dark-mode .badge {
    border-color: rgba(255,255,255,0.14);
    background: rgba(255,255,255,0.06);
    color: var(--text);
}

html.dark-mode .badge--rating {
    border-color: rgba(201,151,77,0.55);
    background: rgba(201,151,77,0.18);
}

html.dark-mode .badge--status {
    border-color: rgba(255,255,255,0.16);
    background: rgba(255,255,255,0.08);
}

html.contrast-mode .badge {
    border-color: var(--card-border);
    background: var(--card);
}

html.contrast-mode .badge--rating,
html.contrast-mode .badge--status {
    border-color: var(--card-border);
    background: var(--card);
}

/* ---- Cover + layout wrapper ---- */
.book-inner {
    display: flex;
    gap: 14px;
    align-items: flex-start;
}

.book-cover {
    /* position:relative + the img absolutely filling it (below) rather
       than display:grid; place-items:center — with the grid approach the
       img's height:100% didn't reliably resolve against this box's height
       (a replaced-element/grid-stretch edge case), so the browser sized it
       by its own intrinsic aspect ratio instead and overflow:hidden sliced
       off whatever didn't match 2:3. inset:0 has no such ambiguity: it
       always fills the positioned ancestor exactly, so object-fit:contain
       can do its job (letterbox, never crop). */
    position: relative;
    overflow: hidden;
    aspect-ratio: 2 / 3;
}

    /* IMPORTANT: contain instead of cover */
    .book-cover img {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        object-fit: contain;
        object-position: center;
        display: block;
    }

/* Optional “No cover” placeholder */
.cover-placeholder {
    display: grid;
    place-items: center;
    border-radius: 8px;
    background: rgba(0,0,0,0.08);
}

/* Grid/list sizing overrides */
#book-list[data-display="grid"] .book-inner {
    flex-direction: column;
    flex: 1;
    min-height: 0;
}

#book-list[data-display="grid"] .book-cover {
    width: 100%;
    /* Fixed height (matching the img override below), not the base rule's
       aspect-ratio:2/3 — every grid cover is the same height by design
       here, but aspect-ratio would size this container from its own
       (variable, per column-count) width instead, leaving a gap between
       the container's bottom and the fixed-height image floating inside
       it via position:absolute. */
    aspect-ratio: auto;
    height: 220px;
}

    #book-list[data-display="grid"] .book-cover img {
        width: 100%;
        height: 220px;
        object-fit: contain;
        background: rgba(0,0,0,0.06);
        border-radius: 10px;
        display: block;
    }

html.dark-mode .book-cover img {
    background: rgba(255,255,255,0.08);
}

/* =========================================================
   COVER HOVER OVERLAY (cover-overlay.js) — eye/heart/⋮ row over a book
   cover on hover. Icons are intentionally theme-independent (white on a
   translucent dark scrim, like .toast) since they sit over arbitrary
   cover art rather than page surface, so no light/dark/contrast-mode
   variants are needed for them specifically. The "⋮" popup (.cover-menu)
   is appended to <body> at open time (see cover-overlay.js) so it can
   escape each cover's overflow:hidden; it reuses var(--card)/var(--ink)
   etc. like every other panel on the site, so it inherits theme support
   for free.
   ========================================================= */

.cover-overlay {
    position: absolute;
    inset: 0;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s ease;
}

.has-cover-overlay:hover .cover-overlay,
.has-cover-overlay:focus-within .cover-overlay {
    opacity: 1;
}

.cover-overlay-scrim {
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, rgba(0,0,0,0.78) 0%, rgba(0,0,0,0.32) 45%, transparent 75%);
    pointer-events: none;
}

.cover-overlay-actions {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 8px 6px;
    pointer-events: auto;
}

.cover-overlay-actions button {
    width: 30px;
    height: 30px;
    flex-shrink: 0;
    border-radius: 50%;
    border: none;
    background: rgba(0,0,0,0.45);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    cursor: pointer;
    transition: background 0.15s ease, transform 0.1s ease;
}

.cover-overlay-actions button:hover {
    background: rgba(0,0,0,0.65);
    transform: translateY(-1px);
}

.cover-overlay-actions button:disabled {
    opacity: 0.6;
    cursor: default;
    transform: none;
}

.cover-menu-btn.is-open {
    background: rgba(0,0,0,0.65);
}

.cover-icon {
    width: 16px;
    height: 16px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.cover-eye-btn.is-active .cover-icon,
.cover-heart-btn.is-active .cover-icon {
    fill: currentColor;
}

.cover-heart-btn.is-active {
    color: #ff4757;
}

.cover-menu-btn {
    font-size: 1.15rem;
    font-weight: 700;
    line-height: 1;
}

/* The "⋮" popup — a body-level singleton positioned via JS
   (cover-overlay.js's positionPopup), not nested in any card. */
.cover-menu {
    position: absolute;
    width: 220px;
    max-height: 320px;
    overflow-y: auto;
    background: var(--card);
    border: 1px solid var(--card-border);
    border-radius: 10px;
    box-shadow: var(--card-shadow);
    padding: 6px;
    z-index: 40;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.cover-menu-item {
    display: block;
    width: 100%;
    box-sizing: border-box;
    text-align: left;
    padding: 8px 10px;
    border-radius: 6px;
    border: none;
    background: transparent;
    color: var(--ink);
    font-family: inherit;
    font-size: 0.88rem;
    text-decoration: none;
    cursor: pointer;
}

.cover-menu-item:hover {
    background: var(--tan-2);
}

.cover-menu-item.is-disabled {
    opacity: 0.5;
    cursor: default;
}

.cover-menu-item.is-disabled:hover {
    background: transparent;
}

.cover-menu-back {
    display: block;
    width: 100%;
    box-sizing: border-box;
    text-align: left;
    padding: 8px 10px 8px 6px;
    margin-bottom: 4px;
    border: none;
    border-bottom: 1px solid var(--card-border);
    background: transparent;
    color: var(--ink);
    font-family: inherit;
    font-weight: 700;
    font-size: 0.85rem;
    cursor: pointer;
}

.cover-menu-loading,
.cover-menu-empty {
    padding: 10px;
    font-size: 0.85rem;
    opacity: 0.75;
}

.cover-activity-row {
    padding: 6px 10px;
    font-size: 0.85rem;
    border-bottom: 1px solid var(--card-border);
}

.cover-activity-row:last-child {
    border-bottom: none;
}

/* Make grid cards not stretch insanely */
#book-list[data-display="grid"] .book {
    height: auto;
    margin-bottom: 0;
    border-left: none; /* more “tile-like” */
    display: flex;
    flex-direction: column;
}

/* Equal-height cards: grid's own default (align-items/self: stretch) makes
   every card in a row match the tallest one — this had been explicitly
   turned off (align-items/self: start below), which is why 1-line vs
   2-line titles produced ragged card bottoms. Flexing .book-content down
   through .book-header/.book-meta and pinning .book-badges via
   margin-top:auto then puts the badges row at a consistent height across
   a row regardless of how much title/author text is above it. */
#book-list[data-display="grid"] .book-content,
#book-list[data-display="grid"] .book-header,
#book-list[data-display="grid"] .book-meta {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

#book-list[data-display="grid"] .book-badges {
    margin-top: auto;
    padding-top: 8px;
}

#book-list[data-display="grid"] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 14px;
    grid-auto-rows: auto;
    /* Cap at 6 columns by capping the grid's own width at exactly "6 cards
       + 5 gaps" (6*260 + 5*14 = 1630px), instead of letting auto-fill grow
       a 7th column and then stretching all of them via 1fr to fill extra
       window width — that grew cards well past their original ~260px
       size. Below 1630px this is unchanged from before: as many 260px+
       columns as fit, minor stretch to fill any leftover row space, same
       as always. Above it, the grid simply stops growing and leaves the
       extra window width unused, so cards stay their original size.
       #book-list is a flex child of .content (flex-direction:column);
       auto left/right margins center it once max-width caps it below the
       container's full width, instead of hugging the left edge — but
       auto-margin centering removes the item's normal flex-stretch width,
       and without an explicit width the grid's auto-fill has no definite
       size to size columns against, so it was overflowing its parent
       instead of shrinking on narrow windows. The explicit width:100%
       restores that definite (parent-relative, then max-width-capped)
       size so auto-fill sizes correctly at every window width. */
    width: 100%;
    max-width: 1630px;
    margin: 18px auto 0;
}

    #book-list[data-display="grid"] .book {
        min-height: 0;
    }

/* LIST: compact rows */
#book-list[data-display="list"] .book-inner {
    align-items: center;
}

#book-list[data-display="list"] .book-cover {
    aspect-ratio: auto;
    width: 70px;
    height: 105px;
    flex: 0 0 auto;
}

    #book-list[data-display="list"] .book-cover img {
        width: 100%;
        height: 100%;
        object-fit: contain;
        display: block;
    }

/* The cover-hover overlay's eye/heart/⋮ row (see .cover-overlay-actions
   further down) is sized for the ~220px grid cover; list mode's cover is
   only 70×105px, so it needs a compact variant or the buttons overflow. */
#book-list[data-display="list"] .cover-overlay-actions {
    gap: 4px;
    padding: 4px 3px;
}

#book-list[data-display="list"] .cover-overlay-actions button {
    width: 20px;
    height: 20px;
}

#book-list[data-display="list"] .cover-icon {
    width: 11px;
    height: 11px;
}

#book-list[data-display="list"] .cover-menu-btn {
    font-size: 0.75rem;
}

/* Chevron / header styling (safe even if not used) */
.book-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
}

/* =========================================================
   ACCORDION FILTERS (SIDEBAR)
   ========================================================= */

.accordion-section {
    margin-bottom: 10px;
    border: 1px solid #ccc;
    border-radius: 6px;
    background-color: rgba(255, 247, 224, 0.95);
    overflow: hidden;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}

html.dark-mode .accordion-section {
    background-color: rgba(30, 30, 30, 0.95);
    border-color: #444;
}

html.contrast-mode .accordion-section {
    background-color: var(--card);
    border-color: var(--card-border);
}

.accordion-header {
    padding: 8px 10px;
    cursor: pointer;
    user-select: none;
    font-weight: bold;
    font-size: 1rem;
    border-bottom: 1px solid #cfc6b8;
    position: relative;
    color: var(--ink);
    background: rgba(246, 239, 224, 0.95);
}

html.dark-mode .accordion-header {
    color: var(--text);
    background: #444;
    border-bottom: 1px solid #555;
}

html.contrast-mode .accordion-header {
    color: var(--ink);
    background: var(--card);
    border-bottom: 1px solid var(--card-border);
}

.accordion-header::after {
    content: '+';
    position: absolute;
    right: 15px;
    font-weight: bold;
}

.accordion-header:hover {
    background: rgba(236, 227, 205, 1);
}

html.dark-mode .accordion-header:hover {
    background: rgba(70,70,70,0.95);
}

html.contrast-mode .accordion-header:hover {
    background: var(--tan-2);
}

.accordion-header.active::after {
    content: '-';
}

.accordion-content {
    display: none;
    padding: 5px 10px;
    background: rgba(255, 250, 240, 0.95);
    color: #333;
}

html.dark-mode .accordion-content {
    background: rgba(30, 30, 30, 0.95);
    color: var(--text);
}

html.contrast-mode .accordion-content {
    background: var(--card);
    color: var(--ink);
}

.accordion-content label {
    display: block;
    margin: 6px 0 4px 0;
    font-size: 0.85rem;
    cursor: pointer;
    color: inherit;
}

.accordion-content input[type="checkbox"] {
    margin-right: 8px;
    transform: scale(1.05);
}

/* Clear button */
button.clear-button {
    margin-top: 10px;
    width: 100%;
    padding: 8px;
    background: var(--btn-bg);
    color: var(--btn-text);
    border: none;
    cursor: pointer;
    border-radius: 5px;
    transition: background-color 0.15s ease, transform 0.05s ease, color 0.2s ease;
}

    button.clear-button:hover {
        background: var(--btn-bg-hover);
    }

    button.clear-button:active {
        transform: translateY(1px);
    }


/* =========================================================
   GENERIC TAG STYLING (GRAPH TOOLTIP GROUPS ETC.)
   ========================================================= */

.tag-group {
    margin-bottom: 15px;
    border: 1px solid #ccc;
    border-radius: 6px;
    padding: 10px;
    background-color: #fff;
}

html.dark-mode .tag-group {
    background: rgba(40, 40, 40, 0.95);
    border-color: #444;
}

html.contrast-mode .tag-group {
    background: var(--card);
    border-color: var(--card-border);
}

.tags {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 10px;
}

.tag {
    padding: 5px 10px;
    border-radius: 15px;
    border: 1px solid #bfa58a;
    background-color: rgba(246,239,224,0.95);
    cursor: pointer;
    font-weight: bold;
    font-size: 0.8rem;
}

html.dark-mode .tag {
    background: rgba(60,60,60,0.75);
    border-color: rgba(255,255,255,0.14);
}

html.contrast-mode .tag {
    background: var(--card);
    border-color: var(--card-border);
}

.tag.active {
    background-color: var(--tan);
    color: #fff;
}

/* Only style “banner-like” paragraphs inside the main index content */
.content > p {
    text-align: center;
    font-weight: bold;
    background-color: rgba(255,255,255,0.85);
    padding: 10px;
    border-radius: 8px;
}

html.dark-mode .content > p {
    background-color: rgba(50,50,50,0.85);
    color: #fff;
}


/* =========================================================
   GRAPH LAYOUT (single source of truth)
   ========================================================= */

#graph-wrapper {
    display: flex;
    flex-direction: row;
    width: 100%;
    flex: 1;
    min-height: 600px;
    height: calc(100vh - 220px);
    margin-top: 12px;
    margin-bottom: 24px;
    padding: 10px;
    box-sizing: border-box;
    border-radius: 10px;
    border: 1px solid #cfc6b8;
    background: rgba(255,255,255,0.95);
    align-items: stretch;
    gap: 10px;
}

html.dark-mode #graph-wrapper {
    background: rgba(30,30,30,0.95);
    border-color: #444;
}

html.contrast-mode #graph-wrapper {
    background: var(--card);
    border-color: var(--card-border);
}

/* Left panel (weights) — a <details class="collapsible">, closed by
   default. #graph-wrapper's align-items:stretch would otherwise stretch
   this to the full row height even while closed (a flex item stretches
   regardless of its own content size unless told not to), so it needs its
   own align-self: flex-start while closed, switching to stretch only
   once opened so the slider list can fill/scroll within the row. */
#weights-panel {
    width: 300px;
    flex: 0 0 300px;
    align-self: flex-start;
    max-height: 100%;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    background: #fff;
    border-radius: 8px;
    box-shadow: 0 0 0 1px rgba(0,0,0,0.08);
}

#weights-panel[open] {
    align-self: stretch;
}

html.dark-mode #weights-panel {
    background: rgba(30, 30, 30, 0.95);
    color: var(--text);
    box-shadow: 0 0 0 1px rgba(255,255,255,0.08);
}

/* Scroll area inside weights */
#weights-container {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
}

/* Persistent action bar below the slider list — NOT inside
   #weights-container, so it never scrolls out of view. Weight changes are
   staged into a draft copy while dragging (see renderWeightSliders() in
   visualize.js) and only take effect on the graph once "Apply Weights" is
   clicked here, which is also the only thing (besides initial page load)
   that re-fits/re-zooms the graph. */
.weights-confirm-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 10px 8px 4px;
    border-top: 1px solid rgba(75, 46, 0, 0.15);
    flex-shrink: 0;
}

html.dark-mode .weights-confirm-bar {
    border-top-color: rgba(255, 255, 255, 0.15);
}

html.contrast-mode .weights-confirm-bar {
    border-top-color: var(--card-border);
}

.weights-confirm-status {
    font-size: 0.78rem;
    font-style: italic;
    color: var(--ink-2);
}

html.dark-mode .weights-confirm-status {
    color: var(--text);
}

.topbar-btn:disabled {
    opacity: 0.5;
    cursor: default;
}

.weight-row {
    padding: 8px 4px;
    border-bottom: 1px solid rgba(75, 46, 0, 0.10);
}

.weight-row:last-child {
    border-bottom: none;
}

html.dark-mode .weight-row {
    border-bottom-color: rgba(255, 255, 255, 0.10);
}

html.contrast-mode .weight-row {
    border-bottom-color: var(--card-border);
}

.weight-row-label {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: baseline;
    gap: 2px 8px;
    margin-bottom: 6px;
}

.weight-row-name {
    font-weight: 700;
    font-size: 0.85rem;
    color: var(--ink);
}

html.dark-mode .weight-row-name {
    color: var(--text);
}

.weight-row-value {
    font-weight: 800;
    font-size: 0.8rem;
    white-space: nowrap;
    text-align: right;
    color: var(--tan-text);
}

/* Custom track + thumb — the native control renders as a thin hairline
   that looks lost in a 300px-wide panel row. -webkit-appearance:none hands
   Chrome/Edge/Safari's whole rendering over to us (including the native
   accent-color fill-to-thumb behavior, which goes away once you do that),
   so the "filled" portion left of the thumb is redrawn as a gradient on
   the input's own background, positioned via the --fill-pct custom
   property (set from JS on load and on every input event) and revealed
   through a transparent ::-webkit-slider-runnable-track. Firefox keeps its
   native ::-moz-range-progress for the same effect instead.

   The generic `input[type="range"] { padding: 6px 8px; border: 1px solid
   #aaa; }` rule near the top of this file (styling text-like inputs
   site-wide) was silently applying to this slider too and — because it's
   content-box, not border-box — inflating its rendered width ~18px beyond
   the 100% this rule asks for, which pushed the thumb past the row's right
   edge at high values AND desynced the fill gradient (painted on the wider
   padding-box) from the actual track (::-webkit-slider-runnable-track,
   confined to the narrower content-box). `input.weight-row-slider` (an
   element+class selector, matching that rule's own specificity) zeroes
   both out. Width is also inset by the thumb's own diameter on each side
   (calc + matching margin) so the 22px thumb can't overhang the row at
   either extreme — appearance:none opts out of the browser's native
   auto-inset-by-thumb-size travel bounds, so this has to be done by hand. */
input.weight-row-slider {
    -webkit-appearance: none;
    appearance: none;
    width: calc(100% - 22px);
    height: 22px;
    margin: 6px 11px 4px;
    padding: 0;
    border: none;
    border-radius: 0;
    cursor: pointer;
    background: linear-gradient(to right,
        var(--tan) 0%, var(--tan) var(--fill-pct, 0%),
        rgba(75, 46, 0, 0.15) var(--fill-pct, 0%), rgba(75, 46, 0, 0.15) 100%);
}

html.dark-mode .weight-row-slider {
    background: linear-gradient(to right,
        var(--tan) 0%, var(--tan) var(--fill-pct, 0%),
        rgba(255, 255, 255, 0.15) var(--fill-pct, 0%), rgba(255, 255, 255, 0.15) 100%);
}

html.contrast-mode .weight-row-slider {
    background: linear-gradient(to right,
        var(--tan) 0%, var(--tan) var(--fill-pct, 0%),
        rgba(0, 0, 0, 0.15) var(--fill-pct, 0%), rgba(0, 0, 0, 0.15) 100%);
}

.weight-row-slider::-webkit-slider-runnable-track {
    height: 7px;
    border-radius: 999px;
    background: transparent;
}

.weight-row-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    margin-top: -7.5px;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--tan);
    border: 3px solid #fff;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
}

.weight-row-slider::-moz-range-track {
    height: 7px;
    border-radius: 999px;
    background: rgba(75, 46, 0, 0.15);
}

html.dark-mode .weight-row-slider::-moz-range-track {
    background: rgba(255, 255, 255, 0.15);
}

html.contrast-mode .weight-row-slider::-moz-range-track {
    background: rgba(0, 0, 0, 0.15);
}

.weight-row-slider::-moz-range-progress {
    height: 7px;
    border-radius: 999px;
    background: var(--tan);
}

.weight-row-slider::-moz-range-thumb {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--tan);
    border: 3px solid #fff;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
}

html.dark-mode .weight-row-slider::-webkit-slider-thumb,
html.dark-mode .weight-row-slider::-moz-range-thumb {
    border-color: #2a2016;
}

/* Middle column */
#graph-container {
    flex: 1 1 auto;
    min-width: 0;
    height: 100%;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

/* Graph surface */
#graph {
    flex: 1 1 auto;
    min-height: 0;
    height: 100%;
    width: 100%;
    border-radius: 8px;
}

    /* SVG fills the surface */
    #graph svg {
        width: 100%;
        height: 100%;
        display: block;
        border: 1px solid #ccc;
        background: transparent;
    }

html.dark-mode #graph svg {
    border-color: #444;
}

html.contrast-mode #graph svg {
    border-color: var(--card-border);
}

/* Right panel (tooltip) */
#tooltip-panel {
    width: 500px;
    flex: 0 0 500px;
    height: 100%;
    min-height: 0;
    overflow-y: auto;
    box-sizing: border-box;
    padding: 16px;
    background-color: #f8f8f8;
    color: #333;
    border-left: 1px solid #ccc;
}

    #tooltip-panel.hidden {
        display: none;
    }

html.dark-mode #tooltip-panel {
    background-color: #1e1e1e;
    color: var(--text);
    border-left: 1px solid #444;
}

html.contrast-mode #tooltip-panel {
    background-color: var(--card);
    color: var(--ink);
    border-left: 1px solid var(--card-border);
}

#tooltip-panel h3 {
    margin-top: 0;
    font-size: 1.2em;
    border-bottom: 1px solid currentColor;
    padding-bottom: 0.2em;
}

#tooltip-panel .tag-group {
    margin-bottom: 0.5em;
}

    #tooltip-panel .tag-group strong {
        display: block;
        margin-bottom: 0.2em;
        font-size: 0.95em;
    }

#tooltip-panel .tags {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

    #tooltip-panel .tags span {
        background-color: #ddd;
        color: #333;
        padding: 2px 6px;
        border-radius: 4px;
        font-size: 0.85em;
    }

html.dark-mode #tooltip-panel .tags span {
    background-color: #444;
    color: #eee;
}

/* #weights-panel (300px) + #tooltip-panel (500px) are both fixed-width —
   on a narrow viewport neither fits next to a usable graph column, so
   stack everything instead. #graph-container/#graph normally size via
   height:100% of #graph-wrapper's own fixed height; #graph-wrapper drops
   that fixed height here (its content now determines height), so the
   graph needs its own explicit height instead of inheriting one that no
   longer resolves to anything. */
@media (max-width: 900px) {
    #graph-wrapper {
        flex-direction: column;
        height: auto;
        min-height: 0;
    }

    #weights-panel {
        width: 100%;
        flex: 0 0 auto;
        max-height: 320px;
    }

    #weights-panel[open] {
        align-self: auto;
    }

    #graph-container,
    #graph {
        height: 60vh;
        min-height: 360px;
    }

    #tooltip-panel {
        width: 100%;
        flex: 0 0 auto;
        max-height: 320px;
        border-left: none;
        border-top: 1px solid #ccc;
    }

    html.dark-mode #tooltip-panel {
        border-top-color: #444;
    }
}



/* =========================================================
   MANAGE LIBRARY PAGE
   Was a modal (#manageOverlay) + a JS-injected second modal
   (#bulkModalBackdrop) — now a real page (manage.html), so everything
   below is scoped to .manage-page instead of a modal wrapper id, and the
   modal-chrome-only rules (backdrop positioning, dialog-box sizing,
   z-index stacking) are gone — this is just ordinary page-flow layout now.
   ========================================================= */

.manage-page {
    max-width: 1300px;
    margin: 0 auto;
    padding: 24px 16px 60px;
    width: 100%;
    box-sizing: border-box;
}

.manage-page-header {
    display: flex;
    align-items: baseline;
    gap: 16px;
    margin-bottom: 16px;
}

.manage-page-header h1 {
    margin: 0;
    color: var(--ink);
}

.manage-mode-tabs {
    display: flex;
    gap: 8px;
    margin-bottom: 20px;
}

.manage-mode-tabs .topbar-btn[aria-pressed="true"] {
    background: var(--tan);
    color: #2b1a00;
}

html.contrast-mode .manage-mode-tabs .topbar-btn[aria-pressed="true"] {
    /* --tan is solid black in this theme; the hardcoded dark-brown text
       above would be nearly invisible against it. */
    color: var(--btn-text);
}

/* Shared two-column shell: sidebar+editor in Edit Entries mode, two
   side-by-side cards in Bulk Tag Edit mode. */
.manage-entries-layout {
    display: flex;
    gap: 20px;
    align-items: flex-start;
}

.manage-entries-layout > .detail-panel {
    flex: 1;
    min-width: 0;
}

@media (max-width: 900px) {
    .manage-entries-layout {
        flex-direction: column;
    }
}

/* ---- Sidebar (entry list) ---- */
.manage-sidebar {
    flex: 0 0 320px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    position: sticky;
    top: 20px;
}

@media (max-width: 900px) {
    .manage-sidebar {
        flex: 0 0 auto;
        width: 100%;
        position: static;
    }
}

.manage-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.manage-actions .danger {
    background: #8b2b2b;
}

.manage-search-input {
    width: 100%;
    box-sizing: border-box;
}

.entry-list {
    max-height: 65vh;
    overflow-y: auto;
    border: 1px solid var(--card-border);
    border-radius: 8px;
    padding: 6px;
    background: rgba(255,250,240,0.65);
}

html.dark-mode .entry-list {
    border-color: #444;
    background: rgba(30,30,30,0.6);
}

html.contrast-mode .entry-list {
    background: var(--card);
}

.entry-item {
    padding: 8px 10px;
    border-radius: 8px;
    cursor: pointer;
    display: grid;
    gap: 2px;
}

.entry-item:hover {
    background: rgba(0,0,0,0.05);
}

html.dark-mode .entry-item:hover {
    background: rgba(255,255,255,0.06);
}

.entry-item.active {
    background: rgba(201,151,77,0.18);
}

html.contrast-mode .entry-item.active {
    background: var(--tan-2);
}

.entry-item .small {
    font-size: 0.85rem;
    opacity: 0.8;
}

/* ---- Import book info: lookup-button columns ----
   #entryForm label has its own margin/font-size (see rule above), and a
   plain `align-items:flex-end` wrapper only pins a button to the bottom of
   its grid cell — which sits past the label's own bottom margin, leaving
   the button visibly lower than its adjacent input. Mirroring the label's
   box model (same margin, an invisible same-text spacer standing in for
   the label text, then the control) makes both columns the same shape so
   the button lines up with the input exactly, however either one wraps. */
.import-lookup-col {
    display: flex;
    flex-direction: column;
    margin: 10px 0;
    font-size: 0.9rem;
}

.import-lookup-col .import-lookup-spacer {
    visibility: hidden;
}

.import-lookup-col button {
    margin-top: 6px;
    width: 100%;
}

/* ---- Import book info: title-search candidate list ---- */

.import-results {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 10px;
    max-height: 320px;
    overflow-y: auto;
}

.import-result-row {
    display: flex;
    gap: 10px;
    align-items: center;
    text-align: left;
    width: 100%;
    padding: 8px;
    border: 1px solid var(--card-border);
    border-radius: 8px;
    background: transparent;
    cursor: pointer;
}

.import-result-row:hover {
    background: rgba(0,0,0,0.05);
}

html.dark-mode .import-result-row:hover {
    background: rgba(255,255,255,0.06);
}

.import-result-cover {
    width: 40px;
    height: 60px;
    object-fit: cover;
    border-radius: 4px;
    flex-shrink: 0;
    background: rgba(0,0,0,0.08);
}

.import-result-info {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.import-result-info .small {
    font-size: 0.85rem;
    opacity: 0.8;
}

/* ---- Editor (right column) — card sections instead of one long form ---- */
.manage-editor {
    flex: 1;
    min-width: 0;
}

.manage-editor .muted {
    opacity: 0.75;
    padding: 10px;
}

.manage-card {
    margin-bottom: 16px;
}

.manage-card .panel-title {
    font-family: 'Cinzel', Georgia, serif;
    margin-bottom: 12px;
    font-size: 1.05rem;
}

#entryForm label {
    display: block;
    margin: 10px 0;
    font-size: 0.9rem;
}

#entryForm input,
#entryForm select,
#entryForm textarea {
    width: 100%;
    box-sizing: border-box;
    margin-top: 6px;
}

.row2 {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
}

#seriesNameRow label {
    margin: 0;
}

.chip-row {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 10px 0;
}

.chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    border-radius: 999px;
    background: rgba(246,239,224,0.95);
    border: 1px solid #bfa58a;
    font-size: 0.85rem;
}

html.dark-mode .chip {
    background: rgba(60,60,60,0.7);
    border-color: #555;
}

html.contrast-mode .chip {
    background: var(--card);
    border-color: var(--card-border);
}

.chip button {
    padding: 0;
    background: transparent;
    color: inherit;
    border: none;
    cursor: pointer;
    font-size: 14px;
    line-height: 1;
}

.manage-form-footer {
    margin-top: 12px;
    display: flex;
    justify-content: flex-end;
}

/* Cover editor — box chrome now comes from .detail-panel.manage-card on
   the wrapping element; these are just the inner layout rules. */
.cover-editor-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 8px;
}

.cover-editor .small {
    font-size: 0.85rem;
    opacity: 0.75;
}

.cover-row {
    display: grid;
    grid-template-columns: 120px 1fr;
    gap: 12px;
    align-items: start;
}

.cover-preview {
    width: 120px;
    height: 170px;
    border-radius: 10px;
    overflow: hidden;
    border: 1px solid rgba(0,0,0,0.12);
    background: rgba(255,255,255,0.65);
    box-shadow: 0 2px 6px rgba(0,0,0,0.12);
}

html.dark-mode .cover-preview {
    border-color: rgba(255,255,255,0.12);
    background: rgba(40,40,40,0.65);
}

.cover-preview img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.cover-actions {
    display: flex;
    gap: 8px;
    margin-top: 8px;
    flex-wrap: wrap;
}

.cover-help {
    margin-top: 8px;
    font-size: 0.85rem;
    opacity: 0.85;
}

/* Tag palette buttons — were inline-styled in the old modal code; same
   look, real classes now. */
.tag-palette-btn {
    appearance: none;
    background: #e7d0a6;
    color: #2b1a00;
    border: none;
    padding: 6px 10px;
    border-radius: 999px;
    font-size: 0.85rem;
    margin: 6px 6px 0 0;
    cursor: pointer;
    font-family: inherit;
}

.tag-palette-btn.is-selected {
    outline: 2px solid rgba(75,46,0,0.55);
    box-shadow: inset 0 0 0 999px rgba(201,151,77,0.25);
}

html.contrast-mode .tag-palette-btn {
    background: var(--card);
    color: var(--ink);
    border: 1px solid var(--card-border);
}

html.contrast-mode .tag-palette-btn.is-selected {
    outline: 2px solid var(--card-border);
    box-shadow: inset 0 0 0 999px var(--tan-2);
}

/* ---- Bulk Tag Edit ---- */
.bulk-list {
    max-height: 55vh;
    overflow: auto;
    border-top: 1px solid var(--card-border);
    padding-top: 8px;
}

.bulk-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 4px;
    border-radius: 8px;
}

.bulk-row:hover {
    background: rgba(236, 227, 205, 0.7);
}

html.dark-mode .bulk-row:hover {
    background: rgba(255,255,255,0.06);
}

html.contrast-mode .bulk-row:hover {
    background: var(--tan-2);
}

.bulk-row small {
    opacity: 0.75;
}


/* =========================================================
   DETAILS PAGE (layout overrides)
   ========================================================= */

/* Detail page — Collapsible section spacing */
.detail-card details.collapsible {
    margin-top: 16px; /* space above each collapsible */
}

    .detail-card details.collapsible + details.collapsible {
        margin-top: 16px; /* consistent spacing between collapsibles */
    }

body.detail-page {
    display: block; /* override flex sidebar layout */
    background: var(--bg);
    min-height: 100vh;
}

.detail-shell {
    max-width: 1100px;
    margin: 0 auto;
    padding: 18px;
}

.detail-topbar {
    display: flex;
    align-items: center;
    gap: 12px;
    justify-content: space-between;
    background: rgba(255,255,255,0.9);
    border-radius: 10px;
    padding: 10px 12px;
    border: 1px solid rgba(0,0,0,0.10);
}

html.dark-mode .detail-topbar {
    background: rgba(50,50,50,0.85);
    border-color: rgba(255,255,255,0.10);
}

.detail-back {
    text-decoration: none;
    color: var(--ink);
    font-weight: bold;
    padding: 8px 10px;
    border-radius: 8px;
    background: rgba(201,151,77,0.12);
    border: 1px solid rgba(201,151,77,0.35);
    white-space: nowrap;
}

    .detail-back:hover {
        background: rgba(201,151,77,0.18);
    }

html.contrast-mode .detail-back {
    background: var(--card);
    border-color: var(--card-border);
}

html.contrast-mode .detail-back:hover {
    background: var(--tan-2);
}

.detail-root {
    margin-top: 14px;
}

.detail-card {
    background: rgba(255,255,255,0.95);
    border: 1px solid rgba(0,0,0,0.10);
    border-radius: 14px;
    padding: 16px;
    box-shadow: 0 8px 22px rgba(0,0,0,0.10);
}

html.dark-mode .detail-card {
    background: rgba(35,35,35,0.95);
    border-color: rgba(255,255,255,0.10);
}

.detail-hero {
    display: grid;
    grid-template-columns: 260px 1fr;
    gap: 16px;
    align-items: start;
}

@media (max-width: 820px) {
    .detail-hero {
        grid-template-columns: 1fr;
    }
}

.detail-cover {
    width: 100%;
    aspect-ratio: 2 / 3;
    border-radius: 14px;
    overflow: hidden;
    border: 1px solid rgba(0,0,0,0.12);
    background: rgba(0,0,0,0.06);
    position: relative;
    /* see .book-cover's comment for why this is position:relative + an
       absolutely-filled img rather than display:grid; place-items:center */
}

    .detail-cover img {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        object-fit: contain;
        display: block;
    }

.detail-meta .detail-kicker {
    font-weight: bold;
    color: var(--ink-2);
    opacity: 0.95;
}

.detail-h1 {
    font-family: 'Cinzel', Georgia, serif;
    margin: 6px 0 8px 0;
    font-size: 2rem;
    color: var(--ink);
}

.detail-sub {
    display: grid;
    gap: 6px;
    font-size: 0.98rem;
    color: var(--ink-2);
}

.detail-summary {
    margin-top: 12px;
    background: rgba(246,239,224,0.65);
    border: 1px solid rgba(191,165,138,0.35);
    border-radius: 12px;
    padding: 12px;
}

html.dark-mode .detail-summary {
    background: rgba(40,40,40,0.85);
    border-color: rgba(255,255,255,0.10);
}

html.contrast-mode .detail-summary {
    background: var(--card);
    border-color: var(--card-border);
}

.detail-summary h2 {
    margin: 0 0 6px 0;
    font-size: 1.1rem;
    color: var(--ink);
}

.detail-summary p {
    margin: 0;
    text-align: left;
    font-weight: normal;
    background: transparent;
    padding: 0;
}

.detail-sep {
    border: none;
    border-top: 1px solid rgba(0,0,0,0.12);
    margin: 16px 0;
}

html.dark-mode .detail-sep {
    border-top-color: rgba(255,255,255,0.10);
}

.detail-section h2 {
    margin: 0 0 10px 0;
    color: var(--ink);
}

.detail-taggroup {
    margin: 10px 0;
    padding: 10px;
    border-radius: 12px;
    border: 1px solid rgba(191,165,138,0.35);
    background: rgba(246,239,224,0.50);
}

html.contrast-mode .detail-taggroup {
    background: var(--card);
    border-color: var(--card-border);
}

/* =========================================================
   Detail page — Rating/status panels
   ========================================================= */

.detail-panels {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 12px;
    margin-top: 10px;
}

@media (max-width: 980px) {
    .detail-panels {
        grid-template-columns: 1fr;
    }
}

.detail-panel {
    background: rgba(255, 255, 255, 0.7);
    border: 1px solid rgba(75, 46, 0, 0.18);
    border-radius: 14px;
    padding: 12px;
    box-shadow: 0 2px 10px rgba(0,0,0,0.06);
}

html.dark-mode .detail-panel {
    background: rgba(0, 0, 0, 0.25);
    border-color: rgba(255, 255, 255, 0.15);
}

html.contrast-mode .detail-panel {
    background: var(--card);
    border-color: var(--card-border);
}

.detail-panel .panel-title {
    font-family: 'Cinzel', Georgia, serif;
    font-weight: 700;
    margin-bottom: 6px;
}

.detail-panel .panel-value {
    font-size: 1.35rem;
    font-weight: 800;
    margin-bottom: 10px;
}

.detail-panel .meta-row {
    margin-top: 6px;
}

/* =========================================================
   Community Rating distribution (Letterboxd-style histogram) —
   detail.js's ratingDistributionHTML()/hydrateRatingDistribution().
   ========================================================= */

.rating-dist-section h2 {
    margin-bottom: 8px;
}

/* No fixed height anywhere in here — .rating-dist-summary can run taller
   than the 44px bar row (e.g. wrapping onto two lines at narrow widths)
   and the row (align-items:center, not stretch-and-clip) just grows to
   fit it, so the readers tally and avg number never overlap the bars. */
.rating-dist {
    display: flex;
    align-items: center;
    gap: 16px;
    max-width: 420px;
    background: rgba(255, 255, 255, 0.7);
    border: 1px solid rgba(75, 46, 0, 0.18);
    border-radius: 14px;
    padding: 12px 16px 14px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06);
}

html.dark-mode .rating-dist {
    background: rgba(0, 0, 0, 0.25);
    border-color: rgba(255, 255, 255, 0.15);
}

html.contrast-mode .rating-dist {
    background: var(--card);
    border-color: var(--card-border);
}

.rating-dist-summary {
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
}

.rating-dist-readers {
    font-size: 0.95rem;
    white-space: nowrap;
}

.rating-dist-readers strong {
    color: var(--ink);
    font-size: 1.05rem;
}

html.dark-mode .rating-dist-readers strong {
    color: var(--text);
}

.rating-dist-avg {
    font-size: 1.6rem;
    font-weight: 800;
    color: var(--ink);
}

html.dark-mode .rating-dist-avg {
    color: var(--text);
}

.rating-dist-bars-wrap {
    position: relative;
    flex: 1;
}

.rating-dist-bars {
    display: flex;
    align-items: stretch;
    gap: 3px;
    height: 44px;
}

/* The full-height hover target — the visible bar inside is often just a
   sliver a couple pixels tall for a low-count bucket, far too small to
   hover precisely on its own. */
.rating-dist-col {
    flex: 1;
    display: flex;
    align-items: flex-end;
    cursor: pointer;
}

.rating-dist-bar {
    width: 100%;
    height: var(--h, 4%);
    min-height: 3px;
    background: #c9974d;
    border-radius: 2px 2px 0 0;
    transition: background 0.1s ease;
    pointer-events: none;
}

.rating-dist-col:hover .rating-dist-bar {
    background: #a97a30;
}

html.dark-mode .rating-dist-bar {
    background: rgba(201, 151, 77, 0.75);
}

html.dark-mode .rating-dist-col:hover .rating-dist-bar {
    background: #c9974d;
}

html.contrast-mode .rating-dist-bar {
    /* The gold fill is only ~2.5:1 against this theme's white background —
       fails the 3:1 WCAG non-text-contrast minimum for a chart element
       whose color conveys the data. */
    background: #000;
}

html.contrast-mode .rating-dist-col:hover .rating-dist-bar {
    background: #333;
}

.rating-dist-tooltip {
    position: absolute;
    bottom: 100%;
    transform: translateX(-50%);
    margin-bottom: 8px;
    background: rgba(30, 20, 10, 0.92);
    color: #fff;
    padding: 4px 8px;
    border-radius: 6px;
    font-size: 0.8rem;
    white-space: nowrap;
    pointer-events: none;
    z-index: 5;
}

.rating-dist-tooltip::after {
    content: "";
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    border: 5px solid transparent;
    border-top-color: rgba(30, 20, 10, 0.92);
}

.rating-history {
    position: relative;
    width: 100%;
}

.rating-history-svg {
    width: 100%;
    height: auto;
    display: block;
    overflow: visible;
}

.rating-history-line {
    fill: none;
    stroke: #c9974d;
    stroke-width: 2.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}

html.contrast-mode .rating-history-line {
    /* Same 3:1 non-text-contrast reasoning as .rating-dist-bar above. */
    stroke: #000;
}

html.dark-mode .rating-history-line {
    stroke: rgba(201, 151, 77, 0.9);
}

.rating-history-dot {
    fill: #c9974d;
    stroke: #fff;
    stroke-width: 2;
    cursor: pointer;
    transition: r 0.1s ease;
}

html.dark-mode .rating-history-dot {
    stroke: #2a1d10;
}

.rating-history-dot:hover {
    r: 7;
    fill: #a97a30;
}

html.dark-mode .rating-history-dot {
    fill: rgba(201, 151, 77, 0.9);
}

html.dark-mode .rating-history-dot:hover {
    fill: #c9974d;
}

html.contrast-mode .rating-history-dot {
    fill: #000;
}

html.contrast-mode .rating-history-dot:hover {
    fill: #333;
}

.rating-history-tooltip {
    position: absolute;
    transform: translate(-50%, -100%);
    margin-top: -10px;
    background: rgba(30, 20, 10, 0.92);
    color: #fff;
    padding: 4px 8px;
    border-radius: 6px;
    font-size: 0.8rem;
    white-space: nowrap;
    pointer-events: none;
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 4px;
}

.rating-history-tooltip.hidden {
    display: none;
}

.rating-history-tooltip .star-row-off path {
    fill: rgba(255, 255, 255, 0.3);
}

.quick-rate {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.qr-btn,
.qp-btn {
    appearance: none;
    border: 1px solid rgba(75, 46, 0, 0.25);
    background: rgba(255, 250, 240, 0.85);
    border-radius: 999px;
    padding: 7px 10px;
    font: inherit;
    cursor: pointer;
    transition: transform 0.05s ease, background 0.15s ease, border-color 0.15s ease;
}

html.dark-mode .qr-btn,
html.dark-mode .qp-btn {
    background: rgba(255, 255, 255, 0.08);
    border-color: rgba(255, 255, 255, 0.18);
}

html.contrast-mode .qr-btn,
html.contrast-mode .qp-btn {
    background: var(--card);
    border-color: var(--card-border);
}

.qr-btn:hover,
.qp-btn:hover {
    transform: translateY(-1px);
}

.qr-btn.is-active,
.qp-btn.is-active {
    border-color: rgba(75, 46, 0, 0.6);
    background: rgba(201, 151, 77, 0.22);
}

html.dark-mode .qr-btn.is-active,
html.dark-mode .qp-btn.is-active {
    background: rgba(201, 151, 77, 0.18);
}

html.contrast-mode .qr-btn.is-active,
html.contrast-mode .qp-btn.is-active {
    border-color: var(--card-border);
    background: var(--tan-2);
}

.qr-clear {
    opacity: 0.85;
}

/* =========================================================
   Star rating picker (hover-preview, half-star clicks) — shared by the
   "Your rating" quick panel and the review form's rating (detail.js).
   Two stacked SVG stars per star: a dim base layer, and a gold layer
   clipped via clip-path:inset() on its own bounding box. An inline SVG's
   path fills its viewBox edge-to-edge and symmetrically (unlike a Unicode
   "★" glyph, which fonts pad unevenly left/right inside its character
   box) — so clipping the SVG's box by % is the same as clipping the star
   shape itself, giving an exact half/half split instead of one that looks
   "mostly full."
   ========================================================= */

.star-pick-row {
    display: flex;
    align-items: center;
    gap: 10px;
}

.star-pick-stars {
    display: inline-flex;
    cursor: pointer;
    line-height: 1;
}

.star-pick {
    position: relative;
    display: inline-block;
    width: 1.3em;
    height: 1.3em;
}

.star-pick svg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

.star-pick-off path {
    fill: rgba(75, 46, 0, 0.22);
}

html.dark-mode .star-pick-off path {
    fill: rgba(255, 255, 255, 0.18);
}

html.contrast-mode .star-pick-off path {
    fill: rgba(0, 0, 0, 0.22);
}

.star-pick-on path {
    fill: #c9974d;
}

html.contrast-mode .star-pick-on path {
    /* The core rating indicator — gold only clears ~2.5:1 against white,
       well under the 3:1 non-text-contrast minimum for this theme. */
    fill: #000;
}

.star-pick-on {
    clip-path: inset(0 calc(100% - var(--fill, 0%)) 0 0);
}

/* Non-interactive star row — book-list badges, histogram tooltip. Same
   SVG + clip-path technique as .star-pick above, just without the
   hover/click wiring (see entries.js's starRowHTML()). */
.star-row {
    display: inline-flex;
    vertical-align: middle;
    line-height: 1;
}

.star-row-star {
    position: relative;
    display: inline-block;
    width: var(--star-row-size, 1em);
    height: var(--star-row-size, 1em);
}

.star-row-star svg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

.star-row-off path {
    fill: rgba(75, 46, 0, 0.25);
}

html.dark-mode .star-row-off path {
    fill: rgba(255, 255, 255, 0.22);
}

html.contrast-mode .star-row-off path {
    fill: rgba(0, 0, 0, 0.25);
}

/* Dark tooltip bubble needs a lighter off-star to stay visible against it */
.rating-dist-tooltip .star-row-off path {
    fill: rgba(255, 255, 255, 0.3);
}

.star-row-on path {
    fill: #c9974d;
}

html.contrast-mode .star-row-on path {
    fill: #000;
}

.star-row-on {
    clip-path: inset(0 calc(100% - var(--fill, 0%)) 0 0);
}

.star-pick-clear {
    appearance: none;
    border: none;
    background: none;
    padding: 0;
    font: inherit;
    color: inherit;
    opacity: 0.75;
    cursor: pointer;
    text-decoration: underline;
}

.star-pick-clear:hover {
    opacity: 1;
}

/* =========================================================
   DETAILS PAGE — Reviews section
   ========================================================= */

.review-form {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.review-form-title {
    font-weight: 700;
    color: var(--ink);
}

.review-form textarea {
    width: 100%;
    padding: 10px 12px;
    border-radius: 8px;
    border: 1px solid var(--card-border);
    background: var(--card);
    color: var(--text);
    font-family: inherit;
    font-size: 0.95rem;
    box-sizing: border-box;
    resize: vertical;
}

.review-form-actions {
    display: flex;
    align-items: center;
    gap: 10px;
}

.review-list {
    display: flex;
    flex-direction: column;
    gap: 14px;
    margin-top: 14px;
}

.review-item {
    display: flex;
    gap: 12px;
    align-items: flex-start;
}

.review-avatar {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
    background: var(--tan-2);
}

.review-item-body {
    flex: 1;
    min-width: 0;
}

.review-item-meta {
    display: flex;
    gap: 10px;
    align-items: baseline;
    font-size: 0.85rem;
}

.review-item-author {
    font-weight: 700;
    color: var(--ink);
    text-decoration: none;
}

.review-item-author:hover {
    text-decoration: underline;
}

.review-item-meta span {
    opacity: 0.7;
}

.review-item-text {
    margin-top: 4px;
    font-size: 0.92rem;
    white-space: pre-wrap;
}

/* ---- Likes (entry-level slot near the title + per-review buttons) ---- */

.entry-like-slot {
    display: inline-block;
    vertical-align: middle;
}

.like-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    appearance: none;
    border: 1px solid rgba(75, 46, 0, 0.25);
    background: rgba(255, 250, 240, 0.85);
    border-radius: 999px;
    padding: 5px 10px;
    font-size: 0.85rem;
    color: inherit;
    cursor: pointer;
    margin-top: 6px;
}

html.dark-mode .like-btn {
    background: rgba(255, 255, 255, 0.08);
    border-color: rgba(255, 255, 255, 0.18);
}

html.contrast-mode .like-btn {
    background: var(--card);
    border-color: var(--card-border);
}

.like-btn:hover {
    transform: translateY(-1px);
}

.like-btn.is-liked {
    border-color: rgba(75, 46, 0, 0.6);
    background: rgba(201, 151, 77, 0.22);
}

html.dark-mode .like-btn.is-liked {
    background: rgba(201, 151, 77, 0.18);
}

html.contrast-mode .like-btn.is-liked {
    border-color: var(--card-border);
    background: var(--tan-2);
}

.entry-like-slot .like-btn {
    margin-top: 0;
    vertical-align: middle;
}

/* ---- Add to list (detail page) ---- */

.add-to-list-panel {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.add-to-list-buttons {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.list-toggle-btn {
    appearance: none;
    border: 1px solid rgba(75, 46, 0, 0.25);
    background: rgba(255, 250, 240, 0.85);
    border-radius: 999px;
    padding: 7px 12px;
    font-size: 0.85rem;
    color: inherit;
    cursor: pointer;
}

html.dark-mode .list-toggle-btn {
    background: rgba(255, 255, 255, 0.08);
    border-color: rgba(255, 255, 255, 0.18);
}

html.contrast-mode .list-toggle-btn {
    background: var(--card);
    border-color: var(--card-border);
}

.list-toggle-btn:hover {
    transform: translateY(-1px);
}

.list-toggle-btn.is-in-list {
    border-color: rgba(75, 46, 0, 0.6);
    background: rgba(201, 151, 77, 0.22);
}

html.dark-mode .list-toggle-btn.is-in-list {
    background: rgba(201, 151, 77, 0.18);
}

html.contrast-mode .list-toggle-btn.is-in-list {
    border-color: var(--card-border);
    background: var(--tan-2);
}

.add-to-list-new {
    display: flex;
    gap: 8px;
    align-items: center;
}

.add-to-list-new input {
    flex: 1;
    max-width: 260px;
    padding: 8px 10px;
    border-radius: 8px;
    border: 1px solid var(--card-border);
    background: var(--card);
    color: var(--text);
    font-family: inherit;
    font-size: 0.9rem;
    box-sizing: border-box;
}

/* =========================================================
   DETAILS PAGE — Collapsible sections (<details>/<summary>)
   ========================================================= */

.collapsible {
    border: 1px solid rgba(0,0,0,0.10);
    border-radius: 12px;
    background: rgba(255,255,255,0.65);
    overflow: hidden;
    flex-shrink: 0; /* prevent flex-column ancestors (e.g. .top-bar, .content) from collapsing this to 0 height when open */
}

html.dark-mode .collapsible {
    background: rgba(40,40,40,0.65);
    border-color: rgba(255,255,255,0.10);
}

html.contrast-mode .collapsible {
    background: var(--card);
    border-color: var(--card-border);
}

.collapsible-summary {
    list-style: none;
    cursor: pointer;
    user-select: none;
    padding: 10px 12px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    background: rgba(246,239,224,0.75);
    border-bottom: 1px solid rgba(0,0,0,0.08);
}

html.dark-mode .collapsible-summary {
    background: rgba(55,55,55,0.65);
    border-bottom-color: rgba(255,255,255,0.08);
}

html.contrast-mode .collapsible-summary {
    background: var(--card);
    border-bottom-color: var(--card-border);
}

.collapsible-summary::after {
    content: "▾";
    opacity: 0.85;
    font-weight: 700;
    transform: rotate(-90deg);
    transition: transform 0.12s ease;
}

.collapsible[open] .collapsible-summary::after {
    transform: rotate(0deg);
}

.collapsible-title {
    font-weight: 800;
    color: var(--ink);
}

html.dark-mode .collapsible-title {
    color: var(--text);
}

.collapsible-body {
    padding: 12px;
}

html.dark-mode .detail-taggroup {
    background: rgba(40,40,40,0.85);
    border-color: rgba(255,255,255,0.10);
}

.detail-taggroup h3 {
    margin: 0 0 8px 0;
    font-size: 1rem;
    color: var(--ink);
}

.detail-tagrow {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.detail-tag {
    display: inline-block;
    padding: 4px 10px;
    border-radius: 999px;
    font-size: 0.85rem;
    border: 1px solid rgba(191,165,138,0.65);
    background: rgba(255,250,240,0.95);
}

html.dark-mode .detail-tag {
    background: rgba(60,60,60,0.75);
    border-color: rgba(255,255,255,0.14);
}

html.contrast-mode .detail-tag {
    background: var(--card);
    border-color: var(--card-border);
}

.detail-books {
    display: grid;
    gap: 10px;
}

.detail-bookrow {
    text-decoration: none;
    color: inherit;
    display: grid;
    grid-template-columns: 60px 1fr;
    gap: 10px;
    align-items: center;
    padding: 10px;
    border-radius: 12px;
    background: rgba(255,250,240,0.85);
    border: 1px solid rgba(0,0,0,0.10);
}

    .detail-bookrow:hover {
        background: rgba(236, 227, 205, 0.75);
    }

html.dark-mode .detail-bookrow {
    background: rgba(40,40,40,0.85);
    border-color: rgba(255,255,255,0.10);
}

    html.dark-mode .detail-bookrow:hover {
        background: rgba(60,60,60,0.65);
    }

html.contrast-mode .detail-bookrow {
    background: var(--card);
}

    html.contrast-mode .detail-bookrow:hover {
        background: var(--tan-2);
    }

.detail-bookcover {
    width: 60px;
    height: 90px;
    border-radius: 10px;
    overflow: hidden;
    background: rgba(0,0,0,0.06);
    border: 1px solid rgba(0,0,0,0.10);
    position: relative;
    /* see .book-cover's comment for why this is position:relative + an
       absolutely-filled img rather than display:grid; place-items:center */
}

    .detail-bookcover img {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        object-fit: contain;
        display: block;
    }

.detail-booktitle {
    font-weight: bold;
}

.detail-booksub {
    opacity: 0.8;
    font-size: 0.9rem;
}

.detail-link {
    color: inherit;
    font-weight: bold;
    text-decoration: underline;
    text-decoration-thickness: 2px;
    text-underline-offset: 3px;
}

.muted-inline {
    opacity: 0.75;
}

/* Ensure palette tag buttons never look disabled */
#tagPalette .topbar-btn {
    opacity: 1 !important;
    filter: none !important;
}

/* =========================================
   Tag Palette — make selected tags POP in dark mode
   ========================================= */

/* Base (light mode) selected state (keep or tweak) */
#tagPalette .topbar-btn.is-selected {
    outline: 2px solid rgba(75,46,0,0.65);
    box-shadow: inset 0 0 0 999px rgba(201,151,77,0.28);
    font-weight: 700;
}

/* Dark mode: higher contrast + glow */
html.dark-mode #tagPalette .topbar-btn.is-selected {
    outline: 3px solid rgba(201,151,77,0.95);
    box-shadow: inset 0 0 0 999px rgba(201,151,77,0.38), 0 0 0 1px rgba(0,0,0,0.55), 0 0 12px rgba(201,151,77,0.55);
    transform: translateY(-1px); /* subtle “raised” look */
    font-weight: 800;
}

    /* Optional: make hover still readable when selected */
    html.dark-mode #tagPalette .topbar-btn.is-selected:hover {
        box-shadow: inset 0 0 0 999px rgba(201,151,77,0.48), 0 0 0 1px rgba(0,0,0,0.55), 0 0 14px rgba(201,151,77,0.7);
    }

html.contrast-mode #tagPalette .topbar-btn.is-selected {
    outline: 2px solid var(--card-border);
    box-shadow: inset 0 0 0 999px var(--tan-2);
    font-weight: 800;
}

/* =========================================================
   DARK MODE — make selected tags pop (sidebar + palette)
   ========================================================= */

/* Sidebar filters: highlight checked rows strongly */
html.dark-mode .accordion-content label {
    border-radius: 8px;
    padding: 6px 8px;
    /* color 0.2s ease matters here specifically: this rule only exists
       under html.dark-mode, so it only becomes active at the exact moment
       of switching INTO dark mode — without redeclaring color here too, it
       fully overrides (not merges with) the universal transition rule for
       every tag-checkbox label, and their text color snaps instantly right
       as the rest of the page fades. */
    transition: background-color 0.12s ease, box-shadow 0.12s ease, transform 0.06s ease, color 0.2s ease;
}

    /* ✅ The big win: highlight the label when its checkbox is checked */
    html.dark-mode .accordion-content label:has(input[type="checkbox"]:checked) {
        background: rgba(201,151,77,0.22);
        box-shadow: inset 0 0 0 2px rgba(201,151,77,0.55), 0 0 0 1px rgba(0,0,0,0.25);
    }

/* Optional: make the checkbox itself brighter */
html.dark-mode .accordion-content input[type="checkbox"] {
    accent-color: var(--tan);
}

/* Optional: slight hover so it’s readable even before checking */
html.dark-mode .accordion-content label:hover {
    background: rgba(255,255,255,0.06);
}

.mood-panel {
    position: fixed;
    right: 16px;
    bottom: 16px;
    width: min(560px, calc(100vw - 32px));
    max-height: min(70vh, 720px);
    overflow: auto;
    background: rgba(255,250,240,0.98);
    border: 1px solid rgba(75,46,0,0.22);
    border-radius: 14px;
    box-shadow: 0 12px 28px rgba(0,0,0,0.22);
    z-index: 2000;
    padding: 14px;
}

html.dark-mode .mood-panel {
    background: rgba(20,20,20,0.97);
    border-color: rgba(255,255,255,0.14);
}

html.contrast-mode .mood-panel {
    background: var(--card);
    border-color: var(--card-border);
}

.mood-panel.hidden {
    display: none;
}

/* =========================================================
   DISCOVERY UI (index) — Find Like panel
   ========================================================= */

.findlike-panel {
    margin-top: -10px; /* tuck under top-bar */
    margin-bottom: 14px;
    background: var(--topbar);
    border-radius: 10px;
    padding: 10px;
    border: 1px solid rgba(0,0,0,0.10);
}

html.dark-mode .findlike-panel {
    border-color: rgba(255,255,255,0.10);
}

.findlike-row {
    display: flex;
    gap: 10px;
    align-items: end;
    flex-wrap: wrap;
}

.findlike-label {
    display: grid;
    gap: 6px;
    font-size: 0.9rem;
    color: var(--ink);
}

html.dark-mode .findlike-label {
    color: var(--text);
}

.findlike-results {
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid rgba(0,0,0,0.10);
}

html.dark-mode .findlike-results {
    border-top-color: rgba(255,255,255,0.10);
}

/* Discovery results grid */
.findlike-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 12px;
    margin-top: 10px;
}

.findlike-item {
    display: grid;
    grid-template-columns: 54px 1fr;
    gap: 10px;
    padding: 10px;
    border-radius: 12px;
    cursor: pointer;
    background: rgba(0,0,0,0.04);
    border: 1px solid rgba(0,0,0,0.08);
}

html.dark-mode .findlike-item {
    background: rgba(255,255,255,0.06);
    border-color: rgba(255,255,255,0.10);
}

.findlike-item:hover {
    transform: translateY(-1px);
}

#insightsPanel.collapsible {
    margin-bottom: 16px;
}

.findlike-cover {
    position: relative;
    overflow: hidden;
    border-radius: 8px;
    flex-shrink: 0;
}

.findlike-cover img {
    width: 54px;
    height: 80px;
    object-fit: cover;
    border-radius: 8px;
    display: block;
}

.findlike-title {
    font-weight: 700;
    line-height: 1.2;
}

.findlike-sub {
    margin-top: 4px;
    font-size: 0.86rem;
    opacity: 0.85;
}

.findlike-score {
    font-weight: 700;
    opacity: 0.9;
}

.qr-btn, .qp-btn {
    color: inherit;
}

/* =========================================================
   Meta filters + Community Averages panel
   ========================================================= */

.meta-avg-row {
    display: flex;
    gap: 10px 14px;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    padding-bottom: 10px;
    border-bottom: 1px solid rgba(0,0,0,0.10);
}

html.dark-mode .meta-avg-row {
    border-bottom: 1px solid rgba(255,255,255,0.12);
}

.meta-avg-grids {
    display: grid;
    grid-template-columns: 1fr;
    gap: 10px;
    margin-top: 10px;
}

@media (min-width: 920px) {
    .meta-avg-grids {
        grid-template-columns: 1fr 1fr;
    }
}

.meta-avg-box {
    border-radius: 12px;
    padding: 10px 12px;
    background: rgba(255,255,255,0.60);
    border: 1px solid rgba(0,0,0,0.08);
}

html.dark-mode .meta-avg-box {
    background: rgba(0,0,0,0.18);
    border: 1px solid rgba(255,255,255,0.10);
}

.meta-avg-title {
    font-weight: 700;
    margin-bottom: 6px;
}

.meta-avg-box ol {
    margin: 0;
    padding-left: 18px;
}

.meta-avg-box li {
    margin: 6px 0;
}

.meta-small {
    opacity: 0.8;
    font-size: 0.9em;
    margin-left: 6px;
}

.meta-avg-box a {
    text-decoration: none;
}

    .meta-avg-box a:hover {
        text-decoration: underline;
    }

/* =========================================================
   INDEX: Meta Panel (min rating / min readers / averages)
   ========================================================= */

#metaPanel.meta-panel {
    margin-top: 16px;
    padding-top: 14px;
    border-top: 1px solid rgba(0,0,0,0.12);
}

html.dark-mode #metaPanel.meta-panel {
    border-top-color: rgba(255,255,255,0.14);
}

#metaPanel .meta-controls {
    display: grid;
    grid-template-columns: 1fr;
    gap: 12px;
}

#metaPanel .meta-label {
    display: grid;
    gap: 6px;
    font-weight: 700;
    color: var(--ink);
}

html.dark-mode #metaPanel .meta-label {
    color: var(--text);
}

#metaPanel select,
#metaPanel input[type="number"] {
    width: 100%;
}

#metaPanel #minReaders {
    margin-top: 2px;
}

/* Apply filters — sticky to the bottom of .sidebar's own scrollport
   (overflow-y:auto above) so it stays reachable while the tag list
   scrolls past one viewport, without needing .sidebar to be flex. */
.apply-filters-btn {
    position: sticky;
    bottom: 0;
    width: 100%;
    margin-top: 16px;
    padding-top: 10px;
    padding-bottom: 10px;
    background-color: var(--sidebar);
    border-top: 1px solid rgba(0,0,0,0.12);
    font-weight: 700;
}

html.dark-mode .apply-filters-btn {
    border-top-color: rgba(255,255,255,0.14);
}

.admin-only {
    display: none !important;
}

    .admin-only.show {
        display: inline-flex !important;
    }

.auth-only {
    display: none !important;
}

    .auth-only.show {
        display: inline-flex !important;
    }

/* =========================================================
   Toast notifications
   ========================================================= */
.toast-host {
    position: fixed;
    right: 16px;
    bottom: 16px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    z-index: 99999;
    pointer-events: none;
}

.toast {
    pointer-events: none;
    background: rgba(30, 20, 5, 0.92);
    color: #fffaf0;
    border: 1px solid rgba(231, 208, 166, 0.35);
    border-radius: 12px;
    padding: 10px 12px;
    min-width: 220px;
    max-width: 360px;
    box-shadow: 0 10px 25px rgba(0,0,0,0.25);
    transform: translateY(6px);
    opacity: 0;
    transition: opacity 160ms ease, transform 160ms ease;
    font-size: 0.92rem;
    line-height: 1.25rem;
}

    .toast.show {
        opacity: 1;
        transform: translateY(0);
    }

    .toast.success {
        border-color: rgba(70, 200, 120, 0.45);
    }

    .toast.error {
        border-color: rgba(255, 90, 90, 0.55);
    }
/* ============================
   Graph Search Bar (Light)
   ============================ */

.graph-search-bar {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    margin: 6px 0;
    border-radius: 10px;
    background: rgba(0, 0, 0, 0.05);
    border: 1px solid rgba(0, 0, 0, 0.12);
}

    .graph-search-bar label {
        font-size: 13px;
        opacity: 0.85;
        white-space: nowrap;
    }

    .graph-search-bar input {
        flex: 1;
        padding: 8px 10px;
        border-radius: 10px;
        border: 1px solid rgba(0, 0, 0, 0.25);
        font-size: 14px;
        background: #fff;
        color: #222;
    }

        .graph-search-bar input::placeholder {
            color: rgba(0, 0, 0, 0.45);
        }

    .graph-search-bar button {
        padding: 8px 10px;
        border-radius: 10px;
        border: 1px solid rgba(0, 0, 0, 0.25);
        background: #f3f3f3;
        color: #222;
        cursor: pointer;
        font-family: inherit;
    }

        .graph-search-bar button:hover {
            background: #e6e6e6;
        }

/* Put the search bar above the SVG with nice spacing */
#graph-container .graph-search-bar {
    margin: 0 0 10px 0;
}

/* ============================
   Graph Search Bar (Dark Mode)
   ============================ */

body.dark-mode .graph-search-bar {
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.12);
}

    body.dark-mode .graph-search-bar label {
        color: rgba(255, 255, 255, 0.85);
    }

    body.dark-mode .graph-search-bar input {
        background: rgba(0, 0, 0, 0.45);
        color: #eee;
        border: 1px solid rgba(255, 255, 255, 0.25);
    }

        body.dark-mode .graph-search-bar input::placeholder {
            color: rgba(255, 255, 255, 0.45);
        }

    body.dark-mode .graph-search-bar button {
        background: rgba(255, 255, 255, 0.12);
        color: #eee;
        border: 1px solid rgba(255, 255, 255, 0.25);
    }

        body.dark-mode .graph-search-bar button:hover {
            background: rgba(255, 255, 255, 0.2);
        }

.graph-search-bar input:focus {
    outline: none;
    box-shadow: 0 0 0 2px rgba(201, 151, 77, 0.45);
}

html.contrast-mode .graph-search-bar input:focus {
    box-shadow: 0 0 0 2px var(--focus);
}

body.dark-mode .graph-search-bar input:focus {
    box-shadow: 0 0 0 2px rgba(201, 151, 77, 0.65);
}

.graph-search-bar button {
    transition: background 0.15s ease, border-color 0.15s ease;
}

    .graph-search-bar button:active {
        transform: translateY(1px);
    }


/* =========================================================
   SIMPLE PAGES — Profile / Books / Readers / Reading List /
   Books Read / Coming Soon
   ========================================================= */

.simple-page {
    max-width: 900px;
    margin: 0 auto;
    padding: 24px 16px 60px;
    width: 100%;
    box-sizing: border-box;
}

.simple-page h1 {
    color: var(--ink);
}

.simple-page-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}

/* ---- Profile ---- */

.profile-header-card {
    display: flex;
    align-items: center;
    gap: 20px;
    background: var(--card);
    border: 1px solid var(--card-border);
    border-radius: 14px;
    padding: 20px;
    box-shadow: var(--card-shadow);
}

.profile-page-avatar {
    width: 96px;
    height: 96px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
    background: var(--tan-2);
}

.profile-header-text h1 {
    margin: 0 0 6px;
}

.follow-row {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-top: 8px;
}

.follow-count-link {
    color: var(--ink);
    text-decoration: none;
    font-size: 0.9rem;
}

.follow-count-link:hover {
    text-decoration: underline;
}

.follow-count-link span {
    font-weight: 700;
}

#followBtn.is-following {
    border-color: rgba(75, 46, 0, 0.6);
    background: rgba(201, 151, 77, 0.22);
}

html.dark-mode #followBtn.is-following {
    background: rgba(201, 151, 77, 0.18);
}

html.contrast-mode #followBtn.is-following {
    border-color: var(--card-border);
    background: var(--tan-2);
}

.profile-stats {
    display: flex;
    gap: 14px;
    margin-top: 16px;
}

.profile-stat {
    flex: 1;
    text-align: center;
    background: var(--card);
    border: 1px solid var(--card-border);
    border-radius: 12px;
    padding: 14px;
    text-decoration: none;
    color: var(--ink);
    transition: background-color 0.15s ease, border-color 0.2s ease, color 0.2s ease;
}

.profile-stat:hover {
    background: var(--tan-2);
}

.profile-stat-num {
    display: block;
    font-size: 1.6rem;
    font-weight: 700;
}

.profile-stat-label {
    display: block;
    font-size: 0.85rem;
    opacity: 0.8;
    margin-top: 2px;
}

.profile-edit {
    margin-top: 20px;
    background: var(--card);
    border: 1px solid var(--card-border);
    border-radius: 14px;
    padding: 18px;
}

.profile-edit h2 {
    margin-top: 0;
    color: var(--ink);
    font-size: 1.1rem;
}

.profile-field {
    display: block;
    margin-bottom: 12px;
    font-weight: 600;
    font-size: 0.9rem;
    color: var(--ink-2);
}

.profile-field input,
.profile-field textarea {
    display: block;
    width: 100%;
    margin-top: 6px;
    padding: 8px 10px;
    border-radius: 8px;
    border: 1px solid var(--card-border);
    background: var(--bg);
    color: var(--text);
    font-family: inherit;
    font-size: 0.95rem;
    box-sizing: border-box;
}

/* Only layout properties here — .styled-select (applied alongside this on
   the privacy dropdowns) already owns the visual styling (border, padding,
   custom chevron background-image); overriding `background` as a shorthand
   here would silently wipe that chevron out. */
.profile-field select {
    display: block;
    width: 100%;
    margin-top: 6px;
    box-sizing: border-box;
}

.profile-edit-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 6px;
}

/* ---- Profile: Favorites / Activity / Lists sections ---- */

.profile-section {
    margin-top: 20px;
}

.profile-section h2 {
    margin: 0 0 10px;
    font-size: 1.1rem;
    color: var(--ink);
}

.profile-section-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
}

.profile-section-header h2 {
    margin: 0;
}

.profile-section-link {
    font-size: 0.85rem;
    color: var(--tan-text);
    text-decoration: none;
}

.profile-section-link:hover {
    text-decoration: underline;
}

.favorites-row {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 14px;
}

.favorites-row .favorite-card {
    max-width: 150px;
}

.activity-preview {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.profile-edit-favorites {
    margin-top: 20px;
}

.favorites-editor-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 12px 0;
}

.favorites-editor-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 10px;
    background: var(--bg);
    border: 1px solid var(--card-border);
    border-radius: 8px;
}

.favorites-editor-item img {
    width: 32px;
    height: 48px;
    object-fit: cover;
    border-radius: 4px;
    flex-shrink: 0;
    background: var(--tan-2);
}

.favorites-editor-title {
    flex: 1;
    min-width: 0;
    font-size: 0.9rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.favorites-editor-actions {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-shrink: 0;
}

.favorites-editor-move,
.favorites-editor-remove {
    appearance: none;
    border: 1px solid var(--card-border);
    background: var(--card);
    color: var(--ink);
    border-radius: 6px;
    width: 26px;
    height: 26px;
    line-height: 1;
    cursor: pointer;
}

.favorites-editor-move:hover:not(:disabled),
.favorites-editor-remove:hover {
    background: var(--tan-2);
}

.favorites-editor-move:disabled {
    opacity: 0.35;
    cursor: default;
}

/* ---- Simple book grids (Books / Reading List / Books Read) ---- */

.simple-book-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 18px;
    margin-top: 20px;
}

.simple-book-card {
    display: block;
    text-decoration: none;
    color: var(--ink);
}

.simple-book-card img {
    width: 100%;
    aspect-ratio: 2 / 3;
    object-fit: contain;
    border-radius: 10px;
    box-shadow: var(--card-shadow);
    background: var(--card);
    display: block;
}

/* Wraps the <img> above (added by cover-overlay.js's callers) so the hover
   overlay only covers the cover art, not the title text below it — also
   gives this card type the hover highlight .book/.detail-bookrow already
   had (this one had none before). */
.simple-book-cover {
    position: relative;
    overflow: hidden;
    border-radius: 10px;
    transition: box-shadow 0.15s ease;
}

.simple-book-card:hover .simple-book-cover {
    box-shadow: 0 4px 14px rgba(0,0,0,0.28);
}

.simple-book-card .sbc-title {
    display: block;
    margin-top: 8px;
    font-weight: 700;
    font-size: 0.92rem;
    line-height: 1.25;
}

.simple-book-card .sbc-sub {
    display: block;
    font-size: 0.82rem;
    opacity: 0.75;
    margin-top: 2px;
}

/* ---- Books page: curated rows (books.html) ---- */

.book-row-section {
    margin-top: 36px;
}

.book-row-section:first-of-type {
    margin-top: 20px;
}

.book-row-section h2 {
    font-family: 'Cinzel', Georgia, serif;
    font-size: 1.3rem;
    color: var(--ink);
    margin: 0 0 4px;
}

/* ---- Readers search ---- */

.reader-search-row {
    display: flex;
    gap: 10px;
    margin-top: 16px;
}

#tabFollowers.is-active,
#tabFollowing.is-active,
#tabEveryone.is-active {
    border-color: rgba(75, 46, 0, 0.6);
    background: rgba(201, 151, 77, 0.22);
}

html.dark-mode #tabFollowers.is-active,
html.dark-mode #tabFollowing.is-active,
html.dark-mode #tabEveryone.is-active {
    background: rgba(201, 151, 77, 0.18);
}

html.contrast-mode #tabFollowers.is-active,
html.contrast-mode #tabFollowing.is-active,
html.contrast-mode #tabEveryone.is-active {
    border-color: var(--card-border);
    background: var(--tan-2);
}

.reader-search-row input {
    flex: 1;
    padding: 10px 12px;
    border-radius: 8px;
    border: 1px solid var(--card-border);
    background: var(--card);
    color: var(--text);
    font-size: 1rem;
    box-sizing: border-box;
}

.reader-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 18px;
}

.reader-card {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px 14px;
    background: var(--card);
    border: 1px solid var(--card-border);
    border-radius: 12px;
    text-decoration: none;
    color: var(--ink);
    transition: background-color 0.15s ease, border-color 0.2s ease, color 0.2s ease;
}

.reader-card:hover {
    background: var(--tan-2);
}

.reader-card img {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
    background: var(--tan-2);
}

.reader-card-name {
    font-weight: 700;
}

.reader-card-bio {
    font-size: 0.85rem;
    opacity: 0.75;
    margin-top: 2px;
}

/* ---- Authors page ---- */

.authors-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 18px;
}

.author-card-books {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 18px;
}

/* ---- Activity feed ---- */

.activity-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 20px;
}

.activity-item {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 10px 14px;
    background: var(--card);
    border: 1px solid var(--card-border);
    border-radius: 12px;
}

.activity-avatar {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
    background: var(--tan-2);
}

.activity-item-body {
    flex: 1;
    min-width: 0;
}

.activity-item-text {
    font-size: 0.92rem;
}

.activity-item-text a {
    color: var(--ink);
    font-weight: 700;
    text-decoration: none;
}

.activity-item-text a:hover {
    text-decoration: underline;
}

.activity-item-time {
    margin-top: 2px;
    font-size: 0.8rem;
    opacity: 0.65;
}

/* ---- Coming soon ---- */

.coming-soon-card {
    text-align: center;
    padding: 60px 24px;
    background: var(--card);
    border: 1px solid var(--card-border);
    border-radius: 14px;
    margin-top: 30px;
}

.coming-soon-card h1 {
    margin-bottom: 10px;
}

/* ---- Diary ---- */

.diary-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-top: 20px;
}

.diary-entry {
    display: flex;
    gap: 14px;
    align-items: flex-start;
    padding: 12px 14px;
    background: var(--card);
    border: 1px solid var(--card-border);
    border-radius: 12px;
}

.diary-entry img {
    width: 56px;
    height: 84px;
    object-fit: cover;
    border-radius: 6px;
    box-shadow: var(--card-shadow);
    background: var(--tan-2);
    flex-shrink: 0;
    display: block;
}

.diary-entry-body {
    flex: 1;
    min-width: 0;
}

.diary-entry-title {
    display: block;
    font-weight: 700;
    color: var(--ink);
    text-decoration: none;
}

.diary-entry-title:hover {
    text-decoration: underline;
}

.diary-entry-meta {
    display: flex;
    gap: 10px;
    margin-top: 2px;
    font-size: 0.85rem;
    opacity: 0.75;
}

.diary-entry-note {
    margin-top: 6px;
    font-size: 0.9rem;
    opacity: 0.9;
}

.diary-entry-delete {
    appearance: none;
    border: none;
    background: transparent;
    color: var(--ink);
    opacity: 0.5;
    cursor: pointer;
    font-size: 1rem;
    padding: 2px 6px;
    flex-shrink: 0;
}

.diary-entry-delete:hover {
    opacity: 1;
}

/* ---- List item actions (list.html — reuses .diary-entry row layout) ---- */

.list-item-actions {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-shrink: 0;
}

.list-item-move {
    appearance: none;
    border: none;
    background: transparent;
    color: var(--ink);
    opacity: 0.6;
    cursor: pointer;
    font-size: 1rem;
    padding: 2px 6px;
}

.list-item-move:hover:not(:disabled) {
    opacity: 1;
}

.list-item-move:disabled {
    opacity: 0.2;
    cursor: default;
}

/* ---- Reviews list (reviews.html) ---- */

.review-card-list {
    display: flex;
    flex-direction: column;
    gap: 14px;
    margin-top: 20px;
}

.review-card {
    display: flex;
    gap: 14px;
    align-items: flex-start;
    padding: 14px;
    background: var(--card);
    border: 1px solid var(--card-border);
    border-radius: 12px;
}

.review-card img {
    width: 56px;
    height: 84px;
    object-fit: cover;
    border-radius: 6px;
    box-shadow: var(--card-shadow);
    background: var(--tan-2);
    flex-shrink: 0;
    display: block;
}

.review-card-body {
    flex: 1;
    min-width: 0;
}

.review-card-title {
    display: block;
    font-weight: 700;
    color: var(--ink);
    text-decoration: none;
}

.review-card-title:hover {
    text-decoration: underline;
}

.review-card-meta {
    display: flex;
    gap: 10px;
    margin-top: 2px;
    font-size: 0.85rem;
    opacity: 0.75;
}

.review-card-text {
    margin-top: 6px;
    font-size: 0.9rem;
    white-space: pre-wrap;
}

.review-card-delete {
    appearance: none;
    border: none;
    background: transparent;
    color: var(--ink);
    opacity: 0.5;
    cursor: pointer;
    font-size: 1rem;
    padding: 2px 6px;
    flex-shrink: 0;
}

.review-card-delete:hover {
    opacity: 1;
}

/* ---- Custom Lists overview (lists.html) ---- */

.list-card-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-top: 20px;
}

.list-card {
    position: relative;
    padding: 14px 40px 14px 14px;
    background: var(--card);
    border: 1px solid var(--card-border);
    border-radius: 12px;
}

.list-card-title {
    display: block;
    font-weight: 700;
    font-size: 1.05rem;
    color: var(--ink);
    text-decoration: none;
}

.list-card-title:hover {
    text-decoration: underline;
}

.list-card-desc {
    margin-top: 4px;
    font-size: 0.9rem;
    opacity: 0.85;
}

.list-card-meta {
    margin-top: 6px;
    font-size: 0.85rem;
    opacity: 0.7;
}

.list-card-delete {
    position: absolute;
    top: 12px;
    right: 12px;
    appearance: none;
    border: none;
    background: transparent;
    color: var(--ink);
    opacity: 0.5;
    cursor: pointer;
    font-size: 1rem;
    padding: 2px 6px;
}

.list-card-delete:hover {
    opacity: 1;
}
