/* =============================================================================
   style.css — the PUBLIC app stylesheet.
   =============================================================================
   @font-face, the design tokens (:root, [data-theme="light"], [data-theme="dark"]
   and the legacy aliases) and the element reset moved to base.css, which both
   index.html and admin.html load immediately before their own sheet. Nothing was
   reordered; this file simply starts further down than it used to.

   The component layer BOTH pages render moved to components.css. admin.html no
   longer loads this file at all, so rules added here are PUBLIC-ONLY in fact and
   not merely by intent: they cannot reach the admin panel, and an edit here
   cannot change it. A rule the admin panel also needs belongs in components.css,
   which is a shared contract -- see CLAUDE.md §3b.
   =============================================================================

   PLATFORM OVERRIDES -- how to style the desktop/mobile shells
   =============================================================================
   `<html>` carries `data-platform`, set before the first painted frame by the
   boot script in index.html and re-applied authoritatively by
   applyPlatformAttribute() in src/shared/platform.js. Its value is one of:

       web | tauri | capacitor

   Write shell-specific rules as OVERRIDES on top of the existing ones, keyed on
   that attribute, and put them next to the rule they override:

       .site-header { padding: 12px 16px; }
       [data-platform="tauri"] .site-header { padding: 6px 12px; }

   Three rules, and they are what keep this from becoming a fork:

   1. NEVER duplicate a component to change it. One component tree, an override
      layer on top -- see the roadmap's 3.7 note. A forked homepage or reader is
      two things to fix every time, and the second one is always the one that
      gets forgotten.
   2. The base rule stays platform-neutral and keeps working with NO attribute
      at all. `data-platform` can legitimately be absent -- a locked-down WebView
      that throws on DOM access leaves it unset, and the boot script swallows
      that. An unstyled page is the failure mode to design against.
   3. Shell overrides belong HERE, in style.css. A `[data-platform="tauri"]`
      block in components.css or base.css also reaches admin.html and makes the
      commit a shared-contract change (CI blocks it without a Contract-Impact
      trailer). The admin panel is web-only and has no shell to style for.

   The bytes ship to web too, unused. That is the intended trade: one stylesheet
   whose cascade you can reason about beats a build-time split you cannot.
   ============================================================================= */


/* ─── UI text uses Inter ─── */
.form-textarea,
.sidebar-tab-item,
.tab,
.toast,
.b2-stat-label,
.stat-label,
.theme-toggle-label,
.empty-state p,
.skeleton,
.tab-text,
.book-card-meta-badges,
.queue-details-column,
.volume-picker-modal,
.panel-header-desc,
.b2-controls-row {
    font-family: var(--font-ui);
}

/* ─── Reduced motion ─── */
@media (prefers-reduced-motion: reduce) {

    /* The book-cover zoom is a transform, not a duration: killing the
       transition alone would still snap the cover to scale(1.03) on hover,
       which is exactly the motion a vestibular-disorder user opted out of.
       !important because `.book-card:hover .book-card-cover` is declared
       later in this file (BOOK CARDS section) at equal specificity, and
       @media adds none — without it the later rule wins. */
    .book-card:hover .book-card-cover {
        transform: none !important;
    }
}

/* ======================== SCROLLBAR STYLING ======================== */


/* ======================== THEME TOGGLE PILL ======================== */
.theme-toggle-pill {
    display: flex;
    align-items: center;
    gap: 2px;
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    padding: 3px;
    box-shadow: var(--shadow-md);
    transition: background 300ms ease, border-color 300ms ease;
}

.theme-toggle-btn {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: none;
    background: transparent;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
    transition: background var(--transition-fast), color var(--transition-fast), transform var(--transition-fast);
    position: relative;
}

.theme-toggle-btn:hover {
    color: var(--text-primary);
}

.theme-toggle-btn.active {
    background: var(--accent-light);
    color: var(--accent);
}

.theme-toggle-btn svg {
    width: 18px;
    height: 18px;
}

/* Navbar theme pill: same component as the reader-settings theme switcher,
   just sized down slightly to sit comfortably in the navbar row next to
   the lang-switcher pill. */
.navbar-theme-pill {
    padding: 2px;
}
.navbar-theme-pill .theme-toggle-btn {
    width: 30px;
    height: 30px;
}
.navbar-theme-pill .theme-toggle-btn svg {
    width: 16px;
    height: 16px;
}

/* ── Navbar settings gear & user auth icon buttons ── */
.navbar-settings-toggle,
.navbar-auth-toggle {
    width: 36px;
    height: 36px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    color: var(--text-secondary);
    box-shadow: var(--shadow-sm);
    transition: color var(--transition-fast), border-color var(--transition-fast), background var(--transition-fast);
    cursor: pointer;
}
.navbar-settings-toggle:hover,
.navbar-auth-toggle:hover {
    color: var(--text-primary);
    border-color: var(--border-strong);
}
.navbar-settings-toggle svg,
.navbar-auth-toggle svg {
    width: 18px;
    height: 18px;
}
.navbar-settings-dropdown.open .navbar-settings-toggle,
.nav-link-dropdown.open .navbar-auth-toggle {
    color: var(--accent);
    border-color: var(--accent);
}

/* The gear wrapper is tiny and sits close to the navbar edge, so anchoring the
   panel to it pushed the panel off-screen (worse in RTL). Pin it to the
   viewport's trailing-top corner instead — it can't clip whatever the navbar
   layout or text direction. (≤768px still overrides this to a bottom sheet.) */
.navbar-settings-panel {
    width: 248px;
    max-width: calc(100vw - 24px);
    gap: 14px;
    padding: 14px;
}
.navbar-settings-dropdown.open .navbar-settings-panel {
    position: fixed;
    top: 60px;
    inset-inline-start: auto;
    inset-inline-end: 14px;
    margin: 0;
}
.navbar-settings-group {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
/* A hairline between groups so the four sections read as distinct. */
.navbar-settings-group + .navbar-settings-group {
    padding-top: 12px;
    border-top: 1px solid var(--border);
}
.navbar-settings-label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--text-muted);
}

/* All four controls are compact full-width segmented rows, ~30px tall, so the
   panel reads as a tidy list rather than a stack of chunky buttons. */
.navbar-settings-panel .navbar-theme-pill {
    align-self: stretch;
    padding: 3px;
}
.navbar-settings-panel .navbar-theme-pill .theme-toggle-btn {
    flex: 1;
    width: auto;
    height: 26px;
    border-radius: var(--radius-full);
}
.navbar-settings-panel .lang-switcher {
    align-self: stretch;
    width: 100%;
}
.navbar-settings-panel .lang-switcher .lang-btn {
    flex: 1;
    padding: 6px 4px;
    font-size: 12px;
}
.navbar-settings-panel .display-mode-toggle {
    display: flex;
    width: 100%;
}
.navbar-settings-panel .display-mode-btn {
    flex: 1;
    justify-content: center;
    padding: 6px 8px;
    font-size: 12px;
}
.navbar-settings-panel .display-mode-btn i {
    width: 14px;
    height: 14px;
}
.navbar-settings-panel .view-options-size-buttons {
    gap: 5px;
}
.navbar-settings-panel .view-options-size-btn {
    flex: 1 1 0;
    min-width: 0;
    padding: 6px 4px;
    font-size: 11px;
}

/* Toast styling moved to components.css and was retargeted at the class names
   shared/components/toast.js actually emits (.toast-message, not .toast). The
   rules that were here matched markup nothing has rendered since the component
   was rewritten, and the admin panel could not see them at all after the
   components.css split. @keyframes toastIn went with them. */

/* ======================== STATUS BAR ======================== */

.status-badge.disconnected {
    background: #fef2f2;
    color: #dc2626;
}

/* All Books' search bar specifically — 0 left/right padding AND no
   max-width cap, instead of the shared .container's 24px padding +
   1300px ceiling. Padding alone still left 33px of auto-centering margin
   on each side once the row was narrower than the viewport; removing
   max-width too lets it use the full width, with the freed space going
   to the search input (the only 1fr column). Scoped to just this
   .container (not the base rule, reused all over the site) — the panel
   below (book list) gets its own alignment from a different ancestor and
   keeps its normal width, so this intentionally makes the search bar
   sit wider than the content under it. */
.search-filter-panel .container {
    max-width: none;
    padding-left: 0;
    padding-right: 0;
}

/* Auto-hides after scrolling a fair way down a book list (toggleNavbar() in
   app.js), reappearing on scroll-up or any click — max-height (not
   display/transform) so it genuinely collapses out of flow instead of just
   visually hiding, letting the search bar and list below slide up into the
   reclaimed space. --chrome-offset drops (see the 4 explicit values above
   and below) at the same time so the list grows to fill that space instead
   of a gap opening up below it.
   overflow: hidden only applies here (while collapsing/collapsed), not on
   the base .navbar rule — the "Public Library" hover dropdown (below) is a
   child of .navbar that intentionally renders past its bottom edge, and
   overflow: hidden on the resting navbar clipped that panel almost
   entirely invisible. */
body.navbar-hidden .navbar {
    max-height: 0;
    padding-top: 0;
    padding-bottom: 0;
    overflow: hidden;
    opacity: 0;
    border-bottom-color: transparent;
}
body.navbar-hidden {
    --chrome-offset: 112px;   /* navbar hidden, tab strip + search bar visible (All Books, scrolled) */
    --offline-chrome-offset: var(--offline-chrome-offset-collapsed);
}

/* Set by switchLibraryTab() (app.js) whenever the active library tab is NOT
   All Books — those tabs (Author/Category/Recent) hide the shared
   .search-filter-panel and show their own local .search-grid header
   instead, so the shared bar contributes 0px of chrome here, not the usual
   80px baked into the default --chrome-offset above. */
body.lib-searchbar-hidden {
    --chrome-offset: 142px;   /* navbar + tab strip visible, search bar hidden (Author/Category/Recent, resting) */
}
body.navbar-hidden.lib-searchbar-hidden {
    --chrome-offset: 32px;   /* navbar + search bar hidden, tab strip still visible */
}

/* ─── Android: the chrome pass ─────────────────────────────────────────────
   Measured on a Galaxy A20 (411x813 CSS px) before this block existed: 312px
   of chrome above the first book and 57px of bottom nav below it. 45% of the
   screen, for four books. The desktop navbar was reflowing onto a phone --
   .navbar-container wraps at mobile widths and .nav-links takes width:100%,
   so the brand, the nav links and the settings gear each claimed a row of
   their own, 135px in total.

   Nothing here is hidden that cannot be reached: "Public Library" and
   "Offline Library" are both in the bottom tab bar, and "Articles" is in the
   More sheet. The links were a second copy, not the only copy -- which is
   why this is a strip and not a redesign.

   The base rules stay platform-neutral and keep working with no attribute at
   all, per rule 2 of the PLATFORM OVERRIDES note at the top of this file. */

/* One row: brand left, settings right. The wrap is what cost two rows. */
[data-platform="capacitor"] .navbar-container {
    flex-wrap: nowrap;
    align-items: center;
}

[data-platform="capacitor"] #navbar-links {
    display: none;
}

/* The tab bar sticks at top:56px, an offset sized for the navbar. With the
   title bar gone that floor sits 56px BELOW where the bar naturally falls, so
   sticky held it pinned there — on top of the first 56px of the book list.
   Measured: search band 56-140, first row starting at 84. top:0 is what
   "sticky" means once there is nothing above it. */
[data-platform="capacitor"] .library-tabs-bar {
    top: 0;
}

/* The gear moves into the More sheet (build-shell.js §8). Only the TOGGLE is
   hidden: #settings-dropdown is the container for the settings panel as well as
   for the button, so hiding the wrapper would hide the panel the new More row
   opens -- a settings entry that does nothing, with no error to find. */
[data-platform="capacitor"] .navbar-settings-toggle {
    display: none;
}

/* ─── Android: both book actions inline, no "…" ────────────────────────────
   At <=768px the row hides its inline save-offline button and clones it into a
   "…" sheet, alongside download and read-later. That split earns its keep on
   the website, where the sheet holds three things.

   It does not here. Read-later is not rendered at all (no accounts in this
   build) and download is already hidden by isNativeApp(), so the sheet was
   opening to show a single item: one tap to reveal one button. Read and Save
   are the only two actions a reader has, and they both fit.

   The inline buttons are re-shown and the trigger removed rather than hidden
   with visibility, so it leaves the tab order and the accessibility tree too --
   a control that opens an empty sheet should not be reachable at all. */
[data-platform="capacitor"] .book-row-actions > .book-opfs-btn,
[data-platform="capacitor"] .books-grid .book-card-actions > .book-opfs-btn {
    display: inline-flex;
}

[data-platform="capacitor"] .book-more-wrap {
    display: none;
}

/* ─── Android: the settings panel must escape the navbar ───────────────────
   THE BUG THIS FIXES IS NOT COSMETIC, and it is not Android-only.

   .navbar carries `backdrop-filter: blur(12px)`. Per spec, a backdrop-filter
   makes an element a containing block for `position: fixed` DESCENDANTS -- the
   same rule `transform` follows, and easy to miss because nothing about the
   markup suggests it. The settings panel lives inside .navbar, so it was never
   fixed to the viewport at all: it was fixed to the navbar, which is sticky and
   had scrolled to -791px. Measured on the device with the list scrolled: the
   panel computed `bottom: 0` and `top: -357px` against a 49px containing block,
   putting its rect at -1148 -- open, painted, full width, and entirely above
   the top of the screen.

   The same thing happens on the website; it is simply harder to reach there.
   Filed as a finding rather than fixed globally, because removing the blur from
   the live site is a visual change to a surface nobody asked me to touch.

   Here the blur has nothing left to blur: §9 takes the wordmark out, so the bar
   is zero-height and invisible. Dropping it restores the viewport as the
   containing block, and the existing `bottom: 0 !important` sheet rule --
   already correct -- starts working. */
[data-platform="capacitor"] .navbar {
    /* Lifted to the sheet rung so the settings panel it hosts can paint above
       the bottom bar -- see the note above this block. */
    z-index: var(--z-sheet-top);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    padding-top: 0;
    padding-bottom: 0;
    min-height: 0;
    border-bottom: 0;
}

/* The status-bar / notch inset lived on .navbar's padding-top. With the bar
   collapsed it has to live somewhere that still has height, or on a notched
   phone the first row of content sits under the system clock. body is the one
   element every screen shares. */
[data-platform="capacitor"] body {
    padding-top: env(safe-area-inset-top, 0px);
}

/* ─── Android: More opens as a side drawer ─────────────────────────────────
   Written with logical properties rather than left/right, so "which side"
   follows the document's `dir` and therefore the UI language with no JS and no
   second rule: inline-start is the right edge in Arabic and Urdu, the left edge
   in English. That is the conventional direction for a navigation drawer in
   each, and it is the side the reader's eye already starts from. */
[data-platform="capacitor"] .more-sheet-panel {
    /* Block axis physical, inline axis logical. `bottom: auto` used to sit
       below `inset-block: 0` here and quietly cancelled its bottom half, so the
       drawer was top-anchored and only as tall as its three rows (252px of an
       813px screen) -- it read as a dropdown, not a drawer. Physical and
       logical properties resolve to the SAME computed property, so the last
       declaration wins and order is the whole story. */
    top: 0;
    bottom: 0;
    inset-inline-end: 0;
    inset-inline-start: auto;
    width: min(82vw, 320px);
    max-height: none;
    border-top: 0;
    border-inline-start: 1px solid var(--border);
    border-radius: 0;
    box-shadow: 0 0 28px rgba(0, 0, 0, 0.22);
    padding-top: calc(var(--space-3) + env(safe-area-inset-top, 0px));
    padding-bottom: calc(var(--space-4) + env(safe-area-inset-bottom, 0px));
    animation: moreDrawerIn 0.24s ease;
}

/* The settings panel has to be able to paint above the bottom bar.

   z-index: 2200 on the panel never meant anything, because z-index only ranks
   siblings within one stacking context and the panel is not a sibling of the
   bar: it lives inside .navbar, which is position:sticky with
   z-index: var(--z-navbar) = 100 and therefore opens a context of its own. The
   bottom bar sits at 200 on the root. So the whole navbar subtree -- panel
   included -- painted underneath it, and the last setting was cut off by a bar
   drawn on top of it. The CSS comment beside .user-dropdown-backdrop already
   describes this trap for the scrim; the panel has it too.

   The bar this raises is zero-height, transparent and has no interactive
   children left, so lifting it to the sheet rung costs nothing and blocks
   nothing. */
/* ─── Android: the settings sheet, sized for a thumb ───────────────────────
   The panel was drawn for a 248px desktop dropdown: small type, generous
   vertical gaps between groups, and controls sized for a cursor. As a
   full-width sheet that reads as a scaled-up desktop menu.

   Each group becomes one row -- label on the inline-start, control on the
   inline-end -- which halves the height, puts the controls under the thumb
   rather than across the whole width, and lets the whole sheet fit without
   scrolling. */
[data-platform="capacitor"] .navbar-settings-panel {
    gap: 0;
    padding: 0 0 calc(env(safe-area-inset-bottom, 0px) + 8px);
}

[data-platform="capacitor"] .navbar-settings-group {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    min-height: 56px;
    padding: 8px 16px;
    border-bottom: 1px solid var(--border-light, var(--border));
}

[data-platform="capacitor"] .navbar-settings-group:last-child {
    border-bottom: 0;
}

[data-platform="capacitor"] .navbar-settings-label {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-primary);
    white-space: nowrap;
}

/* Controls stop stretching to the full row and sit at the inline-end, so the
   whole group reads as one setting rather than a heading over a wide bar. */
[data-platform="capacitor"] .navbar-settings-panel .navbar-theme-pill,
[data-platform="capacitor"] .navbar-settings-panel .lang-switcher,
[data-platform="capacitor"] .navbar-settings-panel .display-mode-toggle,
[data-platform="capacitor"] .navbar-settings-panel .view-options-size-buttons {
    flex: 0 0 auto;
    width: auto;
}

/* 44px is the minimum comfortable touch target; the desktop sizes were ~28px. */
[data-platform="capacitor"] .navbar-settings-panel .theme-toggle-btn,
[data-platform="capacitor"] .navbar-settings-panel .lang-btn,
[data-platform="capacitor"] .navbar-settings-panel .display-mode-btn,
[data-platform="capacitor"] .navbar-settings-panel .view-options-size-btn {
    min-height: 40px;
    min-width: 44px;
    padding-inline: 12px;
    font-size: 13px;
}

/* The sheet already has a title row of its own; the desktop panel's separate
   heading block just repeats it. */
[data-platform="capacitor"] .user-dropdown-mobile-header {
    padding: 14px 16px;
    border-bottom: 1px solid var(--border);
}

/* The grab handle is a bottom-sheet affordance. A drawer does not drag down. */
[data-platform="capacitor"] .more-sheet-panel::before {
    content: none;
}

@keyframes moreDrawerIn {
    from { transform: translateX(calc(var(--more-drawer-from, 1) * 100%)); }
    to   { transform: translateX(0); }
}

/* translateX is physical, so the slide direction has to be told which way the
   document runs; the keyframe reads this. */
[data-platform="capacitor"][dir="rtl"] .more-sheet-panel {
    --more-drawer-from: -1;
}

/* Search field and its three controls share a row instead of stacking. The
   field already carries min-width:0, so it shrinks rather than overflowing. */
[data-platform="capacitor"] .search-grid-primary {
    flex-wrap: nowrap;
}

/* --chrome-offset is deliberately NOT adjusted here, and that is worth writing
   down because it looks like an omission.

   It sizes the book list with calc(100dvh - offset), so reclaiming 129px of
   chrome above the list ought to require shrinking it by the same amount.
   It does not, because on a phone the rule never applies: the
   @media (max-width: 480px) block further down sets
   `.lib-tab-view.active, #lib-view-all-books.active { height: auto; overflow:
   visible }`, so below 480px the DOCUMENT scrolls and the container simply
   grows with its content. Confirmed on the device -- the list container
   measured 561450px tall against an 813px viewport, with overflow visible.

   Four capacitor overrides for those four states were written first and then
   deleted: they were dead rules carrying confident comments, which is worse
   than no rules at all. */

/* ─── Android: the thumb bar ───────────────────────────────────────────────
   The bar is a tab switcher on Android, written by build-shell.js §4c, and its
   buttons carry an icon and no label. Five Arabic words across 411px is what
   made the old bar cramped; the one button whose meaning is not obvious from a
   glyph (the library switch) answers by CHANGING its glyph instead — a globe
   in the public library, a struck-through wifi in the offline one.

   .mbn-label does not exist in this build's markup, so nothing is hidden here;
   these rules only give the icon the room the label used to occupy. */
/* NOTE: no `position` override here, deliberately. The bar is `position: fixed`
   inside @media (max-width: 768px), and an attribute-plus-class selector at top
   level outranks it -- setting `position: relative` here took the bar out of
   fixed positioning and dropped it to the foot of a 561450px-tall document,
   i.e. entirely off-screen, while every DOM assertion still passed. A fixed
   element is already a containing block for absolutely-positioned children, so
   the drop-up anchors to it with nothing added. */

[data-platform="capacitor"] .mbn-item {
    justify-content: center;
    padding-top: 0;
    padding-bottom: 0;
}

[data-platform="capacitor"] .mbn-icon {
    width: 24px;
    height: 24px;
}

/* The drop-up. Anchored to the bar rather than the button so it cannot be
   pushed off-screen by the button's own position in an RTL row, and hidden by
   default with visibility as well as opacity so it is out of the tab order
   when closed rather than merely invisible. */
[data-platform="capacitor"] .mbn-library-menu {
    position: absolute;
    bottom: calc(100% + 8px);
    inset-inline-end: 8px;
    z-index: 60;
    display: flex;
    flex-direction: column;
    min-width: 200px;
    padding: 6px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--bg-elevated);
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.16);
    opacity: 0;
    visibility: hidden;
    transform: translateY(6px);
    transition: opacity 0.16s ease, transform 0.16s ease, visibility 0.16s;
}

[data-platform="capacitor"] .mbn-library-menu.open {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

[data-platform="capacitor"] .mbn-library-option {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 11px 12px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--text-primary);
    font-family: inherit;
    font-size: 14px;
    text-align: start;
    cursor: pointer;
}

[data-platform="capacitor"] .mbn-library-option i,
[data-platform="capacitor"] .mbn-library-option svg {
    width: 18px;
    height: 18px;
    flex: 0 0 auto;
}

[data-platform="capacitor"] .mbn-library-option.active {
    background: var(--bg-hover);
    color: var(--accent);
    font-weight: 600;
}

/* ─── Nav Links ─── */

.nav-link.admin-pill {
    border: 1px solid var(--accent);
    color: var(--accent);
}

.nav-link.admin-pill:hover,
.nav-link.admin-pill.active {
    background: var(--accent);
    color: #fff;
}

/* ─── "Public Library" hover dropdown (All Books/Authors/Categories/Recent) ───
   The 4-tab strip used to be a permanent bar under the navbar; it's now
   tucked behind a hover so it only costs space while actually choosing a
   tab. top:100% (no gap) keeps the link→panel hover path continuous instead
   of flickering closed while the mouse crosses a margin. */
.nav-link-dropdown {
    position: relative;
    display: inline-flex;
}

.nav-dropdown-panel {
    position: absolute;
    top: 100%;
    inset-inline-start: 0;
    z-index: var(--z-dropdown);
    display: none;
    flex-direction: column;
    gap: 2px;
    min-width: 240px;
    padding: var(--space-2);
    margin-top: var(--space-2);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
}

.nav-link-dropdown.open .nav-dropdown-panel {
    display: flex;
}

/* Chevron rotates when dropdown is open */
.nav-link-dropdown.open .dropdown-chevron {
    transform: rotate(180deg);
}

/* Dropdown toggle gets pointer cursor */
.nav-link-dropdown-toggle {
    cursor: pointer;
    user-select: none;
}

/* User Dropdown Component */
.user-dropdown-toggle {
    padding: 6px 10px;
    font-size: 13px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border-radius: 6px;
    border: 1px solid transparent;
    background: transparent;
    cursor: pointer;
}

.user-dropdown-toggle .user-icon {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}

.user-dropdown-name {
    font-weight: 600;
    color: var(--text-secondary);
    max-width: 120px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.user-dropdown-panel {
    min-width: 220px;
    inset-inline-end: 0;
    inset-inline-start: auto;
    top: 100%;
    margin-top: var(--space-2);
}

.user-dropdown-mobile-header {
    display: none;
}

.user-dropdown-item {
    padding: 12px 16px;
    display: flex;
    align-items: center;
    gap: 10px;
    cursor: pointer;
    border-radius: var(--radius-sm);
}

.user-dropdown-item svg {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

/* Invisible bridge over the margin-top gap above, so moving the mouse from
   the link down into the panel doesn't cross a dead zone that closes it. */
.nav-link-dropdown::after {
    content: '';
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    height: var(--space-2);
}

/* Reuse .sidebar-tab-item (same markup as before) but as a vertical menu
   row instead of a horizontal tab — no bottom-border tab underline, full
   width, rounded corners, accent background instead for hover/active. */
.nav-dropdown-panel .sidebar-tab-item {
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    border-bottom: none;
}

.nav-dropdown-panel .sidebar-tab-item:hover {
    background: var(--accent-light);
    color: var(--accent);
}

.nav-dropdown-panel .sidebar-tab-item.active {
    background: var(--accent);
    color: #fff;
}

.nav-dropdown-panel .sidebar-tab-item.active .books-total-count {
    color: inherit;
    opacity: 0.85;
}

/* ─── PDF Badge Nav Link ─── */
.nav-link-pdf {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: var(--space-2) var(--space-4);
}

.nav-pdf-badge {
    flex-shrink: 0;
    height: 28px;
    width: auto;
    filter: drop-shadow(0 1px 2px rgba(0,0,0,0.18));
    transition: transform var(--transition-fast);
}

.nav-link-pdf:hover .nav-pdf-badge {
    transform: scale(1.08);
}

/* ─── Nav Actions Row (search icon, sidebar toggle, theme) ─── */
.nav-actions {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

.nav-icon-btn {
    width: 40px;
    height: 40px;
    border: none;
    background: transparent;
    border-radius: var(--radius-md);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-secondary);
    transition: color var(--transition-fast), background var(--transition-fast), transform var(--transition-fast);
}

.nav-icon-btn:hover {
    color: var(--accent);
    background: var(--accent-light);
}

.nav-icon-btn:active {
    transform: scale(0.97);
    transition: transform 100ms ease;
}

.nav-icon-btn svg {
    width: 20px;
    height: 20px;
}

/* ── Boot screen (pre-JS) ────────────────────────────────────────────────
   index.html hardcodes #screen-home as .active, so without these rules the
   homepage is what gets painted for every URL until app.js runs at the bottom
   of the page. The inline <head> script stamps data-boot-screen from the URL
   before the body is parsed; these rules make it win for that window only —
   preselectScreenFromPath() removes the attribute as soon as it has set the
   real .active class, so nothing here can fight the router afterwards.
   No animation: this is the first paint, there is nothing to transition. */
html[data-boot-screen]:not([data-boot-screen="home"]) #screen-home.active {
    display: none;
    animation: none;
}
html[data-boot-screen="library"] #screen-library,
html[data-boot-screen="search"] #screen-search,
html[data-boot-screen="articles"] #screen-articles,
html[data-boot-screen="contact-support"] #screen-contact-support,
html[data-boot-screen="reading-lists"] #screen-reading-lists,
html[data-boot-screen="download"] #screen-download {
    display: block;
}

/* Lucide icons inside buttons */
.btn svg.lucide,
.btn .lucide {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}

/* ─── Scroll-to-top FAB — dark frosted glass, not a solid accent-filled
   button. A solid --btn-primary-bg circle competes visually with real
   actions (Read/Download); dark-tinted glass reads as passive chrome and
   sits quietly over any page content since it isn't theme-tinted itself —
   same reasoning as the sidebar icon rail's glass treatment nearby. */
.scroll-to-top-fab {
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, 0.16);
    background: rgba(15, 23, 42, 0.45);
    backdrop-filter: blur(10px) saturate(140%);
    -webkit-backdrop-filter: blur(10px) saturate(140%);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
    color: #F8FAFC;
    cursor: pointer;
    transition: opacity 0.3s, transform 0.3s, background 150ms ease, border-color 150ms ease;
}

.scroll-to-top-fab:hover {
    background: rgba(15, 23, 42, 0.62);
    border-color: rgba(255, 255, 255, 0.28);
}

.scroll-to-top-fab:active {
    transform: scale(0.94);
}

/* Dark theme already puts a dark surface behind it — lighten the glass
   itself instead, so it still reads as a distinct floating layer. */
[data-theme="dark"] .scroll-to-top-fab {
    background: rgba(245, 245, 244, 0.14);
    border-color: rgba(245, 245, 244, 0.22);
}

[data-theme="dark"] .scroll-to-top-fab:hover {
    background: rgba(245, 245, 244, 0.22);
}

/* ======================== FORMS ======================== */

.form-textarea {
    width: 100%;
    padding: var(--space-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
    color: var(--text-primary);
    font-family: var(--font-arabic);
    font-size: 15px;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.form-textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--focus-ring);
}

.form-textarea {
    resize: vertical;
    min-height: 100px;
}

/* ======================== BADGES ======================== */

.badge-primary {
    background: var(--accent-gold-light);
    /* --accent-gold-text, not --accent-gold: this is real text, and the fill
       tone only clears the 3:1 non-text bar, not the 4.5:1 AA text bar. */
    color: var(--accent-gold-text);
}

.badge-accent {
    background: var(--accent-light);
    color: var(--accent-hover);
}

.badge-count {
    background: var(--bg-page);
    color: var(--text-secondary);
}

/* ======================== LIBRARY TABS BAR (horizontal, beneath navbar) ======================== */
.library-tabs-bar {
    background: transparent;
    padding: 0;
    position: sticky;
    /* top:0 (was a hardcoded 64px) — sticky's "top" is a floor the element
       won't scroll above, not a fixed offset from its natural flow position.
       At 64px, when the navbar collapses (body.navbar-hidden) this bar's
       natural position moves to ~1px, which is ABOVE that floor, so sticky
       held it pinned at 64px anyway — a 63px dead gap the navbar-collapse
       transition couldn't close no matter what. top:0 only pins it if the
       page ever scrolls above the very top, which is what "sticky" should
       mean here in the first place. */
    top: 56px;
    z-index: var(--z-tabs-bar);
    transition: top 280ms ease, background-color var(--transition-base), border-color var(--transition-base);
}

body.navbar-hidden .library-tabs-bar {
    top: 0;
}

.library-tab-strip-container {
    background: var(--child-bar-bg, var(--bg-surface));
    border-bottom: 1px solid var(--border);
    transition: background-color var(--transition-base), border-color var(--transition-base);
}

/* Direct child only. .search-filter-panel sits inside this same <nav>, so a
   descendant selector also matched its own .container — and overflow-x:auto
   makes overflow-y compute as auto rather than visible, so that 72px-tall
   container clipped the 600px View Options popover out of sight. The strip
   still scrolls; the filter panel below it no longer clips its own popovers. */
.library-tab-strip-container > .container {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    padding: 6px 12px;
    gap: 8px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}
.library-tab-strip-container > .container::-webkit-scrollbar {
    display: none;
}

.library-tabs-inner {
    display: flex;
    align-items: center;
    gap: 0;
    width: 100%;
}

.library-tabs-bar .lang-switcher {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 0;
    margin-inline-start: auto;
}

.library-tabs-bar .lang-switcher .lang-btn {
    padding: 12px 16px;
    border: none;
    border-bottom: 3px solid transparent;
    border-radius: 0;
    background: transparent;
    color: var(--text-secondary);
    font-family: var(--font-ui);
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    transition: background var(--transition-fast), color var(--transition-fast),
                border-color var(--transition-fast);
    white-space: nowrap;
}

.library-tabs-bar .lang-switcher .lang-btn:hover {
    background: var(--bg-page);
    color: var(--text-primary);
}

.library-tabs-bar .lang-switcher .lang-btn.active {
    background: transparent;
    color: var(--accent);
    font-weight: 600;
    border-bottom-color: var(--accent-gold);
}

/* ======================== LIBRARY LAYOUT (full width, no sidebar) ======================== */
.library-layout-wrapper {
    display: block;
    /* Needed so .lib-tab-view.active's height: 100% has a definite height to
       resolve against — #screen-library has an explicit height, but a
       percentage doesn't propagate through an intermediate auto-height
       ancestor, so without this the chain breaks and .lib-tab-view.active
       falls back to shrink-wrapping its own content instead. */
    height: 100%;
}

/* ─── Main Content ─── */
.library-main-content {
    min-width: 0;
    height: 100%;
}

.sidebar-tab-item {
    font-family: var(--font-ui);
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 12px 20px;
    border-radius: 0;
    border-bottom: 3px solid transparent;
    cursor: pointer;
    font-weight: 500;
    font-size: 14px;
    color: var(--text-secondary);
    transition: all var(--transition-fast);
    white-space: nowrap;
}

.sidebar-tab-item:hover {
    background: var(--bg-page);
    color: var(--text-primary);
}

.sidebar-tab-item.active {
    background: transparent;
    color: var(--accent);
    font-weight: 600;
    border-bottom-color: var(--accent-gold);
}

.tab-icon { display: flex; align-items: center; justify-content: center; width: 20px; height: 20px; }
.tab-icon svg { width: 20px; height: 20px; }
.tab-text { font-size: 14px; }

.lib-tab-view {
    display: none;
}

.lib-tab-view.active {
    display: flex;
    flex-direction: column;
    /* Stretch to #screen-library's full computed height instead of just
       shrink-wrapping to content — needed so a flex:1 child further down
       (e.g. .split-layout-container in Author/Category) can correctly claim
       "whatever's left after this tab's own local header" instead of
       overflowing past #screen-library's overflow:hidden bottom edge. */
    height: 100%;
}

/* Author/Category's local .search-grid header is a direct child of this
   flex COLUMN. .search-grid's base rule sets flex: 1 for its OTHER use as a
   child of .library-tabs-bar's flex ROW (so it stretches to fill that row's
   width) — inherited unchanged here, flex:1 instead makes the header grow
   to fill half the column's HEIGHT, squeezing .split-layout-container into
   the remaining half instead of leaving it its natural content height. */
.lib-tab-view > .search-grid {
    flex: 0 0 auto;
}

/* ─── Public Library Tab Strip (compact child tabs below navbar) ─── */
/* Same segmented-control pattern as the offline library, but tighter:
   zero top padding so the child tab strip sits flush against the navbar
   with no gap, and smaller font sizes throughout. */
.library-tab-strip-container {
    background: var(--child-bar-bg);
    padding: 0;
}
.library-tab-strip-container .container {
    display: flex;
    align-items: center;
    padding-top: 0;
    padding-bottom: 0;
}

/* The Public Library child-tab strip and the Offline Library bar are the same
   control and now share ONE definition — see .segmented-control-track below.
   (This block used to be a scaled-down copy that never actually took effect:
   the base .segmented-control-track rules sit later in the cascade at equal
   specificity and overrode every property here, so the two bars diverged.) */

/* ─── Offline Library Tabs Bar (Segmented Control) ─── */
/* Chrome kept identical to the Public Library child-tab strip: flush against
   the navbar (no vertical padding), same surface colour, same border. The bar
   height is then whatever the shared .segmented-control-track needs — the two
   bars match to the pixel. */
.offline-tabs-bar {
    background: var(--child-bar-bg);
    padding: 0;
    position: sticky;
    /* Was top: 64px — same bug/fix as .library-tabs-bar: sticky's "top" is a
       floor, not an offset from the navbar. The hardcoded 64px (stale even
       before auto-hide existed — the navbar is 110px tall) left a gap once
       the navbar collapsed toward 0 on scroll, since this bar would still
       refuse to sit above y=64. */
    top: 56px;
    z-index: var(--z-tabs-bar-offline);
    border-bottom: 1px solid var(--border);
    transition: top 280ms ease, background-color var(--transition-base), border-color var(--transition-base);
}

body.navbar-hidden .offline-tabs-bar {
    top: 0;
}

.offline-tabs-bar > .container {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    padding: 0 12px;
    gap: 8px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}

/* The segmented control track — a grounded warm strip that holds the active
   pill so it reads as a control rather than a pill floating loose on the bar.
   Hugs its own tabs (width: max-content) and is shared verbatim by the Public
   Library child-tab strip (#library-segmented-track) and the Offline Library
   bar (#offline-tabs-bar). */
.segmented-control-track {
    display: flex;
    align-items: center;
    gap: 3px;
    width: max-content;
    max-width: 100%;
    background: var(--segmented-track-bg);
    border-radius: 12px;
    padding: 3px;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}
.segmented-control-track::-webkit-scrollbar {
    display: none;
}

/* Individual tab items inside the segmented control */
.segmented-control-track .sidebar-tab-item {
    flex-shrink: 0;
    font-family: var(--font-ui);
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 8px 16px;
    border-radius: 8px;
    border-bottom: none;
    cursor: pointer;
    font-weight: 500;
    font-size: 14px;
    color: var(--text-secondary);
    transition: all 200ms ease;
    white-space: nowrap;
    background: transparent;
}

.segmented-control-track .sidebar-tab-item:hover {
    background: rgba(255,255,255,0.45);
    color: var(--text-primary);
}

.segmented-control-track .sidebar-tab-item.active {
    background: var(--segmented-item-bg);
    color: var(--text-primary);
    font-weight: 600;
    box-shadow: var(--segmented-item-shadow);
    border-bottom: none;
    border-radius: 9px;
}

/* Tab icon inside segmented control */
.segmented-control-track .tab-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
}

.segmented-control-track .tab-icon svg {
    width: 18px;
    height: 18px;
}

.segmented-control-track .tab-text {
    font-size: 13px;
}

/* Book count badge inside a segmented tab */
.segmented-control-track .books-total-count {
    font-size: 11px;
    background: var(--accent-light);
    color: var(--accent);
    padding: 1px 7px;
    border-radius: 9999px;
    font-weight: 600;
    line-height: 1.4;
}

/* An unpopulated count badge (the Offline "All Books" tab never gets a number
   written to it) was still painting its pill background + padding as a stray
   dash next to the label. No content, no badge. */
.books-total-count:empty {
    display: none;
}

/* ─── Upload Dropzone ─── */
.upload-dropzone {
    border: 2px dashed var(--border-strong);
    border-radius: var(--radius-lg);
    padding: var(--space-12) var(--space-8);
    text-align: center;
    background: var(--bg-elevated);
    transition: border-color var(--transition-fast), background var(--transition-fast);
    margin-top: var(--space-4);
}

.upload-dropzone.drag-over {
    border-color: var(--accent);
    background: var(--accent-light);
}

.upload-dropzone-actions {
    display: flex;
    gap: 10px;
    
    justify-content: center;
    flex-wrap: wrap;
}

/* Dark mode hover adjustment for segmented control */
[data-theme="dark"] .segmented-control-track .sidebar-tab-item:hover {
    background: rgba(255,255,255,0.08);
}

/* ── Search & Filter bar — Flexbox row ── */
.search-grid,
.search-grid-5col {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    width: 100%;
    padding: 6px 0;
}

/* Search input stretches to fill leftover space */
.search-grid > .form-group,
.search-grid-5col > .form-group {
    flex: 1;
    min-width: 180px;
    margin: 0;
}

/* All form controls: shared height, radius, border */
.search-grid .form-input,
.search-grid .form-select {
    height: 38px;
    border-radius: 8px;
    border: 1px solid var(--border);
    font-size: 13.5px;
    font-family: 'Inter', system-ui, -apple-system, sans-serif;
    background: var(--bg-page);
    color: var(--text-primary);
    transition: border-color 150ms ease, box-shadow 150ms ease;
}

.search-grid .form-input:focus,
.search-grid .form-select:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(15, 118, 110, 0.12);
}

/* Filter dropdowns: auto-size to content, never expand past 200px */
.filter-dropdown-group {
    flex-shrink: 0;
}

.search-grid .form-select,
.form-select-compact {
    min-width: 90px;
    max-width: 200px;
    padding: 0 10px;
    cursor: pointer;
}

.search-grid .form-select:hover {
    border-color: var(--accent);
    background: var(--bg-elevated);
    cursor: pointer;
}

/* View options button stays compact */
.search-grid > .list-view-controls {
    flex-shrink: 0;
}

/* form-group-compact: used in author/category subpanels */
.form-group-compact {
    flex-shrink: 0;
    min-width: 0;
}

/* Max-width ceiling to prevent long author names blowing the row */
.search-grid .form-select {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

#lib-view-all-books.active,
#lib-view-recent-books.active {
    height: calc(100vh - var(--chrome-offset));
    height: calc(100dvh - var(--chrome-offset)); /* dynamic viewport for mobile */
    overflow: hidden;
}

#lib-view-all-books .book-row-list,
#lib-view-all-books .books-grid,
#lib-view-recent-books .book-row-list,
#lib-view-recent-books .books-grid {
    flex: 1;
    overflow-y: auto;
    min-height: 0;
    padding-left: 4px;
    padding-bottom: 60px;
    scrollbar-width: none;
    -ms-overflow-style: none;
}

#lib-view-all-books .book-row-list::-webkit-scrollbar,
#lib-view-all-books .books-grid::-webkit-scrollbar,
#lib-view-recent-books .book-row-list::-webkit-scrollbar,
#lib-view-recent-books .books-grid::-webkit-scrollbar {
    display: none;
    width: 0;
    height: 0;
}

.recent-books-layout-container {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
}

/* Author/Category panels sit under the exact same navbar + search-filter-bar
   chrome as All Books (174px), so they get the same calc() instead of the
   base .split-layout-container's flat 70vh — which left ~96px of unfilled
   space below the sidebar/book-list on a typical desktop window. Scoped to
   these two ids only; the Offline Library's author/category panels sit
   under different chrome (navbar + .offline-tabs-bar, no search-filter-bar)
   so they get their own --offline-chrome-offset calc() just below instead. */
/* flex:1 (not a --chrome-offset calc) — Author/Category also have their own
   local .search-grid header sitting ABOVE the split-layout-container inside
   the same .lib-tab-view.active, which --chrome-offset (calibrated for the
   shared navbar+tabs chrome only) doesn't know about. A fixed calc() here
   made the container ~64px taller than the space actually left after that
   local header, and #screen-library's overflow: hidden silently clipped
   the excess off the bottom row instead of showing it. flex:1 with the
   ancestor chain's height: 100% (.lib-tab-view.active) always claims
   exactly what's left, however tall the local header turns out to be. */
#lib-view-authors .split-layout-container,
#lib-view-categories .split-layout-container,
#local-view-author .split-layout-container,
#local-view-category .split-layout-container {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    grid-template-columns: none !important;
    height: auto !important;
    background: transparent;
    border: none;
    box-shadow: none;
    overflow: visible;
}

/* Same fix, same reasoning, for Offline Library's Author/Category panels —
   .local-books-header sits above .local-subview the same way .search-grid
   sits above .split-layout-container on the public Library screen. */
.local-subview {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
}

#local-view-author .split-layout-container,
#local-view-category .split-layout-container {
    flex: 1;
    min-height: 0;
}

/* ─── Local Books (Offline) view scrolling ─── */
/* Was a hardcoded 200px — measured actual chrome (navbar + .offline-tabs-bar)
   is 180px, so 20px of usable height was going unused (same class of stale-
   offset bug found on the public Library screen). Also now drops with
   --offline-chrome-offset when the navbar auto-hides on scroll, instead of
   leaving a gap the way the un-collapsing hardcoded value would have. */
#lib-view-local-books.active {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
}

#lib-view-local-books .local-books-header {
    flex-shrink: 0;
}

#lib-view-local-books .book-row-list,
#lib-view-local-books .books-grid {
    flex: 1;
    overflow-y: auto;
    min-height: 0;
    padding-left: 4px;
    padding-bottom: 60px;
    scrollbar-width: none;
    -ms-overflow-style: none;
}

#lib-view-local-books .book-row-list::-webkit-scrollbar,
#lib-view-local-books .books-grid::-webkit-scrollbar {
    display: none;
    width: 0;
    height: 0;
}

/* ======================== SEARCH & FILTER PANEL ======================== */
.search-filter-panel {
    background: var(--bg-elevated);
    padding: var(--space-2) 0;
    display: none;
    transition: background-color var(--transition-base), border-color var(--transition-base);
}
.search-filter-panel.active {
    display: block;
}


/* Filter dropdown group (indicator word + select) */
.books-total-count {
    font-size: 11px;
    color: var(--text-muted);
    white-space: nowrap;
    margin-inline-start: 6px;
    font-weight: 400;
}

.books-total-count:not(:empty)::before {
    content: "(";
}

.books-total-count:not(:empty)::after {
    content: ")";
}

.filter-dropdown-group {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}

/* Search box */
.search-box-wrapper {
    position: relative;
}

.search-box-wrapper input {
    padding-right: 14px;
    padding-left: 42px;
    border: 1px solid var(--border);
    color: var(--text-primary);
    font-size: 14.5px;
    height: 44px;
    border-radius: var(--radius-lg);
    background: var(--bg-panel);
    box-shadow: var(--shadow-sm);
    transition: all var(--transition-fast);
}

[dir="ltr"] .search-box-wrapper input,
html[dir="ltr"] .search-box-wrapper input {
    padding-right: 42px;
    padding-left: 14px;
}

.search-box-wrapper input:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--focus-ring);
    outline: none;
}

/* ======================== BOOK BAR (list item) ======================== */
.book-row-list {
    display: flex;
    flex-direction: column;
    /* Rows touch directly — .book-row's own border-bottom + alternating
       nth-child(even) tint already separate them, so a flex gap on top of
       that was pure dead space between every row for no visual benefit. */
    gap: 0;
    overflow-x: auto;
}

/* ─── List view options: column visibility + cover size ─── */
.list-view-controls {
    position: relative;
    display: flex;
    justify-content: flex-end;
}
.view-options-btn {
    width: 44px;
    height: 44px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-lg);
    background-color: var(--bg-panel);
    border: 1px solid var(--border);
    color: var(--text-secondary);
    transition: all 0.2s ease;
    box-shadow: var(--shadow-sm);
}

.view-options-btn:hover {
    background-color: var(--bg-surface-hover);
    color: var(--text-primary);
    border-color: var(--border-strong);
}
.view-options-popover {
    position: absolute;
    top: calc(100% + 6px);
    inset-inline-end: 0;
    z-index: var(--z-popover-local);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: var(--space-3);
    min-width: 220px;
    max-height: 60vh;
    overflow-y: auto;
}
.view-options-popover.hidden { display: none; }

.clear-history-btn {
    width: 44px;
    height: 44px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-lg);
    background-color: var(--bg-panel);
    border: 1px solid var(--border);
    color: var(--text-secondary);
    transition: all 0.2s ease;
    box-shadow: var(--shadow-sm);
    cursor: pointer;
}
.clear-history-btn:hover {
    background-color: var(--bg-surface-hover);
    color: var(--danger, #ef4444);
    border-color: var(--border-strong);
}
.clear-history-popover {
    position: absolute;
    top: calc(100% + 6px);
    inset-inline-end: 0;
    z-index: var(--z-popover-local, 100);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: var(--space-3);
    min-width: 200px;
}
.clear-history-popover.hidden { display: none; }
.clear-history-option-btn {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 8px 12px;
    border: none;
    background: transparent;
    color: var(--text-primary);
    font-size: 13px;
    font-weight: 500;
    border-radius: var(--radius-sm);
    cursor: pointer;
    text-align: start;
    transition: background 0.15s ease, color 0.15s ease;
}
.clear-history-option-btn:hover {
    background: var(--bg-surface-hover);
}
.clear-history-option-btn.clear-all-option {
    color: var(--danger, #ef4444);
}
.clear-history-option-btn.clear-all-option:hover {
    background: rgba(239, 68, 68, 0.1);
}
.view-options-group {
    margin-bottom: var(--space-3);
}
.view-options-group:last-child {
    margin-bottom: 0;
}
.view-options-group-title {
    font-size: 11px;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: var(--space-2);
}
.view-options-group .display-mode-toggle {
    display: flex;
    width: 100%;
}
.view-options-group .display-mode-btn {
    flex: 1;
    justify-content: center;
}
.view-options-check {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 0;
    font-size: 13px;
    color: var(--text-primary);
    cursor: pointer;
    user-select: none;
}
.view-options-check input { accent-color: var(--accent); width: 16px; height: 16px; cursor: pointer; }

/* Cover size (Large/Compact/No cover) — same selected-state treatment as
   the Grid/List display-mode toggle (solid accent background) so it's
   obvious which size is currently active. */
.view-options-size-buttons {
    display: flex;
    gap: var(--space-2);
}
.view-options-size-btn {
    flex: 1;
    padding: 6px 10px;
    font-size: 12px;
    font-weight: 600;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--bg-page);
    color: var(--text-secondary);
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}
.view-options-size-btn.active {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}

/* Column visibility — hides the matching cell/button in every row at once.
   .vo-managed is a plain marker class (independent of whatever layout class
   — .book-row-list, .books-grid, .book-bar cards — a given container also
   carries) so every book listing in the app can opt into View Options
   without fighting its own layout rules. */
.vo-managed.hide-author .book-row-author { display: none; }
.vo-managed.hide-category .book-row-category { display: none; }
.vo-managed.hide-language .book-row-language { display: none; }
.vo-managed.hide-volume .book-row-volumes { display: none; }
.vo-managed.hide-readlater .book-readlater-btn { display: none; }
.vo-managed.hide-read .book-read-btn { display: none; }
.vo-managed.hide-download .book-dl-btn { display: none; }
.vo-managed.hide-offline .book-opfs-btn { display: none; }

/* Read Later button: filled bookmark + accent color once saved */
.book-readlater-btn.saved { color: var(--accent); }
.book-readlater-btn.saved i { fill: var(--accent); }

/* "Remove from this list" — injected into each row on the My Lists screen
   (annotations.js). The bookmark toggle is redundant there, so it's hidden. */
.list-remove-btn { color: #ef4444; }
.list-remove-btn:hover { color: #dc2626; background: rgba(239, 68, 68, 0.1); }
#reading-list-books-container .book-readlater-btn { display: none; }

/* ─── OPFS offline save button (library rows + VLE header) ─── */
.book-opfs-btn { color: var(--text-secondary); transition: color 0.2s; }
.book-opfs-btn:hover:not(:disabled):not(.cached) { color: var(--accent); }
.book-opfs-btn.cached { color: #16a34a; cursor: default; opacity: 0.8; }
.book-opfs-btn.cached:hover { color: #16a34a; }
.book-opfs-btn:disabled:not(.cached) { opacity: 0.4; cursor: not-allowed; }

/* Offline-save progress ring — scanned manuscripts run 30-100MB+ and a plain
   spinner gave no way to tell a slow connection from a frozen one. --progress
   (0-100, set from JS as bytes stream in) drives a conic-gradient sweep; the
   inset ::after punches the center back out to the button's own background,
   leaving a ring rather than a filling pie. Falls back to the existing plain
   spinner look (no ring) when the server sends no Content-Length to compute
   a real percentage from. */
.book-opfs-btn.downloading {
    position: relative;
    background: conic-gradient(var(--accent) calc(var(--progress, 0) * 3.6deg), var(--border) 0deg);
    border-radius: 50%;
}
.book-opfs-btn.downloading::after {
    content: "";
    position: absolute;
    inset: 3px;
    border-radius: 50%;
    background: var(--bg-surface);
}
.book-opfs-btn.downloading i { position: relative; z-index: var(--z-content); opacity: 0.5; }

/* ======================== READING LISTS PAGE ======================== */
.reading-lists-tabs {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    flex: 1;
    min-width: 0;
}
.reading-list-tab {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 16px;
    border-radius: var(--radius-full);
    border: 1px solid var(--border);
    background: var(--bg-surface);
    color: var(--text-secondary);
    font-size: 13.5px;
    font-weight: 600;
    cursor: pointer;
    transition: all var(--transition-fast);
    white-space: nowrap;
}
.reading-list-tab:hover { background: var(--bg-hover); color: var(--text-primary); }
.reading-list-tab.active {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}
.reading-list-tab i { width: 15px; height: 15px; flex-shrink: 0; }
.reading-list-tab-count {
    background: rgba(0, 0, 0, 0.08);
    padding: 1px 8px;
    border-radius: var(--radius-full);
    font-size: 11px;
    font-weight: 700;
}
.reading-list-tab.active .reading-list-tab-count { background: rgba(255, 255, 255, 0.25); }
.reading-list-manage-actions {
    display: flex;
    gap: 6px;
    flex-shrink: 0;
}

/* "Add Books" modal — source tabs (public library vs this device) */
.add-books-source-tab {
    padding: 10px 18px;
    border: none;
    background: transparent;
    color: var(--text-secondary);
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    font-family: var(--font-arabic);
}
.add-books-source-tab:hover { color: var(--text-primary); }
.add-books-source-tab.active { color: var(--accent); border-bottom-color: var(--accent); }

/* "Add Books" modal — browse mode within the public-library tab (all books /
   by category / by author), matching the Premium Reader's own book picker. */
.add-books-mode-tabs { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
.add-books-mode-tab {
    padding: 6px 16px;
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    background: var(--bg-page);
    color: var(--text-secondary);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    font-family: var(--font-arabic);
}
.add-books-mode-tab:hover { background: var(--bg-hover); }
.add-books-mode-tab.active { background: var(--accent); border-color: var(--accent); color: #fff; }

.add-books-browse-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 8px;
    border-bottom: 1px solid var(--border);
    cursor: pointer;
    font-family: var(--font-arabic);
    font-size: 14px;
    color: var(--text-primary);
}
.add-books-browse-row:last-child { border-bottom: none; }
.add-books-browse-row:hover { color: var(--accent); }
.add-books-browse-row .add-books-browse-count { font-size: 12px; color: var(--text-muted); }

.add-books-back-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--accent);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    margin-bottom: 12px;
    font-family: var(--font-arabic);
}

html[dir="ltr"] .add-books-back-link i,
html[dir="ltr"] .add-books-back-link svg,
[dir="ltr"] .add-books-back-link i,
[dir="ltr"] .add-books-back-link svg {
    transform: scaleX(-1);
}

.add-books-result-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 8px;
    border-bottom: 1px solid var(--border);
}
.add-books-result-row:last-child { border-bottom: none; }
.add-books-result-cover {
    flex-shrink: 0;
    width: 34px;
    height: 48px;
    object-fit: cover;
    border-radius: 3px;
    border: 1px solid var(--border);
    background: var(--bg-page);
}
.add-books-result-info { flex: 1; min-width: 0; }
.add-books-result-title {
    font-family: var(--font-arabic);
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.add-books-result-meta { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
.add-books-add-btn {
    flex-shrink: 0;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    background: var(--bg-surface);
    color: var(--accent);
    cursor: pointer;
    transition: all var(--transition-fast);
}
.add-books-add-btn:hover { background: var(--accent); color: #fff; border-color: var(--accent); }
.add-books-add-btn.added { background: var(--accent); color: #fff; border-color: var(--accent); pointer-events: none; }

@media (max-width: 768px) {
    .reading-lists-tabs {
        flex-wrap: nowrap;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }
}


/* ═══════════════════════════════════════════════════════════
   Excel-Style Data Table (book rows)
   ═══════════════════════════════════════════════════════════ */

/* ─── Deprecated .book-bar styles — still used by Local Books (OPFS) section ─── */
.book-bar {
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: var(--space-4) var(--space-5);
    transition: transform var(--transition-base), box-shadow var(--transition-base),
                border-color var(--transition-base);
    box-shadow: var(--shadow-sm);
    width: 100%;
}

.book-bar:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-lg);
    border-color: var(--accent);
}

.book-bar-main {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
}

.book-bar-thumb {
    width: 105px;
    object-fit: cover;
    border-radius: 3px;
    border: 1px solid var(--border);
    flex-shrink: 0;
    background: var(--bg-page);
}

.book-bar-thumb-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-page);
    color: var(--text-muted);
    font-size: 32px;
    width: 105px;
    border-radius: 3px;
    border: 1px solid var(--border);
    flex-shrink: 0;
}

.book-bar-thumb-placeholder::after {
    content: "📖";
}

.book-bar-info {
    flex: 1;
    min-width: 0;
}

.book-bar-title {
    font-family: var(--font-arabic);
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--text-primary);
    margin: 0 0 4px 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.book-bar-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    font-size: 13px;
    color: var(--text-secondary);
}

.book-bar-author { font-weight: 600; color: var(--text-primary); }
.book-bar-death { color: var(--accent-hover); font-weight: 600; }
.book-bar-category { color: var(--text-primary); font-weight: 600; }
.book-bar-language { color: var(--text-secondary); }
.book-bar-volumes { color: var(--accent); font-weight: 500; }
.book-bar-separator { color: var(--border); font-size: 16px; }

.book-bar-actions {
    display: flex;
    gap: var(--space-2);
    flex-shrink: 0;
}

/* ─── New Excel-style book rows (main library list) ─── */

.book-table-header {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-4);
    background: var(--bg-surface);
    border-bottom: 2px solid var(--border);
    min-width: fit-content;
    font-size: 12px;
    font-weight: 700;
    color: var(--text-primary);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    user-select: none;
    -webkit-user-select: none;
}

.book-table-header > * {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.book-table-header .sortable {
    cursor: pointer;
    transition: color var(--transition-fast);
    display: inline-flex;
    align-items: center;
    gap: 3px;
}

.book-table-header .sortable:hover {
    color: var(--accent);
}

.book-table-header .sort-arrow {
    font-size: 10px;
    opacity: 0.5;
    transition: opacity var(--transition-fast);
}

.book-table-header .sortable.sort-active .sort-arrow {
    opacity: 1;
    color: var(--accent);
}

/* Column widths (RTL: right to left in DOM order) */
.book-col-thumb   { width: 100px; flex-shrink: 0; text-align: center; }
.book-col-title   { flex: 1; min-width: 0; }
.book-col-author  { width: 180px; flex-shrink: 0; }
.book-col-category { width: 130px; flex-shrink: 0; }
.book-col-actions { width: 120px; flex-shrink: 0; }

/* ─── Book row ─── */
.book-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-4);
    border-bottom: 1px solid var(--border);
    background: var(--bg-surface);
    transition: background-color 150ms ease;
    width: 100%;
    /* NOT fit-content: that sizes to the title's full nowrap content width,
       which defeats .book-row-title's own ellipsis truncation and pushes
       .book-row-actions off the right edge for any long title. min-width:0
       lets the row stay at 100% and the flex:1 title truncate as intended;
       fixed-width columns (flex-shrink:0) still hold their own width. */
    min-width: 0;
}
/* Clean border dividers approach: all rows share uniform background */
/* .book-row:nth-child(even) removed to disable zebra striping */

.book-row:hover {
    background: var(--accent-light);
    /* accent-light already at ~50% opacity via its definition */
}

/* ─── Thumbnail icon cell ─── */
.book-row-thumb-icon {
    width: 100px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    position: relative;
    color: var(--text-muted);
    transition: color var(--transition-fast);
}

.book-row-thumb-icon:hover {
    color: var(--accent);
}

/* Inline cover in the data-table row.
   80px used to be the default here — at that size the 1200px source cover
   (a dense, small-print scanned title page) downscales to a genuinely
   illegible smudge; no resize algorithm fixes that, since the information
   just isn't there at 80px. 96px is the smallest width where the title line
   stays legible in a spot check against real covers — verified by rendering
   an actual cover at both sizes and reading the result, not by assumption. */
.book-row-thumb-img {
    width: 96px;
    aspect-ratio: 2 / 3;
    height: auto;
    object-fit: cover;
    border-radius: 4px;
    display: block;
    border: 1px solid var(--border);
    background: var(--bg-elevated);
}

/* Compact cover — smaller thumbnail so more rows fit on screen; hovering or
   clicking it still opens the full-size preview popup (unchanged, since that
   popup loads the actual cover URL regardless of the inline thumbnail size). */
.vo-managed.compact-covers .book-row-thumb-icon,
.vo-managed.compact-covers .book-col-thumb { width: 40px; }
.vo-managed.compact-covers .book-row-thumb-img { width: 36px; }

/* No cover — hides the thumbnail column entirely so rows read as a dense,
   spreadsheet-style data table (title/author/category/etc only). */
.vo-managed.no-covers .book-row-thumb-icon,
.vo-managed.no-covers .book-col-thumb { display: none; }

/* ═══════════════════════════════════════════════════════════
   Thumbnail Hover Popup
   ═══════════════════════════════════════════════════════════ */

.thumbnail-popup {
    position: fixed;
    pointer-events: none;
    z-index: var(--z-drawer);
    opacity: 0;
    transform: scale(0.9);
    transition: opacity 200ms ease, transform 200ms ease;
    box-shadow: var(--shadow-lg);
    border-radius: 4px;
    overflow: hidden;
    background: var(--bg-surface);
    border: 1px solid var(--border);
}

.thumbnail-popup.visible {
    opacity: 1;
    transform: scale(1);
}

.thumbnail-popup img {
    display: block;
    width: 150px;
    height: 180px;
    object-fit: cover;
    background: var(--bg-page);
}

.thumbnail-popup .popup-spinner {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 150px;
    height: 180px;
    background: var(--bg-page);
    color: var(--text-muted);
}

/* ─── Title cell ─── */
.book-row-title {
    flex: 1;
    min-width: 140px;
    font-family: var(--font-arabic);
    font-size: 0.95rem;
    font-weight: 500;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.book-row-title a {
    color: inherit;
    text-decoration: none;
}

.book-row-title a:hover {
    color: var(--accent);
}

/* ─── Metadata cells ─── */
.book-row-author {
    width: 180px;
    flex-shrink: 0;
    font-family: var(--font-arabic);
    font-size: 13px;
    color: var(--text-secondary);
    font-weight: 400;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}


.book-row-category {
    width: 130px;
    flex-shrink: 0;
    font-family: var(--font-ui);
    font-size: 11px;
    font-weight: 600;
    color: var(--accent-gold-text);
    background: var(--accent-gold-light);
    border-radius: var(--radius-full);
    padding: 3px 10px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: center;
}



/* ─── Actions cell ─── */
.book-row-actions {
    width: 112px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 0;
    justify-content: flex-end;
}

/* Reader, Download, Save Offline, and the "more options" trigger are 4 fixed
   icons now instead of the original 3 â€” shrunk and butted together (no gap)
   so they keep fitting in the same space, including narrow/compact grid cards. */
.book-card-actions .btn-icon,
.book-row-actions .btn-icon,
.book-more-popover .btn-icon {
    width: 28px;
    height: 28px;
}

/* "More options" flyout. Every action (Read / Download / Save Offline / Read
   Later) is inline on desktop, so the trigger is hidden here and re-shown only
   in the ≤768px block, where the condensed card fits just Read + this menu. */
.book-more-wrap { position: relative; display: none; }

.book-more-popover {
    display: none;
    position: fixed;
    z-index: var(--z-popover);
    align-items: center;
    gap: var(--space-1);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: var(--space-1);
}

.book-more-popover.visible { display: flex; }

/* On desktop the primary actions (read/download/save) live inline in the row, so
   their cloned copies inside the "…" popover are hidden. On mobile this flips —
   see the ≤768px rules (inline copies hidden, clones shown in the sheet). */
.book-more-popover .book-more-primary { display: none; }


/* ─── Infinite scroll load-more indicator ─── */
.load-more-indicator {
    text-align: center;
    padding: var(--space-4) var(--space-2);
    font-size: 13px;
    color: var(--text-secondary);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
}
.load-more-end {
    color: var(--text-muted);
    font-size: 12px;
}
.load-more-loading {
    color: var(--accent);
}
.load-more-indicator .spinner {
    display: inline-block;
    width: 16px;
    height: 16px;
    border: 2px solid var(--border);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin 0.6s linear infinite;
}
@keyframes spin {
    to { transform: rotate(360deg); }
}

/* ======================== BOOK CARD (grid view) ======================== */
.books-grid {
    display: grid;
    /* 220px gives each card a comfortable width, preventing squeezed covers
       and cramped typography. */
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    /* Rows must size to each card's full rendered height */
    grid-auto-rows: max-content;
    /* Add a generous gap between cards to let them breathe */
    gap: var(--space-4);
    padding: var(--space-2) 0;
}

.book-card-cover-wrapper {
    width: 100%;
    aspect-ratio: 2 / 3;
    overflow: hidden;
    background: var(--bg-page);
    border-bottom: 1px solid var(--border);
    position: relative;
}

.book-card-cover {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform var(--transition-base);
}

.book-card:hover .book-card-cover {
    transform: scale(1.03);
}

.book-card-body {
    padding: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    flex: 1;
    align-items: flex-start;
    text-align: start;
}

.book-card-title {
    font-family: var(--font-arabic);
    font-size: 1.05rem;
    font-weight: 500;
    color: var(--text-primary);
    line-height: 1.4;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.book-card-author-row {
    font-family: var(--font-arabic);
    font-size: 12px;
    color: var(--text-secondary);
    font-weight: 400;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.book-card-death {
    color: var(--text-muted);
    font-weight: 400;
}

.book-card-category {
    display: inline-block;
    font-family: var(--font-ui);
    font-size: 11px;
    font-weight: 600;
    color: var(--accent-gold-text);
    background: var(--accent-gold-light);
    border-radius: var(--radius-full);
    padding: 3px 10px;
    width: fit-content;
}

.book-card-actions {
    display: flex;
    justify-content: center;
    align-items: center;
    /* No gap â€” icons are shrunk (see .book-card-actions .btn-icon) and butted
       together so all 4 fit without widening the card in narrow grid columns. */
    gap: 0;
    padding: var(--space-2) var(--space-3);
    border-top: 1px solid var(--border);
    margin-top: auto;
}

/* Grid empty state: full-width message */
.books-grid > .empty-state {
    grid-column: 1 / -1;
}

/* Compact cover size in grid view — same View Options preference that shrinks
   the inline thumbnail in bar view instead shrinks the whole card (the cover
   *is* the card in grid view), so more, smaller cards fit per row. */
.books-grid.compact-covers {
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: var(--space-3);
}
/* Shorter than the base 2/3 ratio (height = 1.5x width) — Compact is meant
   to fit more rows on screen, so the cover shouldn't still be as tall
   relative to its own (already narrower) width as Large's cover is. */
.books-grid.compact-covers .book-card-cover-wrapper { aspect-ratio: 2 / 2.5; }
.books-grid.compact-covers .book-card-body { padding: var(--space-2); }
.books-grid.compact-covers .book-card-title { font-size: 0.8rem; }
.books-grid.compact-covers .book-card-author-row { font-size: 11px; }
.books-grid.compact-covers .book-card-category { font-size: 10px; padding: 1px 6px; }
.books-grid.compact-covers .book-card-actions { padding: var(--space-1) var(--space-2); gap: 0; }

/* No cover in grid view — same idea as bar view's no-covers: drop the cover
   image so cards read as plain text tiles, more of them per row. */
.books-grid.no-covers { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }
.books-grid.no-covers .book-card-cover-wrapper { display: none; }

/* ─── Display mode toggle (Grid / Bar) ─── */
.display-mode-toggle {
    display: inline-flex;
    gap: 0;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    overflow: hidden;
    flex-shrink: 0;
}

.display-mode-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 8px 14px;
    font-size: 13px;
    font-weight: 600;
    border: none;
    background: var(--bg-page);
    color: var(--text-secondary);
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast);
    white-space: nowrap;
}

.display-mode-btn:first-child {
    border-inline-end: 1px solid var(--border);
}

.display-mode-btn.active {
    background: var(--accent);
    color: #fff;
}

.display-mode-btn i {
    width: 16px;
    height: 16px;
}

/* ======================== SPLIT LAYOUT (authors / categories) ======================== */
.split-layout-container {
    display: grid;
    grid-template-columns: var(--split-sidebar-width, 280px) 1fr;
    gap: 0;
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-top: none;
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
    overflow: hidden;
    height: 70vh;
    box-shadow: var(--shadow-sm);
}
.split-layout-container.has-resize-handle {
    grid-template-columns: var(--split-sidebar-width, 280px) 4px 1fr;
}



#lib-view-authors .split-resize-handle,
#lib-view-categories .split-resize-handle,
#local-view-author .split-resize-handle,
#local-view-category .split-resize-handle {
    display: none !important;
}

#lib-view-authors .split-layout-container:not([data-mobile-layer="detail"]) .split-panel-sidebar,
#lib-view-categories .split-layout-container:not([data-mobile-layer="detail"]) .split-panel-sidebar,
#local-view-author .split-layout-container:not([data-mobile-layer="detail"]) .split-panel-sidebar,
#local-view-category .split-layout-container:not([data-mobile-layer="detail"]) .split-panel-sidebar,
#lib-view-authors .split-layout-container[data-mobile-layer="master"] .split-panel-sidebar,
#lib-view-categories .split-layout-container[data-mobile-layer="master"] .split-panel-sidebar,
#local-view-author .split-layout-container[data-mobile-layer="master"] .split-panel-sidebar,
#local-view-category .split-layout-container[data-mobile-layer="master"] .split-panel-sidebar {
    display: flex !important;
    width: 100%;
    max-width: 760px;
    margin: 0 auto;
    background: transparent;
    border: none;
    box-shadow: none;
    overflow: visible;
    max-height: none;
    min-width: 0;
}

#lib-view-authors .split-layout-container:not([data-mobile-layer="detail"]) .split-panel-main,
#lib-view-categories .split-layout-container:not([data-mobile-layer="detail"]) .split-panel-main,
#local-view-author .split-layout-container:not([data-mobile-layer="detail"]) .split-panel-main,
#local-view-category .split-layout-container:not([data-mobile-layer="detail"]) .split-panel-main,
#lib-view-authors .split-layout-container[data-mobile-layer="master"] .split-panel-main,
#lib-view-categories .split-layout-container[data-mobile-layer="master"] .split-panel-main,
#local-view-author .split-layout-container[data-mobile-layer="master"] .split-panel-main,
#local-view-category .split-layout-container[data-mobile-layer="master"] .split-panel-main {
    display: none !important;
}

#lib-view-authors .split-layout-container[data-mobile-layer="detail"] .split-panel-sidebar,
#lib-view-categories .split-layout-container[data-mobile-layer="detail"] .split-panel-sidebar,
#local-view-author .split-layout-container[data-mobile-layer="detail"] .split-panel-sidebar,
#local-view-category .split-layout-container[data-mobile-layer="detail"] .split-panel-sidebar {
    display: none !important;
}

#lib-view-authors .split-layout-container[data-mobile-layer="detail"] .split-panel-main,
#lib-view-categories .split-layout-container[data-mobile-layer="detail"] .split-panel-main,
#local-view-author .split-layout-container[data-mobile-layer="detail"] .split-panel-main,
#local-view-category .split-layout-container[data-mobile-layer="detail"] .split-panel-main {
    display: flex !important;
    flex-direction: column;
    width: 100%;
    padding: var(--space-3) 0;
    overflow: visible;
}

#lib-view-authors .split-layout-container[data-mobile-layer="detail"] .mobile-breadcrumb,
#lib-view-categories .split-layout-container[data-mobile-layer="detail"] .mobile-breadcrumb,
#local-view-author .split-layout-container[data-mobile-layer="detail"] .mobile-breadcrumb,
#local-view-category .split-layout-container[data-mobile-layer="detail"] .mobile-breadcrumb {
    display: flex !important;
    max-width: 760px;
    margin: 0 auto 14px auto;
    width: 100%;
    border-radius: var(--radius-lg);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-sm);
    background: var(--bg-panel);
}



.split-panel-sidebar {
    background: var(--bg-elevated);
    border-left: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    min-height: 0;
    min-width: 180px;
    max-width: 500px;
}

/* Resize handle between sidebar and main panels */
.split-resize-handle {
    cursor: col-resize;
    background: transparent;
    user-select: none;
    transition: background 0.15s;
    min-height: 0;
}
.split-resize-handle:hover,
.split-resize-handle.dragging {
    background: var(--accent);
}

.panel-search-box {
    padding: var(--space-3);
    border-bottom: 1px solid var(--border);
    display: flex;
    flex-direction: row;
    gap: 8px;
    align-items: center;
    position: relative;
}

.panel-search-box .search-box-wrapper {
    flex: 1;
    position: relative;
    z-index: 2;
}

.panel-search-box input {
    width: 100%;
    height: 44px;
    font-size: 14.5px;
    border-radius: var(--radius-lg);
    background-color: var(--bg-panel);
    border: 1px solid var(--border);
    padding-left: 42px; /* physical left padding for search icon in RTL */
    padding-right: 14px;
    box-shadow: var(--shadow-sm);
    color: var(--text-primary);
    transition: all 0.2s ease;
    position: relative;
    z-index: 2;
}

[dir="ltr"] .panel-search-box input,
html[dir="ltr"] .panel-search-box input {
    padding-left: 14px;
    padding-right: 42px; /* physical right padding for search icon in LTR */
}

.panel-search-box input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--focus-ring);
    background-color: var(--bg-surface);
}

.panel-search-box .search-icon {
    left: 14px;
    right: auto;
    top: 50%;
    transform: translateY(-50%);
    position: absolute;
    z-index: 5;
    pointer-events: auto;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

[dir="ltr"] .panel-search-box .search-icon,
html[dir="ltr"] .panel-search-box .search-icon {
    left: auto;
    right: 14px;
}

.panel-search-box select {
    width: 120px;
    height: 44px;
    font-size: 13px;
    padding: 0 10px;
    border-radius: var(--radius-lg);
    border: 1px solid var(--border);
    background: var(--bg-panel);
    color: var(--text-primary);
    cursor: pointer;
    flex-shrink: 0;
    transition: border-color var(--transition-fast);
}

.panel-search-box select:focus {
    border-color: var(--accent);
    outline: none;
}

.panel-total-count {
    font-size: 11px;
    color: var(--text-light);
    padding: 4px var(--space-3) var(--space-2);
    border-bottom: 1px solid var(--border);
}

.split-list-scrollable {
    flex: 1;
    overflow-y: auto;
    padding: var(--space-2);
    min-height: 0;
    overscroll-behavior: contain;
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.split-list-scrollable::-webkit-scrollbar {
    display: none;
    width: 0;
    height: 0;
}

.split-list-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px 14px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-family: var(--font-arabic);
    font-size: 13px;
    font-weight: 500;
    transition: background var(--transition-fast), color var(--transition-fast);
    margin-bottom: 2px;
    color: var(--text-primary);
}

.split-list-item:hover { background: var(--bg-page); }

.split-list-item.active {
    background: var(--accent-light);
    color: var(--accent);
    font-weight: 700;
}

.split-list-item .count-badge {
    background: var(--accent-gold-light);
    color: var(--accent-gold-text);
    font-weight: 700;
    font-size: 11px;
    padding: 2px 9px;
    border-radius: var(--radius-full);
}

.split-list-item.active .count-badge {
    background: var(--accent);
    color: #fff;
}

/* ── Author Index century accordion ──────────────────────────────────────── */
.author-century-group { margin-bottom: 2px; }

.author-century-header {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 11px 14px;
    border: none;
    background: var(--bg-page);
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-family: var(--font-arabic);
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
    text-align: start;
    transition: background var(--transition-fast);
}

.author-century-header:hover { background: var(--bg-surface-hover, var(--bg-page)); }
.author-century-header.expanded { background: var(--accent-light); color: var(--accent); }

.author-century-label { flex: 1; min-width: 0; }

.author-century-chevron {
    width: 15px;
    height: 15px;
    flex-shrink: 0;
    color: var(--text-light);
    transition: transform var(--transition-fast);
}
/* The chevron markup is `chevron-left` (`<`), correct for the RTL default where
   it sits at the inline-start edge pointing outward. In LTR it must point the
   other way (`>`, toward the label); the expanded state rotates down, where
   the mirror is moot, so the .expanded rule below still wins on specificity. */
[dir="ltr"] .author-century-chevron { transform: scaleX(-1); }

.author-century-header.expanded .author-century-chevron {
    transform: rotate(-90deg);
    color: var(--accent);
}

.author-century-header .count-badge {
    background: var(--accent-gold-light);
    color: var(--accent-gold-text);
    font-weight: 700;
    font-size: 11px;
    padding: 2px 9px;
    border-radius: var(--radius-full);
    flex-shrink: 0;
}
.author-century-header.expanded .count-badge { background: var(--accent); color: #fff; }

.author-century-body {
    padding-inline-start: 10px;
    margin: 2px 0 4px;
    border-inline-start: 2px solid var(--border);
}
.author-century-body[hidden] { display: none; }

.author-century-more {
    width: 100%;
    border: none;
    background: none;
    color: var(--accent);
    font-family: var(--font-arabic);
    font-size: 12px;
    font-weight: 600;
    padding: 8px;
    cursor: pointer;
    border-radius: var(--radius-sm);
}
.author-century-more:hover { background: var(--bg-page); }

.author-century-loading,
.author-century-empty {
    font-size: 11px;
    text-align: center;
    padding: 10px;
    color: var(--text-light);
}

.split-panel-main {
    display: flex;
    flex-direction: column;
    /* Was var(--space-6) (24px) on all sides — that plus .panel-header-desc's
       own spacing below ate ~115px of vertical room the book grid/list never
       got back, so Author/Category/Offline showed noticeably less of row 2
       than All Books does at the same viewport height (partial cards with
       the Premium Reader/Download icon row cut off entirely). Trimmed
       top/bottom only; left/right unchanged for reading comfort. */
    padding: var(--space-3) var(--space-6);
    overflow-y: hidden;
    overflow-x: auto;
    /* Grid items default to min-height: auto, which stops this panel from
       shrinking below its content's natural height — the book list inside
       would then grow .split-panel-main taller than the grid track, and
       .split-layout-container's overflow: hidden clips the excess instead
       of the inner list's own overflow: auto ever getting a chance to
       scroll. Result: the last row's action icons appear chopped off with
       no way to scroll to them. Same fix as .split-panel-sidebar above. */
    min-height: 0;
}

.split-panel-main .book-row-list,
.split-panel-main .books-grid {
    flex: 1;
    overflow: auto;
    min-height: 0;
    padding-left: 4px;
    padding-bottom: 60px;
    overscroll-behavior: contain;
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.split-panel-main .book-row-list::-webkit-scrollbar,
.split-panel-main .books-grid::-webkit-scrollbar {
    display: none;
    width: 0;
    height: 0;
}

.panel-header-desc {
    border-bottom: 1px solid var(--border);
    padding-bottom: var(--space-2);
    margin-bottom: var(--space-2);
    display: flex;
    justify-content: space-between;
    align-items: center;
}

/* ======================== MOBILE BREADCRUMB ======================== */
.mobile-breadcrumb {
    display: none;
    align-items: center;
    gap: 8px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border);
    background: var(--bg-elevated);
}

.mobile-back-btn {
    background: none;
    border: none;
    cursor: pointer;
    padding: 4px;
    color: var(--text-primary);
    display: flex;
    align-items: center;
    flex-shrink: 0;
}

.mobile-breadcrumb-text {
    font-family: var(--font-arabic);
    font-size: 14px;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 70vw;
}

/* ======================== SKELETON LOADING ======================== */
.skeleton {
    background: linear-gradient(
        90deg,
        var(--bg-page) 25%,
        var(--bg-elevated) 50%,
        var(--bg-page) 75%
    );
    background-size: 400% 100%;
    animation: shimmer 1.5s infinite linear;
    border-radius: var(--radius-sm);
}

.skeleton-book-bar {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-4);
    border-bottom: 1px solid var(--border);
    background: var(--bg-surface);
    width: 100%;
}

.skeleton-book-bar .skeleton-thumb {
    width: 32px;
    height: 32px;
    background: linear-gradient(
        90deg,
        var(--bg-page) 0%,
        var(--bg-elevated) 40%,
        var(--bg-page) 80%
    );
    background-size: 200% 100%;
    animation: shimmer 1.8s ease-in-out infinite;
    border-radius: var(--radius-sm);
    flex-shrink: 0;
}

.skeleton-book-bar .skeleton-col {
    height: 16px;
    background: linear-gradient(
        90deg,
        var(--bg-page) 0%,
        var(--bg-elevated) 40%,
        var(--bg-page) 80%
    );
    background-size: 200% 100%;
    animation: shimmer 1.8s ease-in-out infinite;
    border-radius: var(--radius-sm);
}

.skeleton-book-bar .skeleton-col.skel-title { flex: 1; min-width: 0; }
.skeleton-book-bar .skeleton-col.skel-author { width: 180px; flex-shrink: 0; }
.skeleton-book-bar .skeleton-col.skel-death { width: 90px; flex-shrink: 0; }
.skeleton-book-bar .skeleton-col.skel-category { width: 130px; flex-shrink: 0; }
.skeleton-book-bar .skeleton-col.skel-volumes { width: 70px; flex-shrink: 0; }
.skeleton-book-bar .skeleton-col.skel-language { width: 80px; flex-shrink: 0; }

.skeleton-book-bar .skeleton-actions {
    display: flex;
    gap: var(--space-2);
    width: 120px;
    flex-shrink: 0;
    justify-content: flex-end;
}

.skeleton-book-bar .skeleton-btn {
    width: 40px;
    height: 40px;
    background: linear-gradient(
        90deg,
        var(--bg-page) 0%,
        var(--bg-elevated) 40%,
        var(--bg-page) 80%
    );
    background-size: 200% 100%;
    animation: shimmer 1.8s ease-in-out infinite;
    border-radius: var(--radius-md);
}

@keyframes shimmer {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}

/* ======================== EMPTY STATE ======================== */
.empty-state {
    text-align: center;
    padding: var(--space-16) var(--space-5);
    color: var(--text-secondary);
}

.empty-state-icon {
    font-size: 48px;
    color: var(--text-muted);
    margin-bottom: var(--space-4);
    opacity: 0.6;
}

.empty-state p {
    font-size: 15px;
    color: var(--text-secondary);
    max-width: 360px;
    margin: 0 auto;
    line-height: 1.7;
}


/* NOTE: a verbatim 103-line duplicate of the tab-strip block that follows
   further down this file (.tabs-scroll / .tab / .tab-close / .toolbar-sep
   and their 768px overrides) used to sit here. Both copies were identical,
   so the later one won the cascade and this one was dead weight. Removed the
   EARLIER copy specifically: the later copy already won every property, so
   deleting this one changes nothing, whereas deleting the later one would
   have promoted the intermediate .tab-close rule (~line 5330, cursor /
   user-select) ahead of it. Keep the surviving copy as the single source. */

.toolbar-sep {
    width: 1px;
    height: 20px;
    background: var(--border-strong);
    margin: 0 3px;
    flex-shrink: 0;
}
.btn-search-book {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--bg-page);
    border: 1px solid var(--border);
    color: var(--text-secondary);
    font-size: 12px;
    padding: 5px 10px;
    border-radius: var(--radius-md);
    transition: all var(--transition-fast);
    white-space: nowrap;
    height: 32px;
}
.btn-search-book:hover {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
    transform: none;
    box-shadow: none;
}

/* Mobile Drawer Sheet */
.mobile-sheet-drawer {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    width: 280px;
    background: var(--bg-surface);
    box-shadow: var(--shadow-lg);
    z-index: var(--z-sheet);
    display: flex;
    flex-direction: column;
    padding: 20px;
    animation: slideInLeft 0.3s ease forwards;
}
[dir="rtl"] .mobile-sheet-drawer {
    left: auto;
    right: 0;
    animation: slideInRight 0.3s ease forwards;
}
@keyframes slideInLeft { from { transform: translateX(-100%); } to { transform: translateX(0); } }
@keyframes slideInRight { from { transform: translateX(100%); } to { transform: translateX(0); } }

.mobile-sheet-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    border-bottom: 1px solid var(--border);
    padding-bottom: 16px;
    margin-bottom: 16px;
}

/* ======================== ADMIN DASHBOARD ======================== */

/* ─── Queue Cards ─── */
.queue-card {
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: var(--space-5);
    margin-bottom: var(--space-5);
    box-shadow: var(--shadow-sm);
}

.queue-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-bottom: 1px solid var(--border);
    padding-bottom: var(--space-3);
    margin-bottom: var(--space-4);
}

.queue-grid {
    display: grid;
    grid-template-columns: 150px 1fr;
    gap: var(--space-5);
}

.queue-cover-column { text-align: center; }

.queue-cover-column img {
    width: 100%;
    height: auto;
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
    border: 1px solid var(--border);
}

.queue-details-column {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
}

/* ─── Stats Widgets ─── */
.stats-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--space-5);
    margin-bottom: var(--space-6);
}

.stat-widget {
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: var(--space-5);
    display: flex;
    justify-content: space-between;
    align-items: center;
    box-shadow: var(--shadow-sm);
}

.stat-value {
    font-size: 28px;
    font-weight: 700;
    color: var(--accent);
}

.stat-label {
    font-size: 13px;
    color: var(--text-secondary);
}

/* ─── Upload Dropzone ─── */
.upload-dropzone {
    border: 2px dashed var(--accent);
    border-radius: var(--radius-lg);
    background: var(--accent-light);
    padding: var(--space-10);
    text-align: center;
    cursor: pointer;
    transition: background var(--transition-fast);
}

.upload-dropzone:hover {
    background: var(--accent-light);
    filter: brightness(0.96);
}

/* ─── B2 Stats ─── */
.b2-stats-row {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: var(--space-3);
    margin-bottom: var(--space-6);
}

.b2-stat-card {
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: var(--space-4);
    text-align: center;
    box-shadow: var(--shadow-sm);
}

.b2-stat-number {
    font-size: 28px;
    font-weight: 700;
    color: var(--text-primary);
    line-height: 1.2;
}

.b2-stat-label {
    font-family: var(--font-ui);
    font-size: 12px;
    color: var(--text-secondary);
    margin-top: var(--space-1);
}

.b2-stat-new .b2-stat-number    { color: #f59e0b; }
.b2-stat-queued .b2-stat-number { color: #3b82f6; }
.b2-stat-done .b2-stat-number   { color: #22c55e; }
.b2-stat-error .b2-stat-number  { color: #ef4444; }

.b2-controls-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
}

/* ─── Confidence badges ─── */

.confidence-high   { background: var(--accent-light); color: var(--accent); }
.confidence-medium { background: #fef3c7; color: #92400e; }
.confidence-low    { background: #fef2f2; color: #dc2626; }
.confidence-none   { background: var(--bg-page); color: var(--text-muted); }

/* ======================== LOADING SPINNER ======================== */
@keyframes spin {
    to { transform: rotate(-360deg); }
}

/* ======================== RESPONSIVE ======================== */



@media (max-width: 992px) {
    .library-tabs-inner {
        overflow-x: auto;
    }
    .search-grid { flex-wrap: wrap; }
    .filter-dropdown-group { justify-content: flex-start; }
    .split-layout-container {
        grid-template-columns: 1fr;
        height: auto;
        position: relative;
        overflow: visible;
    }
    .split-layout-container.has-resize-handle {
        grid-template-columns: 1fr;
    }
    .split-resize-handle {
        display: none;
    }
    .split-panel-sidebar {
        border-left: none;
        border-bottom: none;
        max-height: none;
        min-width: 0;
        max-width: none;
        height: auto;
        overflow: visible;
    }
    .split-list-scrollable {
        height: auto;
        overflow: visible;
        overscroll-behavior: auto;
    }
    .split-panel-main {
        overflow: visible;
        min-height: 0;
        height: auto;
    }
    .split-panel-main .book-row-list,
    .split-panel-main .books-grid {
        flex: none;
        overflow: visible;
    }

    /* Mobile master→detail layer toggling */
    [data-mobile-layer="master"] .split-panel-sidebar {
        display: flex;
    }
    [data-mobile-layer="master"] .split-panel-main {
        display: none;
    }
    [data-mobile-layer="detail"] .split-panel-sidebar {
        display: none;
    }
    [data-mobile-layer="detail"] .split-panel-main {
        display: flex;
    }
}

@media (max-width: 768px) {
    /* Allow body to scroll on mobile only if the PDF reader is not active */
    body:not(.reader-open) {
        overflow-y: auto !important;
        height: auto !important;
    }
    /* .library-tabs-bar .container's desktop padding-inline-start:240px (the
       sidebar clearance) has equal-or-higher specificity than the bare
       .container reset above for that one property, so `padding-inline-start`
       specifically stayed 240px on mobile even though top/bottom/inline-end
       correctly reset — 240px eats most of a 375px screen and squeezes the
       segmented tab track to one edge. Same selector here (matching
       specificity, later in the cascade) properly resets it. */
    .library-tab-strip-container > .container { padding-inline-start: var(--space-3); padding-inline-end: var(--space-3); }

    /* Mobile master→detail: let container scroll with the page to prevent scroll locking */
    .split-layout-container {
        height: auto;
        overflow: visible;
    }

    /* Show breadcrumb on mobile */
    .mobile-breadcrumb {
        display: flex;
    }

    /* --- Library filter bar: compact grid on phones ---
       Main "All Books" bar (.search-filter-panel): row 1 = search (full width);
       row 2 = Hijri Century, Language, Sort and the View-options button — all
       four in one row. Category + Author are hidden on mobile to keep the bar to
       two tidy rows. The Author/Category browse panels keep the generic 3-per-row
       grid below. */
    .search-grid {
        grid-template-columns: repeat(3, 1fr);
        column-gap: var(--space-2);
    }
    /* All Books bar: the three filters share the row; the ⚙ view-options button
       gets its own SNUG, icon-sized column (auto) instead of eating a full
       quarter of the row. */
    .search-filter-panel .search-grid {
        grid-template-columns: 1fr 1fr 1fr auto;
    }
    .search-filter-panel .filter-dropdown-group:has(#filter-category),
    .search-filter-panel .filter-dropdown-group:has(#filter-author) {
        display: none;
    }
    .search-grid > .form-group {
        grid-column: 1 / -1;      /* search spans the whole first row */
        margin-inline-start: 0;
    }
    .search-grid > .list-view-controls {
        margin-inline-end: 0;
        justify-content: center;
    }
    /* Filters fill their cells (min-width:0 lets selects shrink+ellipsis). */
    .search-grid .filter-dropdown-group,
    .search-grid .form-select { min-width: 0; width: 100%; }
    /* View-options button stays a compact icon square — not stretched to the
       cell width (its column is auto-sized to it). */
    .search-filter-panel .view-options-btn { width: 44px; min-width: 44px; height: 44px; padding: 0; }

    /* Urdu (Nastaliq) forces 21px / line-height 2.1 on all chrome controls for
       elderly legibility (the [lang="ur"] block near the top). On a phone that
       makes the navbar, its dropdown menu, the filter selects and the search box
       oversized and cramped, so scale them back to phone size here — still
       legible, just fitting the narrow screen. !important is required to beat
       those global rules. */
    [lang="ur"] .form-select-compact,
[lang="ur"] #search-input {
        font-size: 14px !important;
        line-height: 1.6 !important;
    }
    [lang="ur"] .sidebar-tab-item,
    [lang="ur"] .tab-text,
    [lang="ur"] .nav-dropdown-panel .sidebar-tab-item {
        font-size: 14px !important;
        line-height: 1.7 !important;
    }

    /* ─── Bar-view book row → app-style card on phones ───
       The desktop table lays these cells out as flat columns; on mobile we
       re-place the SAME flat cells with CSS Grid (no markup change, so the
       desktop table is untouched):
         inline-start  cover  (spans the full card height, on the reading side)
         middle        title on top, then author, then category + language
         inline-end    action icons as a vertical strip on the far side
       "Reading side" = left in English (ltr), right in Arabic/Urdu (rtl) — see
       the direction rules below. align-items:start pins the title to the
       cover's top edge (no dead space above it). */
    .book-row {
        display: grid;
        grid-template-columns: auto minmax(0, 1fr) auto;
        /* Explicit rows are REQUIRED: `grid-row: 1 / -1` (cover + action strip)
           only spans the whole card when the grid's last row line exists as an
           explicit line. Without grid-template-rows, `-1` resolves back to line
           1 and those two items collapse instead of spanning. */
        grid-template-rows: auto auto auto;
        column-gap: var(--space-3);
        row-gap: 3px;
        align-items: start;
        padding: var(--space-3);
        width: 100%;
    }
    /* Cover follows the reading direction: in English (ltr) it sits on the LEFT
       (where reading starts) with the action strip on the right; in Arabic/Urdu
       (rtl) it mirrors — cover on the RIGHT, actions on the left. The columns are
       assigned logically (cover = column 1 = inline-start, actions = column 3 =
       inline-end); the row's direction decides which physical side that is.
       .book-row-list is force-rtl globally, so we set the row's direction from
       the real UI direction (<html dir>) instead of inheriting the list's. */
    [dir="ltr"] .book-row { direction: ltr; }
    [dir="rtl"] .book-row { direction: rtl; }
    .book-row-thumb-icon {
        grid-column: 1;            /* inline-start → cover on the reading side */
        grid-row: 1 / -1;          /* cover down the whole card height */
        width: 64px;
        align-self: stretch;       /* Large cover fills the full card height */
    }
    /* Large cover: fill the whole cell height (no empty space below it). */
    .book-row-thumb-img { width: 64px; height: 100%; object-fit: cover; }
    /* Compact cover: as tall as the 2×2 icon block on the other side. The icon
       grid (two 40px rows) sets the row height; the cover fills that height at a
       fixed, book-proportioned width — no wasted space above or below it. The
       title is capped to two lines so the text can't push the row taller than
       that icon block. A FIXED width (not auto) is essential: auto lets the
       image claim its intrinsic size and blow the whole row up. */
    .vo-managed.compact-covers .book-row-thumb-icon { align-self: stretch; width: 56px; }
    .vo-managed.compact-covers .book-row-thumb-img {
        width: 56px;
        height: 100%;
        object-fit: cover;
    }
    .vo-managed.compact-covers .book-row-title { -webkit-line-clamp: 2; }
    /* Compact rows are as short as their content — no top/bottom padding, so the
       row is exactly the height of the cover / 2×2 icon block (rows stay
       separated by their 1px border). Horizontal padding is kept. */
    .vo-managed.compact-covers .book-row { padding-top: 0; padding-bottom: 0; }

    .book-row-title {
        grid-column: 2;
        grid-row: 1;
        min-width: 0;
        white-space: normal;        /* wrap instead of single-line ellipsis */
        text-align: start;
        font-size: 0.95rem;
        font-weight: 700;
        line-height: 1.35;
        display: -webkit-box;
        -webkit-line-clamp: 3;
        -webkit-box-orient: vertical;
        overflow: hidden;
    }
    /* On mobile the list is a plain block stack of full-width cards. The desktop
       list is a flex COLUMN with overflow-x:auto for its wide table; inside a
       direction:rtl flex column, our direction:ltr grid rows were mis-positioned
       (shifted sideways and overflowing). display:block makes each row a normal
       full-width block that lays out cleanly in either page direction. */
    .book-row-list { display: block; overflow-x: hidden; }
    /* Meta cells wrap (not the desktop nowrap+ellipsis) and carry min-width:0 so
       a long author/category can never force the grid wider than the viewport.
       text-align:start keeps them reading from the cover side (left). */
    /* Author — muted, lighter weight so it reads as a subtitle under the bold
       title (clear hierarchy; it doesn't blend with the title or the category). */
    .book-row-author {
        grid-column: 2;
        grid-row: 2;
        width: auto;
        min-width: 0;
        white-space: normal;
        overflow: hidden;
        text-align: start;
        font-size: 12px;
        font-weight: 500;
        color: var(--text-secondary);
    }
    /* Category — a rounded chip with a soft tint + shadow, mirroring the grid
       card's badge so list and grid feel consistent and the category stands
       apart from the plain title/author text. */
    .book-row-category {
        grid-column: 2;
        grid-row: 3;
        justify-self: start;
        align-self: center;
        width: fit-content;
        max-width: calc(100% - 52px);   /* leave room for the language chip */
        min-width: 0;
        display: inline-block;
        padding: 2px 10px;
        border-radius: var(--radius-full);
        background: var(--accent-light);
        color: var(--accent);
        font-size: 11px;
        font-weight: 600;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        box-shadow: var(--shadow-sm);
    }
    /* Language — a small outlined chip so it's visually distinct too. */
    .book-row-language {
        grid-column: 2;
        grid-row: 3;
        justify-self: end;
        align-self: center;
        width: fit-content;
        min-width: 0;
        display: inline-block;
        padding: 2px 8px;
        border-radius: var(--radius-full);
        border: 1px solid var(--border);
        background: var(--bg-page);
        color: var(--text-muted);
        font-size: 10px;
        font-weight: 700;
        white-space: nowrap;
    }
    /* Urdu (Nastaliq) forces big fonts + very tall line-heights site-wide for
       elderly legibility (see the [lang="ur"] rules near the top of this file).
       On a phone that blows the compact card up and overrides the category
       chip's colour, so scale the row text back to phone size here — still
       clearly legible, just not desktop-sized — and let the chip keep its
       accent colour. !important is required to beat those global rules. */
    [lang="ur"] .book-row-title {
        font-size: 16px !important;
        line-height: 1.75 !important;
    }
    /* Weights held at 400 here too — the Urdu weight policy near the top of
       this file applies at every breakpoint. 600 on this chip was synthesising
       bold from the single regular Nastaliq face, and faux bold is at its
       worst at exactly this size (12px), where the smeared hairlines have the
       least room to survive. */
    [lang="ur"] .book-row-author {
        font-size: 13px !important;
        line-height: 1.7 !important;
        font-weight: 400 !important;
        color: var(--text-secondary) !important;
    }
    [lang="ur"] .book-row-category {
        font-size: 12px !important;
        line-height: 1.65 !important;
        font-weight: 400 !important;
        color: var(--accent) !important;
        padding: 3px 10px !important;
    }
    [lang="ur"] .book-row-language {
        font-size: 11px !important;
        line-height: 1.4 !important;
    }

    /* Death year + volume count are secondary — keep the narrow card clean
       (both still show in the desktop table). */
    .book-row-death,
    .book-row-volumes { display: none; }

    /* Action icons: a compact 2×2 grid on the far side in EVERY cover mode
       (inline-end). Keeps every card short so the cover — not a tall icon
       strip — drives the card height. */
    .book-row-actions {
        grid-column: 3;              /* inline-end → opposite side from the cover */
        grid-row: 1 / -1;
        width: auto;
        display: grid;
        grid-template-columns: repeat(2, auto);
        grid-auto-rows: auto;
        justify-items: center;
        align-content: start;
        align-self: start;
        gap: 2px;
    }
    /* Comfortable tap size for the icons without making the row huge. */
    .book-row-actions .btn-icon { width: 40px; height: 40px; min-width: 40px; min-height: 40px; }

    /* ─── Mobile: Read stays inline, everything else moves into "…" ───
       Read is the whole point of a book row, so it keeps a visible slot as the
       primary action; hiding it too (as this block used to) meant opening a
       menu before you could open a book. Download and Save-Offline move into
       the "…" popover/sheet as clones added by book-row.js. View-Options
       hide-<col> still wins (higher specificity), so a menu item only appears
       if that action is enabled — default-on ones show; a default-off one
       shows only if the user turns it on. */
    .book-row-actions > .book-dl-btn,
    .book-row-actions > .book-opfs-btn,
    .book-row-actions > .book-readlater-btn { display: none; }
    /* Read is inline, so its clone inside the sheet would just be a duplicate. */
    .book-more-popover .book-read-btn.book-more-primary { display: none !important; }
    /* Mark it as the primary action rather than one more grey icon. */
    .book-row-actions > .book-read-btn {
        color: var(--accent);
        border-color: var(--accent);
    }
    .book-more-popover .book-more-primary { display: inline-flex; }
    /* Re-show the "…" trigger — the condensed mobile card only has room for
       Read inline; everything else lives in its sheet. */
    .book-more-wrap { display: inline-flex; }
}

/* ═══════════════════════════════════════════════════════════════════
   Mobile: open/close menus become bottom sheets
   The View-Options (⚙ filter bar), the per-book "…" more-menu, and the
   Public Library nav dropdown open as tiny floating popovers on desktop. On
   phones they slide up from the bottom as full-width sheets — easy to reach
   with a thumb and never clipped by a narrow screen. The existing open/close
   JS is untouched (it just toggles the same .hidden/.visible/.open classes);
   only the popover's position and size are restyled here.
   ═══════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {
    .view-options-popover:not(.hidden),
    .book-more-popover.visible,
    .nav-link-dropdown.open .nav-dropdown-panel,
    .nav-link-dropdown.open .user-dropdown-panel {
        position: fixed !important;
        left: 0 !important;
        right: 0 !important;
        top: auto !important;
        bottom: 0 !important;
        width: 100% !important;
        min-width: 0 !important;
        max-width: none !important;
        max-height: 80vh !important;
        overflow-y: auto !important;
        margin: 0 !important;
        border: none !important;
        border-top: 1px solid var(--border) !important;
        border-radius: 18px 18px 0 0 !important;
        box-shadow: 0 -6px 28px rgba(0, 0, 0, 0.28) !important;
        padding: var(--space-4) var(--space-3) calc(var(--space-4) + env(safe-area-inset-bottom, 0px)) var(--space-3) !important;
        z-index: var(--z-modal) !important;
        animation: mobileSheetUp 0.25s ease !important;
    }
    @keyframes mobileSheetUp { from { transform: translateY(100%); } to { transform: translateY(0); } }

    .user-dropdown-mobile-header {
        display: flex;
        align-items: center;
        padding: 8px 12px 14px 12px;
        margin-bottom: 8px;
        border-bottom: 1px solid var(--border);
        cursor: pointer;
    }

    .user-dropdown-mobile-header .mobile-header-back-icon {
        width: 20px;
        height: 20px;
        margin-inline-end: 10px;
        color: var(--text-secondary);
    }

    [dir="ltr"] .user-dropdown-mobile-header .mobile-header-back-icon {
        transform: scaleX(-1);
    }

    .user-dropdown-mobile-header .mobile-header-user-name {
        font-weight: 700;
        font-size: 15px;
        color: var(--text-primary);
    }

    /* The scrim is appended to <body> (auth.js), but the panel it sits behind
       lives inside .navbar, which is position:sticky + z-index:--z-navbar and
       so opens its own stacking context — the panel can never paint above a
       root-level sibling that outranks the navbar, whatever z-index the panel
       itself carries. So the scrim has to stay *below* the navbar rung, or it
       covers the very sheet it is dimming for. */
    .user-dropdown-backdrop.active {
        display: block;
        position: fixed;
        inset: 0;
        background: rgba(0, 0, 0, 0.45);
        backdrop-filter: blur(2px);
        z-index: calc(var(--z-navbar) - 1);
    }

    /* Grab-handle pill at the top of every sheet. */
    .view-options-popover:not(.hidden)::before,
    .book-more-popover.visible::before,
    .nav-link-dropdown.open .nav-dropdown-panel::before {
        content: "";
        position: absolute;
        top: 8px;
        left: 50%;
        transform: translateX(-50%);
        width: 40px;
        height: 4px;
        border-radius: 2px;
        background: var(--border-strong, var(--text-muted));
    }

    /* The "…" menu is positioned by JS with inline fixed top/left — the sheet
       layout has to override that. Its actions become a roomy centred row. */
    .book-more-popover.visible {
        top: auto !important;
        left: 0 !important;
        right: 0 !important;
        bottom: 0 !important;
        flex-wrap: wrap;
        justify-content: center;
        align-items: flex-start;
        gap: 14px 24px;
        padding-bottom: calc(var(--space-8) + env(safe-area-inset-bottom, 0px));
    }
    /* Every action in the sheet is a UNIFORM square icon box (same size for all),
       with its short name BELOW the box — the label is absolutely positioned
       outside the button's border (not inside the box), pulled from data-volabel
       which is already localized in all three languages. */
    .book-more-popover.visible .btn-icon {
        position: relative;
        flex-direction: row;
        width: 52px;
        min-width: 52px;
        max-width: 52px;
        height: 52px;
        padding: 0;
        margin-bottom: 24px;      /* room for the name under the box */
        overflow: visible;
    }
    .book-more-popover.visible .btn-icon > i,
    .book-more-popover.visible .btn-icon > svg {
        width: 22px;
        height: 22px;
    }
    .book-more-popover.visible .btn-icon::after {
        content: attr(data-volabel);
        position: absolute;
        top: calc(100% + 5px);
        left: 50%;
        transform: translateX(-50%);
        width: 74px;
        text-align: center;
        font-family: var(--font-ui);
        font-size: 10.5px;
        font-weight: 600;
        line-height: 1.2;
        color: var(--text-secondary);
        white-space: normal;
    }
    /* Nastaliq needs a touch more size + height, and a wider label box. */
    [lang="ur"] .book-more-popover.visible .btn-icon { margin-bottom: 32px; }
    [lang="ur"] .book-more-popover.visible .btn-icon::after {
        font-size: 12px;
        line-height: 1.5;
        width: 86px;
    }

    /* Roomier tap targets inside the View-Options sheet. */
    .view-options-popover .view-options-check { padding: 10px 4px; }
    .view-options-popover .view-options-group { margin-bottom: var(--space-4); }
    .view-options-popover .display-mode-btn,
    .view-options-popover .view-options-size-btn { min-height: 44px; }

    /* Centered dialogs (auth, volume picker, article preview, …): never let a
       tall modal overflow the screen with its buttons off-screen — cap the
       height and let the card scroll instead. (Widths stay as each modal sets
       them inline, which are already responsive: width:90%/max-width.) */
    .modal-card {
        max-height: 90vh;
        overflow-y: auto;
    }

    /* While the View-Options sheet is open, hide the top navbar and the bottom
       nav so the sheet reads as a focused overlay and neither bar peeks through
       or clips it. body.vo-sheet-open is toggled by the sheet controller script
       in index.html (also handles Back = close, not navigate). */
    body.vo-sheet-open .navbar,
    body.vo-sheet-open .mobile-bottom-nav {
        display: none;
    }
    /* Dim the page behind the open sheet. pointer-events:none keeps the existing
       tap-outside-to-close behaviour working through the dim. */
    body.vo-sheet-open::after {
        content: "";
        position: fixed;
        inset: 0;
        background: rgba(0, 0, 0, 0.35);
        z-index: var(--z-sheet-scrim);
        pointer-events: none;
    }
}

@media (max-width: 480px) {

    /* Keep the 3-per-row filter grid from the ≤768px block (search full-width
       on row 1, filters 3-up below) — the old single-column stack made the
       bar far too tall on small phones. */
    .search-grid { grid-template-columns: repeat(3, 1fr); }
    .search-filter-panel { padding: var(--space-3); }
    .search-grid .form-select-compact { font-size: 13px; padding-inline: 8px; }

    /* ─── Excel table → two-line row on mobile ─── */
    .book-table-header {
        gap: var(--space-2);
        padding: var(--space-2) var(--space-2);
        font-size: 10px;
    }
    .book-col-author,
    .book-col-death,
    .book-col-volumes,
    .book-col-language { display: none; }
    .book-col-category { width: 80px; }

    /* The bar-view card layout (cover | text | vertical icon strip) is defined
       once in the ≤768px block via CSS Grid. On the smallest phones we only
       trim the cover a touch — no flex/order rules here so they can't fight
       the grid, and no more mojibake "·" separators (author/category now sit
       on their own lines, so the separators aren't needed). */
    .book-row-thumb-icon,
    .book-row-thumb-img { width: 56px; }

    /* Hide header on small screens — show only the data rows */
    .book-table-header { display: none; }

    /* ─── OPFS local books (still uses .book-bar) ─── */
    .book-bar-main {
        display: flex;
        flex-direction: row;
        align-items: flex-start;
        gap: var(--space-4);
        flex-wrap: wrap;
    }
    .book-bar-thumb {
        width: 80px;
        height: 115px;
    }
    .book-bar-thumb-placeholder {
        width: 80px;
        height: 115px;
        font-size: 24px;
    }
    .book-bar-actions {
        width: 100%;
        justify-content: flex-end;
        margin-top: var(--space-2);
        border-top: 1px solid var(--border-light);
        padding-top: var(--space-2);
        display: flex;
        gap: var(--space-2);
    }
    .book-bar-meta { font-size: 12px; }
    .book-card-info { grid-template-columns: 1fr; gap: var(--space-2); }
    .book-card-actions { width: 100%; justify-content: flex-end; }


    .split-panel-main { padding: var(--space-3); }
    .panel-header-desc { flex-direction: column; gap: var(--space-2); align-items: flex-start; }

    .lib-tab-view.active,
    #lib-view-all-books.active,
    #lib-view-local-books.active { height: auto; overflow: visible; }
    #lib-view-all-books .book-row-list,
    #lib-view-all-books .books-grid,
    #lib-view-local-books .book-row-list,
    #lib-view-local-books .books-grid,
    .split-panel-main .book-row-list,
    .split-panel-main .books-grid { flex: none; overflow: visible; }

    .queue-grid { grid-template-columns: 1fr; }
    .queue-cover-column img { max-width: 120px; margin: 0 auto; }
    .b2-stats-row { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 480px) {
    .book-card-cover-wrapper { flex-basis: 40px; }
    .book-card-cover { width: 40px; height: 60px; }
    /* .book-row's action icons are a vertical strip on mobile (sized 40px by
       the .book-row-actions rule in the ≤768px block — more specific, so it
       wins). Only .book-bar (OPFS local books) keeps the 44px min here. */
    .book-bar .btn-icon { min-height: 44px; min-width: 44px; }
    .upload-dropzone { padding: var(--space-5); }
}

/* Grid responsive */
@media (max-width: 768px) {
    .books-grid {
        grid-template-columns: repeat(2, 1fr);
        gap: var(--space-3);
    }
    .book-card-body {
        padding: var(--space-2);
    }
    .book-card-title {
        font-size: 0.85rem;
    }
    .book-card-actions {
        padding: var(--space-1) var(--space-2);
        gap: var(--space-1);
    }
    .book-card-actions .btn-icon {
        width: 32px;
        height: 32px;
        /* min-width/min-height clamp the used size up regardless of the
           explicit 32px above (CSS spec: min-* always wins over a smaller
           width/height) — verified live at 320-768px: below 480px a separate
           .btn-icon min-size rule already pushed this to 44px, but 481-768px
           (larger phones/phablets) had nothing doing that and rendered the
           bare 32px, i.e. touch targets got SMALLER on bigger screens. This
           makes the whole ≤768px range consistently 44px instead of just
           the ≤480px slice. */
        min-width: 44px;
        min-height: 44px;
    }
}

@media (max-width: 480px) {
    .books-grid {
        grid-template-columns: repeat(2, 1fr);
        gap: var(--space-2);
    }
}

/* ═══════════════════════════════════════════════════════════
   LTR Overrides (English mode)
   ═══════════════════════════════════════════════════════════ */

[dir="ltr"] .sidebar {
  border-right: none;
  border-left: 1px solid var(--border);
  left: 0;
  right: auto;
}

/* Book listings (rows, cards, header) always read as Arabic/Urdu content —
   cover/title/metadata position must stay put regardless of which language
   the UI chrome is in, since an English interface doesn't mean the book data
   is in English. Forcing RTL here (rather than only patching individual
   text-align rules) keeps flex/grid child order, text alignment, and
   justify-content all consistent in one place instead of fighting the
   [dir="ltr"] cascade rule-by-rule as new row/card elements get added. */
.book-row-list,
.books-grid,
.book-table-header {
  direction: rtl;
}

[dir="ltr"] .book-bar-actions {
  margin-right: auto;
  margin-left: 0;
}

[dir="ltr"] .book-bar-info {
  text-align: left;
}

[dir="ltr"] .library-tabs-inner {
  flex-direction: row;
}

[dir="ltr"] .split-layout-container {
  flex-direction: row;
}

[dir="ltr"] .split-panel-sidebar {
  border-right: none;
  border-left: 1px solid var(--border);
}

/* ─── Language Switcher ─── */
.lang-switcher {
  display: flex;
  gap: var(--space-1);
  align-items: center;
}

.lang-btn {
  /* min-height/width, not extra padding: these sit in a tight navbar row, and
     padding alone pushed them to only 30-33 x 31px — under the ~44px touch
     target guideline, and the three of them sit adjacent, so a near-miss hits
     the wrong language rather than nothing. inline-flex centring keeps the
     label optically centred now that the box is taller than the text. */
  min-height: 36px;
  min-width: 36px;
  padding: 4px 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--text-muted);
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: all var(--transition-fast);
}

/* Phones have no hover to disambiguate, so give the row full 44px targets. */
@media (max-width: 768px) {
  .lang-btn {
    min-height: 44px;
    min-width: 44px;
  }
}

.lang-btn:hover {
  background: var(--bg-page);
  color: var(--text-color);
}

.lang-btn.active {
  background: var(--accent-light);
  color: var(--accent);
  border-color: var(--accent);
  font-weight: 600;
}

/* Navbar lang-switcher: a single segmented pill (one shared border) instead
   of three separate pill buttons, matching the reviewed redesign's
   lang-pill treatment. */
.navbar .lang-switcher {
  gap: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-full);
  overflow: hidden;
}
.navbar .lang-switcher .lang-btn {
  color: var(--text-secondary);
  border: none;
  border-radius: 0;
}
.navbar .lang-switcher .lang-btn:hover {
  color: var(--text-color);
  background: var(--bg-page);
}
.navbar .lang-switcher .lang-btn.active {
  background: var(--accent);
  color: #fff;
  font-weight: 600;
}

/* LTR lang switcher in library tabs bar — natural flex order handles positioning */

/* English / LTR UI Mode: Align all book metadata to the LEFT & Order Title on Left */
[lang="en"] .book-row,
html[lang="en"] .book-row,
html[dir="ltr"] .book-row {
  direction: ltr !important;
  flex-direction: row !important;
}

[lang="en"] .book-row-thumb-icon,
html[dir="ltr"] .book-row-thumb-icon {
  order: 1 !important;
}

[lang="en"] .book-row-title,
html[dir="ltr"] .book-row-title {
  order: 2 !important;
  text-align: start !important;
}

[lang="en"] .book-row-author,
html[dir="ltr"] .book-row-author {
  order: 3 !important;
  text-align: start !important;
}

[lang="en"] .book-row-death,
html[dir="ltr"] .book-row-death {
  order: 4 !important;
  text-align: start !important;
}

[lang="en"] .book-row-category,
html[dir="ltr"] .book-row-category {
  order: 5 !important;
  text-align: start !important;
}

[lang="en"] .book-row-volumes,
html[dir="ltr"] .book-row-volumes {
  order: 6 !important;
  text-align: start !important;
}

[lang="en"] .book-row-language,
html[dir="ltr"] .book-row-language {
  order: 7 !important;
  text-align: start !important;
}

[lang="en"] .book-row-actions,
html[dir="ltr"] .book-row-actions {
  order: 8 !important;
  margin-left: auto !important;
  margin-right: 0 !important;
}

[lang="en"] .book-bar-title,
[lang="en"] .book-bar-author,
[lang="en"] .book-bar-category,
[lang="en"] .book-card-title,
[lang="en"] .book-card-author-row,
[lang="en"] .book-card-category,
[lang="en"] .split-list-item {
  text-align: start !important;
}

/* ======================== HOMEPAGE STYLING ======================== */

.homepage-container {
    padding-bottom: var(--space-12);
}

.hero-section {
    position: relative;
    text-align: center;
    padding: var(--space-12) 0 var(--space-4) 0;
    max-width: 800px;
    margin: 0 auto;
}

.hero-section::before {
    content: '';
    position: absolute;
    top: -10%;
    left: 50%;
    transform: translateX(-50%);
    width: min(700px, 130%);
    height: 100%;
    background: radial-gradient(ellipse 50% 55% at center, var(--accent-gold-light), transparent 72%);
    pointer-events: none;
    z-index: var(--z-hero-bg);
}

.hero-section > * {
    position: relative;
    z-index: var(--z-content);
}

.hero-title {
    font-family: 'Tajawal', var(--font-arabic), sans-serif;
    font-size: clamp(2.25rem, 8vw, 4.5rem);
    font-weight: 700;
    color: var(--accent);
    margin-bottom: var(--space-4);
    line-height: 1.2;
}

.homepage-section {
    padding: var(--space-12) 0;
    border-bottom: 1px solid var(--border);
}

.homepage-section:last-of-type {
    border-bottom: none;
}

/* Tagline sits right under the hero — drop its top padding so the two
   lines aren't separated by hero-bottom + section-top padding stacked. */
.hero-section + .homepage-section {
    padding-top: 0;
}

.section-header {
    text-align: center;
    margin-bottom: var(--space-8);
}

/* ── Support Us section ──────────────────────────────────────────────────
   One card system: a single radius (--radius-lg), a single 1px border, a
   single surface. Emphasis is expressed only through the modifier classes
   (--muted for a finished phase, --accent for the live ask) — never through
   ad-hoc per-element borders, shadows or font sizes. */
.support-stack { display: flex; flex-direction: column; gap: var(--space-4); }

.support-card {
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--space-6);
}
.support-card--muted  { background: var(--bg-elevated); }
.support-card--accent { border-color: var(--accent); background: var(--accent-light); }
.support-card--plain  { white-space: pre-line; line-height: 1.8; color: var(--text-secondary); }

.support-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-bottom: var(--space-3);
    padding: 4px 10px;
    border-radius: var(--radius-full);
    background: var(--accent-light);
    color: var(--accent);
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.6px;
    text-transform: uppercase;
}
.support-badge--muted { background: var(--border); color: var(--text-secondary); }

.support-card__title {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin: 0 0 var(--space-2) 0;
    font-size: 1.125rem;
    font-weight: 700;
    line-height: 1.4;
    color: var(--text-primary);
}
.support-card__title :is(i, svg) { width: 20px; height: 20px; margin-top: 2px; color: var(--accent); flex-shrink: 0; }

.support-card__text {
    margin: 0;
    color: var(--text-secondary);
    font-size: 0.9375rem;
    line-height: 1.7;
}

.support-ways { display: flex; flex-direction: column; gap: var(--space-3); margin-top: var(--space-2); }
.support-ways__title {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 var(--space-1) 0;
    font-size: 1.0625rem;
    font-weight: 700;
    color: var(--text-primary);
}
.support-ways__title :is(i, svg) { width: 18px; height: 18px; color: var(--accent); }

.support-way {
    display: flex;
    align-items: center;
    gap: var(--space-5);
    flex-wrap: wrap;
    padding: var(--space-5);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}
.support-way__head {
    flex: 0 0 200px;
    display: flex;
    align-items: center;
    gap: 8px;
    font-weight: 700;
    font-size: 0.9375rem;
    color: var(--text-primary);
}
.support-way__head :is(i, svg) { width: 18px; height: 18px; color: var(--accent); flex-shrink: 0; }

.support-way__desc {
    flex: 1 1 200px;
    margin: 0;
    color: var(--text-secondary);
    font-size: 0.875rem;
    line-height: 1.6;
}

.support-way__action { flex: 0 0 340px; max-width: 100%; display: flex; flex-direction: column; gap: var(--space-2); }
.support-way__action > label { font-size: 0.8125rem; font-weight: 600; color: var(--text-primary); }
.support-way__field { display: flex; gap: var(--space-2); }
.support-way__field .form-input { flex: 1; min-width: 0; }
.support-way__field .btn { flex-shrink: 0; }

.support-way__donate {
    width: 100%;
    border-color: var(--accent);
    color: var(--accent);
    background: transparent;
    font-weight: 600;
}
.support-way__donate:hover { background: var(--accent-light); border-color: var(--accent); }
.support-way__donate :is(i, svg) { fill: currentColor; }

@media (max-width: 640px) {
    .support-way { gap: var(--space-3); }
    .support-way__head,
    .support-way__action { flex-basis: 100%; }
}

.section-title {
    font-family: var(--font-arabic);
    font-size: clamp(1.5rem, 4vw, 2rem);
    font-weight: 700;
    color: var(--text-primary);
    position: relative;
    display: inline-block;
    padding-bottom: 24px;
    margin-bottom: var(--space-4);
}

.section-title::after {
    content: '۞';
    font-family: var(--font-arabic);
    font-size: 14px;
    color: var(--accent-gold);
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 12px;
}

.section-title::before {
    content: '';
    position: absolute;
    bottom: 5px;
    left: 50%;
    transform: translateX(-50%);
    width: 120px;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--accent-gold), transparent);
}

.section-subtitle {
    font-size: 14px;
    color: var(--text-secondary);
}

.grid-3 {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
    gap: var(--space-6);
}

.grid-2 {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
    gap: var(--space-8);
}

.info-card {
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--space-6);
    box-shadow: var(--shadow-sm);
    transition: all var(--transition-base);
}

.info-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 20px rgba(180, 140, 80, 0.15);
    border-color: var(--accent-gold);
}

.info-card-icon {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background: var(--accent-light);
    border: 1px solid var(--accent-light);
    color: var(--accent);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: var(--space-4);
}

.info-card-title {
    font-size: 1.15rem;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: var(--space-2);
}

.info-card-text {
    font-size: 14px;
    color: var(--text-secondary);
    line-height: 1.6;
}

/* Homepage "Everything You Need" grid has 5 cards — a plain 3-column grid
   leaves row 2 (2 cards) flush left with an empty gap on the right. Flexbox
   + justify-content:center centers any incomplete trailing row instead. */
.features-grid {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
}

.features-grid > .info-card {
    flex: 0 0 100%;
}

@media (min-width: 769px) and (max-width: 1024px) {
    .features-grid > .info-card {
        flex: 0 0 calc(50% - var(--space-6) / 2);
    }
}

@media (min-width: 1025px) {
    .features-grid > .info-card {
        flex: 0 0 calc(33.333% - var(--space-6) * 2 / 3);
    }
}

/* Explore Link Cards */
.explore-card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: var(--space-4) var(--space-6);
    text-decoration: none;
    color: var(--text-primary);
    font-weight: 600;
    font-size: 15px;
    transition: all var(--transition-base);
    cursor: pointer;
}

.explore-card:hover {
    background: var(--bg-elevated);
    border-color: var(--accent-gold);
    color: var(--accent);
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(180, 140, 80, 0.1);
}

.explore-card-left {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

/* Stats Counter Grid */
.stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: var(--space-6);
    text-align: center;
}

.stats-card {
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--space-6);
    box-shadow: var(--shadow-sm);
    transition: all var(--transition-base);
}

.stats-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 16px rgba(180, 140, 80, 0.12);
    border-color: var(--accent-gold);
}

.stats-number {
    font-size: clamp(1.75rem, 5vw, 2.5rem);
    font-weight: 700;
    color: var(--accent);
    margin-bottom: var(--space-2);
}

.stats-number.is-loading {
    animation: statsPulse 1.6s ease-in-out infinite;
}

@keyframes statsPulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.35; }
}

.stats-label {
    font-size: 14px;
    color: var(--text-secondary);
    font-weight: 500;
}

/* ─── Stats band: full-bleed accent-colored strip with gold numbers on
   white labels and vertical dividers between the three figures, matching
   the reviewed redesign — replaces the plain bordered-card treatment. ─── */
.homepage-section.stats-band {
    background: var(--accent);
    border-bottom: none;
    padding: var(--space-6) 0;
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
    width: 100vw;
}

.stats-band .section-title,
.stats-band .section-title::after {
    color: var(--btn-primary-text);
}
.stats-band .section-title::before {
    background: linear-gradient(90deg, transparent, var(--accent-gold), transparent);
}
.stats-band .section-header p {
    color: rgba(255, 255, 255, 0.75);
}

.stats-band .stats-grid {
    grid-template-columns: repeat(3, 1fr);
    gap: 0;
}

.stats-band .stats-card {
    background: transparent;
    border: none;
    border-inline-start: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 0;
    box-shadow: none;
    transform: none !important;
    padding: var(--space-3) var(--space-4);
}
.stats-band .stats-card:first-child {
    border-inline-start: none;
}
.stats-band .stats-card:hover {
    transform: none;
    box-shadow: none;
    border-color: rgba(255, 255, 255, 0.15);
}
.stats-band .stats-card:hover:first-child {
    border-inline-start: none;
}

.stats-band .stats-number {
    color: var(--accent-gold);
    font-size: clamp(2rem, 5vw, 2.75rem);
}

.stats-band .stats-label {
    color: rgba(255, 255, 255, 0.85);
}

/* Dark mode: --accent flips to brass gold, which would turn the whole strip
   gold and hide the (also gold) figures. Anchor the strip to a dark ground so
   the gold numbers and light labels keep their contrast. */
[data-theme="dark"] .homepage-section.stats-band {
    background: var(--bg-elevated);
    border-block: 1px solid var(--border);
}

/* Preservation Banner */
.preservation-banner {
    background: var(--accent-light);
    border: 1px solid var(--accent);
    border-radius: var(--radius-lg);
    padding: var(--space-8);
    text-align: center;
    max-width: 900px;
    margin: 0 auto;
}

.preservation-title {
    font-family: var(--font-arabic);
    font-size: clamp(1.35rem, 3.5vw, 1.75rem);
    font-weight: 700;
    color: var(--accent);
    margin-bottom: var(--space-3);
}

.preservation-text {
    font-size: 15px;
    color: var(--text-primary);
    line-height: 1.8;
    max-width: 750px;
    margin: 0 auto;
}

/* ======================== ADVANCED INTERACTIVE MULTI-LINGUAL FOOTER UI ======================== */

.homepage-footer {
    background: linear-gradient(180deg, transparent 0%, var(--bg-surface) 100%);
    border-top: 1px solid var(--border);
    padding: var(--space-12) var(--space-4) var(--space-8) var(--space-4);
    margin-top: var(--space-16);
    color: var(--text-secondary);
    position: relative;
    overflow: hidden;
}

/* Multi-lingual Footer Typography overrides */
[lang="ur"] .homepage-footer {
    font-family: var(--font-arabic);
    font-size: 16px;
    line-height: 2.2;
}

[lang="ur"] .footer-col-title {
    font-family: var(--font-urdu);
    font-size: 1.4rem;
    line-height: 2.0 !important;
    color: var(--text-primary);
}

[lang="ar"] .homepage-footer {
    font-family: 'Tajawal', sans-serif;
    font-size: 15px;
    line-height: 1.85;
}

[lang="ar"] .footer-col-title {
    font-family: 'Tajawal', sans-serif;
    font-size: 1.35rem;
    font-weight: 700;
    color: var(--text-primary);
}

[lang="en"] .homepage-footer {
    font-family: 'Source Serif 4', 'Georgia', serif;
    font-size: 14.5px;
    line-height: 1.7;
}

[lang="en"] .footer-col-title {
    font-family: 'Source Serif 4', 'Georgia', serif;
    font-size: 1.2rem;
    font-weight: 700;
    color: var(--text-primary);
    letter-spacing: -0.01em;
}

.footer-col-title {
    margin-bottom: var(--space-4);
    position: relative;
    display: inline-block;
}

/* Footer Interactive Links & Micro-animations */
.footer-links {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.footer-links a {
    color: var(--text-secondary);
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    transition: all var(--transition-base);
    cursor: pointer !important;
}

.footer-link-icon {
    width: 16px;
    height: 16px;
    opacity: 0.65;
    transition: all var(--transition-base);
}

/* LTR vs RTL hover translation feature */
[dir="rtl"] .footer-links a:hover {
    color: var(--accent);
    transform: translateX(-6px);
}

[dir="ltr"] .footer-links a:hover {
    color: var(--accent);
    transform: translateX(6px);
}

.footer-links a:hover .footer-link-icon {
    opacity: 1;
    color: var(--accent-gold);
    transform: scale(1.15);
}

/* Interactive Footer Language Selection Method */
.footer-lang-selector-wrapper {
    margin-top: var(--space-2);
    padding-top: var(--space-4);
    border-top: 1px dashed var(--border);
    max-width: 420px;
}

.footer-lang-label {
    display: block;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: var(--space-3);
}

[lang="ur"] .footer-lang-label {
    font-size: 15.5px;
}

.footer-lang-pills {
    display: flex;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.footer-lang-pill {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    padding: 6px 14px;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--text-secondary);
    display: inline-flex;
    align-items: center;
    gap: 6px;
    transition: all var(--transition-base);
    cursor: pointer;
}

.footer-lang-pill:hover {
    background: var(--accent-light);
    border-color: var(--accent);
    color: var(--accent);
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(27, 67, 50, 0.12);
}

.footer-lang-pill.active {
    background: var(--accent);
    border-color: var(--accent);
    color: #FFFFFF;
    box-shadow: 0 2px 8px rgba(27, 67, 50, 0.25);
}

.footer-bottom {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-4);
    border-top: 1px solid var(--border);
    padding-top: var(--space-6);
    max-width: 1100px;
    margin: 0 auto;
    color: var(--text-muted);
    font-size: 13px;
}

.footer-back-to-top {
    width: 38px;
    height: 38px;
    border-radius: var(--radius-full);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    color: var(--text-secondary);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: all var(--transition-base);
    cursor: pointer;
}

.footer-back-to-top:hover {
    background: var(--accent);
    border-color: var(--accent);
    color: #FFFFFF;
    transform: translateY(-3px);
    box-shadow: 0 4px 12px rgba(27, 67, 50, 0.2);
}

@media (max-width: 480px) {
    .hero-section {
        padding: var(--space-8) var(--space-4);
    }
    .homepage-section {
        padding: var(--space-8) var(--space-4);
    }
}

[dir="ltr"] .rtl-icon {
    transform: rotate(180deg);
}

[dir="ltr"] #volume-controls button svg {
    transform: rotate(180deg);
}

/* ======================== UNIFIED SCROLLING & CROSS-DEVICE ENGINE ======================== */
#screen-home {
    height: calc(100dvh - 65px);
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    scroll-behavior: smooth;
}

/* Must match #lib-view-all-books.active's own 190px offset (same navbar +
   search-filter-panel chrome above it) — this was a stale 65px left over
   from before the search-filter-bar existed, so #screen-library (the outer
   wrapper) was 125px taller than #lib-view-all-books (the inner content it
   wraps) actually needed, leaving a dead gap below the visible rows/cards
   that only became visible once the page scrolled the navbar out of view. */
#screen-library {
    height: calc(100dvh - var(--chrome-offset));
    overflow: hidden;
}

/* ─── 4K / Ultra-Wide Desktop Screens (1440px+) ─── */
/* Was capped at 1420px (barely above the 1300px base) so a maximized browser
   on a large/full-screen monitor still left large empty margins on both
   sides instead of using the extra width. */
@media (min-width: 1440px) {
    .grid-3 {
        grid-template-columns: repeat(3, 1fr);
    }
}

/* Laptop & Standard Desktop (1025px to 1439px): no .container override here
   anymore — it used to shrink to 1180px, narrower than the 1300px base, a
   leftover from when this layout had a permanent sidebar (see
   .library-main-content's "sidebar removed" note). With the sidebar gone
   there's no reason a standard desktop gets less width than a tablet, so it
   now just falls through to the 1300px base like every other tier below
   1440px. */

/* ─── Tablet & iPad Screens (769px to 1024px) ─── */
@media (min-width: 769px) and (max-width: 1024px) {
    .grid-3 {
        grid-template-columns: repeat(2, 1fr);
    }
    .hero-title {
        font-size: clamp(2rem, 5vw, 3rem);
    }
}

/* ─── Mobile Phones & Small Tablets (max-width: 768px) ─── */
@media (max-width: 768px) {
    /* Prevent double scrolling: window handles smooth touch scroll on mobile */
    body:not(.reader-open) #screen-home,
    body:not(.reader-open) #screen-library {
        height: auto !important;
        overflow: visible !important;
    }

    /* Hide horizontal scrollbar on mobile nav links for clean UI */

    .grid-3, .grid-2 {
        grid-template-columns: 1fr;
        gap: var(--space-4);
    }

    .hero-title {
        font-size: clamp(1.75rem, 6vw, 2.3rem);
        line-height: 1.5;
    }

    [lang="ur"] .hero-title {
        font-size: clamp(1.6rem, 5.5vw, 2.1rem);
        line-height: 1.8 !important;
    }

    .stats-grid {
        grid-template-columns: repeat(2, 1fr);
        gap: var(--space-3);
    }
}

/* ─── Compact / Small Mobile Screens (max-width: 480px) ─── */
@media (max-width: 480px) {
    .stats-grid {
        grid-template-columns: 1fr;
    }
    .hero-section {
        padding: var(--space-6) var(--space-3);
    }
    .footer-lang-pills {
        width: 100%;
        justify-content: stretch;
    }
    .footer-lang-pill {
        flex: 1;
        justify-content: center;
        text-align: center;
    }
}

/* ======================== ADVANCED HOVER & CURSOR OVERRIDES ======================== */

/* Clickable elements get pointer cursor and user-select: none to prevent I-beam selection cursor */
.sidebar-tab-item,
.explore-card,
.split-list-item,
.theme-toggle-btn,
.tab,
.tab-close,
.volume-picker-modal button,
.close-btn {
    cursor: pointer !important;
    user-select: none;
    -webkit-user-select: none;
}

/* Explore Card Icon Sliding Transition */
.explore-card .rtl-icon {
    transition: transform var(--transition-base) !important;
}
[dir="rtl"] .explore-card:hover .rtl-icon {
    transform: translateX(-4px) !important;
}
[dir="ltr"] .explore-card:hover .rtl-icon {
    transform: rotate(180deg) translateX(-4px) !important;
}

/* Theme Toggle Button Micro-Rotation */
.theme-toggle-btn {
    transition: all var(--transition-fast) !important;
}
.theme-toggle-btn:hover {
    transform: scale(1.12) rotate(12deg) !important;
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
.theme-toggle-btn:active {
    transform: scale(0.94) !important;
}

/* Sidebar Navigation Tabs Slide-in Effect */
.sidebar-tab-item {
    transition: all var(--transition-fast) !important;
}
.sidebar-tab-item:hover {
    padding-inline-start: var(--space-6) !important;
    background: var(--bg-page);
    color: var(--text-primary);
}




/* ================= STANDALONE PDF READER ("2nd Reader", restored 2026-07-26) =================
   Original tabs/workspace reader styles (bridges to Mozilla PDF.js via
   pdf-viewer/main.js). Restored from git history (commits cd4b2f1^ / 8b74046^)
   as the "2nd Reader" flyout option, alongside the VLE reader. */







/* ─── Contact page: two-column layout (form + info sidebar) ───
   Previously a single inline-styled column with three mismatched
   max-widths (800/680/560) and no pairing between the form and its
   surrounding info — replaced with a proper grid so the two halves
   align to the same baseline. */
.contact-page {
    /* Was max-width: 960px with no auto margins, so on any screen wider than
       ~1000px the whole page (title, intro, form, cards) hugged the left edge
       and the right third sat empty. Centre the column instead — the <h1>
       now lives inside it, so the title lines up with everything below. */
    max-width: 1080px;
    margin-inline: auto;
    padding: var(--space-2) 0 var(--space-8);
}

.contact-hero-title {
    font-size: 1.3rem;
    font-weight: 700;
    color: var(--text-primary);
    margin: 0 0 var(--space-3) 0;
}

.contact-hero-text {
    font-size: 1rem;
    color: var(--text-secondary);
    line-height: 1.8;
    margin: 0 0 var(--space-3) 0;
    max-width: 68ch;   /* keep a readable line length in the wider column */
}

.contact-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(300px, 340px);
    gap: var(--space-6);
    align-items: stretch;
    margin-top: var(--space-6);
}

.contact-layout-main {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

.contact-form-panel {
    padding: 24px;
    margin: 0;
    flex: 1;
}

.contact-layout-side {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

.contact-email-box {
    background: var(--bg-page);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 20px 22px;
    text-align: center;
}

.contact-email-box p {
    font-size: 0.9rem;
    color: var(--text-secondary);
    margin: 0 0 10px 0;
}

.contact-email-box a {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--accent);
    text-decoration: none;
    word-break: break-all;
}

.contact-email-box a i,
.contact-email-box a svg {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}

.contact-cards-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-3);
}

.contact-info-card {
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 16px 18px;
}

.contact-info-card-title {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0 0 6px 0;
    display: flex;
    align-items: center;
    gap: 8px;
}

.contact-info-card-title i,
.contact-info-card-title svg {
    width: 18px;
    height: 18px;
    color: var(--accent);
    flex-shrink: 0;
}

.contact-info-card-desc {
    font-size: 0.85rem;
    color: var(--text-secondary);
    line-height: 1.7;
}

.contact-response-time {
    border-top: 1px solid var(--border);
    padding-top: var(--space-4);
}

.contact-response-time-title {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0 0 6px 0;
}

.contact-response-time-text {
    font-size: 0.85rem;
    color: var(--text-secondary);
    margin: 0;
    line-height: 1.7;
}

@media (max-width: 768px) {
    .contact-layout {
        grid-template-columns: 1fr;
    }
}

/* ==================== DOWNLOAD APPS PAGE ==================== */
.download-page {
    max-width: 1080px;
    margin-inline: auto;
    padding: var(--space-2) 0 var(--space-8);
}

.download-page-title {
    font-size: 2rem;
    color: var(--accent);
    margin: 0 0 var(--space-5) 0;
}

.download-hero-title {
    font-size: 1.3rem;
    font-weight: 700;
    color: var(--text-primary);
    margin: 0 0 var(--space-3) 0;
}

.download-hero-text {
    font-size: 1rem;
    color: var(--text-secondary);
    line-height: 1.8;
    margin: 0 0 var(--space-6) 0;
    max-width: 68ch;
}

.download-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: var(--space-6);
    margin-bottom: var(--space-6);
}

.download-card {
    padding: 32px 28px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    border-radius: var(--radius-lg, 12px);
    transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
    border: 1px solid var(--border);
    background: var(--bg-surface);
}

.download-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
    border-color: var(--accent-light, var(--accent));
}

.download-card-header {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-bottom: var(--space-4);
}

.download-platform-icon {
    width: 56px;
    height: 56px;
    border-radius: 14px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.download-platform-icon.windows-icon {
    background: rgba(0, 120, 215, 0.12);
    color: #0078d7;
}

.download-platform-icon.android-icon {
    background: rgba(46, 125, 50, 0.14);
    color: #2e7d32;
}

.download-card-header-text {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.download-card-title {
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--text-primary);
    margin: 0;
}

.download-badge {
    display: inline-block;
    align-self: flex-start;
    font-size: 12px;
    font-weight: 600;
    padding: 2px 10px;
    border-radius: 9999px;
    background: var(--accent-light);
    color: var(--accent);
}

.download-card-desc {
    font-size: 0.95rem;
    color: var(--text-secondary);
    line-height: 1.7;
    margin: 0 0 var(--space-5) 0;
    flex-grow: 1;
}

.download-specs {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 14px 16px;
    background: var(--bg-page);
    border-radius: 8px;
    border: 1px solid var(--border-subtle, var(--border));
    margin-bottom: var(--space-6);
}

.download-spec-item {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 13px;
    color: var(--text-secondary);
}

.download-spec-item svg,
.download-spec-item .lucide {
    width: 16px;
    height: 16px;
    color: var(--accent);
    flex-shrink: 0;
}

.download-card-actions {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.download-action-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    width: 100%;
    padding: 12px 20px;
    font-size: 15px;
    font-weight: 600;
    border-radius: 8px;
    text-decoration: none;
    cursor: pointer;
}

.download-action-btn svg {
    width: 18px;
    height: 18px;
}

.download-footer-note {
    font-size: 13px;
    color: var(--text-muted);
    text-align: center;
    margin-top: var(--space-6);
}

/* ================= ARTICLE TYPOGRAPHY & READER ================= */

/* Language-specific font classes for article blocks */

.font-nastaliq {
  font-family: var(--font-urdu);
  line-height: 2.6;
  font-size: 24px;
}

.font-serif {
  font-family: 'Source Serif 4', 'Georgia', serif;
  line-height: 1.8;
  font-size: 16px;
}

/* Article reader layout */
.article-reader-body {
  max-width: 720px;
  margin: 0 auto;
}

.article-reader-body h1,
.article-reader-body h2,
.article-reader-body h3 {
  color: var(--accent);
  margin: 1.5em 0 0.5em;
}

.article-reader-body h1:first-child,
.article-reader-body h2:first-child,
.article-reader-body h3:first-child {
  margin-top: 0;
}

.article-reader-body p {
  margin: 0 0 1em;
}

/* Autosave Pill */
.editor-autosave-status-pill.unsaved {
  background-color: #f3f4f6;
  color: #6b7280;
}
.editor-autosave-status-pill.just-saved {
  background-color: #dcfce7;
  color: #15803d;
  box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.3);
  transform: scale(1.05);
}

/* Revisions Sidebar */
.editor-history-sidebar {
  width: 320px;
  border-left: 1px solid var(--border);
  background: var(--bg-surface);
  display: flex;
  flex-direction: column;
  height: 100%;
  animation: slideInRight 0.25s cubic-bezier(0.16, 1, 0.3, 1);
  z-index: var(--z-overlay-local);
}

@keyframes slideInRight {
  from { transform: translateX(100%); }
  to { transform: translateX(0); }
}

.sidebar-header {
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-weight: 700;
  color: var(--text-primary);
}

.sidebar-content {
  flex: 1;
  overflow-y: auto;
  padding: 12px;
}

.history-item {
  padding: 12px;
  border-radius: 8px;
  border: 1px solid var(--border);
  margin-bottom: 8px;
  cursor: pointer;
  transition: all 0.15s ease;
}

.history-item:hover {
  background: var(--accent-light);
  border-color: var(--accent);
}

.history-item-time {
  font-weight: 700;
  font-size: 13px;
  color: var(--text-primary);
}

.history-item-size {
  font-size: 11px;
  color: var(--text-muted);
  margin-top: 4px;
}

/* Scholarly Image and Captions */
figure.scholarly-image {
  margin: 24px 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}

figure.scholarly-image img {
  max-width: 100%;
  height: auto;
  border-radius: 8px;
  border: 1px solid var(--border);
}

figure.scholarly-image figcaption {
  font-size: 13px;
  color: var(--text-muted);
  margin-top: 8px;
  text-align: center;
  font-style: italic;
}

figure.scholarly-image.align-left {
  float: left;
  margin-right: 24px;
  max-width: 50%;
}

figure.scholarly-image.align-right {
  float: right;
  margin-left: 24px;
  max-width: 50%;
}

figure.scholarly-image.align-full {
  max-width: 100%;
  width: 100vw;
  margin-left: calc(-50vw + 50%);
}

/* Loading Image Placeholder */
.image-upload-placeholder {
  margin: 24px 0;
  padding: 40px;
  border: 2px dashed var(--border);
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  background: var(--bg-surface);
}

.upload-spinner {
  width: 32px;
  height: 32px;
  border: 3px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: uploadSpin 0.8s linear infinite;
}

@keyframes uploadSpin {
  to { transform: rotate(360deg); }
}

.upload-progress-text {
  font-size: 13px;
  color: var(--text-secondary);
  font-weight: 600;
}

/* Mobile responsive horizontal carousel toolbar */


.article-reader-body blockquote {
  border-inline-start: 4px solid var(--accent);
  margin: 1em 0;
  padding: 0.5em 1em;
  /* Fallback dropped: --accent-light is declared in base.css for both themes,
     so `rgba(var(--accent-rgb), 0.06)` was unreachable — and --accent-rgb was
     itself never declared, so had it ever been reached it would have computed
     to nothing. */
  background: var(--accent-light);
  border-radius: 0 8px 8px 0;
}

.article-reader-body blockquote footer {
  margin-top: 0.5em;
  font-size: 13px;
  opacity: 0.8;
}

.article-reader-body ul,
.article-reader-body ol {
  margin: 0.5em 0 1em;
  padding-inline-start: 1.5em;
}

.article-reader-body li {
  margin-bottom: 0.3em;
}

.article-reader-body hr {
  border: none;
  border-top: 1px solid var(--border);
  margin: 2em 0;
}

.article-reader-body img {
  max-width: 100%;
  border-radius: 8px;
  margin: 1em 0;
}

/* Article meta line (author + date) */
.article-meta {
  color: var(--text-muted);
  font-size: 13px;
  margin-bottom: 24px;
  display: flex;
  gap: 12px;
  align-items: center;
}

/* ================= TIPTAP EDITOR THEME OVERRIDES ================= */

/* Fullscreen Immersive Article Writer Workspace */
body.writer-immersive-mode {
  overflow: hidden !important;
}

body.writer-immersive-mode .screen:not(#screen-articles) {
  display: none !important;
  visibility: hidden !important;
  opacity: 0 !important;
}

body.writer-immersive-mode #screen-articles {
  animation: none !important;
  transform: none !important;
}

body.writer-immersive-mode .navbar,
body.writer-immersive-mode #navbar,
body.writer-immersive-mode header,
body.writer-immersive-mode nav,
body.writer-immersive-mode .site-header,
body.writer-immersive-mode #articles-header,
body.writer-immersive-mode #articles-filter-section,
body.writer-immersive-mode #library-tabs-bar,
body.writer-immersive-mode #offline-tabs-bar,
body.writer-immersive-mode .mobile-bottom-nav,
body.writer-immersive-mode #mobile-bottom-nav,
body.writer-immersive-mode footer,
body.writer-immersive-mode .footer {
  display: none !important;
  visibility: hidden !important;
  opacity: 0 !important;
  pointer-events: none !important;
}

.editor-autosave-status {
  font-size: 12px;
  color: var(--text-muted);
}

/* Language selection buttons in toolbar */
.toolbar-lang-btn {
  font-size: 12px;
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg-surface);
  color: var(--text-secondary);
  cursor: pointer;
  font-weight: 600;
  transition: all 0.15s ease;
}

.toolbar-lang-btn:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.toolbar-lang-btn.active {
  background-color: var(--accent);
  color: #fff;
  border-color: var(--accent);
}

/* Tiptap Container & Editor Canvas */

.ProseMirror {
  outline: none;
  min-height: 400px;
  height: 100%;
}

.ProseMirror p {
  margin: 0 0 1em;
  padding: 0;
}

/* Placeholder styling */
.ProseMirror p.is-editor-empty:first-child {
  position: relative;
}

.ProseMirror p.is-editor-empty:first-child::before {
  content: attr(data-placeholder);
  position: absolute;
  left: 0;
  top: 0;
  color: var(--text-muted);
  pointer-events: none;
  height: 0;
  opacity: 0.6;
}

html[dir="rtl"] .ProseMirror p.is-editor-empty:first-child::before,
.ProseMirror p[dir="rtl"].is-editor-empty:first-child::before {
  left: auto;
  right: 0;
}

/* Custom styles for inline lang markup spans */
.ProseMirror span[lang] {
  padding: 0 2px;
}

/* Custom list bullet rendering inside editor */
.ProseMirror ul, .ProseMirror ol {
  padding-inline-start: 24px;
}

/* My Articles section & Saved Drafts Button */
#btn-my-drafts.active {
  background: var(--accent);
  color: white;
  border-color: var(--accent);
}
#btn-my-drafts.active #drafts-count {
  background: white !important;
  color: var(--accent) !important;
}

.my-articles-section {
  margin-bottom: 24px;
}

.my-articles-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
  gap: 12px;
  flex-wrap: wrap;
}

.my-articles-item:last-child {
  border-bottom: none;
}

.my-articles-title {
  font-weight: 600;
  flex: 1;
  min-width: 150px;
}

.my-articles-actions {
  display: flex;
  gap: 6px;
}

/* Status badges */

.status-badge.draft {
  background: var(--bg-page);
  color: var(--text-muted);
  border: 1px solid var(--border);
}

.status-badge.submitted {
  background: #fef3c7;
  color: #b45309;
  border: 1px solid rgba(245, 158, 11, 0.3);
}

.status-badge.published {
  background: rgba(34, 197, 94, 0.12);
  color: #15803d;
  border: 1px solid rgba(34, 197, 94, 0.25);
}

.status-badge.rejected {
  background: #fef2f2;
  color: #dc2626;
  border: 1px solid rgba(239, 68, 68, 0.25);
}


.reader-toolbar-controls {
    display: flex;
    align-items: center;
    gap: 3px;
    flex-shrink: 0;
    padding: 0 6px;
    border-inline-start: 1px solid var(--border);
    height: 100%;
}
.reader-toolbar-controls .btn.btn-sm {
    height: 30px;
    min-width: 30px;
    padding: 0 7px;
    font-size: 12px;
    border-radius: 6px;
    border-color: transparent;
    background: transparent;
    color: var(--text-secondary);
    box-shadow: none;
}
.reader-toolbar-controls .btn.btn-sm:hover {
    background: rgba(255,255,255,0.12);
    color: #fff;
    border-color: transparent;
    transform: none;
    box-shadow: none;
}
.reader-toolbar-controls .btn.btn-sm.active {
    background: rgba(255,255,255,0.18);
    color: #fff;
}



/* --- Tabs bar sub-elements (tabs-scroll, .tab, tab-scroll-btn, tabs-bar-actions) --- */
.tabs-scroll {
    flex: 1;
    display: flex;
    overflow-x: auto;
    scrollbar-width: none;
    min-width: 0;
    gap: 4px;
    align-items: flex-end;
}
.tabs-scroll::-webkit-scrollbar { display: none; }

.tab {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 0 14px;
    height: 38px;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-bottom: none;
    border-radius: 8px 8px 0 0;
    cursor: pointer;
    font-family: var(--font-arabic);
    font-size: 13px;
    font-weight: 600;
    color: var(--text-secondary);
    transition: all var(--transition-fast);
    white-space: nowrap;
    flex-shrink: 0;
    max-width: 220px;
    margin: 6px 2px 0 2px;
}
.tab span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.tab:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}
.tab.active {
    background: var(--bg-page);
    color: var(--accent);
    border-top: 3px solid var(--accent);
    font-weight: 700;
    height: 40px;
    z-index: var(--z-content-raised);
}

/* Tab scroll chevron buttons styling */
.tab-scroll-btn {
    transition: background-color var(--transition-fast), color var(--transition-fast), transform var(--transition-fast);
}
.tab-scroll-btn:hover {
    background-color: var(--bg-hover) !important;
    color: var(--accent) !important;
}
.tab-scroll-btn:active {
    transform: scale(0.9);
}
.tab-close {
    width: 18px;
    height: 18px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 700;
    color: var(--text-muted);
    flex-shrink: 0;
    transition: background var(--transition-fast), color var(--transition-fast);
    line-height: 1;
}
/* Tokens, not #fef2f2/#ef4444: those are fixed light-mode pinks, so the close
   button lit up with a pale pink chip on the dark and sepia grounds too. The
   danger tokens carry a per-theme value for exactly this. */
.tab-close:hover { background: var(--badge-danger-bg); color: var(--badge-danger-text); }
.tab.active .tab-close { color: var(--accent); opacity: 0.6; }
.tab.active .tab-close:hover { background: rgba(38,37,31,0.08); opacity: 1; }
@media (max-width: 768px) {
    /* 18px is a mouse-precision size — comfortable with a cursor, easy to
       miss with a fingertip, and mis-tapping it closes the tab (losing
       reading position) instead of switching to it. The tab itself doesn't
       need to grow to fit this; its own padding already leaves enough room. */
    .tab-close { width: 28px; height: 28px; font-size: 16px; }
}

.tabs-bar-actions {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-shrink: 0;
    padding: 0 6px;
    border-inline-start: 1px solid var(--border);
    height: 100%;
}
.tabs-bar-actions span {
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
    color: var(--text-secondary);
    padding: 0 4px;
}



/* ═══════════════════════════════════════════════════════════════════
   Mobile Bottom Navigation Bar
   Thumb-reachable primary nav for phones. Hidden on ≥769px (top navbar
   handles desktop). Fixed to the bottom with safe-area padding so it
   clears the iOS home indicator. Markup: #mobile-bottom-nav in index.html;
   active state synced from showScreen() in app.js.
   ═══════════════════════════════════════════════════════════════════ */
.mobile-bottom-nav { display: none; }

@media (max-width: 768px) {
    .mobile-bottom-nav {
        display: flex;
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        z-index: var(--z-bottom-nav);
        background: var(--bg-elevated);
        border-top: 1px solid var(--border);
        box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.06);
        padding-bottom: env(safe-area-inset-bottom, 0px);
        transition: transform 240ms ease;
    }
    .mbn-item {
        flex: 1 1 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 3px;
        min-height: 56px;
        padding: 6px 4px;
        border: none;
        background: transparent;
        color: var(--text-muted);
        font-family: var(--font-ui);
        font-size: 11px;
        font-weight: 600;
        line-height: 1;
        cursor: pointer;
        -webkit-tap-highlight-color: transparent;
        transition: color var(--transition-fast);
    }
    .mbn-item.active { color: var(--accent-warm); }
    .mbn-item:active { opacity: 0.6; }
    .mbn-icon { width: 22px; height: 22px; flex-shrink: 0; }
    .mbn-label { white-space: nowrap; }

    /* Reserve space so the fixed bar never hides page content or the footer.
       Only when the reader is closed — the reader is fullscreen and hides
       the bar entirely (below). */
    body:not(.reader-open) .main-wrapper {
        padding-bottom: calc(60px + env(safe-area-inset-bottom, 0px));
    }
    body.reader-open .mobile-bottom-nav {
        transform: translateY(120%);
    }
}

/* ═══════════════════════════════════════════════════════════════════
   iOS Safari zoom-jump fix — any focused input under 16px font-size
   makes Safari zoom the whole viewport in, forcing the user to manually
   pinch back out. Desktop sizing (13-15px, deliberately compact) is left
   alone since the bug is mobile-Safari-only; this only overrides the
   affected inputs at the same ≤768px breakpoint the rest of the mobile
   layout already switches on.
   ═══════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {
    .form-textarea,
.form-select-compact,
.search-box-wrapper input,
.search-grid .form-select-compact,
.panel-search-box input,
#reader-page-input,
.reader-settings-page-input,
.pv-page-input,
[lang="ur"] .form-select-compact,
[lang="ur"] .form-textarea {
        font-size: 16px !important;
    }
    /* The page-jump boxes are sized for their old 13-14px digits; widen
       just enough that a 3-digit page number still fits at 16px. */
    #reader-page-input { width: 74px !important; }
    .reader-settings-page-input { width: 62px; }
    .pv-page-input { width: 50px; }
}

/* ═══════════════════════════════════════════════════════════════════
   Reader Back button
   The icon points right because the app is Arabic-first and "back" runs
   with the reading direction; mirrored for LTR so it points left there.
   ═══════════════════════════════════════════════════════════════════ */
.reader-back-btn { flex-shrink: 0; }
[dir="ltr"] .reader-back-btn i,
[dir="ltr"] .reader-back-btn svg { transform: scaleX(-1); }

/* ═══════════════════════════════════════════════════════════════════
   "More" bottom sheet — secondary destinations off the 5-item bottom nav.
   Mobile only, since the bottom nav that opens it is itself ≤768px.
   ═══════════════════════════════════════════════════════════════════ */
.more-sheet { display: none; }
.more-sheet.hidden { display: none !important; }

@media (max-width: 768px) {
    .more-sheet {
        display: block;
        position: fixed;
        inset: 0;
        z-index: var(--z-sheet-top);   /* above the bottom nav (200) and book sheets (2200) */
    }
    .more-sheet-backdrop {
        position: absolute;
        inset: 0;
        background: rgba(0, 0, 0, 0.38);
        animation: moreSheetFade 0.2s ease;
    }
    .more-sheet-panel {
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0;
        background: var(--bg-elevated);
        border-top: 1px solid var(--border);
        border-radius: 18px 18px 0 0;
        box-shadow: 0 -6px 28px rgba(0, 0, 0, 0.22);
        padding: var(--space-3) var(--space-2);
        padding-bottom: calc(var(--space-4) + env(safe-area-inset-bottom, 0px));
        max-height: 80dvh;
        overflow-y: auto;
        animation: mobileSheetUp 0.25s ease;
    }
    /* Grab-handle pill, matching the other bottom sheets. */
    .more-sheet-panel::before {
        content: "";
        position: absolute;
        top: 8px;
        left: 50%;
        transform: translateX(-50%);
        width: 40px;
        height: 4px;
        border-radius: 2px;
        background: var(--border-strong, var(--text-muted));
    }
    .more-sheet-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: var(--space-4) var(--space-3) var(--space-2);
        font-family: var(--font-ui);
        font-size: 15px;
        font-weight: 700;
        color: var(--text-primary);
    }
    .more-sheet-close {
        width: 44px;
        height: 44px;
        border: none;
        background: transparent;
        font-size: 26px;
        line-height: 1;
        color: var(--text-muted);
        cursor: pointer;
    }
    .more-sheet-item {
        display: flex;
        align-items: center;
        gap: var(--space-3);
        width: 100%;
        min-height: 52px;
        padding: 0 var(--space-3);
        border: none;
        background: transparent;
        text-align: start;
        font-family: var(--font-ui);
        font-size: 16px;
        font-weight: 600;
        color: var(--text-primary);
        cursor: pointer;
        border-radius: var(--radius-md);
    }
    .more-sheet-item:active { background: var(--bg-hover); }
    .more-sheet-item i,
    .more-sheet-item svg { width: 20px; height: 20px; flex-shrink: 0; color: var(--text-secondary); }

    /* Dim the page behind, same treatment the View-Options sheet gets. */
    body.more-sheet-open .navbar { opacity: 0; pointer-events: none; }
}

@keyframes moreSheetFade { from { opacity: 0; } to { opacity: 1; } }

/* Count badge on the More menu's Open Books row — the header badge it
   normally lives on is hidden at these widths. */
.more-sheet-badge {
    margin-inline-start: auto;
    min-width: 22px;
    padding: 2px 7px;
    border-radius: 11px;
    background: var(--accent);
    color: #fff;
    font-family: var(--font-ui);
    font-size: 12px;
    font-weight: 700;
    text-align: center;
}
.more-sheet-item-danger { color: #ef4444; }
.more-sheet-item-danger i,
.more-sheet-item-danger svg { color: #ef4444; }

/* ═══════════════════════════════════════════════════════════════════
   Reader drawers become bottom sheets on mobile
   .mobile-sheet-drawer is a 280px full-height side panel — a desktop
   shape that had no mobile override, so on a phone the Open Books and
   Reading Settings panels slid in from the side covering most of the
   screen while leaving an awkward strip of page behind. Bottom sheets
   match every other mobile surface here (View Options, book actions,
   More) and put the content within thumb reach.
   ═══════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {
    .mobile-sheet-drawer,
    [dir="rtl"] .mobile-sheet-drawer {
        top: auto;
        left: 0;
        right: 0;
        bottom: 0;
        width: auto;
        max-height: 80dvh;
        overflow-y: auto;
        border-radius: 18px 18px 0 0;
        padding: var(--space-5) var(--space-4);
        padding-bottom: calc(var(--space-5) + env(safe-area-inset-bottom, 0px));
        box-shadow: 0 -6px 28px rgba(0, 0, 0, 0.22);
        animation: mobileSheetUp 0.25s ease forwards;
    }
    /* Grab-handle pill, consistent with the other bottom sheets. */
    .mobile-sheet-drawer::before {
        content: "";
        position: absolute;
        top: 8px;
        left: 50%;
        transform: translateX(-50%);
        width: 40px;
        height: 4px;
        border-radius: 2px;
        background: var(--border-strong, var(--text-muted));
    }
}

/* ═══════════════════════════════════════════════════════════════════
   Mobile grid cards — Large / Compact / No cover
   ───────────────────────────────────────────────────────────────────
   The ≤480px block further up carries leftovers from an older, horizontal
   card design: align-items:flex-start, a 40px flex-basis on the cover, a
   fixed 40×60 cover image and padding on the card itself. Against today's
   vertical grid card those do real damage — flex-start stops the children
   stretching to the card width, so each one sizes to its own content and
   spills outside the border (measured at 375px: a 193px body inside a 170px
   card, starting 37px outside it), and the 40px basis flattens the 2:3 cover
   to a 40px strip, so "Large covers" showed barely any cover at all.
   Those rules are left alone for whatever non-grid card still relies on
   them; everything here is scoped under .books-grid so it only re-establishes
   the grid card's own geometry.
   ═══════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {
    .books-grid .book-card {
        align-items: stretch;   /* children span the card, as a column flex normally does */
        padding: 0;             /* body and actions carry their own padding */
    }

    /* The cover is a ratio box: it must not be flex-shrunk or given a basis. */
    .books-grid .book-card-cover-wrapper {
        flex: 0 0 auto;
        width: 100%;
    }
    .books-grid .book-card-cover {
        width: 100%;
        height: 100%;
    }

    /* Arabic titles are long and unbroken; without this they push past the
       card instead of wrapping into the two lines the clamp allows. */
    .books-grid .book-card-title {
        overflow-wrap: anywhere;
        word-break: break-word;
    }

    /* The category pill is a tag, not a paragraph. Translated names like
       "Usul al-Fiqh (Principles of Jurisprudence)" were wrapping to three
       lines and dominating the card. */
    .books-grid .book-card-category {
        max-width: 100%;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    /* Centre the actions on a grid card — the ≤480px rule pushes them to one
       edge, which suits a wide row but looks off-balance on a narrow tile. */
    .books-grid .book-card-actions {
        width: auto;
        justify-content: center;
    }

    /* Four 44px targets need 176px; a tile is ~168px at 375px and ~143px at
       320px, so they overflowed. Read stays inline as the primary action and
       the other two move into the "…" sheet (book-card.js clones them there),
       which is also the one-primary-CTA-plus-one-menu shape the mobile spec
       asks for. Read + "…" is 88px, comfortable even at 320px. */
    .books-grid .book-card-actions > .book-dl-btn,
    .books-grid .book-card-actions > .book-opfs-btn,
    .books-grid .book-card-actions > .book-readlater-btn { display: none; }
    .books-grid .book-more-popover .book-more-primary { display: inline-flex; }
    /* Read is already inline, so its clone would be a duplicate. */
    .books-grid .book-more-popover .book-read-btn.book-more-primary { display: none !important; }
    /* Mark the primary action rather than leaving four identical grey icons. */
    .books-grid .book-card-actions > .book-read-btn {
        color: var(--accent);
        border-color: var(--accent);
    }

    /* The death year sits after the author in a nowrap row; on a narrow tile it
       was the part that got clipped, which is the wrong half to lose. Giving
       the author its own ellipsis keeps the year visible. */
    .books-grid .book-card-author-row {
        display: flex;
        align-items: baseline;
        min-width: 0;
    }
    /* The separator already carries its own spaces; a flex gap on top of them
       would read as a double space. */
    .books-grid .book-card-author-sep { flex-shrink: 0; white-space: pre; }
    .books-grid .book-card-author-row > .book-card-author {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .books-grid .book-card-death { flex-shrink: 0; }

    /* ── Large: the cover is the point. Keep the full 2:3 poster shape. ── */

    /* ── Compact: the point of this mode is more books per screen. The mobile
          .books-grid rule pins every mode to two columns, so Compact was only
          trimming a little cover height and text size — barely denser than
          Large. Three columns is what actually makes it compact; the shorter
          3:4 cover keeps the tiles from getting tall as they get narrow. ── */
    .books-grid.compact-covers { grid-template-columns: repeat(3, 1fr); }
    .books-grid.compact-covers .book-card-cover-wrapper { aspect-ratio: 3 / 4; }
    .books-grid.compact-covers .book-card-body { padding: var(--space-2); gap: 2px; }
    /* At three columns a tile is ~110px, so the action row has to shed the
       padding it can afford at two. */
    .books-grid.compact-covers .book-card-actions { padding: var(--space-1) 0; }

    /* ── No cover: without the image the tile is all text, so give the title
          room to breathe (three lines instead of two) rather than leaving the
          card looking like a cover failed to load. ── */
    .books-grid.no-covers .book-card-title { -webkit-line-clamp: 3; }
    .books-grid.no-covers .book-card-body { padding: var(--space-3); }
}


/* =========================================================
   Redesigned Search & Filter UI (Drawer + Pills)
   ========================================================= */

/* Primary Search Grid & Industry Standard Container */
.search-filter-panel .search-grid.search-grid-primary {
    display: flex;
    gap: var(--space-2);
    align-items: center;
    width: 100%;
    max-width: 760px;
    margin: 0 auto;
}

.search-filter-panel .primary-search-group {
    flex: 1 1 auto;
    min-width: 0;
}

.primary-search-wrapper {
    position: relative;
    width: 100%;
}

.search-filter-panel .primary-search-wrapper .search-icon {
    position: absolute;
    left: 14px; /* physical left side */
    right: auto;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-muted);
    pointer-events: auto;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: color 0.2s ease;
}

[dir="ltr"] .search-filter-panel .primary-search-wrapper .search-icon,
html[dir="ltr"] .search-filter-panel .primary-search-wrapper .search-icon {
    left: auto;
    right: 14px; /* trailing edge for LTR */
}

.search-filter-panel .primary-search-input {
    width: 100%;
    height: 44px;
    background-color: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 0 16px 0 42px; /* physical left padding for search icon in RTL */
    font-size: 14.5px;
    color: var(--text-primary);
    transition: all 0.2s ease;
    box-shadow: var(--shadow-sm);
}

[dir="ltr"] .search-filter-panel .primary-search-input,
html[dir="ltr"] .search-filter-panel .primary-search-input {
    padding: 0 42px 0 16px; /* physical right padding for search icon in LTR */
}

.primary-search-input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--focus-ring);
    background-color: var(--bg-surface);
}

/* Header Actions & Unified 3 Icon Control Group */
.search-filter-panel .header-actions-group {
    display: flex;
    gap: var(--space-2);
    align-items: center;
    flex-shrink: 0;
}

/* Icon-only Select */
.sort-selector-wrapper {
    position: relative;
    width: 44px;
    height: 44px;
    flex-shrink: 0;
    overflow: hidden;
    isolation: isolate;
}

.icon-only-select {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
    z-index: var(--z-content-raised);
}

.sort-icon-overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    color: var(--text-secondary);
    transition: all 0.2s ease;
    z-index: var(--z-content);
    box-shadow: var(--shadow-sm);
}

.sort-selector-wrapper:hover .sort-icon-overlay,
.icon-only-select:focus + .sort-icon-overlay {
    background-color: var(--bg-surface-hover);
    color: var(--text-primary);
    border-color: var(--border-strong);
}

/* An inline toolbar filter (Century / Language) holding a non-default value —
   the accent stands in for the count badge the old filter drawer carried. */
.sort-selector-wrapper.toolbar-filter-active .sort-icon-overlay {
    color: var(--accent);
    border-color: var(--accent);
}

/* Filter Drawer Button */
.filter-drawer-btn {
    position: relative;
    width: 44px;
    height: 44px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    color: var(--text-secondary);
    transition: all 0.2s ease;
    box-shadow: var(--shadow-sm);
    flex-shrink: 0;
}

.filter-drawer-btn:hover {
    background-color: var(--bg-surface-hover);
    color: var(--text-primary);
    border-color: var(--border-strong);
}

.filter-badge {
    position: absolute;
    top: -4px;
    /* inset-inline-end, not right: a count badge belongs on the button's
       trailing corner, which is the LEFT in RTL. Pinned to `right` it stayed
       on the physical right in Arabic/Urdu — the leading corner there — where
       it crowded the start of the label instead of trailing it. */
    inset-inline-end: -4px;
    background-color: var(--accent);
    color: var(--btn-primary-text);
    font-size: 10px;
    font-weight: bold;
    min-width: 18px;
    height: 18px;
    border-radius: 9px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 4px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}

/* Drawer Overlay */
.drawer-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: var(--z-drawer-overlay);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s ease, visibility 0.3s ease;
    backdrop-filter: blur(2px);
}

.drawer-overlay.active {
    opacity: 1;
    visibility: visible;
}

/* Slide-Over Drawer */
.slide-over-drawer {
    position: fixed;
    top: 0;
    right: 0; /* RTL default: slides in from right */
    width: 320px;
    max-width: 90vw;
    height: 100%;
    background-color: var(--bg-page);
    box-shadow: -4px 0 24px rgba(0, 0, 0, 0.15);
    z-index: var(--z-drawer);
    transform: translateX(100%);
    transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
    display: flex;
    flex-direction: column;
}

[dir="ltr"] .slide-over-drawer {
    right: auto;
    left: 0;
    transform: translateX(-100%);
    box-shadow: 4px 0 24px rgba(0, 0, 0, 0.15);
}

.slide-over-drawer.open {
    transform: translateX(0);
}

.drawer-header {
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: space-between;
    background-color: var(--bg-surface);
}

.drawer-title {
    margin: 0;
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--text-primary);
}

.drawer-header-actions {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.btn-drawer-close {
    background: transparent;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    padding: 4px;
    border-radius: var(--radius-md);
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.2s ease;
}

.btn-drawer-close:hover {
    background-color: var(--bg-panel);
    color: var(--text-primary);
}

.btn-drawer-reset {
    background: transparent;
    border: none;
    color: var(--accent);
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    padding: 4px 8px;
    border-radius: var(--radius-md);
}

.btn-drawer-reset:hover {
    background-color: var(--bg-panel);
}

.drawer-body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: var(--space-5);
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.drawer-section {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.drawer-label {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-secondary);
}

.drawer-footer {
    padding: var(--space-4) var(--space-5);
    border-top: 1px solid var(--border);
    background-color: var(--bg-surface);
}

.btn-drawer-apply {
    width: 100%;
    justify-content: center;
    padding: 12px;
    font-size: 15px;
    border-radius: var(--radius-lg);
}

/* Active Filters Row */
.active-filters-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin-top: var(--space-3);
    padding-bottom: var(--space-2);
}

.active-filters-container {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    flex: 1 1 auto;
}

.filter-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    background-color: var(--bg-panel);
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    font-size: 12px;
    color: var(--text-primary);
    transition: all 0.2s ease;
}

.filter-pill-label {
    font-weight: 500;
    color: var(--text-secondary);
}

.filter-pill-value {
    font-weight: 700;
}

.btn-pill-remove {
    background: transparent;
    border: none;
    padding: 2px;
    margin: -2px -4px -2px 0;
    color: var(--text-muted);
    cursor: pointer;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}

[dir="ltr"] .btn-pill-remove {
    margin: -2px 0 -2px -4px;
}

.btn-pill-remove:hover {
    background-color: var(--border);
    color: var(--text-primary);
}

.btn-clear-filters {
    background: transparent;
    border: none;
    color: var(--text-muted);
    font-size: 12px;
    cursor: pointer;
    padding: 4px 8px;
    border-radius: var(--radius-sm);
    flex-shrink: 0;
}

.btn-clear-filters:hover {
    color: var(--accent);
    background-color: var(--bg-panel);
}

/* ==========================================================================
   MEDIUM-STYLE ARTICLES REDESIGN (CARDS, SLIDING TABS, READER & AUTHOR PROFILE)
   ========================================================================== */

/* Article Category Sliding Tabs */
.article-category-tabs {
  display: flex;
  overflow-x: auto;
  white-space: nowrap;
  gap: 8px;
  padding: 6px 0 12px 0;
  border-bottom: 1px solid var(--border);
  scrollbar-width: none;
  -ms-overflow-style: none;
  -webkit-overflow-scrolling: touch;
}

.article-category-tabs::-webkit-scrollbar {
  display: none;
}

.category-tab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 6px 16px;
  min-height: 36px;
  border-radius: 9999px;
  border: 1px solid var(--border);
  background: var(--bg-surface);
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s ease;
  flex-shrink: 0;
  touch-action: manipulation;
}

.category-tab:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.category-tab.active {
  background: var(--accent);
  color: #ffffff;
  border-color: var(--accent);
}

/* Articles category filter: a dropdown that sits beside the search input. */
.articles-filter-row {
  flex-wrap: wrap;
  align-items: center;
}
#articles-category-tabs {
  flex-shrink: 0;
}
.articles-category-select {
  width: 220px;
  max-width: 100%;
  height: 44px;
}
@media (max-width: 560px) {
  #articles-category-tabs { flex: 1 1 100%; }
  .articles-category-select { width: 100%; }
}

/* Feed sort pills: All Articles / Most Viewed / Recent */
.article-sort-pills {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
.article-sort-pill {
  min-height: 32px;
  padding: 5px 14px;
  font-size: 12.5px;
}

/* Medium Single-Column Feed Cards */
.medium-article-card {
  padding: 24px 0;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  transition: opacity 0.2s ease;
}

.medium-article-card:hover {
  opacity: 0.92;
}

.card-meta-line {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--text-muted);
  margin-bottom: 10px;
  flex-wrap: wrap;
}

.card-meta-line .author-link {
  font-weight: 600;
  color: var(--text-primary);
  display: inline-flex;
  align-items: center;
  gap: 4px;
  transition: color 0.15s ease;
}

.card-meta-line .author-link:hover {
  color: var(--accent);
  text-decoration: underline;
}

.card-meta-line .meta-dot {
  color: var(--text-muted);
}

.category-badge {
  font-size: 11px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 4px;
  background: var(--accent-light);
  color: var(--accent);
  margin-inline-start: 4px;
}

.card-main-content {
  display: flex;
  justify-content: space-between;
  gap: 20px;
  align-items: flex-start;
}

.card-text-side {
  flex: 1;
}

.card-title {
  font-size: 1.35rem;
  font-weight: 700;
  color: var(--text-primary);
  margin: 0 0 8px 0;
  line-height: 1.4;
}

.card-snippet {
  font-size: 0.95rem;
  color: var(--text-secondary);
  margin: 0;
  line-height: 1.6;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Reader Page Styling */
.article-reader-container {
  max-width: 740px;
  margin: 0 auto;
  padding: 10px 16px 60px 16px;
}

.article-reader-header {
  margin-bottom: 24px;
}

.article-reader-title {
  font-size: 2.3rem;
  font-weight: 800;
  color: var(--text-primary);
  line-height: 1.3;
  margin: 0 0 16px 0;
}

.article-reader-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--text-muted);
  font-size: 14px;
  flex-wrap: wrap;
}

.article-reader-meta .author-link {
  font-weight: 600;
  color: var(--text-primary);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.article-reader-meta .author-link:hover {
  color: var(--accent);
  text-decoration: underline;
}

/* Author byline for an article whose author account was deleted (author
   chose to keep published articles up under a frozen name snapshot — see
   article.author_display_name). No profile left to link to. */
.card-meta-line .author-link.author-link-inactive,
.article-reader-meta .author-link.author-link-inactive {
  cursor: default;
}

.card-meta-line .author-link.author-link-inactive:hover,
.article-reader-meta .author-link.author-link-inactive:hover {
  color: var(--text-primary);
  text-decoration: none;
}

/* Media Query Responsive Adjustments */
@media (max-width: 640px) {
  .article-reader-title {
    font-size: 1.65rem;
    line-height: 1.35;
  }
  .article-reader-container {
    padding: 10px 12px 40px 12px;
  }
  .card-title {
    font-size: 1.18rem;
    line-height: 1.35;
  }
  .medium-article-card {
    padding: 18px 0;
  }
}

.meta-stat {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--text-muted);
  font-size: 13px;
}

.article-engagement-bar {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 20px 0;
  margin-top: 32px;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.engagement-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 18px;
  border-radius: 9999px;
  border: 1px solid var(--border);
  background: var(--bg-surface);
  color: var(--text-primary);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s ease;
  touch-action: manipulation;
}

.engagement-btn:hover {
  background: var(--bg-page);
  border-color: var(--accent);
  color: var(--accent);
}

.engagement-btn.like-btn.liked {
  background: var(--accent-light, rgba(79, 70, 229, 0.1));
  color: var(--accent);
  border-color: var(--accent);
}

.engagement-btn.like-btn.liked svg {
  fill: var(--accent);
}

/* ======================== KEYBOARD FOCUS RING (BUTTONS) ========================
   The bare `:focus-visible` rule near the top of this file gives every element a
   2px --accent outline, but buttons are what a keyboard user actually traverses
   — four icon buttons per book card, fifty cards per page — and against this
   palette a plain outline alone reads as barely more than the browser default.
   This adds the same soft --focus-ring halo the text inputs already render
   (.form-input:focus, .search-box-wrapper input:focus, .primary-search-input:focus),
   so focus looks consistent whichever control you land on.

   Lives at the END of the file on purpose: the many `.btn:hover` /
   `.btn-icon:hover` box-shadow rules above are the same specificity, and at a
   tie the later rule wins. Patching this in near the top would have been
   silently overridden — the exact trap CLAUDE.md warns about.

   `:focus-visible`, never `:focus`, so a mouse click leaves no ring. No
   `outline: none` anywhere here: the outline IS the indicator, the box-shadow
   only thickens it.

   Visible in all three themes without a per-theme override — the outline is
   drawn in --accent, offset 2px so it sits on the page background rather than
   on the control's own fill (which matters for accent-filled .btn-primary):
     light  --accent #1B4332 on #F6F4EF -> 10.4:1
     dark   --accent #D4A017 on #1C1B19 ->  7.8:1
     sepia  --accent #7A2E1D on #DDD4C0 ->  6.8:1
   all far above the 3:1 WCAG asks of a non-text focus indicator. The halo
   colour --focus-ring is a low-alpha tint (0.25 / 0.25 / 0.20) and is
   decorative only, so sepia's slightly lower alpha needs no special-casing. */
.engagement-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    box-shadow: 0 0 0 4px var(--focus-ring);
}



/* ════════ Jameel Noori Nastaleeq — article prose only ════════
   The 5.55 MB calligraphic face is confined to the two surfaces where a reader
   is settling into long-form Urdu: the article reader and the article writer.
   A browser fetches a font only when something on the page uses it, so keeping
   Jameel out of every other rule is what stops the download from happening on
   the homepage, the library, and the article LIST — whose Urdu snippets carry
   .font-nastaliq too (extractArticleMeta in src/app/articles.js). Those
   previews stay on Noto; opening the article is what pays for Jameel.

   Deliberately NOT scoped to [lang="ur"]. Article bodies are per-block
   multilingual — render-article.js tags each block with the block's own
   language — so an Urdu passage inside an article read by an Arabic-UI visitor
   must still get Nastaliq. Scoping by container, not by UI language, is what
   makes that work.

   Only .font-nastaliq elements are targeted, never the container itself: the
   same article can hold Arabic and English blocks that must keep their own
   faces.

   The !important is required, not defensive. `[lang="ur"]` near the top of this
   file carries `font-family: var(--font-urdu) !important`, and while it reads as
   a rule about <html lang="ur">, it is an attribute selector that matches ANY
   element carrying lang="ur" — including every per-block paragraph inside an
   article, since render-article.js tags each block with its own language
   (`<p dir="rtl" lang="ur" class="font-nastaliq">`). That is a 0,1,0 !important
   declaration landing directly on the same element, so a 0,2,0 normal rule loses
   to it no matter how late it appears. Verified in-browser: an identical rule
   injected last still resolved to Noto; only !important took effect. */
.article-reader-body .font-nastaliq,
.article-reader-title.font-nastaliq,
#tiptap-container .font-nastaliq,
.ProseMirror .font-nastaliq,
.write-article-title-input.font-nastaliq {
    font-family: var(--font-urdu-calligraphic) !important;
}

/* Article Feed Cards (Urdu) */
.card-title.font-nastaliq,
[lang="ur"] .card-title {
    font-family: var(--font-urdu) !important;
    font-size: 1.45rem !important;
    line-height: 2.2 !important;
    font-weight: 400 !important;
    padding-top: 4px !important;
    padding-bottom: 6px !important;
    margin-bottom: 6px !important;
}

.card-snippet.font-nastaliq,
[lang="ur"] .card-snippet {
    font-family: var(--font-urdu) !important;
    font-size: 1.15rem !important;
    line-height: 2.2 !important;
    font-weight: 400 !important;
    padding-top: 2px !important;
    padding-bottom: 4px !important;
}

/* Article Reader (Urdu) */
.article-reader-title.font-nastaliq,
[lang="ur"] .article-reader-title {
    font-family: var(--font-urdu-calligraphic), var(--font-urdu) !important;
    font-size: 2.25rem !important;
    font-weight: 400 !important;
    line-height: 2.2 !important;
    padding-top: 8px !important;
    padding-bottom: 12px !important;
}

.article-reader-body .font-nastaliq,
.article-reader-body p.font-nastaliq,
[lang="ur"] .article-reader-body {
    font-family: var(--font-urdu-calligraphic), var(--font-urdu) !important;
    font-size: 2.15rem !important;
    line-height: 2.85 !important;
    padding-top: 4px !important;
    padding-bottom: 6px !important;
    word-spacing: 2px !important;
}

/* Article Writer Title Input (Urdu) */
.write-article-title-input.font-nastaliq,
[lang="ur"] .write-article-title-input,
#write-article-title.font-nastaliq,
[lang="ur"] #write-article-title {
    font-family: var(--font-urdu-calligraphic), var(--font-urdu) !important;
    font-size: 2.2rem !important;
    font-weight: 400 !important;
    line-height: 2.2 !important;
    padding: 8px 6px 14px 6px !important;
    min-height: 76px !important;
    box-sizing: border-box !important;
}

/* Article Writer Header & Toolbar (Urdu) */
[lang="ur"] .fullscreen-editor-header {
    height: auto !important;
    min-height: 58px !important;
    padding-top: 4px !important;
    padding-bottom: 4px !important;
}

[lang="ur"] .tiptap-toolbar {
    padding-top: 6px !important;
    padding-bottom: 6px !important;
}

/* Article Writer Body / Editor (Urdu) */
#tiptap-container .font-nastaliq,
.ProseMirror .font-nastaliq,
.ProseMirror p.font-nastaliq,
[lang="ur"] .ProseMirror,
[lang="ur"] .ProseMirror p {
    font-family: var(--font-urdu-calligraphic), var(--font-urdu) !important;
    font-size: 1.85rem !important;
    line-height: 2.85 !important;
    padding-top: 4px !important;
    padding-bottom: 6px !important;
    word-spacing: 2px !important;
}

/* ─── Delete Account & Article Options Styling ─── */
.delete-account-article-box {
    margin: 16px 0;
    padding: 16px;
    background: var(--bg-elevated, var(--bg-surface));
    border-radius: 10px;
    border: 1px solid var(--border);
}

.delete-article-option-card {
    display: flex;
    gap: 12px;
    align-items: flex-start;
    padding: 12px 14px;
    margin-bottom: 10px;
    border: 1.5px solid var(--border);
    border-radius: 8px;
    background: var(--bg-surface);
    cursor: pointer;
    transition: border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease;
}

.delete-article-option-card:last-child {
    margin-bottom: 0;
}

.delete-article-option-card:hover {
    border-color: var(--accent);
    background: var(--bg-elevated);
}

.delete-article-option-card:has(input[type="radio"]:checked) {
    border-color: var(--accent);
    background: var(--bg-elevated);
    box-shadow: 0 0 0 1px var(--accent);
}

[dir="ltr"] .delete-article-option-card {
    text-align: left;
}

[dir="rtl"] .delete-article-option-card {
    text-align: right;
}


/* ── Desktop shell: the brand is a label, not a link ───────────────────────
   bin/build-shell.js rewrites the navbar's <a href="/home" class="logo-section">
   into <div class="brand-static"> for the Tauri bundle, because the desktop app
   has no homepage to go back to.

   It drops .logo-section on purpose. That class sits in the
   `cursor: pointer !important` group in components.css with the buttons and
   nav links, and beating an !important needs another one — which §2 forbids.
   An element that is no longer interactive simply should not be in that group.

   Only the flex container is restated here: .logo-icon and .logo-text style
   standalone, so the mark and the wordmark keep every rule they already had,
   including the per-language sizing. The hover animation on
   `.logo-section:hover .logo-icon img` is intentionally not carried over —
   nothing happens on click, so nothing should invite one. */
[data-platform="tauri"] .brand-static {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    color: var(--text-primary);
    flex-shrink: 0;
    cursor: default;
    user-select: none;
    -webkit-user-select: none;
}

/* ── Update banner ───────────────────────────────────────────────────────────
   Shown by app/update-check.js when the server publishes a newer desktop build
   than the one running. Public app only: admin.html does not load this sheet,
   and the website has no version to offer.

   A strip rather than a modal, and prepended to <body> rather than injected
   into the header, so no other module's layout has to know it can exist. It is
   in normal flow on purpose — an overlay would cover the navbar it sits above,
   and an update is not worth hiding the app behind. */
.update-banner {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 12px;
  padding: 10px 16px;
  background: var(--bg-surface);
  color: var(--text-primary);
  border-bottom: 1px solid var(--border);
  font-size: 14px;
}

.update-banner-text { font-weight: 600; }

.update-banner-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Both buttons inherit .btn; only the size is tightened so the strip stays
   thin on a laptop screen. */
.update-banner .btn {
  padding: 5px 14px;
  font-size: 13px;
}

/* "Later" must read as the quieter of the two without becoming invisible —
   it is the choice most users will make, and hiding it is a dark pattern. */
.update-banner-later {
  background: transparent;
  color: var(--text-secondary);
  border: 1px solid var(--border);
}

/* ═══════════ BOOK CARD / ROW LINKS ═══════════
   Book titles became real anchors so crawlers have something to follow and
   people get middle-click and open-in-new-tab. Before this there was no
   <a href="/books/..."> anywhere in the app — cards were JS handlers only, and
   .book-card's `cursor: pointer` was pointing at a click nothing listened for.

   These rules live in style.css rather than beside .book-card in components.css
   on purpose: components.css is loaded by admin.html too, and this is public
   behaviour. The admin panel renders .admin-book-card / .merge-book-card, which
   share none of these class names, so nothing here can reach it. style.css is
   last in the public cascade, so the position/color declarations win without
   needing !important or a duplicated selector. */

.book-card-title,
.book-row-title {
    color: inherit;
    text-decoration: none;
}

.book-card-title:hover,
.book-row-title:hover {
    text-decoration: underline;
}

/* The stretched-link pattern: the title's ::after covers the whole card, so the
   entire tile is one link without nesting the buttons inside an <a> (invalid,
   and it would swallow their clicks). Requires a positioned ancestor. */
.book-card {
    position: relative;
}

.book-card-link::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
}

/* The action buttons must sit ABOVE the stretched overlay or it eats their
   clicks. They already call stopPropagation(), which does nothing against an
   element painted on top of them. */
.book-card-actions,
.book-more-wrap {
    position: relative;
    z-index: 1;
}

/* ═══════════ SERVER-RENDERED LANDING BLOCK ═══════════
   The crawler-visible content utils/landing-block.js injects into
   #ssr-landing. book-page.js removes it once the SPA has painted the real
   screen, so on a fast connection it is on screen for a few hundred
   milliseconds — but on the low-end Android hardware much of this audience
   reads on, it is on screen for the whole API round trip, and it is the FIRST
   thing a visitor arriving from a search result sees.

   Unstyled it was a ~4,700px wall of centred text. This is not decoration:
   an unstyled flash on the pages we are trying to get people to land on is a
   quality signal in the wrong direction, and the height collapse when the SPA
   swaps it out is layout shift.

   Deliberately minimal — it must not try to look like the real book screen,
   only like a plain, readable document. */

.ssr-landing {
    max-width: 720px;
    margin: 0 auto;
    padding: var(--space-4);
    text-align: start;
    color: var(--text-primary);
    font-size: 15px;
    line-height: 1.7;
}

.ssr-landing h1 {
    font-size: 22px;
    margin: 0 0 var(--space-2);
}

.ssr-landing h2 {
    font-size: 16px;
    margin: var(--space-4) 0 var(--space-2);
    color: var(--text-muted);
}

.ssr-landing p {
    margin: 0 0 var(--space-2);
}

.ssr-landing img {
    max-width: 160px;
    height: auto;
    border-radius: var(--radius-md);
}

/* A two-column definition list instead of the browser default, which stacks
   every term and value onto its own line and is most of where the height went. */
.ssr-landing dl {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 2px var(--space-3);
    margin: var(--space-3) 0;
}

.ssr-landing dt {
    color: var(--text-muted);
}

.ssr-landing dd {
    margin: 0;
}

.ssr-landing ul {
    margin: 0;
    padding-inline-start: var(--space-4);
}

/* ═══════════ BOOK DETAIL SCREEN ═══════════
   #screen-book, painted by src/app/book-page.js.

   This shipped with markup and no stylesheet at all — every class below had
   zero rules, so the page rendered as a column of unstyled browser defaults:
   a full-width cover, a Times New Roman h1, and a <dl> with each term on its
   own line. It is the page every search result now lands on, so it is the
   first thing a new visitor sees.

   Public-only by design. .book-page is rendered nowhere but index.html, and
   style.css is the last sheet in the public cascade, so these rules need no
   specificity tricks and cannot reach the admin panel. */

.book-page {
    max-width: 900px;
    margin: 0 auto;
    padding: var(--space-6) var(--space-4) var(--space-12);
}

/* ── Header: cover beside the identifying text ─────────────────────────── */

.book-page-header {
    display: flex;
    gap: var(--space-6);
    align-items: flex-start;
    margin-bottom: var(--space-8);
}

.book-page-cover-wrap {
    flex: 0 0 auto;
    width: 180px;
}

.book-page-cover {
    width: 100%;
    height: auto;
    display: block;
    border-radius: var(--radius-lg);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-md);
    background: var(--bg-surface);
}

.book-page-info {
    flex: 1 1 auto;
    /* Without this a long unbroken Arabic title refuses to shrink and pushes the
       cover off the edge — flex items floor at min-content width by default. */
    min-width: 0;
}

.book-page-title {
    font-family: var(--font-display);
    font-size: 28px;
    line-height: 1.4;
    margin: 0 0 var(--space-2);
    color: var(--text-primary);
}

.book-page-author {
    font-size: 16px;
    margin: 0 0 var(--space-4);
}

.book-page-author a {
    color: var(--accent);
    text-decoration: none;
}

.book-page-author a:hover {
    text-decoration: underline;
}

.book-page-death {
    color: var(--text-muted);
    font-size: 14px;
}

.book-page-description {
    color: var(--text-secondary);
    line-height: 1.8;
    margin: 0 0 var(--space-5);
}

.book-page-actions {
    display: flex;
    gap: var(--space-2);
    flex-wrap: wrap;
}

/* .btn already carries the padding, radius and inline-flex centring, and
   .btn i[data-lucide] in components.css already sizes the icon, so these
   buttons need only what is specific to this page. */

/* Saved-offline is a STATE, not an action — it should read as done rather
   than as something to press. Same green the book cards use for
   .book-opfs-btn.cached. */
.book-page-offline.cached {
    color: var(--green);
    border-color: var(--green);
}

.book-page-actions .btn:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

/* ── Sections: details, volumes ────────────────────────────────────────── */

.book-page-section {
    border-top: 1px solid var(--border);
    padding-top: var(--space-5);
    margin-top: var(--space-5);
}

.book-page-heading {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-muted);
    margin: 0 0 var(--space-3);
}

/* A two-column definition list. The browser default stacks every term and
   value on its own line, which turned six metadata rows into twelve. */
.book-page-meta {
    display: grid;
    grid-template-columns: minmax(90px, max-content) 1fr;
    gap: var(--space-2) var(--space-5);
    margin: 0;
    /* Values carry dir="auto" because the corpus mixes scripts, so a Latin value
       renders LTR and would otherwise align to the far edge of a full-width
       column — metres away from its own label in an RTL page. justify-items
       follows the GRID direction, not each item.s, so every value sits beside
       its label whichever script it is in. */
    justify-items: start;
}

.book-page-meta dt {
    color: var(--text-muted);
    font-size: 14px;
}

.book-page-meta dd {
    margin: 0;
    color: var(--text-primary);
}

.book-page-volumes {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.book-page-volumes li {
    padding: var(--space-2) var(--space-3);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    font-size: 14px;
}

.book-page-vol-num {
    font-weight: 600;
    color: var(--text-primary);
}

.book-page-vol-detail {
    color: var(--text-muted);
}

/* ── Loading and error states ──────────────────────────────────────────── */

.book-page-loading,
.book-page-error {
    max-width: 900px;
    margin: 0 auto;
    padding: var(--space-12) var(--space-4);
    text-align: center;
    color: var(--text-muted);
}

.book-page-error h1 {
    font-family: var(--font-display);
    font-size: 22px;
    color: var(--text-primary);
    margin: 0 0 var(--space-3);
}

.book-page-error a {
    color: var(--accent);
}

/* ── Narrow screens ────────────────────────────────────────────────────── */

@media (max-width: 640px) {
    .book-page {
        padding: var(--space-4) var(--space-3) var(--space-10);
    }

    /* Stacked and centred. Side-by-side at 375px leaves the title a ~140px
       column, which wraps a classical Arabic title into six lines. */
    .book-page-header {
        flex-direction: column;
        align-items: center;
        text-align: center;
        gap: var(--space-4);
    }

    .book-page-cover-wrap {
        width: 150px;
    }

    .book-page-title {
        font-size: 22px;
    }

    .book-page-actions {
        justify-content: center;
    }

    /* The title and author centre well under the cover; a five-line paragraph
       does not — centring leaves it ragged on both edges, which is markedly
       harder to read in Arabic than a single justified edge. */
    .book-page-description {
        text-align: start;
    }

    /* One column: a 90px label gutter is too much of a 375px screen. */
    .book-page-meta {
        grid-template-columns: 1fr;
        gap: 0;
    }

    .book-page-meta dt {
        margin-top: var(--space-2);
    }
}
