/* The letter bar and search box are .clu-letter-bar / .clu-search (clu-page.css).
   collection.js builds the search box inside this slot (only past 25 items), so
   the slot takes the place .clu-search takes as a direct child of a toolbar row,
   and drops out of the row while it is empty. */
.clu-toolbar-row > .collection-search-slot {
    flex: 1 1 14rem;
    max-width: 22rem;
    min-width: 0;
}

/* The view buttons are shown and hidden one by one with inline display; with
   none showing (a library root), the empty group must not leave a gap. */
.collection-search-slot:empty,
#viewToggleButtons:not(:has(> .btn:not([style*="display: none"]))) {
    display: none !important;
}

@media (max-width: 575.98px) {
    .clu-toolbar-row > .collection-search-slot {
        flex-basis: 100%;
        max-width: none;
    }
}

.file-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 1.5rem;
    padding: 1rem 0;
    /* Breathing room when changePage() jumps the viewport back to the first row */
    scroll-margin-top: 1rem;
}

/* Large cards: roughly 15% wider than standard.
   Columns are whole numbers, so raising the floor alone drops a column and the
   cards absorb every pixel it freed -- around +38% at tablet widths, far past
   "a bit bigger". Capping the track at 190px instead lets the leftover become
   margin, which holds the increase near 15% from 992px up (+13%/+15%/+15%) and
   caps the worst case at +22%. Below ~500px the floor still fits two columns,
   so phones are left alone. */
.file-grid.cards-lg {
    grid-template-columns: repeat(auto-fill, minmax(173px, 190px));
    justify-content: center;
}

/* Text layout (#694): the same .grid-item nodes as rows -- icon, name, counts
   or size, then the badges, row buttons and menu. Only the layout changes, so
   the read icon (inside .issue-badge), selection and menus keep working.
   .item-info is display:contents so its icon, details and menu become row
   items that `order` can interleave with the thumbnail container's badges;
   it needs !important to beat Bootstrap's .d-flex. The art is hidden, not
   removed: lazy covers then never load here, and do on switching back. */
.list-icon {
    display: none;
}

.file-grid.view-text {
    grid-template-columns: 1fr;
    gap: 0;
    padding: 0.25rem 0;
}

.file-grid.view-text .grid-item {
    flex-direction: row;
    align-items: center;
    gap: 0.75rem;
    padding: 0.45rem 0.75rem;
    border-bottom: 1px solid var(--bs-border-color);
}

.file-grid.view-text .grid-item:hover {
    transform: none;
    background-color: var(--bs-tertiary-bg);
}

.file-grid.view-text .item-info {
    display: contents !important;
}

.file-grid.view-text .list-icon {
    display: inline-block;
    order: 0;
    flex: 0 0 auto;
    font-size: 1.15rem;
    color: var(--bs-secondary-color);
}

.file-grid.view-text .list-icon.bi-folder-fill {
    color: var(--bs-warning);
}

.file-grid.view-text .item-details {
    order: 1;
    flex: 1 1 auto;
    min-width: 0;
}

.file-grid.view-text .thumbnail-container {
    order: 2;
    position: static;
    width: auto;
    aspect-ratio: auto;
    border-radius: 0;
    overflow: visible;
    box-shadow: none;
    contain: none;
    gap: 0.4rem;
    /* A slot as wide as the usual badges plus a row button, so the counts and
       sizes before it line up as a column whether or not a row has badges. */
    flex: 0 0 auto;
    min-width: 7.5rem;
    justify-content: flex-end;
}

.file-grid.view-text .item-actions {
    order: 3;
    flex: 0 0 auto;
}

/* Inline styles set by renderGrid (display:block on folder art, flex on the
   row buttons) are why these need !important. */
.file-grid.view-text .thumbnail,
.file-grid.view-text .icon-overlay,
.file-grid.view-text .info-button {
    display: none !important;
}

.file-grid.view-text .issue-badge,
.file-grid.view-text .xml-badge {
    position: static;
    order: -1;
    white-space: nowrap;
}

/* Favorite / To Read: inline icon buttons, shown on row hover or focus and
   always once set, so a marked row says so without hovering. */
.file-grid.view-text .favorite-button,
.file-grid.view-text .to-read-button {
    position: static;
    width: 28px;
    height: 28px;
    background: transparent;
}

.file-grid.view-text .favorite-button i,
.file-grid.view-text .to-read-button i {
    color: var(--bs-secondary-color);
}

.file-grid.view-text .favorite-button:hover i,
.file-grid.view-text .to-read-button:hover i {
    color: var(--bs-emphasis-color);
}

.file-grid.view-text .grid-item:hover .favorite-button,
.file-grid.view-text .grid-item:hover .to-read-button,
.file-grid.view-text .favorite-button:focus-visible,
.file-grid.view-text .to-read-button:focus-visible,
.file-grid.view-text .favorite-button.favorited,
.file-grid.view-text .to-read-button.marked {
    opacity: 1;
}

.file-grid.view-text .favorite-button.favorited i,
.file-grid.view-text .to-read-button.marked i {
    color: var(--bs-primary);
}

/* Selection: the row is the target, and the check sits inline at its end. */
.file-grid.view-text .grid-item.file.bulk-selected {
    background-color: rgba(var(--bs-primary-rgb), 0.15);
    box-shadow: inset 3px 0 0 var(--clu-select);
}

.file-grid.view-text .grid-item.file.bulk-selected .thumbnail-container {
    outline: none;
}

.file-grid.view-text .grid-item.file.bulk-selected .thumbnail-container::after {
    content: none;
}

.file-grid.view-text .selection-checkbox {
    position: static;
    order: 1;
    flex: 0 0 auto;
}

/* Wide enough for columns: name, then counts/size in a right-aligned column. */
@media (min-width: 768px) {
    .file-grid.view-text .item-details {
        display: flex;
        align-items: baseline;
        gap: 1rem;
    }

    .file-grid.view-text .item-name {
        flex: 1 1 auto;
        min-width: 0;
    }

    .file-grid.view-text .item-meta {
        flex: 0 0 11rem;
        text-align: right;
    }
}

@media (pointer: coarse) {
    .file-grid.view-text .favorite-button,
    .file-grid.view-text .to-read-button {
        opacity: 1;
    }
}

.grid-item {
    cursor: pointer;
    transition: transform 0.2s;
    position: relative;
    display: flex;
    flex-direction: column;
}

.grid-item.file.has-comic:hover .thumbnail {
    opacity: 0.8;
}

.grid-item:hover {
    transform: translateY(-5px);
    z-index: 10;
    /* Bring hovered item to front */
}

/* Ensure dropdown is above everything */
.item-actions .dropdown-menu {
    z-index: 1050;
    /* Bootstrap default is 1000, go higher */
}

/* Card menu toggle: the theme's muted ink, darkening to full emphasis on hover,
   so it stays legible on light and dark themes alike. */
.item-actions .btn-link {
    color: var(--bs-secondary-color);
}

.item-actions .btn-link:hover {
    color: var(--bs-emphasis-color);
}

.item-actions .btn-link i {
    color: inherit;
}

/* Make dropdown item hover more visible: the theme's own active-item fill and
   ink, which every Bootswatch and local theme sets as a contrasting pair. */
.dropdown-item:hover,
.dropdown-item:focus {
    background-color: var(--bs-dropdown-link-active-bg, var(--bs-primary));
    color: var(--bs-dropdown-link-active-color, #fff);
}

.thumbnail-container {
    position: relative;
    width: 100%;
    aspect-ratio: 2/3;
    border-radius: 8px;
    overflow: clip;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    contain: layout;
}

.thumbnail {
    object-fit: cover;
    transition: opacity 0.3s;
}

/* Every card's art fills its frame, whatever the source pixel dimensions.
   Without width/height the <img> is a flex item sized from its intrinsic size --
   and min-width:auto forbids it shrinking -- so a 200x300 folder.png overflows
   and is centre-cropped while a 167x250 one sits inside the frame at 1:1. Same
   2:3 art, two apparent sizes. Sizing from the container makes the source
   dimensions irrelevant: comic covers, both generated folder variants, and
   user-supplied folder.png/jpg/gif/webp of any size in any folder. */
.grid-item .thumbnail {
    width: 100%;
    height: 100%;
}

.icon-overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: rgba(0, 0, 0, 0.05);
    opacity: 0;
    /* Hidden by default for files with thumbnails */
}

.grid-item.folder .icon-overlay {
    opacity: 1;
    background-color: var(--bs-secondary-bg);
}

.grid-item.folder .thumbnail {
    display: none;
}

.grid-item.folder .icon-overlay i {
    font-size: 3rem;
    color: var(--bs-warning);
    /* Folder color */
}

.item-info {
    margin-top: 0.5rem;
}

.item-name {
    font-size: 0.9rem;
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--bs-body-color);
}

.item-name-tooltip .tooltip-inner {
    max-width: 300px;
    text-align: center;
}

.item-meta {
    font-size: 0.75rem;
    color: var(--bs-secondary-color);
}

/* Metadata loading state for progressive loading */
.item-meta.metadata-loading {
    font-style: italic;
    animation: metaPulse 1.5s ease-in-out infinite;
}

.item-meta.metadata-error {
    opacity: 0.6;
}

/* Not "pulse": base.html owns that name for the navbar count badges, and a
   second @keyframes pulse here silently replaced it on every page that loads
   this file. */
@keyframes metaPulse {

    0%,
    100% {
        opacity: 1;
    }

    50% {
        opacity: 0.5;
    }
}

/* Dark themes: the empty cover frame gets a well and a stronger edge, since a
   10% black shadow has nothing to darken. Scoped to :root so it can never match
   inside the navbar, which is data-bs-theme="dark" on every theme. */
:root[data-bs-theme="dark"] .thumbnail-container {
    background-color: var(--bs-tertiary-bg);
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
}

/* Drag and drop styles. An outline rather than a border, so the hovered
   card doesn't grow by 4px and shove its row. */
.grid-item.drag-over {
    transform: scale(1.05);
    box-shadow: 0 8px 16px rgba(var(--bs-primary-rgb), 0.3);
    outline: 2px solid var(--clu-select);
    outline-offset: 2px;
    border-radius: 8px;
    background-color: rgba(var(--bs-primary-rgb), 0.05);
}

.grid-item.folder.drag-over::after {
    content: 'Drop files here';
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: var(--clu-select-fill);
    color: #fff;
    padding: 8px 16px;
    border-radius: 4px;
    font-weight: 500;
    font-size: 0.9rem;
    pointer-events: none;
    z-index: 10;
}

/* Loading more indicator for progressive loading */
#loading-more-indicator {
    position: sticky;
    bottom: 20px;
    z-index: 100;
    margin: 1rem auto;
    max-width: 400px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    border-radius: 8px;
    animation: slideUp 0.3s ease-out;
}

@keyframes slideUp {
    from {
        opacity: 0;
        transform: translateY(20px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

#loading-more-indicator.fade-out {
    animation: fadeOut 0.3s ease-out;
}

@keyframes fadeOut {
    from {
        opacity: 1;
    }

    to {
        opacity: 0;
    }
}

/* ---- Sticky pagination bar (Browse Library) ----
   #pagination-controls is the last in-flow child of the tall #library-section, so a
   bottom-sticky nav pins itself to the viewport for exactly as long as there is grid
   left to scroll, then lands inline at the true bottom. No JS drives the position —
   the .is-floating class below is purely cosmetic (see initPaginationStickyObserver). */
body {
    --clu-bulkbar-h: 0px;
}

/* The bulk action bar is position:fixed with nothing reserving its space, so it used
   to cover the pager and the last grid row. Reserve its measured height instead. */
body.collection-select-mode {
    padding-bottom: var(--clu-bulkbar-h, 0px);
}

#pagination-controls {
    position: sticky;
    bottom: var(--clu-bulkbar-h, 0px);
    /* Above .grid-item:hover (10); below .collection-bulk-action-bar (1050) and modals */
    z-index: 1020;
    padding: 0.5rem 0;
}

/* The pager reads as a raised control surface rather than a patch of page.
   Both the fill and the edge are mixed from the theme's own body colour and
   background, which is the one pair every Bootswatch theme guarantees to be
   high-contrast. Deriving them beats using --bs-tertiary-bg / --bs-border-color
   directly, which several themes leave all but identical to --bs-body-bg
   (cyborg 1.01:1, simplex 1.03:1; borders on cyborg, minty and pulse).
   The plain declarations come first as the pre-color-mix() fallback. */
#pagination-controls .collection-pager-inner {
    padding: 0.5rem 0.75rem;
    border-radius: 0.75rem;
    background: var(--bs-tertiary-bg);
    background: color-mix(in srgb, var(--bs-body-color) 8%, var(--bs-body-bg));
    border: 1px solid var(--bs-border-color);
    border-color: color-mix(in srgb, var(--bs-body-color) 26%, var(--bs-body-bg));
    transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}

/* Detached state — add lift on top of the surface while the bar is pinned.
   The inset highlight is what reads as "raised" on dark themes, where a black
   drop shadow has almost nothing to darken. */
#pagination-controls.is-floating .collection-pager-inner {
    border-color: color-mix(in srgb, var(--bs-body-color) 34%, var(--bs-body-bg));
    box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.18),
                inset 0 1px 0 rgba(255, 255, 255, 0.06);
    animation: slideUp 0.3s ease-out;
}

#pagination-controls .pager-jump {
    width: auto;
}

/* Size to the widest "N of N" option rather than stretching to fill the pager row,
   which is what an .input-group child does by default. */
#pagination-controls #pageJumpSelect {
    width: auto;
    flex: 0 0 auto;
}

/* Back-to-top button: never shrink it away when a long readout competes for the row,
   and keep it square so it reads as an icon control next to the Jump-to group. */
#pagination-controls .pager-top {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 2rem;
}

/* Full body colour, not --bs-secondary-color: any mix toward the background can
   only lose contrast, and several themes (minty, morph, quartz, spacelab) start
   muted enough that the result fell to 1.8-2.8:1. Hierarchy comes from the
   smaller type instead, and legibility then matches the theme's own body text. */
#pagination-controls .pager-readout {
    color: var(--bs-body-color);
}

/* Soft busy state for in-place page changes: the bar dims but never moves or vanishes */
#pagination-controls.is-busy {
    opacity: 0.65;
}

#pagination-controls.is-busy .page-link,
#pagination-controls.is-busy .pager-jump select {
    pointer-events: none;
}

.file-grid.grid-busy {
    opacity: 0.55;
    pointer-events: none;
    transition: opacity 0.15s ease;
}

#pagination-sentinel {
    width: 100%;
    height: 1px;
    margin: 0;
}

/* .page-link touch targets are global (clu-page.css); the Back to top button is ours. */
@media (pointer: coarse) {
    #pagination-controls .pager-top {
        min-width: 2.75rem;
        min-height: 2.75rem;
        display: flex;
        align-items: center;
        justify-content: center;
    }
}

@media (max-width: 575.98px) {
    #pagination-controls .collection-pager-inner {
        padding: 0.4rem 0.5rem;
    }
}

@media (prefers-reduced-motion: reduce) {

    #pagination-controls .collection-pager-inner,
    #pagination-controls.is-floating .collection-pager-inner {
        animation: none;
        transition: none;
    }
}

@supports not (position: sticky) {
    #pagination-controls {
        /* Degrade to the previous inline-only behaviour */
        position: static;
    }
}

/* Info Button Styling */
.info-button {
    position: absolute;
    bottom: 8px;
    right: 8px;
    background: rgba(0, 0, 0, 0.7);
    border: none;
    border-radius: 50%;
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transition: opacity 0.2s ease, background 0.2s ease;
    cursor: pointer;
    z-index: 10;
}

.info-button i {
    color: white;
    font-size: 16px;
}

.thumbnail-container:hover .info-button {
    opacity: 1;
}

.info-button:hover {
    background: rgba(0, 0, 0, 0.9);
}

.info-button:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: 2px;
}

/* Text File Viewer Modal Styling */
.text-file-content {
    background-color: var(--bs-tertiary-bg);
    color: var(--bs-body-color);
    border: 1px solid var(--bs-border-color);
    border-radius: 4px;
    padding: 1rem;
    font-family: var(--bs-font-monospace);
    font-size: 14px;
    line-height: 1.6;
    white-space: pre-wrap;
    word-wrap: break-word;
    max-height: 70vh;
    overflow-y: auto;
}

.text-file-content pre {
    margin: 0;
    padding: 0;
    background: none;
    border: none;
}

/* Favorite Button Styling */
.favorite-button {
    position: absolute;
    top: 10px;
    left: 10px;
    background: rgba(0, 0, 0, 0.6);
    border: none;
    border-radius: 50%;
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    z-index: 10;
    opacity: 0;
    transition: opacity 0.2s ease, background 0.2s ease;
}

.favorite-button i {
    color: white;
    font-size: 16px;
}

.thumbnail-container:hover .favorite-button,
.dashboard-card-img-container:hover .favorite-button {
    opacity: 1;
}

.favorite-button:hover {
    background: rgba(0, 0, 0, 0.9);
}

.favorite-button:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: 2px;
}

/* Cover actions are hover-revealed; a keyboard user tabbing onto one gets the
   same reveal, or the focused control would be invisible. */
.info-button:focus-visible,
.favorite-button:focus-visible,
.to-read-button:focus-visible,
.mark-unread-btn:focus-visible {
    opacity: 1;
}

.favorite-button.favorited {
    opacity: 1;
    background: rgba(255, 193, 7, 0.9);
}

.favorite-button.favorited i {
    color: #212529;
}

.favorite-button.favorited:hover {
    background: rgba(255, 193, 7, 1);
}

/* To Read Button Styling */
.to-read-button {
    position: absolute;
    top: 10px;
    left: 10px;
    background: rgba(0, 0, 0, 0.6);
    border: none;
    border-radius: 50%;
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    z-index: 10;
    opacity: 0;
    transition: opacity 0.2s ease, background 0.2s ease;
}

.to-read-button i {
    color: white;
    font-size: 16px;
}

.thumbnail-container:hover .to-read-button,
.dashboard-card-img-container:hover .to-read-button {
    opacity: 1;
}

.to-read-button:hover {
    background: rgba(0, 0, 0, 0.9);
}

.to-read-button:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: 2px;
}

.to-read-button.marked {
    opacity: 1;
    background: rgba(255, 193, 7, 0.9);
}

.to-read-button.marked i {
    color: #212529;
}

.to-read-button.marked:hover {
    background: rgba(255, 193, 7, 1);
}

/* Mark Unread Button Styling (for Continue Reading cards) */
.mark-unread-btn {
    position: absolute;
    top: 8px;
    right: 8px;
    background: rgba(220, 53, 69, 0.8);
    border: none;
    border-radius: 50%;
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    z-index: 10;
    opacity: 0;
    transition: opacity 0.2s ease, background 0.2s ease;
}

.mark-unread-btn i {
    color: white;
    font-size: 14px;
}

.dashboard-card-img-container:hover .mark-unread-btn {
    opacity: 1;
}

.mark-unread-btn:hover {
    background: rgba(220, 53, 69, 1);
}

.mark-unread-btn:focus-visible {
    outline: 2px solid var(--bs-danger);
    outline-offset: 2px;
}

/* Missing ComicInfo.xml Badge Styling */
.xml-badge {
    position: absolute;
    bottom: 8px;
    left: 8px;
    padding: 2px 6px;
    border-radius: 4px;
    background: rgba(220, 53, 69, 0.85);
    color: white;
    font-size: 0.7rem;
    font-weight: bold;
    z-index: 10;
}

/* Issue Number Badge Styling */
.issue-badge {
    position: absolute;
    top: 8px;
    right: 8px;
    padding: 2px 6px;
    border-radius: 4px;
    background: rgb(253 200 63 / 85%);
    /* Dark ink: white on this gold is about 1.6:1 and unreadable at 0.7rem. */
    color: #212529;
    font-size: 0.7rem;
    font-weight: bold;
    z-index: 10;
    border: thin solid #000;
}

.issue-badge .read-icon {
    margin-left: 4px;
    font-size: 0.65rem;
}

/* A comic whose issue number is unknown still shows its read status, so the
   badge renders with the icon alone -- there is nothing to its left to separate
   it from. */
.issue-badge-unnumbered .read-icon {
    margin-left: 0;
}

/* Dashboard Section Styles */
.dashboard-section {
    position: relative;
}

.dashboard-swiper {
    padding: 10px 5px 30px 5px;
    /* Bottom padding for pagination/scrollbar if needed */
    overflow: hidden;
}

.dashboard-swiper .swiper-slide {
    width: 165px;
}

.dashboard-card {
    width: 100%;
    cursor: pointer;
    transition: transform 0.2s ease;
}

.dashboard-card:hover {
    transform: translateY(-5px);
}

.dashboard-card-img-container {
    position: relative;
    width: 100%;
    height: 247px;
    aspect-ratio: auto;
    border-radius: 8px;
    overflow: hidden;
    background-color: var(--bs-tertiary-bg);
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    margin-bottom: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    contain: layout;
}

.dashboard-card-img-container img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

/* Dashboard cards with folder thumbnails */
.dashboard-card.has-thumbnail .dashboard-card-img-container {
    /* overflow: visible; */
    background-color: transparent;
}

.dashboard-card .icon-overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--bs-secondary-bg);
}

.dashboard-card .icon-overlay i {
    font-size: 3rem;
    color: var(--bs-warning);
}

.dashboard-card.has-thumbnail .icon-overlay {
    display: none;
}

.dashboard-card-overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.3);
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transition: opacity 0.2s;
}

.dashboard-card:hover .dashboard-card-overlay {
    opacity: 1;
}

.dashboard-card-overlay i {
    font-size: 3rem;
    color: rgba(255, 255, 255, 0.9);
}

.dashboard-card-badge {
    position: absolute;
    top: 8px;
    right: 8px;
    padding: 4px 8px;
    border-radius: 4px;
    color: white;
    font-size: 0.75rem;
    font-weight: bold;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}

.dashboard-card-body h6 {
    margin-bottom: 2px;
    font-weight: 600;
    color: var(--bs-body-color);
}

/* Swiper Navigation Customization */
.dashboard-swiper .swiper-button-next,
.dashboard-swiper .swiper-button-prev {
    width: 32px;
    height: 32px;
    background-color: var(--bs-body-bg);
    background-color: color-mix(in srgb, var(--bs-body-bg) 90%, transparent);
    border-radius: 50%;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
    color: var(--bs-body-color);
}

.dashboard-swiper .swiper-button-next:after,
.dashboard-swiper .swiper-button-prev:after {
    font-size: 14px;
    font-weight: bold;
}

.dashboard-swiper .swiper-button-disabled {
    opacity: 0;
    cursor: default;
}

:root[data-bs-theme="dark"] .dashboard-card-img-container {
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
}

/* =============================================================================
   Drag-Drop Upload Zone for Edit Modal
   ============================================================================= */

#editCBZModal .modal-body.drag-over {
    background-color: rgba(var(--bs-primary-rgb), 0.1);
    border: 2px dashed var(--bs-primary);
    transition: all 0.2s ease;
}

#editCBZModal .modal-body.drag-over::after {
    content: "Drop files to add";
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: var(--clu-select-fill);
    color: #fff;
    padding: 1rem 2rem;
    border-radius: 0.5rem;
    font-weight: bold;
    font-size: 1.2rem;
    z-index: 9999;
    pointer-events: none;
}

/* CBZ Preview image info display */
.cbz-image-info {
    margin-top: 0.75rem;
    padding: 0.5rem 1rem;
    background-color: var(--bs-tertiary-bg);
    border-radius: 0.375rem;
    font-size: 0.85rem;
    text-align: center;
}

/* Multi-Select & Bulk Action Bar */

/* Selection colours.
   --clu-select is an outline: the theme's primary pushed 30% toward its
   emphasis colour (black on light themes, white on dark), so it stands off the
   page on either.
   --clu-select-fill carries white ink (the check, the drop label), so it is
   always pushed toward black - pushing it toward white on a dark theme would
   leave Cyborg's or Solar's white check at about 2:1.
   Plain primary first: a custom property holding an unsupported color-mix()
   is not discarded at parse time, it is invalid wherever it is used. */
:root {
    --clu-select: var(--bs-primary);
    --clu-select-fill: var(--bs-primary);
}

@supports (color: color-mix(in srgb, red 50%, blue)) {
    :root {
        --clu-select: color-mix(in srgb, var(--bs-primary) 70%, var(--bs-emphasis-color));
        --clu-select-fill: color-mix(in srgb, var(--bs-primary) 70%, #000);
    }
}

.grid-item.file.bulk-selected .thumbnail-container {
    outline: 3px solid var(--clu-select);
    outline-offset: -3px;
    border-radius: 8px;
}

.grid-item.file.bulk-selected .thumbnail-container::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(var(--bs-primary-rgb), 0.15);
    pointer-events: none;
    z-index: 5;
    border-radius: 8px;
}

/* Selection checkbox circle overlay (top-left of thumbnail) */
.selection-checkbox {
    position: absolute;
    top: 8px;
    left: 8px;
    width: 24px;
    height: 24px;
    background: var(--clu-select-fill);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 15;
    opacity: 0;
    transition: opacity 0.15s ease;
    pointer-events: none;
}

.selection-checkbox i {
    color: white;
    font-size: 14px;
}

.grid-item.file.bulk-selected .selection-checkbox {
    opacity: 1;
}

/* Show checkbox on hover when selection mode active */
.file-grid.selection-active .grid-item.file:hover .selection-checkbox {
    opacity: 0.6;
}

.file-grid.selection-active .grid-item.file.bulk-selected:hover .selection-checkbox {
    opacity: 1;
}

/* Multi-Select mode: always show checkbox indicator on file cards */
body.collection-select-mode .grid-item.file .selection-checkbox {
    opacity: 0.6;
}

body.collection-select-mode .grid-item.file.bulk-selected .selection-checkbox {
    opacity: 1;
}

body.collection-select-mode .grid-item.file {
    cursor: pointer;
}

/* Active state for the Multi-Select toggle button. Bootstrap's .active already
   fills it with the theme's own matched fill-and-ink pair; the ring is what
   says "a mode is on". */
#toggleCollectionSelectModeBtn.active {
    box-shadow: 0 0 0 0.2rem rgba(var(--bs-secondary-rgb), 0.35);
}

/* Hide favorite/to-read button when selection checkbox is visible */
.grid-item.file.bulk-selected .favorite-button,
.grid-item.file.bulk-selected .to-read-button {
    opacity: 0 !important;
}

/* Bulk action bar (fixed bottom, same pattern as series.html) */
.collection-bulk-action-bar {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1050;
    background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
    padding: 12px 0;
    box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.3);
    animation: collectionSlideUpBar 0.2s ease-out;
}

@keyframes collectionSlideUpBar {
    from {
        transform: translateY(100%);
        opacity: 0;
    }

    to {
        transform: translateY(0);
        opacity: 1;
    }
}

/* Selection hint (below breadcrumb) */
.selection-hint {
    font-size: 0.78rem;
    color: var(--bs-secondary-color);
    margin-bottom: 0.5rem;
    opacity: 0.7;
}

.selection-hint i {
    font-size: 0.72rem;
}

/* Reduced motion. Kept last on purpose: it overrides rules of equal
   specificity (.dashboard-card:hover, .collection-bulk-action-bar...) and
   only wins by coming after them. */
@media (prefers-reduced-motion: reduce) {

    #loading-more-indicator,
    #loading-more-indicator.fade-out,
    .collection-bulk-action-bar,
    .item-meta.metadata-loading {
        animation: none;
    }

    .grid-item:hover,
    .dashboard-card:hover,
    .grid-item.drag-over {
        transform: none;
    }
}

/* A search or letter is narrowing the grid: the shelves above it step aside
   (collection.js renderPage). Important: the shelves carry inline display. */
#dashboard-sections.clu-narrowing .dashboard-section:not(#library-section) {
    display: none !important;
}
