:root {
    color-scheme: light;

    /* Every color in this file comes from a token below, never a literal, so an
       alternate theme (say high contrast) is just a different set of these values.
       tests/test_theme.py enforces both that and the contrast ratios. */

    /* Surfaces */
    --color-bg: #F2F0E5;
    --color-surface: #FAF9F3;
    --color-surface-muted: #E4E9DA;

    /* Text */
    --color-text: #2C4233;
    --color-text-muted: #4D6A4A;
    --color-text-disabled: #7E9079;
    --color-link: #2B5E3B;
    --color-link-hover: #1B3F27;

    /* Lines: containers and dividers are light; controls need a stronger edge */
    --color-border: #B8C6B1;
    --color-border-strong: #5F7E5C;

    /* Buttons */
    --color-button-bg: #D9E2CF;
    --color-button-bg-hover: #C8D5BC;

    /* Status */
    --color-danger: #8B2E2E;
    --color-danger-bg: #F5E3DE;
    --color-success: #2B5E3B;
    --color-success-bg: #E2EBDA;

    /* Dims the page behind the phone filter drawer (shown at partial opacity) */
    --color-scrim: #2C4233;

    /* Comment highlights: drawn on the book's page (comments.js reads this token) and
       echoed by the comment panel's accent bar so the two read as the same thing. */
    --color-highlight: #DE9CA2;

    /* The page the book is drawn on. Deliberately NOT part of the site theme: the
       reader keeps its own look regardless of how the rest of the site is themed. */
    --reader-page-bg: #FFFFFF;

    /* How wide Archie is drawn (see .mascot); the room left for him at the end of a page follows it. */
    --mascot-width: clamp(6.5rem, 16vw, 9rem);
}

/* Rules like `form { display: flex }` would otherwise override the browser's own
   hidden-attribute styling and leave "hidden" elements on screen. */
[hidden] {
    display: none !important;
}

body {
    font-family: system-ui, sans-serif;
    background: var(--color-bg);
    color: var(--color-text);
    max-width: 960px;
    margin: 0 auto;
    padding: 0 1rem;
}

/* :visited is spelled out because browsers color visited links themselves (purple). */
a,
a:visited {
    color: var(--color-link);
}

a:hover {
    color: var(--color-link-hover);
}

button {
    font-family: inherit;
    font-size: 1rem;
    color: var(--color-text);
    background: var(--color-button-bg);
    border: 1px solid var(--color-border-strong);
    border-radius: 4px;
    padding: 0.35rem 0.8rem;
    cursor: pointer;
}

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

button:disabled {
    color: var(--color-text-disabled);
    background: var(--color-surface-muted);
    border-color: var(--color-border);
    cursor: not-allowed;
}

input:not([type]),
input[type="text"],
input[type="password"],
input[type="email"],
input[type="search"],
textarea {
    font-family: inherit;
    font-size: 1rem;
    color: var(--color-text);
    background: var(--color-surface);
    border: 1px solid var(--color-border-strong);
    border-radius: 4px;
    padding: 0.4rem 0.5rem;
}

::placeholder {
    color: var(--color-text-muted);
    opacity: 1;
}

input[type="file"] {
    font-family: inherit;
    color: var(--color-text);
}

input[type="file"]::file-selector-button {
    font-family: inherit;
    color: var(--color-text);
    background: var(--color-button-bg);
    border: 1px solid var(--color-border-strong);
    border-radius: 4px;
    padding: 0.3rem 0.7rem;
    margin-right: 0.6rem;
    cursor: pointer;
}

input[type="file"]::file-selector-button:hover {
    background: var(--color-button-bg-hover);
}

.site-nav {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 1rem 0;
    border-bottom: 1px solid var(--color-border);
    margin-bottom: 1.5rem;
}

.site-nav .brand {
    margin-right: auto;
    font-weight: bold;
    text-decoration: none;
}

.nav-links,
.nav-user {
    display: flex;
    gap: 1rem;
    align-items: center;
}

.nav-user {
    min-width: 0;
}

/* A long username is cut short rather than pushing the header wider than the screen. */
.nav-greeting {
    min-width: 0;
    max-width: 12rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Phones: brand and account controls on the first row, the page links on their own
   row underneath. (Order is only visual; the markup order is brand, links, account.) */
@media (max-width: 40rem) {
    .site-nav {
        flex-wrap: wrap;
        row-gap: 0.75rem;
    }

    /* The brand keeps its full name on one line and the account controls take whatever
       room is left (starting from zero width, so wrapping doesn't count their full
       size against the row) - the greeting is what gives way when it's tight. */
    .site-nav .brand {
        order: 1;
        flex: 0 0 auto;
        margin-right: 0;
        white-space: nowrap;
    }

    .nav-user {
        order: 2;
        flex: 1 1 0;
        justify-content: flex-end;
    }

    .nav-links {
        order: 3;
        flex: 1 0 100%;
        gap: 1.5rem;
    }

    .nav-greeting {
        max-width: 8rem;
    }
}

form {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    max-width: 320px;
}

label,
.field {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.form-error {
    color: var(--color-danger);
    background: var(--color-danger-bg);
    border: 1px solid var(--color-danger);
    padding: 0.5rem;
    border-radius: 4px;
    margin-bottom: 1rem;
}

/* A heads-up before an action, not a failure: plain text in the danger color. */
.form-warning {
    margin: 0.5rem 0 0;
    font-size: 0.9rem;
    color: var(--color-danger);
}

.edit-book-form {
    margin-top: 0.75rem;
    max-width: 28rem;
}

.edit-book-form label {
    display: block;
    margin-bottom: 0.5rem;
}

.edit-book-form input {
    display: block;
    box-sizing: border-box;
    width: 100%;
}

.edit-book-actions {
    display: flex;
    gap: 0.5rem;
}

.field-hint {
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

.form-info {
    color: var(--color-success);
    background: var(--color-success-bg);
    border: 1px solid var(--color-success);
    padding: 0.5rem;
    border-radius: 4px;
    margin-bottom: 1rem;
}

.library-layout {
    display: flex;
    gap: 2rem;
    align-items: flex-start;
}

.tag-sidebar {
    width: 200px;
    flex-shrink: 0;
}

.tag-sidebar ul {
    list-style: none;
    padding: 0;
    margin: 0.5rem 0;
    max-height: 400px;
    overflow-y: auto;
}

.tag-sidebar #tag-filter-box {
    width: 100%;
    box-sizing: border-box;
}

.tag-row a {
    text-decoration: none;
}

.tag-state-include a {
    font-weight: bold;
    color: var(--color-success);
}

.tag-state-exclude a {
    text-decoration: line-through;
    color: var(--color-danger);
}

.tag-legend {
    font-size: 0.8rem;
    color: var(--color-text-muted);
}

.library-main {
    flex: 1;
    min-width: 0;
}

.tag-panel-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.tag-panel-header h2 {
    flex: 1;
}

/* The phone filter controls: hidden on wider screens, and on phones until the script
   that drives them is running (without it the sidebar just stacks above the list). */
.filters-close,
.filters-bar,
.active-filters,
.tag-panel-actions,
.filters-backdrop {
    display: none;
}

@media (max-width: 40rem) {
    .library-layout {
        flex-direction: column;
        align-items: stretch;
        gap: 1rem;
    }

    .tag-sidebar {
        width: auto;
    }

    /* With the script running, the sidebar becomes a drawer over the book list. */
    .filters-drawer .filters-bar {
        display: block;
        margin-bottom: 0.75rem;
    }

    .filters-drawer .active-filters {
        display: flex;
        flex-wrap: wrap;
        gap: 0.4rem;
        list-style: none;
        margin: 0 0 0.75rem;
        padding: 0;
    }

    .filters-drawer .filters-close {
        display: inline-block;
    }

    .filters-drawer .tag-panel-actions {
        display: flex;
        gap: 0.5rem;
        justify-content: flex-end;
        padding-top: 0.75rem;
        border-top: 1px solid var(--color-border);
    }

    .filters-drawer .filters-backdrop {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 90;
        background: var(--color-scrim);
        opacity: 0.45;
    }

    .filters-drawer .tag-sidebar {
        position: fixed;
        top: 0;
        bottom: 0;
        left: 0;
        z-index: 100;
        display: flex;
        flex-direction: column;
        box-sizing: border-box;
        width: min(85vw, 22rem);
        padding: 1rem;
        background: var(--color-surface);
        border-right: 1px solid var(--color-border);
        transform: translateX(-100%);
        transition: transform 0.2s;
    }

    .filters-drawer .tag-sidebar.open {
        transform: none;
    }

    .filters-drawer .tag-sidebar ul {
        flex: 1;
        min-height: 0;
        max-height: none;
    }

    /* Big enough to tap comfortably. */
    .filters-drawer .tag-row a {
        display: block;
        padding: 0.65rem 0.25rem;
    }

    body.filters-open {
        overflow: hidden;
    }
}

@media (max-width: 40rem) and (prefers-reduced-motion: reduce) {
    .filters-drawer .tag-sidebar {
        transition: none;
    }
}

.active-filter {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.1rem 0.2rem 0.1rem 0.6rem;
    background: var(--color-surface-muted);
    border: 1px solid var(--color-border);
    border-radius: 4px;
}

.active-filter-remove {
    padding: 0 0.4rem;
    color: inherit;
    background: none;
    border: none;
    font-size: 1.1rem;
    line-height: 1;
}

.search-form {
    flex-direction: row;
    max-width: none;
}

/* Let the search box shrink to the space it's given instead of forcing the page wider. */
.search-form input {
    flex: 1;
    min-width: 0;
}

.library-controls {
    display: flex;
    gap: 1.5rem;
    margin: 1rem 0;
}

.book-list {
    list-style: none;
    padding: 0;
}

.book-list li {
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--color-border);
}

/* Covers: a fixed 2:3 frame, so rows and the page header don't jump as images load. A
   book without one gets the same frame left empty (the default cover). */
.book-cover {
    display: block;
    box-sizing: border-box;
    flex-shrink: 0;
    width: var(--cover-width);
    height: auto;
    aspect-ratio: 2 / 3;
    object-fit: contain;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 3px;
}

.book-cover-small {
    --cover-width: 3.5rem;
}

.book-cover-large {
    --cover-width: clamp(7rem, 30vw, 11.5rem);
}

.book-row {
    display: flex;
    gap: 0.75rem;
    align-items: flex-start;
}

.book-row-text {
    flex: 1;
    min-width: 0;
}

.book-header {
    display: flex;
    gap: 1.25rem;
    align-items: flex-start;
    margin-bottom: 1rem;
}

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

.book-header-info h1 {
    margin-top: 0;
}

.account-list {
    list-style: none;
    padding: 0;
}

.account-row {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: 0.75rem;
    padding: 0.75rem 0;
    border-bottom: 1px solid var(--color-border);
}

.account-info {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
    overflow-wrap: anywhere;
}

.account-detail {
    font-size: 0.9rem;
    color: var(--color-text-muted);
}

.account-actions {
    display: flex;
    gap: 0.5rem;
}

.account-actions .inline-form {
    margin-left: 0;
}

.inline-form {
    display: inline;
    margin-left: 0.5rem;
}

.book-author {
    color: var(--color-text-muted);
}

.book-tags,
.tag-chip {
    font-size: 0.8rem;
}

/* Under the title and author, wrapping onto more lines when there are many. */
.book-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    margin-top: 0.3rem;
}

.book-tags .tag-chip {
    margin-left: 0;
}

.tag-chip {
    display: inline-block;
    background: var(--color-surface-muted);
    border: 1px solid var(--color-border);
    border-radius: 4px;
    padding: 0.1rem 0.4rem;
    margin-left: 0.3rem;
}

/* Archie, peeking up from the bottom right corner of the screen (see _mascot.html). In front of
   the page, so text and dividers scrolling past never run across the drawing; it ignores clicks
   and selection, so nothing under it stops working. Below the filter drawer and its dimmer. */
.mascot {
    position: fixed;
    right: 1rem;
    bottom: 0;
    z-index: 10;
    width: var(--mascot-width);
    height: auto;
    pointer-events: none;
    user-select: none;
}

/* Empty room at the very end of the page, as tall as Archie (the picture is 354 by 133), so the
   last of the page can always be scrolled clear of him. */
.mascot-clearance {
    height: calc(var(--mascot-width) * 133 / 354 + 1rem);
}

@media print {
    .mascot {
        display: none;
    }
}

.pagination {
    display: flex;
    justify-content: space-between;
    margin-top: 1rem;
}

#tag-chip-list {
    list-style: none;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

#tag-chip-list .tag-chip button {
    border: none;
    background: none;
    color: inherit;
    padding: 0;
    cursor: pointer;
    margin-left: 0.2rem;
}

/* Wide enough for its hint ("Type a tag and press Enter"); the browser's default of 20
   characters cut it off. Full width on a phone, where the Save button then sits below. */
#tag-input {
    box-sizing: border-box;
    width: 100%;
    max-width: 20rem;
    margin: 0 0.4rem 0.4rem 0;
}

#tag-suggestions {
    list-style: none;
    padding: 0;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    max-width: 300px;
}

#tag-suggestions li {
    padding: 0.3rem;
    cursor: pointer;
}

#tag-suggestions li:hover {
    background: var(--color-surface-muted);
}

/* The upload form has room for a list of files when several are chosen at once. */
#upload-form {
    max-width: 34rem;
}

.form-section {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.form-section h2 {
    margin: 0;
    font-size: 1.1rem;
}

.form-section p {
    margin: 0;
}

.series-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.series-row {
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-areas:
        "main remove"
        "status status";
    gap: 0.35rem 0.5rem;
    align-items: start;
    padding: 0.5rem;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 4px;
}

.series-row-main {
    grid-area: main;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-width: 0;
}

.series-row-main input {
    box-sizing: border-box;
    width: 100%;
}

.series-file {
    overflow-wrap: anywhere;
}

.series-status {
    grid-area: status;
    font-size: 0.9rem;
    color: var(--color-text-muted);
}

.series-row[data-state="done"] .series-status {
    color: var(--color-success);
}

.series-row[data-state="failed"] .series-status,
.series-row[data-state="rejected"] .series-status {
    color: var(--color-danger);
}

.series-remove {
    grid-area: remove;
    min-width: 2.25rem;
    min-height: 2.25rem;
    padding: 0;
}

#series-summary {
    margin: 0;
}

#chapter-nav {
    margin-bottom: 1rem;
}

#chapter-select-button {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    width: 100%;
    box-sizing: border-box;
    padding: 0.4rem 0.8rem;
    text-align: left;
}

#chapter-select-label {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.chapter-menu-icon,
.chapter-menu-chevron {
    flex-shrink: 0;
}

.chapter-menu-chevron {
    transition: transform 0.15s;
}

#chapter-select-button[aria-expanded="true"] .chapter-menu-chevron {
    transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
    .chapter-menu-chevron {
        transition: none;
    }
}

#chapter-select-list {
    list-style: none;
    padding: 0;
    margin: 0.25rem 0 0;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    max-height: 220px;
    overflow-y: auto;
}

#chapter-select-list li {
    /* --depth is set by reader.js: how far down the contents this entry sits. */
    padding: 0.3rem 0.5rem 0.3rem calc(0.5rem + var(--depth, 0) * 1rem);
    cursor: pointer;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

#chapter-select-list li:hover {
    background: var(--color-surface-muted);
}

#reader-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1rem;
}

#reader-toolbar button {
    padding: 0.4rem 0.8rem;
}

/* One fixed height for every button here: the font toggle's label is drawn in
   different fonts (OpenDyslexic is much taller than most), which would otherwise
   change its size. */
#reader-settings button {
    box-sizing: border-box;
    height: 2.25rem;
    padding: 0 0.8rem;
    line-height: 1;
}

#reader-settings {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 1rem;
}

/* The toggle's label is set in OpenDyslexic (when it offers the dyslexia font) or in
   the book's own font (when it offers to go back), so it has to load in the page too. */
@font-face {
    font-family: "OpenDyslexic";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("/static/fonts/opendyslexic-latin-400-normal.woff2") format("woff2");
}

#dyslexic-toggle {
    width: 12rem;
    font-family: "OpenDyslexic", sans-serif;
}

/* Fixed width, so long font names are cut short rather than resizing the button. */
#dyslexic-toggle-label {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

#reader-viewport {
    position: relative;
    background: var(--reader-page-bg);
    border: 1px solid var(--color-border);
    border-radius: 4px;
    max-width: 100%;
    overflow: auto;
}

#reader-viewport canvas {
    display: block;
    margin: 0 auto;
    max-width: 100%;
}

/* Touch screens: turning pages and starting a comment live in a bar fixed to the bottom of the
   screen, always within reach of a thumb, and nothing lies over the page itself - so the text
   can be selected, and highlights tapped, anywhere on it. */
@media (pointer: coarse) {
    /* Previous at the far left, Next at the far right, Comment between them, and the page
       counter on a line of its own above. (The markup order is Prev, counter, Next, Comment,
       which is what the desktop toolbar shows; the grid areas rearrange it here.) */
    #reader-toolbar {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 20;
        box-sizing: border-box;
        margin: 0;
        padding: 0.35rem 0.75rem calc(0.5rem + env(safe-area-inset-bottom, 0px));
        display: grid;
        grid-template-columns: auto 1fr auto;
        grid-template-areas:
            "status status status"
            "prev comment next";
        align-items: center;
        gap: 0.2rem 0.5rem;
        background: var(--color-bg);
        border-top: 1px solid var(--color-border);
    }

    #reader-toolbar button {
        min-height: 2.75rem;
        padding: 0 0.9rem;
    }

    #reader-toolbar #reader-status {
        grid-area: status;
        font-size: 0.85rem;
        text-align: center;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    #reader-toolbar #reader-prev {
        grid-area: prev;
    }

    #reader-toolbar #comment-button {
        grid-area: comment;
        justify-self: center;
    }

    #reader-toolbar #reader-next {
        grid-area: next;
    }

    /* Room at the end of the page, so the bar never hides the last of it. */
    .reader-bar-spacer {
        height: 6.5rem;
    }

    #comment-panel {
        scroll-margin-bottom: 6.5rem;
    }
}

.comment-panel {
    margin-top: 1rem;
    padding: 0.75rem 1rem;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 4px;
}

.comment-panel-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.comment-panel-header h2 {
    margin: 0;
    font-size: 1.1rem;
}

.comment-quote {
    margin: 0.75rem 0;
    padding-left: 0.75rem;
    border-left: 3px solid var(--color-highlight);
    font-style: italic;
}

.comment-meta {
    margin: 0 0 0.25rem;
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

.comment-text {
    margin: 0;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.comment {
    margin-bottom: 1rem;
    padding-bottom: 0.75rem;
    border-bottom: 1px solid var(--color-border);
}

.comment:last-child {
    margin-bottom: 0;
    border-bottom: none;
}

.comment-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.5rem;
}

/* Replies sit under the comment they answer, set in by a rule in the same accent as the
   quote's so the two read as one conversation. */
.comment-replies {
    margin: 0.75rem 0 0 0.25rem;
    padding-left: 0.75rem;
    border-left: 3px solid var(--color-border-strong);
}

/* Deep in a long chain the indent stops growing (see MAX_INDENT_DEPTH in comments.js). */
.comment-replies-flat {
    margin-left: 0;
    padding-left: 0;
    border-left: none;
}

.comment-reply {
    margin-bottom: 0.75rem;
    padding-bottom: 0;
    border-bottom: none;
}

.comment-reply:last-child {
    margin-bottom: 0;
}

.comment-reply-context {
    margin: 0 0 0.75rem;
    padding-left: 0.75rem;
    border-left: 3px solid var(--color-border-strong);
    font-size: 0.9rem;
    color: var(--color-text-muted);
    overflow-wrap: anywhere;
}

.comment-add {
    margin-top: 0.75rem;
}

.comment-form {
    max-width: none;
}

.comment-form textarea {
    width: 100%;
    box-sizing: border-box;
    font: inherit;
}

.comment-form-actions {
    display: flex;
    gap: 0.5rem;
}

/* epub.js draws highlights in an SVG layer that ignores pointer events and instead
   relies on listeners inside the book iframe to forward clicks - which WebKit never
   runs there. Making the highlight shapes hit-testable lets the main page catch taps
   directly (and keeps them from also turning the page). */
.comment-highlight {
    pointer-events: auto;
    cursor: pointer;
}

/* A short message fixed to the screen, so it can appear without moving the page (see comments.js). */
.reader-toast {
    position: fixed;
    left: 50%;
    bottom: 1rem;
    transform: translateX(-50%);
    z-index: 40;
    box-sizing: border-box;
    width: max-content;
    max-width: min(90vw, 26rem);
    padding: 0.6rem 0.9rem;
    border-radius: 6px;
    background: var(--color-text);
    color: var(--color-bg);
    font-size: 0.95rem;
    text-align: center;
    pointer-events: none;
}

@media (pointer: coarse) {
    .reader-toast {
        bottom: 6.5rem; /* above the page-turning bar */
    }
}
