/* The rules of the site. Colours, fonts, sizes, corners and shadows are not set here: they are the variables of
   tokens.css, which also brings in Bootstrap and which every page links right before this file. A new value goes
   there, and is read here with var().

   Every page on this site is built from Bootstrap's own components and
   utility classes -- no site-wide hand-rolled design system sits behind
   it anymore. There are four small exceptions below, each scoped to the
   one page/widget that needs it and built from Bootstrap
   components/utilities everywhere else: the chapter reading page's prose
   typography (hyphenation, aligned and indented paragraphs, a font-family/column-width picker, and a sepia reading
   theme -- see ARCHITECTURE.md for the full writeup of that decision),
   the fandom/pairing/author pickers' dropdown positioning
   (works/search.html -- fandom/pairing only on
   the former, see WorkViewController's own javadoc; see
   fandom-picker.js/pairing-picker.js/author-picker.js for their
   interaction logic; language is plain checkboxes, same pattern as
   genre/rating/status/relationship-category below, not a picker), an
   accordion filter section's header
   sharing its row with a second, always-visible "?" control that opens
   an explanation popup (works/search.html),
   and those same two pages' fixed-width filters-sidebar-on-desktop
   layout (see .works-layout below). Site-wide light/dark mode is
   Bootstrap's own
   "data-bs-theme" color mode (see theme.js); the site's palette, fonts and
   corners are the tokens of tokens.css, which redefine Bootstrap's own
   variables for both modes. */
/* Bootstrap's type, from tokens.css ("Text sizes", "Fonts"): Bootstrap sets it in numbers. At the top of the file, so a
   component's own rule below still wins, as it did over Bootstrap. */
h1, .h1 { font-size: var(--lf-heading-1); }
h2, .h2 { font-size: var(--lf-heading-2); }
h3, .h3 { font-size: var(--lf-heading-3); }
h4, .h4 { font-size: var(--lf-heading-4); }
h5, .h5 { font-size: var(--lf-heading-5); }
h6, .h6 { font-size: var(--lf-heading-6); }
h1, .h1, h2, .h2, h3, .h3, h4, .h4, h5, .h5, h6, .h6 { line-height: var(--lf-leading-tight); }
.fs-1 { font-size: var(--lf-heading-1) !important; }
.fs-2 { font-size: var(--lf-heading-2) !important; }
.fs-3 { font-size: var(--lf-heading-3) !important; }
.fs-4 { font-size: var(--lf-heading-4) !important; }
.fs-5 { font-size: var(--lf-heading-5) !important; }
.fs-6 { font-size: var(--lf-heading-6) !important; }
.display-1 {
    font-size: var(--lf-text-display-1);
    font-weight: var(--lf-weight-light);
    line-height: var(--lf-leading-tight);
}
.lead {
    font-size: var(--lf-text-lead);
    font-weight: var(--lf-weight-light);
}
small, .small { font-size: var(--lf-text-small); }
.fw-light { font-weight: var(--lf-weight-light) !important; }
.fw-normal { font-weight: var(--lf-weight-regular) !important; }
.fw-medium { font-weight: var(--lf-weight-medium) !important; }
.fw-semibold { font-weight: var(--lf-weight-semibold) !important; }
.fw-bold { font-weight: var(--lf-weight-bold) !important; }

/* The small upper-case labels (a section's heading in a menu or the footer, a table's heading, a date, a kicker): one
   look from tokens.css ("Labels"); each component sets only its colour and place. */
.home-fest-label,
.home-update-time,
.page-footer h3,
.site-navbar .nav-menu .dropdown-header,
.site-navbar .nav-find-heading,
.news-types-head,
.hidden-label,
.stats-label, .stats-total-label,
.stats-table th,
.work-facts dt, .table > thead th,
.review-card-date,
.ref-kicker,
.tag-tile-category,
.top-leader,
.top-move-new,
.compose-eyebrow {
    font-family: var(--lf-label-font);
    font-size: var(--lf-label-size);
    font-weight: var(--lf-label-weight);
    text-transform: uppercase;
    letter-spacing: var(--lf-label-tracking);
}

/* Links read --bs-link-color. Bootstrap reads three numbers (--bs-link-color-rgb), which a derived shade of the brand
   cannot give. As in Bootstrap, hovering only changes the variable: a link with a colour of its own keeps it. */
a {
    color: var(--lf-link-color, var(--bs-link-color));
}
a:hover {
    --lf-link-color: var(--bs-link-hover-color);
}

/* The picture of an author in the settings. */
.avatar-preview {
    width: var(--lf-avatar-lg);
    height: var(--lf-avatar-lg);
    border-radius: var(--lf-radius-avatar);
    object-fit: cover;
    object-position: center;
}
/* The list of suggestions under a search field. */
.picker-list,
.pm-emoji-panel {
    box-shadow: var(--lf-shadow-menu);
}
.picker-list {
    z-index: var(--lf-z-picker);
}


/* The small cross of a badge's "remove" (a .btn-close is sized in em). */
.btn-close-tiny {
    font-size: var(--lf-text-3xs);
}

/* ===== Works filters/results layout (works/search.html, prompts/index.html) =====
   Per explicit user request: on a tablet and narrower the filters stay
   above the results, same stacked block Bootstrap's own normal flow
   already gives these pages -- .works-layout is plain block-level below
   this rule's breakpoint, no override needed. On a desktop screen the
   filters become a FIXED-width sidebar beside the results instead of a
   stacked block -- deliberately not a Bootstrap grid column (.col-xl-3
   and friends size as a *percentage* of the row, which isn't what
   "fixed width" asked for), so this needs its own flex rule. The
   breakpoint is plain viewport width, same as every Bootstrap
   breakpoint -- CSS has no way to ask "is this a tablet or a desktop"
   directly -- chosen at Bootstrap's own 1200px ("xl") step since a
   typical tablet, portrait or landscape (including a landscape iPad,
   ~1024px), stays under it, while a laptop/desktop viewport (1200px+)
   clears it. */
@media (min-width: 1200px) {
    .works-layout {
        display: flex;
        align-items: flex-start;
        gap: 2rem;
    }

    .works-filters {
        flex: 0 0 320px;
        width: 320px;
    }

    .works-results {
        flex: 1 1 auto;
        min-width: 0;
    }
}

/* ===== Fandom picker (works/search.html only) =====
   The one piece of this widget Bootstrap has no ready-made utility for:
   absolutely positioning the filtered match list under the input it
   belongs to. Everything else -- the chips, the input, the list items --
   is plain Bootstrap components/utilities directly in the template
   (badge/rounded-pill, btn-close, form-control, list-group/
   list-group-item-action). See fandom-picker.js for the interaction
   logic (typing to filter, clicking to pick, the "x" to un-pick). */
.fandom-picker-dropdown {
    top: 100%;
    left: 0;
    z-index: var(--lf-z-dropdown);
    max-height: 16rem;
    overflow-y: auto;
}

/* ===== Author picker (works/search.html) =====
   Same one exception as the fandom picker above, for the author
   picker's own dropdown -- a separate widget/class (it filters authors,
   not fandoms, and has no universe-style grouping), so it needs its own
   selector even though the rule itself is identical. See
   author-picker.js for the interaction logic. */
.author-picker-dropdown {
    top: 100%;
    left: 0;
    z-index: var(--lf-z-dropdown);
    max-height: 16rem;
    overflow-y: auto;
}

/* ===== Tag picker (works/search.html) =====
   Same one exception as the fandom/author pickers above, for the tag
   picker's own dropdown -- a separate widget/class, so it needs its own
   selector even though the rule itself is identical. See tag-picker.js
   for the interaction logic. */
.tag-picker-dropdown {
    top: 100%;
    left: 0;
    z-index: var(--lf-z-dropdown);
    max-height: 16rem;
    overflow-y: auto;
}

/* ===== Pairing picker (works/search.html only) =====
   Same one exception as the fandom picker above, for the pairing
   picker's own character dropdown -- a separate widget/class (it filters
   characters, not fandoms, and builds a pairing out of several picks
   rather than one chip per pick), so it needs its own selector even
   though the rule itself is identical. See pairing-picker.js for the
   interaction logic. */
.pairing-picker-dropdown {
    top: 100%;
    left: 0;
    z-index: var(--lf-z-dropdown);
    max-height: 16rem;
    overflow-y: auto;
}

/* ===== Character picker (extract/form.html) =====
   Same one exception as the other pickers above, for its own dropdown.
   See character-picker.js. */
.character-picker-dropdown {
    top: 100%;
    left: 0;
    z-index: var(--lf-z-dropdown);
    max-height: 16rem;
    overflow-y: auto;
}

/* ===== The prompts' chip picker (fragments/prompt.html) =====
   The same height limit for its list of matches; see prompt-picker.js. */
[data-chip-picker] > ul {
    max-height: 16rem;
    overflow-y: auto;
}

/* ===== Filter info button (works/search.html --
   any accordion-item filter section that wants one; rating and
   relationship-category are the two using it so far) =====
   Bootstrap's .accordion-button is width:100% by design, and its own
   chevron (::after) is pushed to that full-width button's own right
   edge via margin-left:auto -- shrinking the button (an earlier version
   of this rule did, to make room for a plain sibling <button>) pulls the
   chevron in close to the text instead, inconsistent with every other
   accordion section's chevron sitting flush at the row's far right, per
   explicit user feedback. This version leaves .accordion-button
   completely untouched (full width, default chevron position) and
   instead layers a "?" button (opens that section's own info modal --
   e.g. #ratingInfoModal, #relationshipCategoryInfoModal -- with every
   option's full explanation; see ContentRating#code()/#displayName's own
   javadoc for why the filter checkboxes and a modal like this
   deliberately show different things) on top of it with
   position:absolute, offset far enough from the right edge
   (accordion-btn-icon-width 1.25rem + accordion-btn-padding-x 1.25rem =
   2.5rem reserved for the chevron itself, per Bootstrap's own
   _variables.scss) to sit just to its left without overlapping it.
   Needs its accordion-header ancestor to carry Bootstrap's own
   position-relative utility (see the template) so this positions
   against that row, not the page. No button-in-button nesting, no extra
   JS -- two ordinary Bootstrap buttons, one layered over the other.
   .filter-info-button is the one other thing Bootstrap has no utility
   for here: a small, fixed-size round button (btn-sm + rounded-circle
   alone gives an oval, since btn-sm's padding isn't equal left/right vs
   top/bottom). One shared class/rule for every such button, since the
   sizing and positioning are identical regardless of which filter
   section it belongs to -- only the modal it opens (data-bs-target) and
   that modal's own id/content differ per section. Each modal itself
   (its header/body/footer, and the dt/dl list of options inside) is
   plain Bootstrap components and needs nothing here. */
.filter-info-button {
    top: 50%;
    right: 3rem;
    transform: translateY(-50%);
    z-index: 2;
}

/* ===== Reader typography exception (works/chapter.html only) =====
   Everything below is scoped to .reader-page (#readerPage) and exists
   only because Bootstrap has no utility for long-form prose typography.
   The reading page's own chrome -- toolbar, offcanvas panels, buttons,
   prev/next row -- is built entirely from Bootstrap components/utilities
   in the template itself and needs nothing here. */

/* The page itself is edge to edge (no margin or padding against the browser window, so the
   toolbar sits flush with the top, left and right edges); the text column lives in the inner
   .reader-content, which carries the width / padding below. */
.reader-page {
    width: 100%;
    max-width: none;
    margin: 0;
    padding: 0;
    font-family: var(--lf-font-reader-georgia);
    line-height: var(--lf-leading-loose);
    text-rendering: optimizeLegibility;
    -webkit-font-smoothing: antialiased;
}

/* The reader's toolbar slides out of view while the text is scrolled down and back in when
   scrolling up (reader.js toggles .reader-toolbar-hidden). It stays sticky, so it keeps its
   place in the page flow either way. */
.reader-toolbar {
    transition: transform var(--lf-duration-base) ease-in-out;
}
.reader-toolbar.reader-toolbar-hidden {
    transform: translateY(-100%);
}

/* Icons on the toolbar's "Зміст" and "Повернутись на сайт" buttons: inline SVG drawn in the
   text colour (currentColor), slightly taller than the text and centred by the buttons' flex row. */
.reader-btn-icon {
    flex-shrink: 0;
}

/* The ⚙ on the settings button: larger than the button's text. The button is an inline flex row
   with align-items: center, so the icon box is centred against the text; the box has line-height 1
   (no extra line space making the button taller than the others) and the glyph itself is nudged up
   a little, since the gear sits low inside its own em box in most fonts. */
.reader-settings-icon {
    display: inline-block;
    font-size: var(--lf-text-xl);
    line-height: var(--lf-leading-none);
    transform: translateY(-0.03em);
}

/* The work's title in the toolbar: larger than the buttons' text. */
.reader-toolbar-title {
    font-size: var(--lf-text-xl);
}

/* The work's title: on medium and wider screens it is taken out of the row and centred on the
   page itself (left: 50%), not in the gap between the left buttons and the settings button,
   whose widths differ. Its max-width keeps it clear of the left buttons; clicks pass through to
   whatever is under it. On narrow screens there is no room for that, so it stays in the row,
   between the buttons. */
@media (min-width: 768px) {
    .reader-toolbar-title {
        position: absolute;
        left: 50%;
        transform: translateX(-50%);
        max-width: 35%;
        pointer-events: none;
    }
}
@media (min-width: 992px) {
    .reader-toolbar-title {
        max-width: 50%;
    }
}
/* On a phone the buttons show only their icons (works/chapter.html hides the names below md), each a square of
   the same size; the row becomes a grid of three columns whose sides are equally wide (the left one holds two
   buttons, the right one the settings, pushed to its end), so the title in the middle is centred on the page. It
   wraps onto at most two lines instead of being cut to one. */
@media (max-width: 767.98px) {
    .reader-toolbar-row {
        --reader-toolbar-button: var(--lf-btn-side);
        display: grid !important;
        grid-template-columns: calc(2 * var(--reader-toolbar-button) + 0.5rem) minmax(0, 1fr)
                               calc(2 * var(--reader-toolbar-button) + 0.5rem);
    }
    .reader-toolbar-row .btn {
        width: var(--reader-toolbar-button);
        height: var(--reader-toolbar-button);
        padding: 0;
        justify-content: center;
    }
    .reader-toolbar-row > .btn {
        justify-self: end;
        margin-left: 0 !important;
    }
    .reader-toolbar-title {
        font-size: var(--lf-text-base);
        line-height: var(--lf-leading-snug);
        white-space: normal;
        overflow-wrap: anywhere;
        display: -webkit-box;
        -webkit-box-orient: vertical;
        -webkit-line-clamp: 2;
        line-clamp: 2;
        overflow: hidden;
        padding-left: 0 !important;
        padding-right: 0 !important;
    }
}
@media (prefers-reduced-motion: reduce) {
    .reader-toolbar {
        transition: none;
    }
}

.reader-page .reader-content {
    width: var(--reader-width, 95%);
    max-width: none;
    margin: 0 auto;
    padding: 0.5rem clamp(0.6rem, 2vw, 1.25rem) 3rem;
}

/* The page mode (reader-nav.js puts .reader-paged on #readerPage and .reader-paged-root on <html>):
   the document stops scrolling, #readerFrame clips the text to one page and #readerFlow is laid out
   in columns as tall as the window (--reader-page-height), one or two of them per page
   (--reader-page-columns), and moved sideways by --reader-page-offset. The gap between the columns
   is twice the frame's side padding, so the neighbouring pages stay just outside the frame. Screen
   only: printing gets the plain long column. */
@media screen {
    html.reader-paged-root,
    html.reader-paged-root body {
        overflow: hidden;
        overscroll-behavior: none;
    }
    .reader-page.reader-paged .reader-content {
        overflow: hidden;
        overflow: clip;
        padding-bottom: 0;
        /* Sideways drags are the pages' (reader-nav.js); pinch zoom stays the browser's. */
        touch-action: pinch-zoom;
    }
    .reader-page.reader-paged .reader-flow {
        height: var(--reader-page-height, 70vh);
        column-count: var(--reader-page-columns, 1);
        column-gap: var(--reader-page-gap, 2rem);
        column-fill: auto;
        transform: translateX(var(--reader-page-offset, 0px));
        transition: transform var(--lf-duration-slow) ease;
        will-change: transform;
    }
    /* Two columns side by side: a thin line between them. */
    .reader-page.reader-paged .reader-flow.reader-flow-spread {
        column-rule: 1px solid var(--bs-border-color-translucent);
    }
    .reader-page.reader-paged .reader-flow.reader-flow-instant {
        transition: none;
    }
    /* Nothing taller than a page; pictures, code and tables are not cut between pages when they fit
       on one, and a heading stays with what follows it. */
    .reader-page.reader-paged .reader-flow img,
    .reader-page.reader-paged .reader-flow video {
        max-height: var(--reader-page-height, 70vh);
        object-fit: contain;
    }
    .reader-page.reader-paged .reader-flow img,
    .reader-page.reader-paged .reader-flow pre,
    .reader-page.reader-paged .reader-flow table,
    .reader-page.reader-paged .reader-flow figure {
        break-inside: avoid;
    }
    .reader-page.reader-paged .reader-flow :is(h1, h2, h3, h4, h5, h6) {
        break-after: avoid;
    }
    /* The page-turn strips (works/chapter.html, reader-nav.js): along the frame's left and right edges, over the
       text but letting every click through (reader-nav.js turns the page on a click inside a strip's box). Touch
       screens get wide, invisible strips; with a mouse they are narrow and show (.reader-turn-active) while the
       pointer is over one -- a pale rounded band with an arrow. */
    .reader-page.reader-paged .reader-content {
        position: relative;
    }
    .reader-turn {
        display: none;
    }
    .reader-page.reader-paged .reader-turn {
        position: absolute;
        top: 0;
        bottom: 0;
        z-index: 2;
        width: clamp(3.5rem, 18vw, 7rem);
        display: flex;
        align-items: center;
        justify-content: center;
        pointer-events: none;
        opacity: 0;
        border-radius: var(--bs-border-radius);
        background-color: rgba(var(--bs-secondary-rgb), 0.16);
        color: rgba(var(--bs-secondary-rgb), 0.75);
        transition: opacity var(--lf-duration-fast) ease;
    }
    .reader-page.reader-paged .reader-turn-prev {
        left: 0;
    }
    .reader-page.reader-paged .reader-turn-next {
        right: 0;
    }
    .reader-turn svg {
        width: 1.6rem;
        height: 1.6rem;
    }
    @media (hover: hover) and (pointer: fine) {
        .reader-page.reader-paged .reader-turn {
            width: 3.5rem;
        }
        .reader-page.reader-paged .reader-turn.reader-turn-active {
            opacity: 1;
        }
        .reader-page.reader-paged .reader-content.reader-turn-hover {
            cursor: pointer;
        }
    }
    .reader-page-status {
        padding: 0.25rem 0 0.5rem;
        font-variant-numeric: tabular-nums;
    }
}
@media (prefers-reduced-motion: reduce) {
    .reader-page.reader-paged .reader-flow {
        transition: none;
    }
}

/* Font family picker -- a data-attribute on #readerPage, set by reader.js; Georgia (the base rule
   above) is the default and needs no attribute of its own. The web fonts (Roboto, Alegreya,
   Comfortaa, Fira Sans, PT Sans) are loaded by reader.js from the jsDelivr CDN (@fontsource) only
   once picked; until then, or if the CDN is unreachable, the fallbacks after them apply. */
.reader-page[data-font="roboto"] {
    font-family: var(--lf-font-reader-roboto);
}
.reader-page[data-font="verdana"] {
    font-family: var(--lf-font-reader-verdana);
}
.reader-page[data-font="arial"] {
    font-family: var(--lf-font-reader-arial);
}
.reader-page[data-font="alegreya"] {
    font-family: var(--lf-font-reader-alegreya);
}
.reader-page[data-font="times"] {
    font-family: var(--lf-font-reader-times);
}
.reader-page[data-font="comfortaa"] {
    font-family: var(--lf-font-reader-comfortaa);
}
.reader-page[data-font="fira-sans"] {
    font-family: var(--lf-font-reader-fira);
}
.reader-page[data-font="pt-sans"] {
    font-family: var(--lf-font-reader-pt-sans);
}

/* Text width: a slider (reader.js) sets --reader-width on #readerPage as a percentage of the
   window (30-100%, default 95%), used by .reader-content above. */

/* Sepia reading theme -- the one reading-theme option that isn't a
   Bootstrap color mode ("light"/"dark" are handled by reader.js scoping
   Bootstrap's own data-bs-theme to <body> instead, no CSS needed
   here for those two). Redefines the handful of "--bs-*" variables this
   site's pages actually read for color, the same bridge technique the
   site used before going all-in on Bootstrap, but now scoped to just
   this one reading-only exception instead of the whole site. */


/* Base paragraph typography -- justified (the reader may choose another alignment, data-align below), hyphenated,
   first-line indent. No "text-wrap: pretty" while words are hyphenated: Chrome's balancing of the lines then leaves
   Ukrainian words whole, and the justified lines get wide spaces. */
.reader-page article p {
    margin: 0 0 0.95rem;
    text-align: justify;
    hyphens: auto;
    hanging-punctuation: first;
    text-indent: 1.25em;
}

/* A chapter's paragraph is a Markdown block: a div with its own paragraphs. A line of dialogue (.dialogue, see
   ParagraphPresenter: it opens with a dash) looks like any other paragraph, as in a printed book -- shifted aside
   without its indent it read like an item of a list. An aligned block (:::center and so on) overrides the alignment
   of the text inside it, the reader's chosen one too. */
.reader-page article .chapter-md .text-start p,
.reader-page article .chapter-md .text-center p,
.reader-page article .chapter-md .text-end p {
    text-indent: 0;
}
/* The buttons under a selection of the chapter's text (js/reader-selection.js) and the passage in their dialog. */
.reader-selection-menu {
    position: absolute;
    z-index: var(--lf-z-selection-menu);
    display: flex;
    gap: .25rem;
    padding: .25rem;
    border-radius: var(--bs-border-radius);
    background: var(--bs-dark);
    box-shadow: var(--lf-shadow-float);
}
.reader-selection-menu .btn {
    color: var(--lf-white);
    white-space: nowrap;
}
.reader-selection-menu .btn:hover,
.reader-selection-menu .btn:focus-visible {
    background: rgba(var(--lf-white-rgb), .15);
    color: var(--lf-white);
}
/* A paragraph with working notes not resolved, marked in the reader for the work's participants (works/chapter.html):
   a tint only, so the text keeps its place in the pages. */
.reader-noted {
    background: rgba(var(--bs-warning-rgb), .15);
    border-radius: var(--lf-radius-xs);
}
/* "Повідомлення про помилки" (author/error-reports.html): the reader's description keeps its line breaks. */
.error-report-description {
    white-space: pre-line;
}
.reader-selection-quote {
    max-height: 12rem;
    overflow-y: auto;
    margin-bottom: .75rem;
    padding: .5rem .75rem;
    border-left: var(--lf-border-quote);
    color: var(--bs-secondary-color);
    white-space: pre-line;
}
.reader-page article .chapter-md .text-start p {
    text-align: left;
}
.reader-page article .chapter-md .text-center p {
    text-align: center;
}
.reader-page article .chapter-md .text-end p {
    text-align: right;
}

/* data-align (see reader.js): the alignment the reader chose instead of the justified default. A centred or
   right-aligned paragraph has no first-line indent. */
.reader-page[data-align="left"] article p {
    text-align: left;
}
.reader-page[data-align="center"] article p {
    text-align: center;
    text-indent: 0;
}
.reader-page[data-align="right"] article p {
    text-align: right;
    text-indent: 0;
}

/* "Біонічний шрифт" (data-bionic="on", see reader.js): the first half of every word is wrapped in .reader-bionic.
   Inside bold text (b/strong) the whole word is bold anyway. */
.reader-page[data-bionic="on"] .reader-bionic {
    font-weight: var(--lf-weight-bold);
}

/* data-hyphens="off" (see reader.js) -- hyphenation defaults to on
   (matches the base rule above), this un-sets it; with words kept whole, the lines are balanced. */
.reader-page[data-hyphens="off"] article p {
    hyphens: none;
    text-wrap: pretty;
}

/* Print: the reading column itself needs a layout reset with no
   Bootstrap utility equivalent (arbitrary width/margin/padding values,
   a bumped font size for the printed page). Everything else that should
   disappear on paper -- the site nav, the sticky toolbar, both
   offcanvas panels, the prev/next row -- is hidden with Bootstrap's own
   ".d-print-none" utility class applied directly in the templates,
   nothing to do here for those. */
@media print {
    .reader-page,
    .reader-page .reader-content {
        width: 100%;
        max-width: none;
        margin: 0;
        padding: 0;
        background: none;
        color: var(--lf-black);
    }

    .reader-page article p {
        font-size: 24pt;
    }
}

/* ===== Relationship-category colors (works/search.html cards) =====
   One color per RelationshipCategory: gen = brown, het = green, slash =
   light blue, femslash = purple, multi = yellow, other = turquoise. A
   card with a category gets .relationship-card plus
   .relationship-<name lower-cased>, which only sets two custom
   properties (--relationship-bg / --relationship-fg); .relationship-card
   uses --relationship-bg for a thick left border -- the colored vertical
   line down the card's side -- and the direction badge inside it
   (.relationship-badge, always the first badge) uses both as its
   background/text, so the line and the badge always match. Solid colors
   with their own text color, not Bootstrap's theme-aware ones, so they
   read the same in light and dark mode. A card without a category
   carries none of these classes and keeps the plain list-group look. */
/* No category set: the card still gets the stripe, in neutral grey. */
.relationship-card {
    border-left: 6px solid var(--relationship-bg);
}
.relationship-badge {
    background-color: var(--relationship-bg);
    color: var(--relationship-fg);
}

/* ===== Work card: rating and status badges =====
   Outlined ("inverted") badges: the colour is the border and the text,
   the fill is the page background. The .rating-<enum name lower-cased> /
   .status-<enum name lower-cased> class only sets --badge-border and
   --badge-text (the text is a darker shade for the light colours, so it
   stays readable on a white fill); .work-badge paints them.
   Rating: G dark green, PG-13 light green, R yellow, NC-17 red, NC-21
   burgundy. Status: in progress yellow, completed green, frozen blue. A
   translation (.badge-translation, after the status): the site's purple. */
.work-badge {
    background-color: var(--bs-body-bg);
    border: 1px solid var(--badge-border);
    color: var(--badge-text);
}
.status-announced {
    --badge-border: var(--lf-brand);
    --badge-text: var(--lf-accent-ink);
}
.badge-translation {
    --badge-border: var(--lf-brand);
    --badge-text: var(--lf-accent-ink);
}

/* ===== Work card description =====
   Clamped to 7 lines with an ellipsis; description-clamp.js adds a
   "show more" toggle only when the text is actually cut off, and
   toggling adds .expanded (no clamp). The annotation is Markdown turned
   into HTML (it also carries .news-body), a single line break in it is a <br>. */
.work-description {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 7;
    line-clamp: 7;
    overflow: hidden;
}
.work-description.expanded {
    display: block;
    -webkit-line-clamp: unset;
    line-clamp: unset;
    overflow: visible;
}

/* The description's show-more / show-less toggle: one chevron, pointing
   down while collapsed ("show more") and flipped up once expanded. The
   button spans the whole row (the entire row is the click target) with
   the chevron centred in it. */
.work-description-toggle {
    display: block;
    width: 100%;
    padding: 0.1rem 0;
    text-align: center;
    line-height: var(--lf-leading-none);
}
.work-description-toggle[hidden] {
    display: none;
}
.work-description-toggle .toggle-chevron {
    width: 1.25rem;
    height: 1.25rem;
    transition: transform var(--lf-duration-fast);
}
.work-description-toggle[aria-expanded="true"] .toggle-chevron {
    transform: rotate(180deg);
}

/* ===== Work card tags: one box per tag (like Ficbook). */
.tag-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.1rem 0.5rem;
    border-radius: var(--lf-radius-xs);
    background-color: var(--bs-tertiary-bg);
    color: var(--bs-body-color);
    text-decoration: none;
}
.tag-chip:hover {
    background-color: var(--bs-secondary-bg);
    color: var(--bs-body-color);
}
/* The "…" box that shows a card's hidden tags (js/tag-more.js): a tag box that is a button. */
.tag-chip-more {
    border: 0;
    font: inherit;
    font-weight: var(--lf-weight-semibold);
    line-height: inherit;
    cursor: pointer;
}

/* The annotation on the work page: shown in full (it is HTML made from Markdown, also .news-body). */
.work-description-full {
    overflow-wrap: anywhere;
}
/* Pictures in an annotation on a catalogue card must not make it huge. */
.work-description img {
    max-height: 14rem;
    width: auto;
    max-width: 100%;
}
.news-body > :last-child {
    margin-bottom: 0;
}
/* The editor's preview shows the same HTML as the readers. */
.editor-preview img,
.editor-preview-side img {
    max-width: 100%;
    height: auto;
}
/* The editor's toolbar stays in sight under the site's sticky bar while the text is scrolled (js/md-editor.js keeps
   --md-toolbar-top at the bar's height); not in full screen, which places it itself, nor in the short comment box. */
.EasyMDEContainer .editor-toolbar:not(.fullscreen) {
    position: sticky;
    top: var(--md-toolbar-top, 0px);
    z-index: var(--lf-z-editor-toolbar);
    background: var(--bs-body-bg);
}
.work-comments .EasyMDEContainer .editor-toolbar:not(.fullscreen) {
    position: static;
}
/* The table button has the class "table", which Bootstrap's tables would stretch over the whole row. */
.editor-toolbar button.table {
    width: 30px;
    margin-bottom: 0;
    vertical-align: baseline;
    color: inherit;
    border-color: transparent;
}
.editor-toolbar button.table:hover,
.editor-toolbar button.table.active {
    border-color: var(--lf-editor-border);
}

/* Work page: the facts sidebar -- a card with a top accent in the work's
   direction colour (the --relationship-bg set by .relationship-<name>), and
   a definition list whose labels sit above their values. */
.relationship-accent {
    border-top: 6px solid var(--relationship-bg);
}
.work-facts dt {
    font-weight: var(--lf-weight-semibold);
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-md);
    margin-top: 0.75rem;
}
.work-facts dt:first-child {
    margin-top: 0;
}
.work-facts dd {
    margin-bottom: 0;
}

/* Dashed variant of <hr> (work cards: divider after the fandoms). */
.hr-dashed {
    border-top-style: dashed;
}

/* Home page: the sections under the hero ("Останні оновлення", "Нові твори"). */
.home-section {
    margin-top: 3rem;
}
.home-section-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem 1rem;
    margin-bottom: 1rem;
}
/* The fest of the hero (fest.FestShowcase): a tinted card with a small mono label. */
.home-fest {
    background: var(--lf-accent-soft);
    border-color: var(--lf-accent-line);
    transition: box-shadow var(--lf-duration-fast) ease;
}
.home-fest:hover,
.home-fest:focus-visible {
    box-shadow: var(--lf-shadow-hover);
}
.home-fest-label {
    color: var(--lf-brand);
    margin-bottom: 0.5rem;
}
.home-fest-title {
    font-family: var(--lf-font-title);
}
/* "Останні оновлення": two columns of rows on a wide screen; a row is the cover, the work, its latest chapters and the time.
   Under the pointer (or with a link of it focused) a row lifts a little on a soft purple ground, as the cards of
   "Популярне" do; its own padding keeps the text where it was (the negative margin), so nothing moves sideways. */
.home-updates {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 420px), 1fr));
    gap: 0 2rem;
    margin-bottom: 0;
}
.home-update {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.75rem;
    margin: 0 -0.75rem;
    border-bottom: var(--lf-border);
    transition: background-color var(--lf-duration-fast) ease, box-shadow var(--lf-duration-fast) ease, transform var(--lf-duration-fast) ease, border-color var(--lf-duration-fast) ease;
}
/* Rounded only when lifted: the separators between the rows stay straight. */
.home-update:hover,
.home-update:focus-within {
    border-radius: var(--bs-border-radius-lg);
    background: var(--lf-accent-soft);
    border-bottom-color: transparent;
    box-shadow: var(--lf-shadow-hover);
    transform: translateY(-2px);
}
@media (prefers-reduced-motion: reduce) {
    .home-update { transition: none; }
    .home-update:hover,
    .home-update:focus-within { transform: none; }
}
.home-update-main {
    flex: 1 1 auto;
    min-width: 0;
}
/* The work's cover at the start of a row (fragments/work-card :: cover): a small upright book, as on the cards. */
.home-update-cover {
    position: relative;
    flex: none;
    align-self: flex-start;
    width: 3.5rem;
    aspect-ratio: 2 / 3;
    border-radius: var(--bs-border-radius);
    overflow: hidden;
    background: var(--bs-tertiary-bg);
    box-shadow: var(--lf-shadow-cover);
}
.home-update-title {
    font-weight: var(--lf-weight-semibold);
    text-decoration: none;
    margin-right: 0.25rem;
}
.home-update-chapters li {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.home-update-chapters li::before {
    content: "▸ ";
    color: var(--lf-brand);
}
.home-update-time {
    flex: none;
    color: var(--bs-secondary-color);
    white-space: nowrap;
}
/* "Продовжити читання" (reader/home.html): how far into the chapter the reader is, and the way back to it. */
.home-continue-progress {
    height: 4px;
    max-width: 14rem;
    border-radius: var(--lf-radius-hair);
    background: var(--lf-accent-line);
    overflow: hidden;
}
.home-continue-progress > span {
    display: block;
    height: 100%;
    background: var(--lf-brand);
}
.home-continue-go {
    flex: none;
    align-self: center;
    font-size: var(--lf-text-md);
    font-weight: var(--lf-weight-semibold);
    text-decoration: none;
    white-space: nowrap;
}
/* The home page's body and the narrow column on its right (home.html): "Продовжити читання", "Останні коментарі",
   "Новини", "Фандоми".
   On a narrower screen the column is undone (display: contents): "Продовжити читання" (.home-aside-lead) goes above
   the body, the rest under it. 300px beside the body leave it five cards of a card row on the widest screen. */
.home-layout {
    display: flex;
    flex-direction: column;
}
.home-main {
    min-width: 0;
}
.home-aside {
    display: contents;
}
.home-aside-lead {
    order: -1;
}
.home-aside-panel {
    margin-top: 3rem;
    padding: 0.75rem 1rem 0.5rem;
    border: var(--lf-border);
    border-radius: var(--bs-border-radius-lg);
    background: var(--lf-surface);
}
@media (min-width: 992px) {
    .home-layout {
        display: grid;
        grid-template-columns: minmax(0, 1fr) 300px;
        gap: 0 2rem;
        align-items: start;
    }
    .home-layout-wide {
        grid-template-columns: minmax(0, 1fr);
    }
    .home-aside {
        display: block;
        margin-top: 3rem;
    }
    .home-aside-panel {
        margin-top: 0;
    }
    .home-aside-panel + .home-aside-panel {
        margin-top: 1.5rem;
    }
}
.home-aside-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    padding-bottom: 0.5rem;
    border-bottom: var(--lf-border);
}
/* A line of the column: the work on the left, cut to one line; on the right the numbers and the time, in mono. */
.home-aside-line,
.home-aside-continue {
    display: flex;
    gap: 0.75rem;
    padding: 0.5rem 0;
    border-bottom: var(--lf-border);
}
.home-aside-line:last-child,
.home-aside-continue:last-child {
    border-bottom: 0;
}
.home-aside-line {
    justify-content: space-between;
    color: var(--bs-body-color);
    text-decoration: none;
}
.home-aside-line:hover,
.home-aside-line:focus-visible {
    color: var(--lf-brand);
}
.home-aside-line-main {
    display: flex;
    flex-direction: column;
    min-width: 0;
}
.home-aside-line-authors {
    font-size: var(--lf-text-xs);
    color: var(--bs-secondary-color);
}
.home-aside-line-title,
.home-aside-line-authors {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.home-aside-line-title {
    font-size: var(--lf-text-md);
    font-weight: var(--lf-weight-semibold);
}
.home-aside-line-meta {
    flex: none;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    font-family: var(--lf-font-mono);
    font-size: var(--lf-text-xs);
}
.home-aside-line-time {
    color: var(--bs-secondary-color);
    white-space: nowrap;
}
.home-aside-continue .home-update-cover {
    width: 2.5rem;
}
.home-aside-continue .home-update-title {
    display: block;
    font-size: var(--lf-text-md);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* "Почати писати" (info/start-writing.html): the steps' numbers in purple circles. */
.start-writing-number {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: var(--lf-radius-circle);
    background: var(--lf-accent-soft);
    color: var(--lf-brand);
    font-weight: var(--lf-weight-bold);
}
/* "Фандоми", "Жанри": chips with the name and the number of works. */
.home-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}
.home-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0.75rem;
    border: var(--lf-border-accent);
    border-radius: var(--bs-border-radius-pill);
    color: var(--bs-body-color);
    text-decoration: none;
    transition: background-color var(--lf-duration-fast) ease, border-color var(--lf-duration-fast) ease;
}
.home-chip:hover,
.home-chip:focus-visible {
    background: var(--lf-accent-soft);
    border-color: var(--lf-brand);
    color: var(--bs-body-color);
}
.home-chip-count {
    font-family: var(--lf-font-mono);
    font-size: var(--lf-text-xs);
    color: var(--lf-brand);
}
/* "Новини" in the column on the right (news/home.html): the newest posts as a carousel of one slide at a time
   (js/news-carousel.js), the newest first, with the arrows and "2 / 5" under it; the excerpt is cut after three lines. */
.news-carousel-slide {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 0.6rem 0;
    color: var(--bs-body-color);
    text-decoration: none;
}
.news-carousel-slide:hover,
.news-carousel-slide:focus-visible {
    color: var(--lf-brand);
}
.home-news-date {
    font-family: var(--lf-font-mono);
    font-size: var(--lf-text-xs);
    color: var(--bs-secondary-color);
}
.home-news-title {
    font-weight: var(--lf-weight-semibold);
}
.news-carousel-slide .home-news-title {
    font-size: var(--lf-text-md);
}
.home-news-excerpt {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-size: var(--lf-text-sm);
    color: var(--bs-secondary-color);
}
.news-carousel:not(.is-carousel) .news-carousel-slide + .news-carousel-slide {
    border-top: var(--lf-border);
}
.news-carousel.is-carousel .news-carousel-slides {
    min-height: 6.5rem;
}
.news-carousel.is-carousel .news-carousel-slide.is-off {
    display: none;
}
.news-carousel.is-carousel .news-carousel-slide:not(.is-off) {
    animation: home-carousel-in var(--lf-duration-base) ease;
}
@media (prefers-reduced-motion: reduce) {
    .news-carousel.is-carousel .news-carousel-slide:not(.is-off) { animation: none; }
}
.news-carousel-all {
    justify-content: center;
    min-height: 6.5rem;
    font-weight: var(--lf-weight-semibold);
    text-align: center;
    color: var(--lf-brand);
}
.news-carousel-controls {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.25rem 0 0.5rem;
}
.news-carousel-controls[hidden] {
    display: none;
}
.news-carousel-place {
    font-family: var(--lf-font-mono);
    font-size: var(--lf-text-xs);
    color: var(--bs-secondary-color);
}
/* "Фандоми" in the column on the right (works/home-sections.html): smaller chips. */
.home-aside-chips {
    gap: 0.375rem;
    padding: 0.6rem 0 0.25rem;
}
.home-aside-chips .home-chip {
    gap: 0.375rem;
    padding: 0.2rem 0.6rem;
    font-size: var(--lf-text-sm);
}

/* Sticky-footer layout: the body is a full-height column, the footer (fragments/footer.html)
   takes the leftover space above it via margin-top: auto. */
body {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}
.page-footer {
    margin-top: auto;
    /* A square block in the header's purple (the footer carries data-bs-theme="dark", so its text,
       links and buttons are light), the column titles set as small mono labels. */
    padding-top: 2.5rem !important;
    border: 0 !important;
    position: relative;
    background-color: var(--lf-bar-bg);
    color: var(--lf-white);
}
/* Inverted corners over the footer, the header's ones turned upwards: a square of the footer's purple
   stands on each end of it with a quarter circle cut out, so the page above looks like a sheet with
   rounded bottom corners. */
.page-footer::before,
.page-footer::after {
    content: "";
    position: absolute;
    bottom: 100%;
    width: var(--lf-bar-corner);
    height: var(--lf-bar-corner);
    pointer-events: none;
}
.page-footer::before {
    left: 0;
    background: radial-gradient(circle at 100% 0, transparent calc(var(--lf-bar-corner) - .5px), var(--lf-bar-bg) var(--lf-bar-corner));
}
.page-footer::after {
    right: 0;
    background: radial-gradient(circle at 0 0, transparent calc(var(--lf-bar-corner) - .5px), var(--lf-bar-bg) var(--lf-bar-corner));
}
.page-footer h3 {
    color: rgba(var(--lf-white-rgb), .75);
}
/* The footer's last column: the rest of the row from md up, wrapping to a row of its own when less than
   its basis is left (the columns before it are as wide as their content, with equal gutters). */
@media (min-width: 768px) {
    .page-footer .footer-notes {
        flex: 1 1 19rem;
    }
}
/* From xl up all four columns fit in one row: the last one stands at the right edge, the gaps stay equal. */
@media (min-width: 1200px) {
    .page-footer .footer-columns {
        justify-content: space-between;
    }
    .page-footer .footer-notes {
        flex: 0 0 22rem;
    }
}
.page-footer .footer-nav a {
    color: var(--lf-white);
    text-decoration: none;
}
.page-footer .footer-nav a:hover {
    color: var(--lf-white);
    text-decoration: underline;
}
/* Small print, the rule and the theme button on the footer's purple: white at reduced strength, so they
   read on it (Bootstrap's own greys are too dark there). */
.page-footer .text-secondary {
    color: rgba(var(--lf-white-rgb), .8) !important;
}
.page-footer hr {
    border-color: rgba(var(--lf-white-rgb), .35);
    opacity: 1;
}
.page-footer .btn-outline-secondary {
    --bs-btn-color: var(--lf-white);
    --bs-btn-border-color: rgba(var(--lf-white-rgb), .5);
    --bs-btn-hover-color: var(--lf-white);
    --bs-btn-hover-bg: rgba(var(--lf-white-rgb), .15);
    --bs-btn-hover-border-color: var(--lf-white);
    --bs-btn-active-color: var(--lf-white);
    --bs-btn-active-bg: rgba(var(--lf-white-rgb), .22);
    --bs-btn-active-border-color: var(--lf-white);
}
/* The social networks under "Написати нам": round outlined icon buttons. */
.page-footer .footer-social a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border: 1px solid rgba(var(--lf-white-rgb), .5);
    border-radius: var(--lf-radius-circle);
    color: var(--lf-white);
}
.page-footer .footer-social a:hover,
.page-footer .footer-social a:focus-visible {
    background: rgba(var(--lf-white-rgb), .15);
    border-color: var(--lf-white);
}
.page-footer .footer-social svg {
    width: 1.15rem;
    height: 1.15rem;
}
/* The footer theme menu (fragments/theme-picker.html): wide enough for the two schedule times in one row. */
.page-footer .theme-menu {
    min-width: 17rem;
}

/* Purple navbar (fragments/nav.html): a square bar flush with the window's top and side edges. The
   nav carries data-bs-theme="dark", so its
   text is white in both site themes (white on the brand's purple is ~4.8:1); a deeper purple when the whole
   site is in dark mode. .btn-nav is an inverted (light-tint) button for the login / theme buttons. */
.site-navbar {
    border: 0 !important;
    background-color: var(--lf-bar-bg);
}
/* Inverted ("concave") bottom corners: a small square of the bar's purple hangs under each end of the
   bar with a quarter circle cut out of it, so the bar seems to flow down into the page's sides and the
   page below looks like a sheet with rounded top corners. They follow the sticky bar while scrolling
   and let clicks through. */
.site-navbar::before,
.site-navbar::after {
    content: "";
    position: absolute;
    top: 100%;
    width: var(--lf-bar-corner);
    height: var(--lf-bar-corner);
    pointer-events: none;
}
.site-navbar::before {
    left: 0;
    background: radial-gradient(circle at 100% 100%, transparent calc(var(--lf-bar-corner) - .5px), var(--lf-bar-bg) var(--lf-bar-corner));
}
.site-navbar::after {
    right: 0;
    background: radial-gradient(circle at 0 100%, transparent calc(var(--lf-bar-corner) - .5px), var(--lf-bar-bg) var(--lf-bar-corner));
}
.site-navbar .navbar-brand {
    font-weight: var(--lf-weight-semibold) !important;
    letter-spacing: var(--lf-tracking-snug);
}
/* Once after signing in (js/nav-move.js): the copy of a visitor's item flying from its place in the bar to the menu
   that holds it for a signed-in user. Over everything, never in the way of a click. */
.site-navbar .nav-move-ghost {
    position: fixed;
    z-index: var(--lf-z-ghost);
    margin: 0;
    white-space: nowrap;
    pointer-events: none;
}
/* Bootstrap's dark navbar ([data-bs-theme=dark] on .navbar, specificity 0,2,0) dims the menu
   links to 55% white, which a plain .site-navbar rule can't override -- so the selector here
   is deliberately more specific. Menu links are plain white, brand and buttons too. */
.navbar.site-navbar[data-bs-theme="dark"] {
    --bs-navbar-color: var(--lf-white);
    --bs-navbar-hover-color: var(--lf-white);
    --bs-navbar-active-color: var(--lf-white);
    --bs-navbar-brand-color: var(--lf-white);
    --bs-navbar-brand-hover-color: var(--lf-white);
}
.navbar.site-navbar .nav-link {
    color: var(--lf-white);
}
.navbar.site-navbar .nav-link:hover,
.navbar.site-navbar .nav-link:focus {
    color: var(--lf-white);
    text-decoration: underline;
}
/* Dropdown menus: a plain panel in the page's own colours (cream with dark text in light mode -- the
   light tokens are put back on them, see tokens.css -- and charcoal in dark mode), a thin
   border and a soft shadow; the hovered item gets the purple tint. */
.site-navbar .dropdown-menu {
    --bs-dropdown-bg: var(--lf-surface);
    --bs-dropdown-color: var(--bs-body-color);
    --bs-dropdown-border-color: var(--bs-border-color);
    --bs-dropdown-link-color: var(--bs-body-color);
    --bs-dropdown-link-hover-color: var(--lf-accent-ink);
    --bs-dropdown-link-hover-bg: var(--lf-accent-soft);
    --bs-dropdown-link-active-color: var(--lf-white);
    --bs-dropdown-link-active-bg: var(--lf-brand);
    --bs-dropdown-divider-bg: var(--bs-border-color);
    --bs-dropdown-header-color: var(--bs-secondary-color);
    color: var(--bs-body-color);
    box-shadow: var(--lf-shadow-menu);
}
/* On the expanded navbar the menu hangs from the nav item, i.e. from inside the bar's own
   padding -- so it overlapped the header. Push it down by that padding (plus a small gap)
   so it opens below the header edge. The collapsed (phone / tablet) menu keeps its normal flow.
   1200px = the navbar-expand-xl breakpoint in fragments/nav.html. */
@media (min-width: 1200px) {
    .site-navbar .dropdown-menu {
        --bs-dropdown-spacer: 0.7rem;
    }
}
/* The section menus of the header (fragments/nav.html: ul.nav-menu -- "Спільнота", "Читачу", "Автору", "Асистенту",
   "Модератору", "Адміну", and the profile menu), in the look of "Знайти": rounded items, a line icon before each name
   (fragments/nav-icons.html), the soft purple under the pointer, a counter at the right, the groups' small mono
   purple labels. */
.site-navbar .nav-menu {
    min-width: 15rem;
    padding: 0.5rem;
}
.site-navbar .nav-menu .dropdown-item {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.45rem 0.7rem 0.45rem 0.6rem;
    border-radius: var(--bs-border-radius);
    font-size: var(--lf-text-md);
    font-weight: var(--lf-weight-medium);
    color: var(--bs-secondary-color);
    transition: background-color var(--lf-duration-fast) ease, color var(--lf-duration-fast) ease;
}
.site-navbar .nav-menu .dropdown-item:hover,
.site-navbar .nav-menu .dropdown-item:focus-visible {
    color: var(--lf-accent-ink);
    background-color: var(--lf-accent-soft);
}
.site-navbar .nav-menu .dropdown-item:active {
    color: var(--lf-white);
    background-color: var(--lf-brand);
}
.site-navbar .nav-menu .nav-menu-icon {
    width: 1.15rem;
    height: 1.15rem;
    flex-shrink: 0;
}
.site-navbar .nav-menu .nav-menu-label {
    flex: 1 1 auto;
}
.site-navbar .nav-menu .badge {
    margin-left: auto;
}
.site-navbar .nav-menu .dropdown-header {
    padding: 0.55rem 0.7rem 0.25rem;
    color: var(--lf-brand);
}
.site-navbar .nav-menu li:first-child > .dropdown-header {
    padding-top: 0.2rem;
}
/* The collapsed menu centres its items: the icon stays before the name. */
@media (max-width: 1199.98px) {
    .site-navbar .nav-menu .dropdown-item {
        justify-content: center;
    }
    .site-navbar .nav-menu .nav-menu-label {
        flex: 0 1 auto;
    }
}
/* "Знайти" (fragments/nav.html, js/nav-find.js): on the expanded navbar a panel of two parts -- the kinds ("Оригінали",
   "Фанфікшн", "Збірники") and the plain links with their icons on the left, the panel of the kind pointed at on the
   right (the site's tinted card, like the fest on the home page: soft purple with a purple line, the small mono
   labels; a line about the kind, the popular genres / fandoms / tags / collections as white chips, the ways to all
   of them). In the collapsed menu, and before the script has run, the panels are one under another, each with its
   own title. */
.site-navbar .nav-find {
    padding: 0.75rem;
}
.site-navbar .nav-find-side {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}
.site-navbar .nav-find-kind,
.site-navbar .nav-find-link {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    border-radius: var(--bs-border-radius);
    color: var(--bs-secondary-color);
    text-decoration: none;
    transition: background-color var(--lf-duration-fast) ease, color var(--lf-duration-fast) ease;
}
.site-navbar .nav-find-kind {
    padding: 0.6rem 0.75rem;
    font-weight: var(--lf-weight-semibold);
    font-size: var(--lf-text-ui);
}
.site-navbar .nav-find-link {
    padding: 0.45rem 0.75rem 0.45rem 0.6rem;
    font-weight: var(--lf-weight-medium);
    font-size: var(--lf-text-md);
}
.site-navbar .nav-find-link svg {
    width: 1.15rem;
    height: 1.15rem;
    flex-shrink: 0;
}
.site-navbar .nav-find-kind:hover,
.site-navbar .nav-find-kind:focus-visible,
.site-navbar .nav-find-link:hover,
.site-navbar .nav-find-link:focus-visible {
    color: var(--bs-body-color);
    background-color: var(--bs-tertiary-bg);
}
.site-navbar .nav-find-kind.is-active {
    color: var(--lf-white);
    background-color: var(--lf-brand);
}
.site-navbar .nav-find-divider {
    height: 1px;
    margin: 0.35rem 0.5rem;
    background-color: var(--bs-border-color);
}
.site-navbar .nav-find-pane {
    padding: 1rem 1.1rem;
    border: var(--lf-border-accent);
    border-radius: var(--bs-border-radius-lg);
    background-color: var(--lf-accent-soft);
}
.site-navbar .nav-find-pane + .nav-find-pane {
    margin-top: 0.5rem;
}
.site-navbar .nav-find-pane-title {
    display: block;
    margin-bottom: 0.25rem;
    font-weight: var(--lf-weight-semibold);
    font-size: var(--lf-text-md);
    color: var(--bs-body-color);
    text-decoration: none;
}
.site-navbar .nav-find-intro {
    margin: 0 0 0.85rem;
    font-size: var(--lf-text-md);
    line-height: var(--lf-leading-base);
    color: var(--bs-secondary-color);
}
.site-navbar .nav-find-heading {
    margin: 0.6rem 0 0.45rem;
    color: var(--lf-brand);
}
/* The popular genres / fandoms / tags in rows, as many a row as fit: a chip as wide as its name, at most a third of the
   row (so at least three a row), a longer name cut with an ellipsis, the whole of it in the chip's title; the
   collections at most half the row (at least two a row). js/nav-find.js keeps three rows (data-rows; four for the
   collections) and hides the chips after them. */
.site-navbar .nav-find-chips {
    --nav-find-gap: 0.35rem;
    display: flex;
    flex-wrap: wrap;
    gap: var(--nav-find-gap);
    margin-bottom: 0.5rem;
}
.site-navbar .nav-find-chip {
    flex: 0 1 auto;
    min-width: 0;
    max-width: calc((100% - 2 * var(--nav-find-gap)) / 3);
    padding: 0.25rem 0.65rem;
    overflow: hidden;
    border: var(--lf-border-accent);
    border-radius: var(--bs-border-radius-pill);
    background-color: var(--lf-surface);
    font-size: var(--lf-text-sm);
    line-height: var(--lf-leading-normal);
    color: var(--bs-body-color);
    text-decoration: none;
    text-overflow: ellipsis;
    white-space: nowrap;
    transition: background-color var(--lf-duration-fast) ease, border-color var(--lf-duration-fast) ease, color var(--lf-duration-fast) ease;
}
.site-navbar .nav-find-chips-wide .nav-find-chip {
    max-width: calc((100% - var(--nav-find-gap)) / 2);
}
.site-navbar .nav-find-chip:hover,
.site-navbar .nav-find-chip:focus-visible {
    border-color: var(--lf-brand);
    background-color: var(--lf-brand);
    color: var(--lf-white);
}
.site-navbar .nav-find-more {
    display: flex;
    flex-wrap: wrap;
    gap: 0.15rem 0.35rem;
    margin-top: 0.75rem;
    padding-top: 0.6rem;
    border-top: var(--lf-border-accent);
}
.site-navbar .nav-find-more a {
    padding: 0.25rem 0.5rem;
    border-radius: var(--bs-border-radius-sm);
    font-size: var(--lf-text-md);
    font-weight: var(--lf-weight-medium);
    color: var(--lf-brand);
    text-decoration: none;
}
.site-navbar .nav-find-more a:hover,
.site-navbar .nav-find-more a:focus-visible {
    background-color: var(--lf-surface);
    color: var(--lf-accent-ink);
}
/* The collapsed menu shows the panels with their titles; the kinds would only repeat them. */
@media (max-width: 1199.98px) {
    .site-navbar .nav-find-kind {
        display: none;
    }
    .site-navbar .nav-find-side {
        align-items: center;
        margin-bottom: 0.5rem;
    }
    .site-navbar .nav-find-divider {
        display: none;
    }
    .site-navbar .nav-find-chips {
        justify-content: center;
    }
    .site-navbar .nav-find-more {
        justify-content: center;
    }
}
@media (min-width: 1200px) {
    .site-navbar .nav-find.show {
        display: flex;
        gap: 0.75rem;
        width: min(54rem, calc(100vw - 2rem));
    }
    .site-navbar .nav-find-side {
        flex: 0 0 13.5rem;
    }
    .site-navbar .nav-find-panes {
        display: flex;
        flex-direction: column;
        flex: 1 1 auto;
        min-width: 0;
    }
    /* With the script: one panel, the kind's own, without its title (the kind on the left names it), as tall as its
       content but never lower than the left column, the ways to all of them at its bottom. */
    .site-navbar .nav-find.is-ready .nav-find-pane {
        display: none;
    }
    .site-navbar .nav-find.is-ready .nav-find-pane.is-active {
        display: flex;
        flex-direction: column;
        flex: 1 1 auto;
        margin-top: 0;
    }
    .site-navbar .nav-find.is-ready .nav-find-more {
        margin-top: auto;
    }
    /* Pointing at a plain link ("Усі роботи", "Жанри", ...): no panel, the menu down to its left column. */
    .site-navbar .nav-find.is-empty.show {
        width: auto;
    }
    .site-navbar .nav-find.is-empty .nav-find-panes {
        display: none;
    }
    .site-navbar .nav-find.is-ready .nav-find-pane-title {
        display: none;
    }
}
/* The collapsed (phone / tablet) menu under the hamburger: its items -- the menus, the icon links, the language and
   the user menu, and what their drop-downs open -- are centred instead of lined up on the left. */
@media (max-width: 1199.98px) {
    .site-navbar .navbar-collapse,
    .site-navbar .navbar-collapse .dropdown-menu {
        text-align: center;
    }
    .site-navbar .navbar-collapse > .d-flex {
        justify-content: center;
    }
}
/* Icon-only links in the header (private messages, news): drawn in the text colour (currentColor),
   centred in the link's box. */
.site-navbar .nav-icon-link {
    display: inline-flex;
    align-items: center;
}
/* Unread private messages: a small count on the corner of the envelope icon. */
.site-navbar .nav-icon-badge {
    position: absolute;
    top: 0;
    right: -0.15rem;
    min-width: 1.05rem;
    height: 1.05rem;
    padding: 0 0.3rem;
    border-radius: var(--bs-border-radius);
    background: var(--lf-danger);
    color: var(--lf-white);
    font-size: var(--lf-text-3xs);
    font-weight: var(--lf-weight-semibold);
    line-height: 1.05rem;
    text-align: center;
    pointer-events: none;
}
.site-navbar .nav-icon-link svg {
    width: 1.4rem;
    height: 1.4rem;
}

/* The search popup (fragments/nav.html, js/global-search.js), opened by the header's magnifier: like macOS's Spotlight,
   a wide rounded panel a little above the middle of the dimmed, blurred page, with a large box and the hits growing
   under it. A native <dialog>, so the focus stays inside and the page behind does not react. */
.site-search {
    position: fixed;
    inset: 16vh 0 auto;
    width: min(40rem, calc(100vw - 2rem));
    max-width: none;
    max-height: 68vh;
    max-height: 68dvh;
    margin: 0 auto;
    padding: 0;
    overflow: hidden;
    color: var(--bs-body-color);
    background: var(--lf-surface);
    border: var(--lf-border);
    border-radius: var(--bs-border-radius-xl);
    box-shadow: var(--lf-shadow-overlay);
}
.site-search[open] {
    display: flex;
    animation: site-search-in var(--lf-duration-fast) ease-out;
}
.site-search::backdrop {
    background: rgba(var(--lf-backdrop-rgb), 0.35);
    backdrop-filter: blur(4px);
}
@keyframes site-search-in {
    from {
        opacity: 0;
        transform: scale(0.98);
    }
}
@media (prefers-reduced-motion: reduce) {
    .site-search[open] {
        animation: none;
    }
}
/* The page does not scroll behind the popup. */
html:has(.site-search[open]) {
    overflow: hidden;
}
.site-search-form {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-width: 0;
    min-height: 0;
}
.site-search-bar {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-shrink: 0;
    padding: 0.9rem 1.15rem;
}
.site-search-icon {
    width: 1.5rem;
    height: 1.5rem;
    flex-shrink: 0;
    color: var(--bs-secondary-color);
}
.site-search-input {
    flex: 1;
    min-width: 0;
    padding: 0;
    font-size: var(--lf-text-xl);
    color: inherit;
    background: transparent;
    border: 0;
    outline: 0;
    appearance: none;
}
.site-search-input::placeholder {
    color: var(--bs-secondary-color);
}
/* Its own cross is the popup's, on a touch screen; the browser's would be a second one that only empties the box. */
.site-search-input::-webkit-search-cancel-button {
    display: none;
}
.site-search-close {
    flex-shrink: 0;
}
@media (hover: hover) and (pointer: fine) {
    .site-search-close {
        display: none;
    }
}
.site-search-results {
    flex: 1;
    min-height: 0;
    padding: 0.4rem 0.5rem 0.5rem;
    overflow-y: auto;
    overscroll-behavior: contain;
    border-top: var(--lf-border);
}
.site-search-results:empty {
    display: none;
}
.site-search-heading {
    margin: 0.6rem 0 0.2rem;
    padding: 0 0.65rem;
    font-size: var(--lf-text-xs);
    font-weight: var(--lf-weight-semibold);
    color: var(--bs-secondary-color);
}
.site-search-empty {
    margin: 0;
    padding: 0.6rem 0.65rem;
    color: var(--bs-secondary-color);
}
.site-search-item {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    padding: 0.4rem 0.65rem;
    color: inherit;
    text-decoration: none;
    border-radius: var(--bs-border-radius);
}
.site-search-item:hover {
    color: inherit;
    background: var(--lf-accent-soft);
}
.site-search-item.active {
    color: var(--lf-white);
    background: var(--lf-brand);
}
.site-search-name {
    min-width: 0;
    overflow-wrap: anywhere;
}
.site-search-hint {
    margin-left: auto;
    flex-shrink: 0;
    max-width: 45%;
    font-size: var(--lf-text-sm);
    opacity: var(--lf-opacity-muted);
    text-align: right;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.site-search-all {
    margin-top: 0.4rem;
    font-weight: var(--lf-weight-semibold);
}

/* Signed-in user menu (fragments/nav.html): the long menu scrolls inside itself on short screens;
   its sections collapse, and the logout button (a POST form) looks like any other item. */
/* The signed-in user's avatar in the header: a square thumbnail with rounded corners, like the
   author cards (.author-tile-avatar), or the first letter on the purple tile without a picture.
   object-fit: cover scales the picture to fill the square and crops the overflow evenly from both
   sides (object-position: center), so a non-square photo is centred, not stretched. */
.site-navbar .user-avatar {
    width: 1.75rem;
    height: 1.75rem;
    flex-shrink: 0;
    border-radius: var(--bs-border-radius-sm);
    font-size: var(--lf-text-md);
    object-fit: cover;
    object-position: center;
}
/* The purple letter tile would vanish on the purple header: a light translucent one instead. */
.site-navbar .user-avatar.assistant-avatar-letter {
    background: rgba(var(--lf-white-rgb), .22);
}
.site-navbar .user-menu {
    min-width: 17rem;
    max-height: calc(100vh - 6rem);
    overflow-y: auto;
}
.site-navbar .user-menu form .dropdown-item {
    width: 100%;
}
/* The interface language in the profile menu: a row laid out like the items, the language codes at the right,
   the current one filled. */
.site-navbar .user-menu-language {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.25rem 0.4rem 0.25rem 0.6rem;
    font-size: var(--lf-text-md);
    font-weight: var(--lf-weight-medium);
    color: var(--bs-secondary-color);
}
.site-navbar .user-menu-languages {
    display: flex;
    gap: 0.2rem;
    flex-shrink: 0;
}
.site-navbar .user-menu-languages a {
    padding: 0.2rem 0.5rem;
    border-radius: var(--bs-border-radius);
    font-size: var(--lf-text-sm);
    color: inherit;
    text-decoration: none;
    transition: background-color var(--lf-duration-fast) ease, color var(--lf-duration-fast) ease;
}
.site-navbar .user-menu-languages a:hover,
.site-navbar .user-menu-languages a:focus-visible {
    color: var(--lf-accent-ink);
    background-color: var(--lf-accent-soft);
}
.site-navbar .user-menu-languages a.active {
    color: var(--lf-white);
    background-color: var(--lf-brand);
}
/* The collapsed menu centres its items: so this row too. */
@media (max-width: 1199.98px) {
    .site-navbar .user-menu-language {
        justify-content: center;
    }
}

.site-navbar .btn-nav {
    /* Inverted: a button in the brand's light tint (--lf-shade-soft) with purple text, so it
       doesn't melt into the purple bar. */
    --bs-btn-color: var(--lf-nav-btn-color);
    --bs-btn-bg: var(--lf-shade-soft);
    --bs-btn-border-color: var(--lf-shade-line);
    --bs-btn-hover-color: var(--lf-shade-ink);
    --bs-btn-hover-bg: var(--lf-nav-btn-hover-bg);
    --bs-btn-hover-border-color: var(--lf-nav-btn-hover-border);
    --bs-btn-active-color: var(--lf-shade-ink);
    --bs-btn-active-bg: var(--lf-nav-btn-active-bg);
    --bs-btn-active-border-color: var(--lf-nav-btn-active-border);
    --bs-btn-focus-shadow-rgb: var(--lf-white-rgb);
}
.site-navbar .btn-link {
    --bs-btn-color: var(--bs-emphasis-color);
    --bs-btn-hover-color: var(--bs-emphasis-color);
}

/* ---- Buttons in the site's purple ----
   Bootstrap's primary / secondary / outline-primary / outline-secondary buttons re-coloured through
   their own --bs-btn-* variables from the design tokens, so every page picks it up without touching
   markup, and a colour mode scoped to an element (the reading page's <body>) follows on its own.
   Primary is the solid purple; secondary is a quiet cream (charcoal in dark mode) button with a thin
   border. .btn-nav (navbar) keeps its own rule above. Semantic colours (danger, success, ...) are
   left alone. */
.btn {
    --bs-btn-font-weight: var(--lf-weight-medium);
}
/* A button with only an icon or a glyph (tokens.css, "Buttons"): a square as tall as a text button of its size beside
   it, the icon in the middle. The words are in its title and aria-label. */
.btn-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: var(--lf-btn-side);
    height: var(--lf-btn-side);
    padding: 0;
    font-size: calc(var(--bs-btn-font-size) * var(--lf-btn-icon-scale));
    line-height: var(--lf-leading-none);
}
.btn-icon.btn-sm {
    width: var(--lf-btn-side-sm);
    height: var(--lf-btn-side-sm);
}
/* An icon beside a button's words, as big as an icon button's. */
.btn > svg {
    width: calc(1em * var(--lf-btn-icon-scale));
    height: calc(1em * var(--lf-btn-icon-scale));
    flex-shrink: 0;
}
.btn-icon > svg {
    width: 1em;
    height: 1em;
}
/* Quiet: no line nor fill until the pointer is over it (a comment's actions, a notification's). -accent is in the
   site's colour, -danger turns red under the pointer. */
.btn-quiet {
    --bs-btn-color: var(--bs-secondary-color);
    --bs-btn-bg: transparent;
    --bs-btn-border-color: transparent;
    --bs-btn-hover-color: var(--bs-emphasis-color);
    --bs-btn-hover-bg: var(--bs-tertiary-bg);
    --bs-btn-hover-border-color: transparent;
    --bs-btn-active-color: var(--bs-emphasis-color);
    --bs-btn-active-bg: var(--bs-tertiary-bg);
    --bs-btn-active-border-color: transparent;
    --bs-btn-disabled-color: var(--bs-secondary-color);
    --bs-btn-disabled-bg: transparent;
    --bs-btn-disabled-border-color: transparent;
}
.btn-quiet-accent {
    --bs-btn-color: var(--bs-primary-text-emphasis);
    --bs-btn-hover-color: var(--bs-primary-text-emphasis);
    --bs-btn-hover-bg: var(--bs-primary-bg-subtle);
    --bs-btn-active-color: var(--bs-primary-text-emphasis);
    --bs-btn-active-bg: var(--bs-primary-bg-subtle);
}
.btn-quiet-danger {
    --bs-btn-hover-color: var(--bs-danger-text-emphasis);
    --bs-btn-hover-bg: var(--bs-danger-bg-subtle);
    --bs-btn-active-color: var(--bs-danger-text-emphasis);
    --bs-btn-active-bg: var(--bs-danger-bg-subtle);
}
/* Floating over a cover or a row of cards: a light, slightly see-through face, a thin line and a shadow, so it shows
   on any picture. */
.btn-float {
    --bs-btn-color: var(--bs-body-color);
    --bs-btn-bg: color-mix(in srgb, var(--bs-body-bg) 92%, transparent);
    --bs-btn-border-color: var(--bs-border-color-translucent);
    --bs-btn-hover-color: var(--bs-body-color);
    --bs-btn-hover-bg: var(--bs-body-bg);
    --bs-btn-hover-border-color: var(--bs-border-color-translucent);
    --bs-btn-active-color: var(--bs-body-color);
    --bs-btn-active-bg: var(--bs-body-bg);
    --bs-btn-active-border-color: var(--bs-border-color-translucent);
    box-shadow: var(--lf-shadow-raised);
}
.btn-primary {
    --bs-btn-color: var(--lf-white);
    --bs-btn-bg: var(--lf-brand);
    --bs-btn-border-color: var(--lf-brand);
    --bs-btn-hover-color: var(--lf-white);
    --bs-btn-hover-bg: var(--lf-accent-hover);
    --bs-btn-hover-border-color: var(--lf-accent-hover);
    --bs-btn-active-color: var(--lf-white);
    --bs-btn-active-bg: var(--lf-accent-deep);
    --bs-btn-active-border-color: var(--lf-accent-deep);
    --bs-btn-focus-shadow-rgb: var(--lf-brand-rgb);
    --bs-btn-disabled-color: var(--lf-white);
    --bs-btn-disabled-bg: var(--lf-brand);
    --bs-btn-disabled-border-color: var(--lf-brand);
}
.btn-outline-primary {
    --bs-btn-color: var(--bs-link-color);
    --bs-btn-border-color: var(--lf-brand);
    --bs-btn-hover-color: var(--lf-white);
    --bs-btn-hover-bg: var(--lf-brand);
    --bs-btn-hover-border-color: var(--lf-brand);
    --bs-btn-active-color: var(--lf-white);
    --bs-btn-active-bg: var(--lf-accent-hover);
    --bs-btn-active-border-color: var(--lf-accent-hover);
    --bs-btn-focus-shadow-rgb: var(--lf-brand-rgb);
    --bs-btn-disabled-color: var(--bs-link-color);
    --bs-btn-disabled-border-color: var(--lf-brand);
}
.btn-secondary,
.btn-outline-secondary {
    --bs-btn-color: var(--bs-body-color);
    --bs-btn-bg: var(--lf-surface);
    --bs-btn-border-color: var(--bs-border-color);
    --bs-btn-hover-color: var(--bs-emphasis-color);
    --bs-btn-hover-bg: var(--bs-tertiary-bg);
    --bs-btn-hover-border-color: var(--lf-border-strong);
    --bs-btn-active-color: var(--bs-emphasis-color);
    --bs-btn-active-bg: var(--lf-surface-deep);
    --bs-btn-active-border-color: var(--lf-border-strong);
    --bs-btn-focus-shadow-rgb: var(--lf-brand-rgb);
    --bs-btn-disabled-color: var(--bs-secondary-color);
    --bs-btn-disabled-bg: var(--lf-surface);
    --bs-btn-disabled-border-color: var(--bs-border-color);
}
.btn-outline-secondary {
    --bs-btn-bg: transparent;
    --bs-btn-disabled-bg: transparent;
}
/* Bootstrap's other buttons have their colours inside: redrawn from the palette (tokens.css), with its own shades. */
.btn-success {
    --bs-btn-color: var(--lf-white);
    --bs-btn-bg: var(--lf-success);
    --bs-btn-border-color: var(--lf-success);
    --bs-btn-hover-color: var(--lf-white);
    --bs-btn-hover-bg: color-mix(in srgb, var(--lf-success) 85%, var(--lf-black));
    --bs-btn-hover-border-color: color-mix(in srgb, var(--lf-success) 80%, var(--lf-black));
    --bs-btn-focus-shadow-rgb: var(--lf-success-rgb);
    --bs-btn-active-color: var(--lf-white);
    --bs-btn-active-bg: color-mix(in srgb, var(--lf-success) 80%, var(--lf-black));
    --bs-btn-active-border-color: color-mix(in srgb, var(--lf-success) 75%, var(--lf-black));
    --bs-btn-disabled-color: var(--lf-white);
    --bs-btn-disabled-bg: var(--lf-success);
    --bs-btn-disabled-border-color: var(--lf-success);
}
.btn-danger {
    --bs-btn-color: var(--lf-white);
    --bs-btn-bg: var(--lf-danger);
    --bs-btn-border-color: var(--lf-danger);
    --bs-btn-hover-color: var(--lf-white);
    --bs-btn-hover-bg: color-mix(in srgb, var(--lf-danger) 85%, var(--lf-black));
    --bs-btn-hover-border-color: color-mix(in srgb, var(--lf-danger) 80%, var(--lf-black));
    --bs-btn-focus-shadow-rgb: var(--lf-danger-rgb);
    --bs-btn-active-color: var(--lf-white);
    --bs-btn-active-bg: color-mix(in srgb, var(--lf-danger) 80%, var(--lf-black));
    --bs-btn-active-border-color: color-mix(in srgb, var(--lf-danger) 75%, var(--lf-black));
    --bs-btn-disabled-color: var(--lf-white);
    --bs-btn-disabled-bg: var(--lf-danger);
    --bs-btn-disabled-border-color: var(--lf-danger);
}
.btn-warning {
    --bs-btn-color: var(--lf-black);
    --bs-btn-bg: var(--lf-warning);
    --bs-btn-border-color: var(--lf-warning);
    --bs-btn-hover-color: var(--lf-black);
    --bs-btn-hover-bg: color-mix(in srgb, var(--lf-warning) 85%, var(--lf-white));
    --bs-btn-hover-border-color: color-mix(in srgb, var(--lf-warning) 90%, var(--lf-white));
    --bs-btn-focus-shadow-rgb: var(--lf-warning-rgb);
    --bs-btn-active-color: var(--lf-black);
    --bs-btn-active-bg: color-mix(in srgb, var(--lf-warning) 80%, var(--lf-white));
    --bs-btn-active-border-color: color-mix(in srgb, var(--lf-warning) 90%, var(--lf-white));
    --bs-btn-disabled-color: var(--lf-black);
    --bs-btn-disabled-bg: var(--lf-warning);
    --bs-btn-disabled-border-color: var(--lf-warning);
}
.btn-light {
    --bs-btn-color: var(--lf-black);
    --bs-btn-bg: var(--lf-light);
    --bs-btn-border-color: var(--lf-light);
    --bs-btn-hover-color: var(--lf-black);
    --bs-btn-hover-bg: color-mix(in srgb, var(--lf-light) 85%, var(--lf-black));
    --bs-btn-hover-border-color: color-mix(in srgb, var(--lf-light) 80%, var(--lf-black));
    --bs-btn-focus-shadow-rgb: var(--lf-light-rgb);
    --bs-btn-active-color: var(--lf-black);
    --bs-btn-active-bg: color-mix(in srgb, var(--lf-light) 80%, var(--lf-black));
    --bs-btn-active-border-color: color-mix(in srgb, var(--lf-light) 75%, var(--lf-black));
    --bs-btn-disabled-color: var(--lf-black);
    --bs-btn-disabled-bg: var(--lf-light);
    --bs-btn-disabled-border-color: var(--lf-light);
}
.btn-outline-success {
    --bs-btn-color: var(--lf-success);
    --bs-btn-border-color: var(--lf-success);
    --bs-btn-hover-color: var(--lf-white);
    --bs-btn-hover-bg: var(--lf-success);
    --bs-btn-hover-border-color: var(--lf-success);
    --bs-btn-focus-shadow-rgb: var(--lf-success-rgb);
    --bs-btn-active-color: var(--lf-white);
    --bs-btn-active-bg: var(--lf-success);
    --bs-btn-active-border-color: var(--lf-success);
    --bs-btn-disabled-color: var(--lf-success);
    --bs-btn-disabled-border-color: var(--lf-success);
}
.btn-outline-danger {
    --bs-btn-color: var(--lf-danger);
    --bs-btn-border-color: var(--lf-danger);
    --bs-btn-hover-color: var(--lf-white);
    --bs-btn-hover-bg: var(--lf-danger);
    --bs-btn-hover-border-color: var(--lf-danger);
    --bs-btn-focus-shadow-rgb: var(--lf-danger-rgb);
    --bs-btn-active-color: var(--lf-white);
    --bs-btn-active-bg: var(--lf-danger);
    --bs-btn-active-border-color: var(--lf-danger);
    --bs-btn-disabled-color: var(--lf-danger);
    --bs-btn-disabled-border-color: var(--lf-danger);
}
.btn-outline-warning {
    --bs-btn-color: var(--lf-warning);
    --bs-btn-border-color: var(--lf-warning);
    --bs-btn-hover-color: var(--lf-black);
    --bs-btn-hover-bg: var(--lf-warning);
    --bs-btn-hover-border-color: var(--lf-warning);
    --bs-btn-focus-shadow-rgb: var(--lf-warning-rgb);
    --bs-btn-active-color: var(--lf-black);
    --bs-btn-active-bg: var(--lf-warning);
    --bs-btn-active-border-color: var(--lf-warning);
    --bs-btn-disabled-color: var(--lf-warning);
    --bs-btn-disabled-border-color: var(--lf-warning);
}
.btn-outline-dark {
    --bs-btn-color: var(--lf-dark);
    --bs-btn-border-color: var(--lf-dark);
    --bs-btn-hover-color: var(--lf-white);
    --bs-btn-hover-bg: var(--lf-dark);
    --bs-btn-hover-border-color: var(--lf-dark);
    --bs-btn-focus-shadow-rgb: var(--lf-dark-rgb);
    --bs-btn-active-color: var(--lf-white);
    --bs-btn-active-bg: var(--lf-dark);
    --bs-btn-active-border-color: var(--lf-dark);
    --bs-btn-disabled-color: var(--lf-dark);
    --bs-btn-disabled-border-color: var(--lf-dark);
}
.link-secondary {
    color: var(--lf-gray) !important;
    text-decoration-color: var(--lf-gray) !important;
}
.link-secondary:hover,
.link-secondary:focus {
    color: color-mix(in srgb, var(--lf-gray) 80%, var(--lf-black)) !important;
    text-decoration-color: color-mix(in srgb, var(--lf-gray) 80%, var(--lf-black)) !important;
}
.link-danger {
    color: var(--lf-danger) !important;
    text-decoration-color: var(--lf-danger) !important;
}
.link-danger:hover,
.link-danger:focus {
    color: var(--lf-danger-deep) !important;
    text-decoration-color: var(--lf-danger-deep) !important;
}

/* The selected row of a list group (e.g. the current chapter in the reading page's "Зміст"
   panel) in the site's purple instead of Bootstrap's blue, the same as the primary buttons. */
.list-group {
    --bs-list-group-active-color: var(--lf-white);
    --bs-list-group-active-bg: var(--lf-brand);
    --bs-list-group-active-border-color: var(--lf-brand);
}

/* Logo mark (fragments/logo.html, an inline SVG nib): it takes the surrounding text colour
   (fill: currentColor), so it is white in the purple navbar and tinted where it's set. */
.brand-mark {
    display: inline-block;
    flex: none;
    width: 0.85rem;
    height: 1.9rem;
    fill: currentColor;
}
/* The header's rotating tagline (fragments/nav.html): in the room between the menu and the search, one line, set off
   from the menu's last item ("Почати писати" or a cabinet) by a thin vertical line; on the widest screens only. The box
   keeps its width whatever the text: a phrase too long for it moves left as it is typed, its start hidden under the
   line (the text is at least as wide as the box and aligned to its right end, so only an overflow sticks out, on the
   left, where it is clipped). */
.site-navbar .site-tagline {
    flex: 1 1 0;
    min-width: 0;
    justify-content: flex-end;
    margin: 0 1.5rem 0 0.75rem;
    padding-left: 1.25rem;
    border-left: 1px solid rgba(var(--lf-white-rgb), 0.45);
    overflow: hidden;
    white-space: nowrap;
    font-size: var(--lf-text-md);
    color: rgba(var(--lf-white-rgb), 0.85);
}
/* Without it the menu bar takes the width of the whole phrase (a flex item's automatic minimum is its content) and
   pushes the search and the account menu to the right, past the screen's edge, while a long phrase is typed. */
.site-navbar .navbar-collapse {
    min-width: 0;
}
.site-navbar .site-tagline .typewriter {
    flex: 0 0 auto;
    min-width: 100%;
}
/* Typewriter effect (js/typewriter.js): the script types the text out and adds
   .typewriter-active, which shows a blinking caret after it. */
.typewriter-active::after {
    content: "";
    display: inline-block;
    width: 0.07em;
    height: 0.95em;
    margin-left: 0.08em;
    vertical-align: -0.1em;
    background-color: currentColor;
    animation: typewriter-caret var(--lf-duration-caret) steps(1) infinite;
}
@keyframes typewriter-caret {
    50% {
        opacity: 0;
    }
}

/* A grouped news line (news/index.html): <details> whose <summary> is the group's card; the events it
   stands for are the stack below. The label of the toggle follows the open state. */
.news-group > summary {
    list-style: none;
    cursor: pointer;
}
.news-group > summary::-webkit-details-marker {
    display: none;
}
.news-group .news-collapse,
.news-group[open] .news-expand {
    display: none;
}
.news-group[open] .news-collapse {
    display: inline;
}

/* The news page's two columns (news/index.html; "Обговорення", reader/discussions.html, and the blogs, blog/index.html,
   are laid out the same way with the same classes): the types of news on the left (a sticky list of links, each with
   its icon, its unread badge and its count; the member's ones under the "Підписки" heading) and the stream in the middle (.news-item cards: a title or an event's
   text, a two-line excerpt and a small line with the type chip and the date). Below the md breakpoint the types are
   a scrolling row of chips above the stream. */
.news-shell {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1.5rem;
}
@media (min-width: 768px) {
    .news-shell {
        grid-template-columns: 232px minmax(0, 1fr);
    }
    .news-types-col {
        position: sticky;
        top: 5rem;
        align-self: start;
    }
}
.news-types {
    display: flex;
    gap: .375rem;
    overflow-x: auto;
    scrollbar-width: none;
    padding-bottom: .25rem;
}
.news-types::-webkit-scrollbar {
    display: none;
}
@media (min-width: 768px) {
    .news-types {
        flex-direction: column;
        gap: .25rem;
        overflow: visible;
        padding-bottom: 0;
    }
}
.news-type {
    display: flex;
    flex-shrink: 0;
    align-items: center;
    gap: .5rem;
    padding: .5rem .75rem;
    border-radius: var(--bs-border-radius);
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-ui);
    font-weight: var(--lf-weight-medium);
    text-decoration: none;
    white-space: nowrap;
    transition: background-color var(--lf-duration-fast), color var(--lf-duration-fast);
}
.news-type:hover,
.news-type:focus-visible {
    background: var(--bs-tertiary-bg);
    color: var(--bs-body-color);
}
.news-type.active {
    background: var(--bs-primary-bg-subtle);
    color: var(--bs-primary-text-emphasis);
}
.news-type .nav-menu-icon {
    width: 1.15rem;
    height: 1.15rem;
    flex-shrink: 0;
}
.news-type-label {
    flex: 1 1 auto;
    min-width: 0;
}
@media (min-width: 768px) {
    .news-type-label {
        white-space: normal;
    }
}
.news-type-count {
    display: none;
    font-family: var(--bs-font-monospace);
    font-size: var(--lf-text-xs);
    color: var(--bs-secondary-color);
}
.news-type.active .news-type-count {
    color: inherit;
}
.news-types-sep {
    display: none;
}
.news-types-head {
    display: none;
    padding: .625rem .75rem .25rem;
    color: var(--bs-secondary-color);
}
@media (min-width: 768px) {
    .news-type-count {
        display: inline;
    }
    .news-types-head {
        display: block;
    }
    .news-types-sep {
        display: block;
        height: 1px;
        margin: .25rem 0;
        background: var(--bs-border-color-translucent);
    }
}
.news-item {
    display: flex;
    flex-direction: column;
    gap: .375rem;
    padding: .875rem;
    border: var(--lf-border-soft);
    border-radius: var(--bs-border-radius-lg);
    background: var(--bs-body-bg);
    color: inherit;
    text-decoration: none;
    transition: border-color var(--lf-duration-fast), background-color var(--lf-duration-fast);
}
a.news-item:hover,
a.news-item:focus-visible,
.news-group > summary:hover .news-item {
    border-color: var(--bs-border-color);
    background: var(--bs-tertiary-bg);
}
.news-item-unread {
    background: var(--bs-primary-bg-subtle);
}
.news-item-child {
    padding: .5rem .75rem;
    border-left: var(--lf-border-quote);
}
.news-item-title {
    margin: 0;
    min-width: 0;
    font-size: var(--lf-text-ui);
    font-weight: var(--lf-weight-semibold);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    transition: color var(--lf-duration-fast);
}
a.news-item:hover .news-item-title {
    color: var(--bs-link-hover-color);
}
.news-item-excerpt {
    display: -webkit-box;
    margin: 0;
    overflow: hidden;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-md);
    line-height: var(--lf-leading-normal);
    overflow-wrap: anywhere;
}
.news-item-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .25rem .625rem;
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-sm);
}
.news-item-chip {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    padding: .125rem .375rem;
    border-radius: var(--bs-border-radius-sm);
    background: var(--bs-secondary-bg);
    font-size: var(--lf-text-xs);
    font-weight: var(--lf-weight-medium);
}
.news-item-chip .nav-menu-icon {
    width: .9rem;
    height: .9rem;
}

/* The system notifications (notifications/index.html): the heading with the unread count and the bulk actions, then
   one card per notification -- a round icon of its area (red for a refusal or a removal, green for an approval), the
   text and the date, and on the right small icon buttons (open, mark read, delete). An unread card is tinted, has a
   purple stripe on its left edge and a dot by the date. */
.notif-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: .75rem 1rem;
    margin-bottom: 1.25rem;
}
.notif-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
}
.notif-list {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    margin: 0;
    padding: 0;
    list-style: none;
}
.notif-item {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: .875rem;
    padding: .875rem .75rem .875rem 1rem;
    border: var(--lf-border);
    border-radius: var(--bs-border-radius-lg);
    background: var(--bs-body-bg);
    overflow: hidden;
    transition: border-color var(--lf-duration-fast);
}
.notif-item:hover {
    border-color: var(--lf-border-strong);
}
/* On a phone the buttons go under the text, to the right, so the text keeps the card's width. */
@media (max-width: 575.98px) {
    .notif-item {
        display: grid;
        grid-template-columns: auto minmax(0, 1fr);
        row-gap: 0;
    }
    .notif-actions {
        grid-column: 2;
        justify-self: end;
        margin: -.25rem -.25rem -.5rem 0;
    }
}
.notif-unread {
    background: rgba(var(--lf-brand-rgb), .06);
    border-color: rgba(var(--lf-brand-rgb), .28);
}
.notif-unread::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 3px;
    background: var(--lf-brand);
}
.notif-icon {
    display: inline-flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: var(--lf-radius-circle);
    background: var(--bs-primary-bg-subtle);
    color: var(--bs-primary-text-emphasis);
}
.notif-bad .notif-icon {
    background: var(--bs-danger-bg-subtle);
    color: var(--bs-danger-text-emphasis);
}
.notif-good .notif-icon {
    background: var(--bs-success-bg-subtle);
    color: var(--bs-success-text-emphasis);
}
.notif-icon .nav-menu-icon {
    width: 1.15rem;
    height: 1.15rem;
}
.notif-body {
    flex: 1 1 auto;
    min-width: 0;
    padding-top: .125rem;
}
.notif-text {
    font-size: var(--lf-text-ui);
    line-height: var(--lf-leading-normal);
    overflow-wrap: anywhere;
}

.notif-item:not(.notif-unread) .notif-text {
    color: var(--bs-secondary-color);
}
.notif-item:not(.notif-unread) .notif-text a {
    color: inherit;
    text-decoration-color: var(--bs-border-color);
}
.notif-unread .notif-text {
    color: var(--bs-body-color);
}
.notif-meta {
    display: flex;
    align-items: center;
    gap: .375rem;
    margin-top: .25rem;
    color: var(--bs-secondary-color);
    font-family: var(--bs-font-monospace);
    font-size: var(--lf-text-xs);
}
.notif-dot {
    width: .45rem;
    height: .45rem;
    border-radius: var(--lf-radius-circle);
    background: var(--lf-brand);
}
/* Three fixed slots (open, mark read, delete), so the buttons stay in the same place from card to card. */
.notif-actions {
    display: grid;
    flex-shrink: 0;
    grid-template-columns: repeat(3, 2rem);
    gap: .125rem;
}
.notif-actions form {
    margin: 0;
}
.notif-actions .notif-btn-open {
    grid-column: 1;
}
.notif-actions .notif-slot-read {
    grid-column: 2;
}
.notif-actions .notif-slot-delete {
    grid-column: 3;
}
@media (hover: hover) and (min-width: 576px) {
    .notif-item:not(:hover):not(:focus-within) .notif-btn:not(.notif-btn-open) {
        opacity: var(--lf-opacity-faint);
    }
}
.notif-empty {
    padding: 3rem 1.5rem;
    border: var(--lf-border-dashed);
    border-radius: var(--bs-border-radius-lg);
    text-align: center;
}
.notif-empty-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3.25rem;
    height: 3.25rem;
    margin-bottom: .75rem;
    border-radius: var(--lf-radius-circle);
    background: var(--bs-primary-bg-subtle);
    color: var(--bs-primary-text-emphasis);
}
.notif-empty-icon .nav-menu-icon {
    width: 1.5rem;
    height: 1.5rem;
}
.notif-empty .small {
    max-width: 30rem;
    margin-inline: auto;
}

/* The text of a news post (news/article.html) and the editor's preview: Markdown rendered as HTML. */
.news-body {
    overflow-wrap: anywhere;
}
.news-body img,
.chapter-md img {
    max-width: 100%;
    height: auto;
}
.news-body table,
.chapter-md table {
    border-collapse: collapse;
    margin-bottom: 1rem;
}
.news-body th,
.news-body td,
.chapter-md th,
.chapter-md td {
    border: var(--lf-border);
    padding: 0.25rem 0.6rem;
}
.news-body blockquote,
.chapter-md blockquote {
    border-left: var(--lf-border-quote);
    padding-left: 0.9rem;
    color: var(--bs-secondary-color);
}
.news-body pre,
.chapter-md pre {
    background: var(--bs-tertiary-bg);
    padding: 0.6rem 0.8rem;
    border-radius: var(--bs-border-radius-sm);
    overflow-x: auto;
}

/* Every cover is a book: .book-binding shades its left edge as a binding (a soft inner shadow and one thin crease, a
   dark line with a light edge after it) over the picture or the drawn cover. A cover without a picture takes one of ten muted
   gradients (.book-tone-<n> of tokens.css, chosen by the work's id: webcommon.Tones), so the shelves are not one colour. */
.book-binding {
    position: relative;
}
.book-binding::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    border-radius: inherit;
    pointer-events: none;
    background: linear-gradient(to right,
        rgba(var(--lf-black-rgb), 0.3) 0,
        rgba(var(--lf-black-rgb), 0.1) 3.6%,
        rgba(var(--lf-black-rgb), 0.38) 4.4%,
        rgba(var(--lf-black-rgb), 0.38) 5%,
        rgba(var(--lf-white-rgb), 0.2) 5.4%,
        rgba(var(--lf-black-rgb), 0.06) 7.5%,
        transparent 11%);
}

/* Covers of works: full width on the work page, a thumbnail on the catalogue cards, a tiny one in the
   author's list, and a preview in the work editor. */
.work-cover {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--bs-border-radius);
}
.work-cover-frame {
    border-radius: var(--bs-border-radius);
}
/* The drawn cover of a work without a picture on the work page: upright, as a library card, and its text larger. */
.work-cover-frame-drawn {
    aspect-ratio: 2 / 3;
    overflow: hidden;
}
.work-cover-frame-drawn .book-cover-text {
    gap: 0.75rem;
    padding: 3rem 1.5rem 2.5rem 2rem;
}
.work-cover-frame-drawn .book-cover-title {
    font-size: var(--lf-text-4xl);
    -webkit-line-clamp: 7;
}
.work-cover-frame-drawn .book-cover-authors {
    font-size: var(--lf-text-base);
    -webkit-line-clamp: 3;
}
.work-cover-thumb {
    display: block;
    width: 240px; /* 240 x 360 */
    aspect-ratio: 2 / 3; /* always upright: a wide cover is cropped to the middle, not squeezed */
    object-fit: cover;
    object-position: center;
    border-radius: var(--bs-border-radius-sm);
}
/* A card of the works search (works/search.html): the facts on the left, the cover on
   the right. On a phone the cover floats in the top right corner and the facts flow on under it, across the whole
   card, instead of leaving the space under the cover empty. */
.work-card-layout {
    display: flex;
    gap: 1rem;
    align-items: flex-start;
}
.work-card-layout > .work-card-cover {
    order: 2;
    flex-shrink: 0;
    border-radius: var(--bs-border-radius-sm);
}
.work-card-layout > .work-card-facts {
    flex-grow: 1;
    min-width: 0;
}
@media (max-width: 575.98px) {
    .work-card-layout {
        display: block;
    }
    .work-card-layout::after {
        content: "";
        display: block;
        clear: both;
    }
    .work-card-layout > .work-card-cover {
        float: right;
        margin: 0 0 0.5rem 0.75rem;
    }
    /* A divider beside the cover stops at it instead of running under it. */
    .work-card-layout hr {
        overflow: hidden;
    }
    .work-cover-thumb {
        width: 120px;
    }
}
.work-cover-preview {
    max-width: 160px;
    max-height: 240px;
    height: auto;
    border-radius: var(--bs-border-radius-sm);
}

/* ===== The reader's library (library/index.html) =====
   A grid of upright cards. A card is the cover (cropped to fill it) or, without a cover, a drawn one (the title and
   the authors on a muted gradient); the direction's bookmark hangs at its top left, the fandom and the title at the bottom
   over a dark gradient. .relationship-<name> sets --relationship-bg / --relationship-fg, so the badge and the hover outline take
   the colour of the work's direction. */
.library-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(var(--lf-card-min), 1fr));
    gap: 1rem;
}
/* The card rows of the home page ("Популярне", "Нові твори"): one full row of the grid at any width, no lone card
   on a second one. The grid fits n columns of --lf-card-min (150px) with 1rem gaps once it is 166n - 16 px wide (written out: a
   container query cannot read a variable); the cards past
   them are hidden -- without js/home-carousel.js; with it the row is a carousel of all the section's cards
   (HomeWorkSections.CARDS). A container query,
   not overflow: the full card that opens over a card must not be cut. */
.home-card-row {
    container-type: inline-size;
}
@container (width < 316px) { .home-card-row:not(.is-carousel) > :nth-child(n+2) { display: none; } }
@container (316px <= width < 482px) { .home-card-row:not(.is-carousel) > :nth-child(n+3) { display: none; } }
@container (482px <= width < 648px) { .home-card-row:not(.is-carousel) > :nth-child(n+4) { display: none; } }
@container (648px <= width < 814px) { .home-card-row:not(.is-carousel) > :nth-child(n+5) { display: none; } }
@container (814px <= width < 980px) { .home-card-row:not(.is-carousel) > :nth-child(n+6) { display: none; } }
@container (980px <= width < 1146px) { .home-card-row:not(.is-carousel) > :nth-child(n+7) { display: none; } }
@container (1146px <= width) { .home-card-row:not(.is-carousel) > :nth-child(n+8) { display: none; } }
/* A card row in its own box, as a carousel (js/home-carousel.js): the cards out of the row are .is-off, the arrows sit
   on the box's edges, over the row's ends, under the full card that opens over a card. */
.home-box {
    padding: 1rem 1.25rem 1.25rem;
    border: var(--lf-border);
    border-radius: var(--bs-border-radius-lg);
    background: var(--lf-surface);
}
.home-section.home-box + .home-section.home-box {
    margin-top: 1.5rem;
}
.home-carousel {
    position: relative;
}
.home-card-row.is-carousel > .is-off {
    display: none;
}
.home-card-row.is-carousel > :not(.is-off) {
    animation: home-carousel-in var(--lf-duration-base) ease;
}
@keyframes home-carousel-in {
    from { opacity: 0.3; }
    to { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    .home-card-row.is-carousel > :not(.is-off) { animation: none; }
}
.home-carousel-btn {
    position: absolute;
    top: 50%;
    z-index: 5;
    margin-top: calc(var(--lf-btn-side) / -2);
}
.home-carousel-btn[hidden] {
    display: none;
}
.home-carousel-prev {
    left: -1.75rem;
}
.home-carousel-next {
    right: -1.75rem;
}
/* A phone: a narrower box keeps two cards a row; the arrows over the cards' edges. */
@media (max-width: 575.98px) {
    .home-box {
        padding: 0.75rem 0.75rem 1rem;
    }
    .home-carousel-prev {
        left: -0.5rem;
    }
    .home-carousel-next {
        right: -0.5rem;
    }
}
.library-card {
    position: relative;
    display: block;
    aspect-ratio: 2 / 3;
    border-radius: var(--bs-border-radius-lg);
    overflow: hidden;
    color: var(--lf-white);
    text-decoration: none;
    background: var(--bs-tertiary-bg);
    box-shadow: var(--lf-shadow-cover);
    transition: box-shadow var(--lf-duration-base) ease;
}
/* The hover outline in the direction's colour is a layer over the whole card: the cover and the placeholder fill
   the card (position: absolute, inset: 0) and are painted over the card's own outline, which hid it. */
.library-card::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 2;
    border-radius: inherit;
    box-shadow: inset 0 0 0 3px transparent;
    pointer-events: none;
    transition: box-shadow var(--lf-duration-fast) ease;
}
/* The book: the link and the "i" beside it (a button cannot lie inside a link). On hover it rises and, with a mouse,
   tilts away from the pointer (js/cover-tilt.js sets --tilt-x / --tilt-y), the "i" along with the cover. */
.library-book {
    position: relative;
    transition: transform var(--lf-duration-base) var(--lf-ease-out);
}
.library-book:hover,
.library-book:has(:focus-visible) {
    transform: perspective(700px) translateY(-6px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg));
}
@media (prefers-reduced-motion: reduce) {
    .library-book:hover,
    .library-book:has(:focus-visible) {
        transform: none;
    }
}
.library-book:hover .library-card,
.library-book:has(:focus-visible) .library-card {
    color: var(--lf-white);
    box-shadow: 0 14px 22px -8px rgba(var(--lf-ink-rgb), 0.45);
}
.library-card:focus-visible {
    outline: none;
}
.library-book:hover .library-card::after,
.library-book:has(:focus-visible) .library-card::after {
    box-shadow: inset 0 0 0 3px var(--relationship-bg);
}
.library-card-cover {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.library-card-placeholder {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(var(--book-from, var(--lf-accent-deep)), var(--book-to, var(--lf-accent-deep)));
    isolation: isolate;
}
/* A cover without a picture carries the site's nib (the two halves of img/logo-mark.svg drawn out in a data URI: a
   mask image ignores that file's <use>), large and faint in its middle, as part of its ground: under the title and
   the authors of a drawn cover (z-index -1 within the cover), and in place of the small logo the other drawn covers
   had (cover stacks, the home page's rows, the lists' thumbnails). */
.library-card-placeholder::before,
.hidden-work-cover-empty::before {
    content: "";
    position: absolute;
    inset: 10% 14%;
    z-index: -1;
    background: var(--lf-white);
    opacity: 0.08;
    --nib: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='300 40 400 890'%3E%3Cpath d='M486 50C480 200 420 450 315 680C350 740 410 810 486 920L486 688.3A60 60 0 0 1 486 571.7Z'/%3E%3Cpath transform='translate(1000 0) scale(-1 1)' d='M486 50C480 200 420 450 315 680C350 740 410 810 486 920L486 688.3A60 60 0 0 1 486 571.7Z'/%3E%3C/svg%3E");
    -webkit-mask: var(--nib) center / contain no-repeat;
    mask: var(--nib) center / contain no-repeat;
    pointer-events: none;
}
.library-card-placeholder .brand-mark {
    display: none;
}
/* A card's cover without a picture, drawn as a book (as author.today does): the title, a short rule and the authors,
   in the middle, a little to the right of the binding. */
.library-card-placeholder.book-cover-text {
    flex-direction: column;
    gap: 0.5rem;
    padding: 2.6rem 0.75rem 1.9rem 1.1rem;
    color: var(--lf-white);
    text-align: center;
    overflow-wrap: anywhere;
}
/* The same drawn cover in a card of the works search (fragments/work-search-card.html): in the flow, at the size of a
   cover picture (.work-cover-thumb), rather than over the whole card. */
.work-card-cover:has(.work-cover-drawn) {
    text-decoration: none;
}
.work-card-cover .work-cover-drawn {
    position: relative;
    inset: auto;
}
.book-cover-title {
    font-family: var(--lf-font-title);
    font-size: var(--lf-text-base);
    font-weight: var(--lf-weight-bold);
    line-height: var(--lf-leading-tight);
    display: -webkit-box;
    -webkit-line-clamp: 5;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.book-cover-separator {
    flex: none;
    width: 30%;
    height: 1px;
    background: rgba(var(--lf-white-rgb), 0.6);
}
.book-cover-authors {
    font-size: var(--lf-text-2xs);
    line-height: var(--lf-leading-snug);
    opacity: var(--lf-opacity-soft);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
/* The badges and the fandom over the binding (.book-binding::before). */
.library-direction,
.library-shelf-tag,
.library-unpublished-tag,
.library-card-info {
    z-index: 1;
}
/* Under a drawn cover the fandom needs no dark ground. */
.library-card-info.library-card-info-plain {
    padding-top: 0.55rem;
    background: none;
}
.library-card-info {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 2.2rem 0.6rem 0.55rem;
    background: linear-gradient(to top, rgba(var(--lf-black-rgb), 0.85), rgba(var(--lf-black-rgb), 0));
    overflow-wrap: anywhere;
}
/* The main fandom (or "Оригінальна робота") centred on the cover, above the title. */
.library-card-fandom {
    font-size: var(--lf-text-2xs);
    line-height: var(--lf-leading-tight);
    text-align: center;
    opacity: var(--lf-opacity-soft);
    display: -webkit-box;
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.library-card-title {
    font-size: var(--lf-text-ui);
    font-weight: var(--lf-weight-semibold);
    line-height: var(--lf-leading-tight);
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
/* The direction as a short rectangular bookmark ribbon in its colour, coming straight out of the cover's top edge,
   with the direction's symbol in its middle. */
.library-direction {
    position: absolute;
    top: 0;
    left: 0.75rem;
    width: 1.5rem;
    height: 1.9rem;
    border-radius: 0 0 var(--lf-radius-hair) var(--lf-radius-hair);
    display: flex;
    align-items: center;
    justify-content: center;
    padding-top: 0.15rem;
    background: var(--relationship-bg);
    color: var(--relationship-fg);
    font-size: var(--lf-text-md);
    font-weight: var(--lf-weight-bold);
    line-height: var(--lf-leading-none);
    box-shadow: var(--lf-shadow-raised);
}
/* The icon of each direction: a text symbol (the variation selector keeps it from turning into an emoji). */
.library-direction-gen::before { content: "\25CB\FE0E"; }
.library-direction-het::before { content: "\26A5\FE0E"; }
.library-direction-slash::before { content: "\26A3\FE0E"; }
.library-direction-femslash::before { content: "\26A2\FE0E"; }
.library-direction-multi::before { content: "\2726\FE0E"; }
.library-direction-other::before { content: "\2022\FE0E"; }
/* The owner's page: the cards dragged into the works' order (js/work-order-drag.js); the gap they would fill is faint. */
.library-item-draggable {
    cursor: grab;
}
.library-item-ghost {
    opacity: var(--lf-opacity-faint);
}
/* A work a moderator took off publication (its owner and the moderators still see it): dimmed, with a label. */
.library-item-unpublished .library-card-cover,
.library-item-unpublished .library-card-placeholder {
    filter: grayscale(1);
    opacity: var(--lf-opacity-faint);
}
.library-unpublished-tag {
    position: absolute;
    left: 0.45rem;
    bottom: 3.2rem;
    padding: 0.1rem 0.5rem;
    border-radius: var(--bs-border-radius-pill);
    background: var(--lf-danger-deep);
    color: var(--lf-white);
    font-size: var(--lf-text-3xs);
    font-weight: var(--lf-weight-semibold);
}
/* A work its authors scheduled to publish (their own author page): the time instead of "Знято з публікації". */
.library-scheduled-tag {
    max-width: calc(100% - 0.9rem);
    line-height: var(--lf-leading-snug);
    background: var(--lf-blue);
}
/* An announcement (WorkStatus.ANNOUNCED): nothing to read yet, the cover is not dimmed. */
.library-announced-tag {
    background: var(--lf-brand);
}
/* What the work is, at the top right: round icons in a column (a fanfic or an original work, then a translation). */
.library-kinds {
    position: absolute;
    top: 0.45rem;
    right: 0.45rem;
    z-index: 1;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}
.library-kind {
    width: 1.6rem;
    height: 1.6rem;
    border-radius: var(--lf-radius-circle);
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(var(--lf-black-rgb), 0.55);
    color: var(--lf-white);
}
.library-kind .nav-menu-icon {
    width: 0.95rem;
    height: 0.95rem;
}
/* The "i" left of them, in the same row: it opens the full card (js/library-card.js). Over the card's hover outline. */
.library-info {
    position: absolute;
    top: 0.45rem;
    right: 2.3rem;
    z-index: 3;
    padding: 0;
    border: 0;
    cursor: pointer;
    transition: background-color var(--lf-duration-fast) ease;
}
.library-info:hover,
.library-info:focus-visible {
    background: rgba(var(--lf-black-rgb), 0.85);
}
.library-info:focus-visible {
    outline: 2px solid var(--lf-white);
    outline-offset: 1px;
}
.library-info .nav-menu-icon {
    stroke-width: 2.6;
}
/* A finger needs more than the icon's own size. */
@media (hover: none) {
    .library-info::before {
        content: "";
        position: absolute;
        inset: -0.4rem;
    }
}
/* The shelf, the time, the place or "📌" under the "i". */
.library-shelf-tag {
    position: absolute;
    top: 2.5rem;
    right: 2.35rem;
    max-width: calc(100% - 5rem);
    padding: 0.1rem 0.45rem;
    border-radius: var(--bs-border-radius-pill);
    background: rgba(var(--lf-black-rgb), 0.6);
    font-size: var(--lf-text-3xs);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
@media (max-width: 575.98px) {
    .library-grid {
        grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
        gap: 0.75rem;
    }
}

/* The full card that opens over a library card from its "i": after the pointer has rested on it for 1.5 seconds, on a
   tap or on Enter (library-card.js). It lies inside the cell (.library-item, which carries the direction colour), is placed by the
   script, and grows from the card with a short animation. */
.library-item {
    position: relative;
}
.library-item.is-open {
    z-index: var(--lf-z-card-open);
}
.library-popover {
    display: none;
    position: absolute;
    z-index: var(--lf-z-card-open);
    box-sizing: border-box;
    width: min(26rem, calc(100vw - 1rem));
    max-height: min(80vh, 40rem);
    overflow-y: auto;
    padding: 0.9rem;
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
    border: 3px solid var(--relationship-bg);
    border-radius: var(--bs-border-radius);
    box-shadow: 0 0 0 1px var(--relationship-bg), var(--lf-shadow-overlay);
    opacity: 0;
    transform: scale(0.45);
    transition: transform var(--lf-duration-base) ease, opacity var(--lf-duration-base) ease;
}
.library-item.is-open .library-popover {
    display: block;
}
.library-item.is-open .library-popover.is-shown {
    opacity: 1;
    transform: scale(1);
}
.library-popover:focus {
    outline: none;
}
.library-popover-description {
    max-height: 11rem;
    overflow-y: auto;
    font-size: var(--lf-text-ui);
}

/* Collections (collections/show.html, collections/catalogue.html): a work of the collection is a library card with the
   owner's note under it; a description in the catalogue is cut to a few lines. */
.collection-entry {
    min-width: 0;
}
.collection-note {
    margin: 0.4rem 0.1rem 0;
    font-size: var(--lf-text-sm);
    line-height: var(--lf-leading-snug);
    color: var(--bs-secondary-color);
    overflow-wrap: anywhere;
}
.collection-excerpt {
    max-height: 6.5rem;
    overflow: hidden;
}

/* The card buttons (like, add, hide) on the work cards of a list (fragments/work-menu.html): in the top right corner of
   the card, over the cover when it has one. */
.work-menu {
    position: absolute;
    top: 0.6rem;
    right: 0.6rem;
    z-index: 6;
}
.work-menu-like.liked,
.work-menu-like.liked:hover,
.work-menu-like.liked:focus-visible {
    color: var(--bs-danger);
}
.work-menu-like.liked .nav-menu-icon {
    fill: currentColor;
}
/* The work page's like button while the work is liked: the outline is red too, not only the heart. */
.btn-outline-secondary.work-menu-like.liked {
    border-color: var(--bs-danger);
}
/* A work's like counters after its badges (fragments/work-likes.html): a red heart and its number, a blue one. */
.work-likes {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    margin-left: 0.5rem;
    font-size: var(--lf-text-md);
    font-weight: var(--lf-weight-semibold);
    white-space: nowrap;
    vertical-align: middle;
}
/* The blue heart: a little darker than Bootstrap's info cyan, which is pale on the light theme. */
.work-like-count-other {
    color: var(--lf-like-other);
}
[data-bs-theme="dark"] .work-like-count-other {
    color: var(--bs-info);
}
.work-menu-list {
    min-width: 13rem;
    max-width: 18rem;
}
.work-menu-list .dropdown-item.disabled {
    cursor: not-allowed;
}
/* A card the page jumps back to after a button (the #card-<id> anchor) is not hidden under the navbar. */
[id^="card-"] {
    scroll-margin-top: 5rem;
}

/* A short notice after an action of the card menu (js/work-menu.js) and the fade of a hidden card. */
.work-toast {
    position: fixed;
    left: 50%;
    bottom: 1.5rem;
    transform: translateX(-50%);
    z-index: var(--lf-z-toast);
    max-width: min(32rem, calc(100vw - 2rem));
    margin: 0;
    box-shadow: var(--lf-shadow-float);
    transition: opacity var(--lf-duration-slow) ease;
}
.work-toast-out {
    opacity: 0;
}
.work-card-leaving {
    opacity: 0;
    transition: opacity var(--lf-duration-slow) ease;
}
/* A card the reader hides from the list (js/work-menu.js, hideCard): a veil like an eyelid closes over it from the top,
   with the crossed-out eye in the middle, and the card sinks a little; then the script folds its slot's height so the
   cards below move up instead of jumping. */
.work-card-hiding {
    overflow: hidden;
    pointer-events: none;
    animation: work-card-sink var(--lf-duration-slower) var(--lf-ease-standard) forwards;
}
.work-card-hiding::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 7;
    border-radius: inherit;
    background: linear-gradient(to bottom, var(--bs-tertiary-bg) 0%, var(--bs-body-bg) 85%, transparent 100%);
    transform-origin: top;
    animation: work-card-eyelid var(--lf-duration-slower) var(--lf-ease-in-out) forwards;
}
.work-card-hiding::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 8;
    background-color: var(--lf-gray-light);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 3l18 18'/%3E%3Cpath d='M10.6 6.1A10.4 10.4 0 0 1 12 6c5.5 0 9 6 9 6a17.6 17.6 0 0 1-2.6 3.3M6.6 6.6C4.3 8.1 3 12 3 12s3.5 6 9 6c1.6 0 3-.5 4.3-1.2'/%3E%3Cpath d='M9.9 9.9a3 3 0 0 0 4.2 4.2'/%3E%3C/svg%3E") center / 2.6rem no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 3l18 18'/%3E%3Cpath d='M10.6 6.1A10.4 10.4 0 0 1 12 6c5.5 0 9 6 9 6a17.6 17.6 0 0 1-2.6 3.3M6.6 6.6C4.3 8.1 3 12 3 12s3.5 6 9 6c1.6 0 3-.5 4.3-1.2'/%3E%3Cpath d='M9.9 9.9a3 3 0 0 0 4.2 4.2'/%3E%3C/svg%3E") center / 2.6rem no-repeat;
    transform-origin: top;
    animation: work-card-eyelid var(--lf-duration-slower) var(--lf-ease-in-out) forwards;
}
@keyframes work-card-eyelid {
    from { transform: scaleY(0); opacity: 0.6; }
    70% { opacity: 1; }
    to { transform: scaleY(1); opacity: 1; }
}
@keyframes work-card-sink {
    to { transform: scale(0.97); opacity: 0.85; filter: grayscale(1); }
}
@media (prefers-reduced-motion: reduce) {
    .work-card-hiding,
    .work-card-hiding::before,
    .work-card-hiding::after {
        animation: none;
    }
}

/* "Приховати роботу" / "Показувати роботу" on a work's page (works/detail.html): both eyes are in the button, one over
   the other; the one for the button's state is open, the other squeezed shut, so a change of state is a blink. */
.work-hide-btn {
    position: relative;
}
.work-hide-icon {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: transform var(--lf-duration-base) var(--lf-ease-standard), opacity var(--lf-duration-base) ease;
}
.work-hide-icon svg {
    width: 1em;
    height: 1em;
}
.work-hide-icon-on,
.work-hidden .work-hide-icon-off {
    transform: scaleY(0.1);
    opacity: 0;
}
.work-hidden .work-hide-icon-on {
    transform: none;
    opacity: 1;
    transition-delay: var(--lf-delay-short);
}
.work-hide-btn:not(.work-hidden) .work-hide-icon-off {
    transition-delay: var(--lf-delay-short);
}
/* The blink over the page when the reader hides the work (js/work-hide.js): two eyelids with curved edges close from
   the top and the bottom, the crossed-out eye shows where they meet, and the closed eye dissolves. Showing the work
   again plays the same keyframes backwards (.work-blink-open): the closed eye with the open one on it fades in and the
   eyelids part. --lf-duration-blink long, which js/work-hide.js reads. */
.work-blink {
    position: fixed;
    inset: 0;
    z-index: var(--lf-z-blink);
    pointer-events: none;
    overflow: hidden;
    animation: work-blink-veil var(--lf-duration-blink) linear forwards;
}
.work-blink-lid {
    position: absolute;
    left: -10%;
    right: -10%;
    height: calc(50% + 6rem);
    background: color-mix(in srgb, var(--bs-body-bg) 88%, transparent);
    -webkit-backdrop-filter: blur(6px) grayscale(0.6);
    backdrop-filter: blur(6px) grayscale(0.6);
    box-shadow: 0 0 2.5rem rgba(var(--lf-black-rgb), 0.18);
}
.work-blink-lid-top {
    top: 0;
    border-radius: 0 0 50% 50% / 0 0 6rem 6rem;
    animation: work-blink-top var(--lf-duration-blink) linear forwards;
}
.work-blink-lid-bottom {
    bottom: 0;
    border-radius: 50% 50% 0 0 / 6rem 6rem 0 0;
    animation: work-blink-bottom var(--lf-duration-blink) linear forwards;
}
.work-blink-eye {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 4.5rem;
    height: 4.5rem;
    margin: -2.25rem 0 0 -2.25rem;
    color: var(--bs-secondary-color);
    animation: work-blink-eye var(--lf-duration-blink) linear forwards;
}
.work-blink-open,
.work-blink-open .work-blink-lid,
.work-blink-open .work-blink-eye {
    animation-direction: reverse;
}
@keyframes work-blink-veil {
    0%, 62% { opacity: 1; }
    100% { opacity: 0; }
}
@keyframes work-blink-top {
    0% { transform: translateY(-100%); animation-timing-function: var(--lf-ease-in-out); }
    40%, 100% { transform: translateY(0); }
}
@keyframes work-blink-bottom {
    0% { transform: translateY(100%); animation-timing-function: var(--lf-ease-in-out); }
    40%, 100% { transform: translateY(0); }
}
@keyframes work-blink-eye {
    0%, 26% { opacity: 0; transform: scale(0.6) scaleY(0.2); }
    48% { opacity: 1; transform: scale(1.08); }
    58%, 100% { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .work-hide-icon {
        transition: none;
    }
}

/* A fest's card on the fests and contests pages (fragments/fest.html), one per row, drawn like the header of a
   universe's page (.ref-hero): the purple wash, the phase as .ref-kicker, the title, the theme, the dates as .ref-stat
   tiles and the options as white chips; the fest's picture on the right, the card's full height (on a phone a banner
   above). At least 12.4rem tall. */
.fest-card {
    display: flex;
    gap: 1.5rem;
    min-height: 12.4rem;
    padding: 1.25rem 1.5rem;
    border: var(--lf-border-accent);
    border-radius: var(--lf-radius-2xl);
    background: linear-gradient(135deg, var(--lf-accent-soft), var(--bs-body-bg) 75%);
    color: inherit;
    text-decoration: none;
    transition: border-color var(--lf-duration-fast), box-shadow var(--lf-duration-fast);
}
.fest-card:hover,
.fest-card:focus-visible {
    border-color: var(--lf-brand);
    box-shadow: var(--lf-shadow-hover);
    color: inherit;
}
.fest-card-body {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    align-items: flex-start;
    min-width: 0;
}
.fest-card-title {
    margin: .5rem 0 .25rem;
    font-family: var(--lf-font-title);
    font-size: var(--lf-text-display-sm);
    font-weight: var(--lf-weight-extrabold);
    letter-spacing: var(--lf-tracking-snug);
    line-height: var(--lf-leading-tight);
    overflow-wrap: anywhere;
}
.fest-card:hover .fest-card-title,
.fest-card:focus-visible .fest-card-title {
    color: var(--lf-accent-ink);
}
.fest-card-theme {
    margin-bottom: 1rem;
    color: var(--bs-secondary-color);
}
.fest-card-stats {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    margin-top: auto;
}
.fest-card .ref-stat-value {
    font-size: var(--lf-text-lg);
}
.fest-card-options {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    margin-top: .75rem;
}
.fest-card-option {
    padding: .2rem .65rem;
    border: var(--lf-border-soft);
    border-radius: var(--bs-border-radius-pill);
    background: var(--bs-body-bg);
    font-size: var(--lf-text-sm);
}
.fest-card-image {
    order: 2;
    flex: 0 0 16rem;
    width: 16rem;
    min-height: 10rem;
    max-height: 18rem;
    object-fit: cover;
    border-radius: var(--bs-border-radius-lg);
}
@media (max-width: 575.98px) {
    .fest-card {
        flex-direction: column;
        gap: 1rem;
        padding: 1rem;
    }
    .fest-card-image {
        order: 0;
        flex: none;
        width: 100%;
        min-height: 0;
        aspect-ratio: 16 / 9;
    }
    .fest-card-stats {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: .5rem;
        width: 100%;
    }
    .fest-card .ref-stat {
        min-width: 0;
        padding: .5rem .65rem;
    }
    .fest-card .ref-stat-value {
        font-size: var(--lf-text-ui);
    }
}
/* A fest's picture on its page (fests/show.html) and in its form (admin/fest-form.html). */
.fest-page-image {
    display: block;
    width: 100%;
    max-height: 22rem;
    margin-bottom: 1.25rem;
    object-fit: cover;
    border-radius: var(--lf-radius-2xl);
}
.fest-image-preview {
    width: 12rem;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    border-radius: var(--bs-border-radius);
}

/* "Приховане" (reader/hidden.html): a header with the crossed-out eye in a soft circle; the mode as two tiles, each
   with a small drawing of a list (the folded one shows a thin line where the card was); the tabs as a segmented strip
   with icons and the number of hidden works; the works as cards, two in a row on a wide screen, the cover greyed and a
   little faded (they are hidden, after all) until the card is pointed at. */
.hidden-page {
    max-width: 960px;
}
.hidden-hero {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
}
.hidden-hero-icon,
.hidden-empty-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 3rem;
    height: 3rem;
    border-radius: var(--lf-radius-circle);
    background: var(--lf-accent-soft);
    color: var(--lf-accent-ink);
}
.hidden-hero-icon .nav-menu-icon,
.hidden-empty-icon .nav-menu-icon {
    width: 1.5rem;
    height: 1.5rem;
}
.hidden-label {
    color: var(--bs-secondary-color);
    margin-bottom: .5rem;
}

.hidden-modes {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: .75rem;
}
.hidden-mode {
    display: flex;
    align-items: center;
    gap: .9rem;
    padding: .85rem 1rem;
    border: var(--lf-border);
    border-radius: var(--bs-border-radius-lg);
    background: var(--lf-surface);
    cursor: pointer;
    transition: border-color var(--lf-duration-fast) ease, background-color var(--lf-duration-fast) ease, box-shadow var(--lf-duration-fast) ease;
}
.hidden-mode:hover {
    border-color: var(--lf-accent-line);
}
.hidden-mode:has(input:checked) {
    border-color: var(--lf-brand);
    background: var(--lf-accent-soft);
    box-shadow: var(--lf-ring-selected);
}
.hidden-mode:has(input:focus-visible) {
    outline: var(--lf-focus-outline);
    outline-offset: var(--lf-focus-offset);
}
.hidden-mode-art {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 4px;
    flex-shrink: 0;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0 .4rem;
    border-radius: var(--bs-border-radius);
    background: var(--bs-tertiary-bg);
    border: var(--lf-border);
}
.hidden-mode-line {
    height: 7px;
    border-radius: var(--lf-radius-hair);
    background: var(--lf-border-strong);
}
.hidden-mode-line-folded {
    height: 2px;
    background: repeating-linear-gradient(90deg, var(--lf-brand) 0 3px, transparent 3px 5px);
}
.hidden-mode:has(input:checked) .hidden-mode-art {
    background: var(--lf-surface);
    border-color: var(--lf-accent-line);
}
.hidden-mode-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}
.hidden-mode-title {
    font-weight: var(--lf-weight-semibold);
}
.hidden-mode-note {
    font-size: var(--lf-text-md);
    color: var(--bs-secondary-color);
}

/* A row of tabs in a pill (the periods of the statistics and of the top, the tabs of the hidden things): one look; each
   component sets only how its row flows. */
.stats-periods,
.top-periods,
.hidden-tabs {
    gap: .25rem;
    padding: .25rem;
    border: var(--lf-border);
    border-radius: var(--bs-border-radius-pill);
    background: var(--bs-tertiary-bg);
}
.stats-periods a,
.top-periods a,
.hidden-tabs .nav-link {
    padding: .4rem .9rem;
    border-radius: var(--bs-border-radius-pill);
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-md);
    font-weight: var(--lf-weight-medium);
    text-decoration: none;
    transition: background-color var(--lf-duration-fast), color var(--lf-duration-fast);
}
.stats-periods a:hover,
.top-periods a:hover,
.hidden-tabs .nav-link:hover {
    color: var(--bs-body-color);
}
.stats-periods a.active,
.top-periods a.active,
.hidden-tabs .nav-link.active {
    background: var(--lf-surface);
    color: var(--lf-accent-ink);
    box-shadow: var(--lf-shadow-tab);
}
[data-bs-theme="dark"] .stats-periods a.active,
[data-bs-theme="dark"] .top-periods a.active,
[data-bs-theme="dark"] .hidden-tabs .nav-link.active {
    background: var(--lf-accent-soft);
    color: var(--bs-body-color);
}

.hidden-tabs {
    display: inline-flex;
    flex-wrap: wrap;
}
.hidden-tabs .nav-link {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
}
.hidden-tabs .nav-menu-icon {
    width: 1.05rem;
    height: 1.05rem;
}
.hidden-tab-count {
    min-width: 1.4rem;
    padding: 0 .4rem;
    border-radius: var(--bs-border-radius-xl);
    background: var(--lf-brand);
    color: var(--lf-white);
    font-size: var(--lf-text-xs);
    font-weight: var(--lf-weight-semibold);
    line-height: 1.4rem;
    text-align: center;
}

.hidden-works {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(22rem, 1fr));
    gap: .75rem;
}
@media (max-width: 575.98px) {
    .hidden-works {
        grid-template-columns: 1fr;
    }
}
.hidden-work {
    position: relative;
    display: flex;
    gap: 1rem;
    padding: .9rem;
    border: var(--lf-border);
    border-radius: var(--bs-border-radius-lg);
    background: var(--lf-surface);
    transition: border-color var(--lf-duration-fast) ease, box-shadow var(--lf-duration-fast) ease;
}
.hidden-work:hover,
.hidden-work:focus-within {
    border-color: var(--lf-accent-line);
    box-shadow: var(--lf-shadow-hover);
}
.hidden-work-cover-link {
    flex-shrink: 0;
    align-self: flex-start;
}
.hidden-work-cover {
    display: block;
    width: 64px;
    aspect-ratio: 2 / 3;
    object-fit: cover;
    border-radius: var(--bs-border-radius-sm);
    border-left: 4px solid var(--relationship-bg, var(--lf-relationship-none-bg));
    filter: grayscale(.85);
    opacity: var(--lf-opacity-muted);
    transition: filter var(--lf-duration-base) ease, opacity var(--lf-duration-base) ease;
}
.hidden-work:hover .hidden-work-cover,
.hidden-work:focus-within .hidden-work-cover {
    filter: none;
    opacity: 1;
}
.hidden-work-cover-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(var(--book-from, var(--lf-accent-deep)), var(--book-to, var(--lf-accent-deep)));
    color: var(--lf-white);
    position: relative;
    isolation: isolate;
}
.hidden-work-cover-empty .brand-mark {
    display: none;
}
.hidden-work-body {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;
}
.hidden-work-title {
    font-family: var(--lf-font-title);
    font-weight: var(--lf-weight-bold);
    font-size: var(--lf-text-plus);
    line-height: var(--lf-leading-snug);
    color: var(--bs-body-color);
    text-decoration: none;
}
.hidden-work-title:hover {
    color: var(--lf-brand);
}
.hidden-work-meta {
    margin: .2rem 0 0;
    font-size: var(--lf-text-md);
    color: var(--bs-secondary-color);
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}
.hidden-work-meta .nav-menu-icon {
    width: .95rem;
    height: .95rem;
    margin-right: .3rem;
    vertical-align: -.15em;
    color: var(--bs-tertiary-color);
}
.hidden-work-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    margin-top: auto;
    padding-top: .65rem;
}
.hidden-work-show {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
}
.hidden-work-show .nav-menu-icon {
    width: 1rem;
    height: 1rem;
}
/* "Показати знову" pressed (js/hidden-page.js): the card brightens, an open eye swells in its middle, and it fades;
   then the script folds its place in the grid. */
.hidden-work-opening {
    pointer-events: none;
    animation: hidden-work-open var(--lf-duration-slower) ease forwards;
}
.hidden-work-opening .hidden-work-cover {
    filter: none;
    opacity: 1;
}
.hidden-work-opening::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: rgba(var(--lf-brand-rgb), .1);
}
.hidden-work-opening::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    background-color: var(--lf-brand);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 12c.5-1.5 4-6 9-6s8.5 4.5 9 6c-.5 1.5-4 6-9 6s-8.5-4.5-9-6z'/%3E%3Ccircle cx='12' cy='12' r='3'/%3E%3C/svg%3E") center / 2.6rem no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 12c.5-1.5 4-6 9-6s8.5 4.5 9 6c-.5 1.5-4 6-9 6s-8.5-4.5-9-6z'/%3E%3Ccircle cx='12' cy='12' r='3'/%3E%3C/svg%3E") center / 2.6rem no-repeat;
    animation: hidden-work-eye var(--lf-duration-slower) ease forwards;
}
@keyframes hidden-work-open {
    60% { opacity: 1; transform: none; }
    to { opacity: 0; transform: scale(1.02); }
}
@keyframes hidden-work-eye {
    from { -webkit-mask-size: 0; mask-size: 0; opacity: 0; }
    40% { -webkit-mask-size: 3rem; mask-size: 3rem; opacity: 1; }
    to { -webkit-mask-size: 2.6rem; mask-size: 2.6rem; opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    .hidden-work-cover,
    .hidden-work-opening,
    .hidden-work-opening::before,
    .hidden-work-opening::after {
        transition: none;
        animation: none;
    }
}
/* "Приховане — Автори": the cards of "Автори" (.author-tile), the avatar greyed like the hidden works' covers until
   the card is pointed at, and the eye that shows the author again in the card's corner. */
.hidden-author {
    position: relative;
}
.hidden-author .author-tile {
    height: 100%;
    padding-right: 3rem;
}
.hidden-author .author-tile-avatar {
    filter: grayscale(1);
    opacity: var(--lf-opacity-muted);
    transition: filter var(--lf-duration-base) ease, opacity var(--lf-duration-base) ease;
}
.hidden-author:hover .author-tile-avatar,
.hidden-author:focus-within .author-tile-avatar {
    filter: none;
    opacity: 1;
}
.hidden-author-show {
    position: absolute;
    top: .625rem;
    right: .625rem;
    margin: 0;
}
.hidden-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .75rem;
    padding: 2.5rem 1rem;
    border: var(--lf-border-dashed);
    border-radius: var(--bs-border-radius-lg);
    color: var(--bs-secondary-color);
    text-align: center;
}

/* Comments under a work (fragments/work-comments.html). A top-level comment and its branch sit on a soft card; a
   comment is an avatar (the first letter of the name) beside its head, text and actions; the replies hang under the
   text, behind a thin line that folds the branch when clicked. */
.work-comments .comment-list {
    display: flex;
    flex-direction: column;
    gap: .75rem;
}
.work-comments .comment-list > .comment {
    padding: 1rem 1rem .75rem;
    border: var(--lf-border-soft);
    border-radius: var(--bs-border-radius-lg);
    background: var(--lf-surface);
}
/* A comment reached by its link, just written, or just come from other people (js/work-comments.js) is lit. */
.work-comments .comment:target > .comment-row > .comment-main > .comment-body,
.work-comments .comment:target > .comment-row > .comment-main > .comment-head,
.work-comments .comment-lit > .comment-row > .comment-main > .comment-body,
.work-comments .comment-lit > .comment-row > .comment-main > .comment-head {
    animation: comment-flash var(--lf-duration-flash) ease-out;
}
.work-comments .comment-fresh > .comment-row > .comment-main > .comment-body,
.work-comments .comment-fresh > .comment-row > .comment-main > .comment-head {
    animation: comment-flash var(--lf-duration-flash-long) ease-out;
}
@keyframes comment-flash {
    from { background: rgba(var(--lf-brand-rgb), .15); }
    to { background: transparent; }
}
.work-comments .comment-reply {
    margin-top: .85rem;
}
.work-comments .comment-row {
    display: flex;
    align-items: flex-start;
    gap: .75rem;
}
.work-comments .comment-main {
    flex: 1 1 auto;
    min-width: 0;
}

/* The avatar: a round letter in one of six calm, flat colours. */
.work-comments .comment-avatar {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: var(--lf-radius-circle);
    font-weight: var(--lf-weight-semibold);
    font-size: var(--lf-text-base);
    line-height: var(--lf-leading-none);
    color: var(--lf-white);
    text-decoration: none;
    user-select: none;
    background: var(--lf-avatar-bg, var(--lf-brand)); /* .avatar-tone-<n>, tokens.css */
}
.work-comments .comment-reply .comment-avatar {
    width: 1.75rem;
    height: 1.75rem;
    font-size: var(--lf-text-sm);
}
.work-comments .comment-avatar-deleted {
    background: var(--bs-tertiary-bg);
    border: var(--lf-border-dashed);
}

/* The head: name, badge, time (a link to the comment itself). */
.work-comments .comment-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: .5rem;
    row-gap: .1rem;
    margin-bottom: .2rem;
    border-radius: var(--lf-radius-xs);
}
.work-comments .comment-author {
    font-weight: var(--lf-weight-semibold);
    color: var(--bs-emphasis-color);
    text-decoration: none;
}
.work-comments .comment-author:hover {
    text-decoration: underline;
}
.work-comments .comment-time,
.work-comments .comment-reply-to {
    font-size: var(--lf-text-sm);
    color: var(--bs-secondary-color);
    text-decoration: none;
}
.work-comments a.comment-time:hover {
    color: var(--bs-body-color);
}
/* The chapter a top-level comment is about. */
.work-comments .comment-chapter {
    font-size: var(--lf-text-sm);
    color: var(--bs-secondary-color);
    text-decoration: none;
    border: var(--lf-border);
    border-radius: var(--bs-border-radius-pill);
    padding: 0 .5rem;
    max-width: 18rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.work-comments .comment-chapter::before {
    content: "\00A7\00A0";
}
.work-comments .comment-chapter:hover {
    color: var(--bs-body-color);
}
.work-comments .comment-badge {
    display: inline-block;
    align-self: center;
    padding: .05rem .5rem;
    border-radius: var(--bs-border-radius-pill);
    font-size: var(--lf-text-2xs);
    font-weight: var(--lf-weight-semibold);
    letter-spacing: var(--lf-tracking-wide);
    line-height: var(--lf-leading-normal);
    color: var(--lf-accent-ink);
    background: var(--lf-accent-soft);
}
/* The site role of the writer: the admin (red) or a moderator (blue). */
.work-comments .comment-badge.comment-badge-admin {
    color: var(--lf-danger-deep);
    background: rgba(var(--lf-danger-rgb), .12);
}
.work-comments .comment-badge.comment-badge-moderator {
    color: var(--lf-blue-deep);
    background: rgba(var(--lf-blue-rgb), .12);
}
[data-bs-theme="dark"] .work-comments .comment-badge.comment-badge-admin {
    color: var(--lf-danger-night);
    background: rgba(var(--lf-danger-rgb), .22);
}
[data-bs-theme="dark"] .work-comments .comment-badge.comment-badge-moderator {
    color: var(--lf-blue-night);
    background: rgba(var(--lf-blue-rgb), .22);
}
[data-bs-theme="dark"] .work-comments .comment-badge {
    color: var(--lf-accent-ink);
    background: var(--lf-accent-soft);
}
.work-comments .comment-body {
    border-radius: var(--lf-radius-xs);
}

/* The actions: quiet small buttons (.btn-quiet). */
.work-comments .comment-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .15rem;
    margin-left: -.5rem;
}
.work-comments .comment-form {
    margin-top: .5rem;
}
.work-comments .comment-form textarea {
    min-height: 5rem;
}

/* "Є нові коментарі — показати": while the reader is writing, the block is not swapped under their hands. */
.work-comments .comments-fresh {
    position: sticky;
    top: .75rem;
    z-index: 5;
    display: flex;
    align-items: center;
    gap: .5rem;
    margin: 0 auto .75rem;
    padding: .35rem .9rem;
    border: 0;
    border-radius: var(--bs-border-radius-pill);
    font-size: var(--lf-text-md);
    font-weight: var(--lf-weight-medium);
    color: var(--lf-white);
    background: var(--lf-brand);
    box-shadow: var(--lf-shadow-raised);
}
.work-comments .comments-fresh:hover {
    background: var(--lf-accent-hover);
}
.work-comments .comments-fresh-dot {
    width: .5rem;
    height: .5rem;
    border-radius: var(--lf-radius-circle);
    background: var(--lf-white);
    animation: comments-pulse var(--lf-duration-pulse) ease-in-out infinite;
}
@keyframes comments-pulse {
    50% { opacity: .35; }
}
/* A folded branch that got a comment from other people. */
.work-comments .comment-branch-open.has-fresh .comment-branch-count {
    color: var(--lf-white);
    background: var(--lf-brand);
}

/* Branches of replies: "+ Розгорнути гілку (n)" when folded; when open, the line along the left side folds it. Past
   depth 6 a branch is no longer indented (it moves back under its comment's avatar). */
.work-comments .comment-branch-open {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    margin-top: .5rem;
    margin-left: -.5rem;
    padding: .2rem .65rem .2rem .5rem;
    border: 0;
    border-radius: var(--bs-border-radius-pill);
    background: transparent;
    font-size: var(--lf-text-sm);
    font-weight: var(--lf-weight-medium);
    color: var(--bs-link-color);
}
.work-comments .comment-branch-open:hover,
.work-comments .comment-branch-open:focus-visible {
    background: var(--bs-tertiary-bg);
}
.work-comments .comment-branch-sign {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.1rem;
    height: 1.1rem;
    border: 1px solid currentColor;
    border-radius: var(--lf-radius-circle);
    font-size: var(--lf-text-sm);
    line-height: var(--lf-leading-none);
}
.work-comments .comment-branch-count {
    padding: 0 .4rem;
    border-radius: var(--bs-border-radius-pill);
    font-size: var(--lf-text-2xs);
    background: var(--bs-tertiary-bg);
    color: var(--bs-secondary-color);
}
.work-comments .comment-branch {
    position: relative;
    padding-left: 1rem;
}
.work-comments .comment-branch-close {
    position: absolute;
    top: .85rem;
    bottom: 0;
    left: 0;
    width: .75rem;
    padding: 0;
    border: 0;
    background: transparent;
    cursor: pointer;
}
.work-comments .comment-branch-close::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(50% - 1px);
    width: 2px;
    border-radius: var(--lf-radius-hair);
    background: var(--bs-border-color);
    transition: background-color var(--lf-duration-fast);
}
.work-comments .comment-branch-close:hover::before,
.work-comments .comment-branch-close:focus-visible::before {
    background: var(--lf-brand);
}
.work-comments .comment-branch.comment-flat {
    margin-left: -3.5rem;   /* avatar + gap + the branch's own padding: the replies line up under this comment */
}
/* On a phone a level of replies moves in by only .75rem: the branch starts under the avatar, not beside it. */
@media (max-width: 575.98px) {
    .work-comments .comment-list > .comment {
        padding: .75rem .75rem .5rem;
    }
    .work-comments .comment-row {
        gap: .5rem;
    }
    .work-comments .comment-avatar {
        width: 1.75rem;
        height: 1.75rem;
        font-size: var(--lf-text-sm);
    }
    .work-comments .comment-branch {
        margin-left: -2.25rem;
        padding-left: .75rem;
    }
    .work-comments .comment-branch.comment-flat {
        margin-left: -3rem;
    }
}

/* The text of a comment is Markdown made into HTML (fragments/work-comments.html). */
.work-comments .comment-md {
    overflow-wrap: anywhere;
}
.work-comments .comment-md p {
    margin-bottom: .5rem;
}
.work-comments .comment-md blockquote {
    margin: 0 0 .5rem;
    padding: .25rem .75rem;
    border-left: 3px solid var(--bs-secondary-color);
    color: var(--bs-secondary-color);
    background: var(--bs-tertiary-bg);
    border-radius: 0 var(--lf-radius-xs) var(--lf-radius-xs) 0;
}
.work-comments .comment-md blockquote p:last-child {
    margin-bottom: 0;
}
.work-comments .comment-md pre {
    padding: .5rem .75rem;
    background: var(--bs-tertiary-bg);
    border-radius: var(--lf-radius-xs);
    overflow-x: auto;
}
.work-comments .comment-md ul,
.work-comments .comment-md ol {
    margin-bottom: .5rem;
}
/* A spoiler (||text||): a striped plate with the text hidden under it, until it is clicked (js/work-comments.js). */
.work-comments span.spoiler {
    border-radius: var(--lf-radius-xs);
    padding: 0 .15em;
    box-decoration-break: clone;
    -webkit-box-decoration-break: clone;
    transition: background-color var(--lf-duration-base), color var(--lf-duration-base);
}
.work-comments span.spoiler:not(.spoiler-open) {
    cursor: pointer;
    user-select: none;
    color: transparent;
    background-color: var(--bs-secondary-color);
    background-image: repeating-linear-gradient(135deg, rgba(var(--lf-white-rgb), .12) 0 6px, transparent 6px 12px);
}
.work-comments span.spoiler:not(.spoiler-open) * {
    color: transparent !important;
    background: transparent !important;
    border-color: transparent !important;
    pointer-events: none;
}
.work-comments span.spoiler:not(.spoiler-open):hover {
    background-color: var(--bs-emphasis-color);
}
.work-comments span.spoiler:focus-visible {
    outline: var(--lf-focus-outline);
    outline-offset: var(--lf-focus-offset);
}
.work-comments span.spoiler.spoiler-open {
    background-color: rgba(var(--lf-brand-rgb), .1);
}
/* The comment editor (EasyMDE): compact, in the colours of the page. */
.work-comments .EasyMDE,
.work-comments .editor-toolbar,
.work-comments .CodeMirror {
    border-color: var(--bs-border-color);
}
.work-comments .CodeMirror {
    min-height: var(--lf-editor-comment-min-height);
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
}
.work-comments .CodeMirror-cursor {
    border-left-color: var(--bs-body-color);
}
.work-comments .editor-toolbar button {
    color: var(--bs-body-color);
}
.work-comments .editor-preview {
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
}

/* Reading bookmarks: a paragraph reached by #p-... stops under the sticky reader toolbar. */
#readerChapter [data-paragraph-id] { scroll-margin-top: 80px; }
.bookmark-progress { height: 6px; max-width: 320px; }

/* "Кабінет адміна - Модератори" (admin/moderators.html): the card of a new moderator and the list. */
.moderator-create {
    overflow: hidden;
    border: var(--lf-border-soft);
    border-radius: var(--bs-border-radius-lg);
    background: var(--lf-surface);
}
.moderator-create-head {
    display: flex;
    align-items: center;
    gap: .9rem;
    padding: 1.1rem 1.25rem;
    color: var(--lf-white);
    background: var(--lf-brand);
}
.moderator-create-sub {
    color: rgba(var(--lf-white-rgb), .85);
}
.moderator-create-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 2.6rem;
    height: 2.6rem;
    border-radius: var(--lf-radius-circle);
    background: rgba(var(--lf-white-rgb), .18);
}
.moderator-create .card-body {
    padding: 1.25rem;
}
.moderator-create-foot {
    padding: .85rem 1.25rem;
    background: var(--bs-tertiary-bg);
    border-top: var(--lf-border-soft);
}
.moderator-create [data-password-toggle].active {
    color: var(--lf-white);
    background: var(--bs-secondary);
}
.moderator-list .list-group-item {
    padding: .85rem 1rem;
}
.moderator-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 2.4rem;
    height: 2.4rem;
    border-radius: var(--lf-radius-circle);
    font-weight: var(--lf-weight-semibold);
    color: var(--lf-blue-deep);
    background: rgba(var(--lf-blue-rgb), .12);
}
.moderator-password {
    margin-top: .25rem;
    padding: .85rem 1rem;
    border-radius: var(--bs-border-radius-lg);
    background: var(--bs-tertiary-bg);
}
.moderator-password [data-password-toggle].active {
    color: var(--lf-white);
    background: var(--bs-secondary);
}
.moderator-active {
    color: var(--lf-success-deep);
    background: rgba(var(--lf-success-rgb), .14);
}
.moderator-blocked .moderator-avatar {
    color: var(--bs-secondary-color);
    background: var(--bs-tertiary-bg);
}
.moderator-blocked a {
    color: var(--bs-secondary-color);
}

/* Author cabinet -> "Відгуки" (author/reviews.html): the comments are drawn like the ones under a work (.work-comments);
   a new one has a coloured edge, and "Відповісти" is the summary of a <details> holding the reply form. */
.author-reviews .comment-list > .comment.author-review-new {
    border-left: 4px solid var(--bs-danger);
}
.author-reviews .author-review-reply > summary {
    display: inline-block;
    cursor: pointer;
    list-style: none;
}
/* Open, the form takes the whole width of the row of actions. */
.author-reviews .author-review-reply[open] {
    flex-basis: 100%;
}
.author-reviews .author-review-reply > summary::-webkit-details-marker {
    display: none;
}
.author-reviews .author-review-reply[open] > summary {
    color: var(--bs-emphasis-color);
}
/* In the left column of works (.news-shell, like "Історія змін"): the new comments of a work, before its count. */
.author-reviews-works .author-reviews-new-count {
    flex-shrink: 0;
    padding: 0 .4rem;
    border-radius: var(--bs-border-radius-xl);
    background: var(--bs-danger);
    color: var(--lf-white);
    font-size: var(--lf-text-2xs);
    font-weight: var(--lf-weight-semibold);
    line-height: 1.25rem;
}

/* Author cabinet -> "Історія змін" (author/history.html, author/history-change.html). A change of the text is shown word
   by word (WordDiff): removed text struck through on red, added text on green; Bootstrap's subtle colours follow the
   light / dark theme. The text is the editor's Markdown source, so its line breaks are kept. */
.work-history .work-history-what {
    min-width: 0;
    flex: 1 1 20rem;
}
/* The works in the left column (.news-shell): on a narrow screen a long title is cut short in its chip. */
@media (max-width: 767.98px) {
    .work-history-works .news-type-label,
    .author-reviews-works .news-type-label {
        max-width: 14rem;
        overflow: hidden;
        text-overflow: ellipsis;
    }
}
.history-paragraph {
    margin-bottom: 0.75rem;
}
.history-paragraph-head {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    margin-bottom: 0.25rem;
}
.history-diff {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    padding: 0.5rem 0.75rem;
    border: var(--lf-border);
    border-radius: var(--bs-border-radius);
    background: var(--bs-body-bg);
}
.history-diff del {
    background: var(--bs-danger-bg-subtle);
    color: var(--bs-danger-text-emphasis);
    text-decoration: line-through;
}
.history-diff ins {
    background: var(--bs-success-bg-subtle);
    color: var(--bs-success-text-emphasis);
    text-decoration: none;
}

/* "Автори" (authors/list.html), to the right of the .news-shell's left column: the authors as a grid of linked cards,
   up to three in a row, each with a rounded square avatar and its counts as small pills. */
.author-tiles {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: .875rem;
}
@media (min-width: 576px) {
    .author-tiles {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}
@media (min-width: 992px) {
    .author-tiles {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}
/* A tile that links somewhere (an author, a fandom, a tag, a figure of a reference page): one look; each component
   sets only how its content is laid out. */
.author-tile,
.fandom-tile,
.ref-stat,
.tag-tile {
    border: var(--lf-border-soft);
    border-radius: var(--bs-border-radius-xl);
    background: var(--bs-body-bg);
    color: inherit;
    text-decoration: none;
    transition: border-color var(--lf-duration-fast), box-shadow var(--lf-duration-fast);
}
.author-tile:hover,
.author-tile:focus-visible,
.fandom-tile:hover,
.fandom-tile:focus-visible,
.ref-stat:hover,
.ref-stat:focus-visible,
.tag-tile:hover,
.tag-tile:focus-visible {
    border-color: var(--lf-brand);
    box-shadow: var(--lf-shadow-hover);
}
.author-tile {
    display: flex;
    align-items: flex-start;
    gap: .875rem;
    padding: .875rem;
}
.author-tile-avatar {
    width: 3.75rem;
    height: 3.75rem;
    flex: 0 0 3.75rem;
    border-radius: var(--bs-border-radius-lg);
    font-size: var(--lf-text-xl);
    object-fit: cover;
}
.author-tile-body {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: .125rem;
    min-width: 0;
}
.author-tile-title {
    display: flex;
    align-items: center;
    gap: .375rem;
    min-width: 0;
}
.author-tile-name {
    overflow: hidden;
    font-size: var(--lf-text-ui);
    font-weight: var(--lf-weight-semibold);
    text-overflow: ellipsis;
    white-space: nowrap;
}
.author-tile:hover .author-tile-name,
.author-tile:focus-visible .author-tile-name {
    color: var(--bs-primary-text-emphasis);
}
.author-tile .author-role {
    flex-shrink: 0;
    padding: 0 .375rem;
    font-size: var(--lf-text-3xs);
    line-height: var(--lf-leading-normal);
}
.author-tile-line {
    overflow: hidden;
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-xs);
    text-overflow: ellipsis;
    white-space: nowrap;
}
.author-tile-facts {
    display: flex;
    flex-wrap: wrap;
    gap: .25rem;
    margin-top: .375rem;
}
.author-tile-fact {
    padding: .0625rem .5rem;
    border-radius: var(--bs-border-radius-pill);
    background: var(--bs-primary-bg-subtle);
    color: var(--bs-primary-text-emphasis);
    font-size: var(--lf-text-2xs);
    font-weight: var(--lf-weight-medium);
}

/* An author's public page (authors/profile.html): a header card with a banner in the navbar's purple, the avatar
   overlapping it, the name, @handle and the account's roles as coloured pills; a row of numbers and facts. The role
   colours use Bootstrap's subtle palette, so they follow the light / dark theme. */
.author-hero {
    overflow: hidden;
}
/* An author the reader hides ("Приховати автора"): the banner and the avatar in grey, faded in and out when the reader
   hides or shows the author without a reload (js/author-relation.js); the rest of the page gives way to
   .author-hidden-note. */
.author-hero-banner,
.author-hero-avatar {
    transition: filter var(--lf-duration-slowest) ease, opacity var(--lf-duration-slowest) ease;
}
.author-hero-hidden .author-hero-banner,
.author-hero-hidden .author-hero-avatar,
.author-hero-blocked .author-hero-avatar {
    filter: grayscale(1);
    opacity: var(--lf-opacity-muted);
}
/* An author the reader blocks ("Заблокувати"): the grey avatar under the "blocked" mark, which pops in when the reader
   blocks the author without a reload. */
.author-hero-avatar-frame {
    position: relative;
    flex-shrink: 0;
    margin-top: -76px;
}
.author-hero-avatar-frame .author-hero-avatar {
    display: block;
    margin-top: 0;
}
/* The letter keeps its flex centring over the frame's display: block. */
.author-hero-avatar-frame .author-hero-avatar-letter {
    display: flex;
}
.author-hero-blocked-mark {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--bs-danger);
    opacity: 0;
    transform: scale(0.6);
    transition: opacity var(--lf-duration-slower) ease, transform var(--lf-duration-slower) var(--lf-ease-spring);
    pointer-events: none;
}
.author-hero-blocked-mark .nav-menu-icon {
    width: 55%;
    height: 55%;
    stroke-width: 2.25;
    filter: drop-shadow(0 0 3px var(--bs-body-bg));
}
.author-hero-blocked .author-hero-blocked-mark {
    opacity: 1;
    transform: none;
}
.author-hidden-note .nav-menu-icon {
    flex-shrink: 0;
    width: 1.75rem;
    height: 1.75rem;
    color: var(--bs-secondary-color);
}
@media (prefers-reduced-motion: reduce) {
    .author-hero-banner,
    .author-hero-avatar,
    .author-hero-blocked-mark {
        transition: none;
    }
}
.author-hero-banner {
    height: 140px;
    /* The purple of the header and the footer. */
    background: var(--lf-bar-bg);
}
/* The author's own background (profile settings), cropped to fill the banner. */
.author-hero-banner.author-hero-banner-picture,
:root[data-bs-theme="dark"] .author-hero-banner.author-hero-banner-picture {
    height: 200px;
    background-color: var(--bs-secondary-bg);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}
.author-hero-body {
    padding-top: 0;
}
.author-hero-avatar {
    width: 152px;
    height: 152px;
    margin-top: -76px;
    border-radius: var(--lf-radius-avatar);
    object-fit: cover;
    object-position: center;
    border: 4px solid var(--bs-body-bg);
    background: var(--bs-secondary-bg);
    flex-shrink: 0;
}
.author-hero-avatar-letter {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--lf-text-8xl);
    font-weight: var(--lf-weight-semibold);
    color: var(--lf-white);
    background: var(--lf-brand);
}
.author-hero-name {
    min-width: 12rem;
}
.author-hero-actions {
    margin-left: auto;
}
/* "Заблокувати" and "Поскаржитися": a grey outline, a red one under the pointer. */
.author-action-report {
    --bs-btn-color: var(--lf-gray);
    --bs-btn-border-color: var(--lf-gray);
    --bs-btn-hover-color: var(--lf-danger);
    --bs-btn-hover-bg: transparent;
    --bs-btn-hover-border-color: var(--lf-danger);
    --bs-btn-active-color: var(--lf-danger);
    --bs-btn-active-bg: transparent;
    --bs-btn-active-border-color: var(--lf-danger);
    --bs-btn-focus-shadow-rgb: var(--lf-danger-rgb);
}
/* "Приховати автора" has a grey outline and "Показувати автора" a blue one: the site's .btn-outline-secondary takes the
   text colour and its .btn-outline-primary is purple. */
.author-action-hide {
    --bs-btn-color: var(--lf-gray);
    --bs-btn-border-color: var(--lf-gray);
    --bs-btn-hover-color: var(--lf-white);
    --bs-btn-hover-bg: var(--lf-gray);
    --bs-btn-hover-border-color: var(--lf-gray);
    --bs-btn-active-color: var(--lf-white);
    --bs-btn-active-bg: var(--lf-gray-deep);
    --bs-btn-active-border-color: var(--lf-gray-deep);
    --bs-btn-focus-shadow-rgb: var(--lf-gray-rgb);
}
.author-action-show {
    --bs-btn-color: var(--lf-blue);
    --bs-btn-border-color: var(--lf-blue);
    --bs-btn-hover-color: var(--lf-white);
    --bs-btn-hover-bg: var(--lf-blue);
    --bs-btn-hover-border-color: var(--lf-blue);
    --bs-btn-active-color: var(--lf-white);
    --bs-btn-active-bg: var(--lf-blue-deep);
    --bs-btn-active-border-color: var(--lf-blue-deep);
    --bs-btn-focus-shadow-rgb: var(--lf-blue-rgb);
}
.author-role {
    display: inline-block;
    padding: 0.2rem 0.65rem;
    border-radius: var(--bs-border-radius-pill);
    font-size: var(--lf-text-sm);
    font-weight: var(--lf-weight-semibold);
    border: 1px solid transparent;
}
.author-role-admin {
    background: var(--bs-danger-bg-subtle);
    color: var(--bs-danger-text-emphasis);
    border-color: var(--bs-danger-border-subtle);
}
.author-role-moderator {
    background: var(--bs-warning-bg-subtle);
    color: var(--bs-warning-text-emphasis);
    border-color: var(--bs-warning-border-subtle);
}
.author-role-author {
    background: var(--bs-primary-bg-subtle);
    color: var(--bs-primary-text-emphasis);
    border-color: var(--bs-primary-border-subtle);
}
.author-role-reader {
    background: var(--bs-info-bg-subtle);
    color: var(--bs-info-text-emphasis);
    border-color: var(--bs-info-border-subtle);
}
.author-role-assistant {
    background: var(--bs-success-bg-subtle);
    color: var(--bs-success-text-emphasis);
    border-color: var(--bs-success-border-subtle);
}
.author-role-imported,
.author-role-blocked {
    background: var(--bs-secondary-bg-subtle);
    color: var(--bs-secondary-text-emphasis);
    border-color: var(--bs-secondary-border-subtle);
}
.author-bio {
    white-space: pre-line;
    line-height: var(--lf-leading-relaxed);
}
/* A textarea that grows with its text (profile settings, "Про себе"; the height is set by the page's script). */
textarea.auto-grow {
    overflow: hidden;
    resize: none;
}
.author-stats {
    display: flex;
    gap: 0.75rem;
}
.author-stat {
    flex: 1 1 0;
    text-align: center;
    padding: 0.6rem 0.5rem;
    border-radius: var(--bs-border-radius);
    background: var(--bs-tertiary-bg);
}
.author-stat-value {
    font-size: var(--lf-text-3xl);
    font-weight: var(--lf-weight-bold);
    line-height: var(--lf-leading-tight);
}
.author-stat-label {
    font-size: var(--lf-text-sm);
    color: var(--bs-secondary-color);
}
.author-facts {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.35rem 1rem;
    font-size: var(--lf-text-ui);
}
.author-facts dt {
    font-weight: var(--lf-weight-regular);
    color: var(--bs-secondary-color);
}
.author-facts dd {
    margin: 0;
    overflow-wrap: anywhere;
}
/* The author's support links and links elsewhere: a line saying where the link leads, the link under it. */
.author-links dt {
    font-size: var(--lf-text-sm);
    font-weight: var(--lf-weight-regular);
    color: var(--bs-secondary-color);
}
.author-links dd {
    margin-bottom: 0.6rem;
    overflow-wrap: anywhere;
}
.author-links dd:last-child {
    margin-bottom: 0;
}
@media (max-width: 575.98px) {
    .author-hero-actions {
        margin-left: 0;
        width: 100%;
    }
    .author-hero-banner.author-hero-banner-picture {
        height: 140px;
    }
    .author-hero-avatar {
        width: 120px;
        height: 120px;
        margin-top: -60px;
    }
    .author-hero-avatar-frame {
        margin-top: -60px;
    }
}

/* Assistants: the questionnaires ("Кабінет асистента" - "Анкета", "Асистенти", the author page;
   templates/fragments/assistant-profile.html). One colour per kind of help, Bootstrap's subtle
   tokens so that both themes work. */
.assistant-role-badge {
    display: inline-block;
    padding: 0.2rem 0.7rem;
    border-radius: var(--bs-border-radius-pill);
    font-size: var(--lf-text-md);
    font-weight: var(--lf-weight-semibold);
    border: 1px solid var(--bs-success-border-subtle);
    background: var(--bs-success-bg-subtle);
    color: var(--bs-success-text-emphasis);
}
.assistant-role-gamma-reader {
    border-color: var(--bs-info-border-subtle);
    background: var(--bs-info-bg-subtle);
    color: var(--bs-info-text-emphasis);
}
.assistant-role-co-author {
    border-color: var(--bs-primary-border-subtle);
    background: var(--bs-primary-bg-subtle);
    color: var(--bs-primary-text-emphasis);
}
.assistant-role-editor {
    border-color: var(--bs-warning-border-subtle);
    background: var(--bs-warning-bg-subtle);
    color: var(--bs-warning-text-emphasis);
}
.assistant-role-translator {
    border-color: var(--bs-danger-border-subtle);
    background: var(--bs-danger-bg-subtle);
    color: var(--bs-danger-text-emphasis);
}
.assistant-role-illustrator,
.assistant-role-consultant {
    border-color: var(--bs-secondary-border-subtle);
    background: var(--bs-secondary-bg-subtle);
    color: var(--bs-secondary-text-emphasis);
}
.assistant-kind-card.assistant-kind-filled {
    border-color: var(--bs-success-border-subtle);
}
.assistant-avatar {
    width: 2.5rem;
    height: 2.5rem;
    flex: 0 0 2.5rem;
    border-radius: var(--lf-radius-circle);
    object-fit: cover;
}
.assistant-avatar-letter {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: var(--lf-weight-bold);
    color: var(--lf-white);
    background: var(--lf-brand);
}
.assistant-facts {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.3rem 0.9rem;
    font-size: var(--lf-text-ui);
}
.assistant-facts dt {
    font-weight: var(--lf-weight-regular);
    color: var(--bs-secondary-color);
}
.assistant-facts dd {
    margin: 0;
    overflow-wrap: anywhere;
}
.assistant-snippet {
    font-size: var(--lf-text-ui);
    overflow-wrap: anywhere;
}
.assistant-text {
    white-space: pre-line;
    line-height: var(--lf-leading-relaxed);
    overflow-wrap: anywhere;
}
.assistant-text-block + .assistant-text-block {
    margin-top: 1rem;
}
.assistant-tag-liked {
    background: var(--bs-success-bg-subtle);
    color: var(--bs-success-text-emphasis);
    border: 1px solid var(--bs-success-border-subtle);
    font-weight: var(--lf-weight-medium);
}
.assistant-tag-avoided {
    background: var(--bs-danger-bg-subtle);
    color: var(--bs-danger-text-emphasis);
    border: 1px solid var(--bs-danger-border-subtle);
    font-weight: var(--lf-weight-medium);
}
.assistant-genres summary {
    cursor: pointer;
}
@media (max-width: 575.98px) {
    .assistant-facts {
        grid-template-columns: 1fr;
        gap: 0.1rem;
    }
    .assistant-facts dd {
        margin-bottom: 0.35rem;
    }
}

/* The participants of a work ("Учасники", "Співпраця"; templates/works/participants.html,
   templates/author/collaborations.html). */
.collab-role {
    background: var(--bs-primary-bg-subtle);
    color: var(--bs-primary-text-emphasis);
    border: 1px solid var(--bs-primary-border-subtle);
    font-weight: var(--lf-weight-medium);
}
.collab-status-active {
    background: var(--bs-success-bg-subtle);
    color: var(--bs-success-text-emphasis);
}
.collab-status-invited {
    background: var(--bs-info-bg-subtle);
    color: var(--bs-info-text-emphasis);
}
.collab-status-left,
.collab-status-declined {
    background: var(--bs-secondary-bg-subtle);
    color: var(--bs-secondary-text-emphasis);
}
.collab-status-removed {
    background: var(--bs-warning-bg-subtle);
    color: var(--bs-warning-text-emphasis);
}
.collab-message {
    white-space: pre-line;
    overflow-wrap: anywhere;
    padding-left: 0.75rem;
    border-left: var(--lf-border-quote);
}
.collab-table td {
    overflow-wrap: anywhere;
}

/* ---- Statistics (fragments/stats.html, author/stats.html; the chart and the numbers also admin/activity.html):
   a bar chart of plain blocks, no chart library. The author's page: the heading with the period switch (a segmented
   control), the totals as tiles, the charts, the works and the sources each in a card. ---- */
.stats-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .75rem 1rem;
    margin-bottom: .5rem;
}
.stats-note {
    max-width: 46rem;
    margin-bottom: 1.25rem;
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-md);
}
.stats-periods {
    display: inline-flex;
}
.stats-tiles {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: .75rem;
    margin-bottom: .75rem;
}
.stats-tile {
    padding: .9rem 1.1rem;
    background: var(--lf-surface);
    border: var(--lf-border);
    border-top: var(--lf-border-accent-strong);
    border-radius: var(--bs-border-radius-lg);
}
.stats-label,
.stats-total-label {
    color: var(--bs-secondary-color);
}
.stats-number {
    font-size: var(--lf-text-2xl);
    font-weight: var(--lf-weight-semibold);
    line-height: var(--lf-leading-tight);
}
.stats-tile .stats-number {
    margin-top: .25rem;
    font-family: var(--lf-font-title);
    font-size: var(--lf-text-5xl);
    font-weight: var(--lf-weight-extrabold);
    font-variant-numeric: tabular-nums;
}
.stats-card {
    margin-bottom: .75rem;
    padding: 1rem 1.15rem;
    background: var(--lf-surface);
    border: var(--lf-border);
    border-radius: var(--bs-border-radius-lg);
}
.stats-pair {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0 .75rem;
}
.stats-pair .stats-chart {
    height: 120px;
}
.stats-card-title {
    margin: 0 0 .5rem;
    font-size: var(--lf-text-base);
    font-weight: var(--lf-weight-semibold);
}
.stats-figcaption {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .15rem .6rem;
    margin-bottom: .75rem;
}
.stats-figcaption .stats-card-title {
    flex: 1 1 100%;
    margin: 0;
}
.stats-total {
    font-family: var(--lf-font-title);
    font-size: var(--lf-text-2xl);
    font-weight: var(--lf-weight-extrabold);
    line-height: var(--lf-leading-tight);
    font-variant-numeric: tabular-nums;
}
.stats-chart {
    display: flex;
    align-items: flex-end;
    gap: 2px;
    height: 140px;
    /* Room over the highest bar for its value on hover. */
    padding: 1.1rem 0 0;
    border-bottom: var(--lf-border);
    /* Faint guide lines at a quarter, a half and three quarters of the height. */
    background: repeating-linear-gradient(to top, transparent 0, transparent calc(25% - 1px),
            var(--bs-border-color-translucent) calc(25% - 1px), var(--bs-border-color-translucent) 25%) content-box;
}
.stats-bar-cell {
    flex: 1 1 0;
    height: 100%;
    display: flex;
    align-items: flex-end;
}
.stats-bar {
    position: relative;
    width: 100%;
    background: var(--lf-brand);
    opacity: var(--lf-opacity-muted);
    border-radius: var(--lf-radius-hair) var(--lf-radius-hair) 0 0;
    min-height: 0;
    transition: opacity var(--lf-duration-instant) ease;
}
.stats-bar-cell:hover .stats-bar {
    opacity: 1;
}
/* The bar's value over it on hover (the date stays in the cell's title). */
.stats-bar-cell:hover .stats-bar::after {
    content: attr(data-value);
    position: absolute;
    bottom: calc(100% + 2px);
    left: 50%;
    transform: translateX(-50%);
    padding: 0 .3rem;
    background: var(--bs-body-color);
    color: var(--bs-body-bg);
    border-radius: var(--lf-radius-xs);
    font-size: var(--lf-text-2xs);
    line-height: var(--lf-leading-normal);
    white-space: nowrap;
    pointer-events: none;
    z-index: 1;
}
.stats-axis {
    display: flex;
    justify-content: space-between;
    margin-top: .3rem;
    font-family: var(--lf-font-mono);
    font-size: var(--lf-text-2xs);
}
.stats-table th {
    color: var(--bs-secondary-color);
    background: transparent;
}
.stats-table td {
    padding-top: .55rem;
    padding-bottom: .55rem;
    font-variant-numeric: tabular-nums;
    background: transparent;
}
.stats-table tbody tr:last-child td {
    border-bottom: 0;
}
.stats-work {
    font-weight: var(--lf-weight-medium);
    text-decoration: none;
}
.stats-work:hover {
    text-decoration: underline;
}
.stats-table .stats-share {
    max-width: 16rem;
    margin-top: .35rem;
}
.stats-share {
    height: 6px;
    background: var(--bs-secondary-bg);
    border-radius: var(--lf-radius-hair);
    overflow: hidden;
}
.stats-share > div {
    max-width: 100%;
    height: 100%;
    background: var(--lf-brand);
    border-radius: var(--lf-radius-hair);
}
.stats-source {
    display: flex;
    align-items: center;
    gap: .75rem;
    font-size: var(--lf-text-md);
}
.stats-source + .stats-source {
    margin-top: .55rem;
}
.stats-source-name {
    flex: 0 0 11rem;
}
.stats-source-value {
    flex: 0 0 6rem;
    text-align: end;
    font-variant-numeric: tabular-nums;
}
.stats-source-value b {
    font-weight: var(--lf-weight-semibold);
    margin-right: .25rem;
}
@media (max-width: 767.98px) {
    .stats-pair {
        grid-template-columns: minmax(0, 1fr);
    }
}
@media (max-width: 575.98px) {
    .stats-tiles {
        gap: .5rem;
    }
    .stats-tile {
        padding: .65rem .75rem;
    }
    .stats-tile .stats-number {
        font-size: var(--lf-text-2xl);
    }
    .stats-source {
        flex-wrap: wrap;
        gap: .25rem .75rem;
    }
    .stats-source-name {
        flex: 1 1 auto;
    }
    .stats-source .stats-share {
        order: 3;
        flex: 1 1 100%;
    }
}
/* The work page's "Статистика" tab: the numbers in one wrapping row, "12 Перегляди · 3 Читачі · ...". */
.stats-inline {
    display: flex;
    flex-wrap: wrap;
    gap: .25rem 1.25rem;
    margin: 0;
}
.stats-inline b {
    font-weight: var(--lf-weight-semibold);
}
.stats-inline span {
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-small);
}

/* ---- "Кабінет адміна — Активність сайту" (admin/activity.html, fragments/activity.html). ---- */
.activity-tile {
    height: 100%;
    padding: .75rem 1rem;
    border: var(--lf-border);
    border-radius: var(--bs-border-radius);
}
.activity-table td {
    vertical-align: top;
}
/* A long address or page cut to one line (its whole text is the title). */
.activity-cut {
    max-width: 22rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: bottom;
}
.activity-line + .activity-line {
    margin-top: .5rem;
}
.activity-share {
    height: 4px;
    margin-top: 2px;
    background: var(--bs-secondary-bg);
    border-radius: var(--lf-radius-hair);
}
.activity-share > div {
    height: 100%;
    background: var(--bs-primary);
    border-radius: var(--lf-radius-hair);
}

/* A work folded for the reader ("Приховане" - "згортати"; fragments/hidden-fold.html, js/hidden-fold.js): the
   reason and "Показати" in place of the card. In a grid the cell keeps its place with the reason only. */
.hidden-fold {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .25rem .75rem;
    padding: .5rem .75rem;
    border: var(--lf-border-dashed);
    border-radius: var(--bs-border-radius);
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-md);
}

.library-item-folded .library-book,
.library-item-folded .library-popover {
    display: none;
}

.library-item-folded .hidden-fold {
    height: 100%;
    flex-direction: column;
    justify-content: center;
}

/* "Запропонувати правку" (manage/proposal-new.html): the paragraphs picked for the proposal. */
.proposal-paragraphs .proposal-picked {
    background: var(--bs-primary-bg-subtle);
    border-radius: var(--bs-border-radius-sm);
}

/* "Робочі примітки" (manage/chapter-notes.html, fragments/note-thread.html): the paragraphs with their note icon, the
   threads (a resolved one dimmed). */
.note-paragraph {
    border-bottom: var(--lf-border-soft);
    padding: .35rem 0;
}
.note-paragraph summary {
    list-style: none;
    cursor: pointer;
}
.note-paragraph summary::-webkit-details-marker {
    display: none;
}
.note-paragraph-text {
    white-space: pre-wrap;
}
/* A text as typed (the unsaved rows of manage/chapter-translate.html). */
.align-text {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}
.note-icon {
    font-size: var(--lf-text-md);
    padding: 0 .35rem;
    border-radius: var(--bs-border-radius-pill);
    color: var(--bs-secondary-color);
}
.note-icon-none {
    opacity: var(--lf-opacity-faint);
}
.note-icon-open {
    background: var(--bs-warning-bg-subtle);
    color: var(--bs-warning-text-emphasis);
}
.note-thread {
    border: var(--lf-border);
    border-radius: var(--bs-border-radius);
    padding: .5rem .75rem;
    background: var(--bs-body-bg);
}
.note-thread-resolved {
    opacity: var(--lf-opacity-muted);
}
.note-message + .note-message {
    border-top: var(--lf-border-dashed);
    margin-top: .35rem;
    padding-top: .35rem;
}

/* Badges (badge module, fragments/badge.html): a chip like the role badges, with the badge's picture drawn in the
   chip's colour. */
.user-badge {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .15rem .55rem;
    border-radius: var(--bs-border-radius-pill);
    border: var(--lf-border);
    background: var(--bs-tertiary-bg);
    font-size: var(--lf-text-sm);
    line-height: var(--lf-leading-normal);
}
.user-badge-icon {
    width: 1.1rem;
    height: 1.1rem;
    flex: none;
    fill: none;
    stroke: currentColor;
    stroke-width: 4;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* A badge's tile: the shades of the badge's hue (--h, Badge#hue) -- a pale frame, a deeper panel, the ink of the
   lines and the paper of the picture; framed in gold on the top level. */
.badge-medal {
    display: block;
    width: 100%;
    height: auto;
    overflow: visible;
}
.badge-medal-outer {
    fill: var(--medal-rim);
    stroke: var(--medal-ink);
    stroke-width: 3;
}
.badge-medal-top .badge-medal-outer {
    stroke: var(--medal-gold);
    stroke-width: 5;
}
.badge-medal-inner {
    fill: var(--medal-fill);
}
/* "Fundatores": the site's purple, like its chip. */
.badge-medal-ring {
    fill: none;
    stroke: var(--medal-ink);
    stroke-width: 3;
    stroke-dasharray: 6 7;
    opacity: .75;
}
.badge-medal-halo {
    fill: none;
    stroke: var(--medal-ink);
    stroke-width: 1.4;
    opacity: .4;
}
.badge-medal-top .badge-medal-halo {
    stroke: var(--medal-gold);
    stroke-width: 2;
    opacity: 1;
}
/* The picture (static/img/badge-glyphs.svg) takes its colours from here: fill -- the paper, stroke and currentColor --
   the ink. */
.badge-medal-glyph {
    color: var(--medal-ink);
    fill: var(--medal-paper);
    stroke: var(--medal-ink);
    stroke-width: 3;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.badge-medal-plate rect:first-child {
    fill: var(--medal-ink);
}
.badge-medal-top .badge-medal-plate rect:first-child {
    fill: var(--medal-gold);
}
.badge-medal-plate rect + rect {
    fill: none;
    stroke: var(--medal-paper);
    stroke-width: 1.6;
}
.badge-medal-plate rect.on {
    fill: var(--medal-paper);
}

/* The achievements' tab (authors/profile.html): the sections under a rule, the badges with a track of their levels,
   and a card for each level -- the tile on the left, the texts on the right; tinted in the badge's hue once got,
   grey and dashed while locked. */
.badge-showcase-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .5rem 1rem;
    margin-bottom: .25rem;
}
.badge-summary {
    font-size: var(--lf-text-plus);
    font-weight: var(--lf-weight-semibold);
    margin: 0;
}
/* Only the levels got, until "Показати всі" is switched on: then the levels not got, and the badges and sections with
   none, too (and no "Відзнак ще немає"). */
.badge-showcase:not(:has(#badgesShowAll:checked)) :is(.badge-card:not(.badge-card-earned), .badge-family-empty, .badge-section-empty),
.badge-showcase:has(#badgesShowAll:checked) .badge-showcase-none {
    display: none;
}
.badge-section {
    margin-top: 2rem;
}
.badge-section-title {
    display: flex;
    align-items: center;
    gap: .75rem;
    font-size: var(--lf-text-ui);
    font-weight: var(--lf-weight-bold);
    color: var(--lf-brand);
    margin-bottom: 1rem;
}
.badge-section-title::after {
    content: "";
    flex: 1;
    height: 2px;
    border-radius: var(--lf-radius-hair);
    background: linear-gradient(to right, var(--lf-accent-line), transparent);
}
.badge-family + .badge-family {
    margin-top: 1.5rem;
}
.badge-family-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .35rem .9rem;
}
.badge-family-head h3 {
    font-size: var(--lf-text-lg);
    font-weight: var(--lf-weight-semibold);
    margin: 0;
}
.badge-track {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: var(--lf-text-xs);
    color: var(--bs-secondary-color);
}
.badge-track i {
    width: 1.1rem;
    height: .4rem;
    border-radius: var(--lf-radius-hair);
    background: var(--bs-secondary-bg);
}
.badge-track i.on {
    background: hsl(var(--h) 45% 55%);
}
.badge-track span {
    margin-left: .35rem;
    font-variant-numeric: tabular-nums;
}
.badge-family-goals {
    margin: .15rem 0 .6rem;
    font-size: var(--lf-text-xs);
    color: var(--bs-secondary-color);
}
.badge-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
    gap: .6rem;
}
.badge-card {
    display: flex;
    gap: .9rem;
    align-items: flex-start;
    padding: .85rem .95rem;
    border: var(--lf-border-soft);
    border-radius: var(--bs-border-radius-lg);
    background: var(--bs-body-bg);
}
.badge-card-earned {
    border-color: hsl(var(--h) 40% 82%);
    background: linear-gradient(135deg, hsl(var(--h) 60% 97%), var(--bs-body-bg) 70%);
}
:root[data-bs-theme="dark"] .badge-card-earned {
    border-color: hsl(var(--h) 25% 32%);
    background: linear-gradient(135deg, hsl(var(--h) 25% 16%), var(--bs-body-bg) 70%);
}
.badge-card-locked {
    border-style: dashed;
}
.badge-card-art {
    position: relative;
    flex: none;
    width: 4.75rem;
}
/* A sheen over the tile of a card got, on hover (the tile's own corners: 7 % in, rounded like its frame). */
.badge-card-earned .badge-card-art::after {
    content: "";
    position: absolute;
    inset: 7%;
    border-radius: var(--lf-radius-medal);
    background: linear-gradient(115deg, transparent 40%, rgba(var(--lf-white-rgb), .6) 50%, transparent 60%) no-repeat;
    background-size: 300% 100%;
    background-position: 100% 0;
    pointer-events: none;
}
.badge-card-earned:hover .badge-card-art::after {
    background-position: 0 0;
    transition: background-position var(--lf-duration-slowest) ease;
}
.badge-card-locked .badge-card-art,
.badge-card-next .badge-card-art {
    filter: grayscale(1);
    opacity: var(--lf-opacity-faint);
}
.badge-card-next .badge-card-art {
    opacity: var(--lf-opacity-muted);
}
.badge-card-body {
    flex: 1;
    min-width: 0;
}
.badge-card-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
}
.badge-card-goal {
    font-size: var(--lf-text-2xs);
    font-weight: var(--lf-weight-semibold);
    color: hsl(var(--h) 40% 35%);
}
:root[data-bs-theme="dark"] .badge-card-goal {
    color: hsl(var(--h) 45% 75%);
}
.badge-card-locked .badge-card-goal,
.badge-card-next .badge-card-goal {
    color: var(--bs-secondary-color);
}
.badge-card-check {
    display: inline-grid;
    place-items: center;
    width: 1.15rem;
    height: 1.15rem;
    border-radius: var(--lf-radius-xs);
    background: hsl(var(--h) 45% 45%);
    color: var(--lf-white);
    font-size: var(--lf-text-2xs);
    line-height: var(--lf-leading-none);
}
.badge-card-name {
    margin: .15rem 0 0;
    font-weight: var(--lf-weight-semibold);
    line-height: var(--lf-leading-snug);
}
.badge-card-locked .badge-card-name {
    color: var(--bs-secondary-color);
}
.badge-card-desc {
    margin: .2rem 0 0;
    font-size: var(--lf-text-xs);
    line-height: var(--lf-leading-normal);
    color: var(--bs-secondary-color);
}
.badge-card-progress {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-top: .45rem;
    font-size: var(--lf-text-2xs);
    color: var(--bs-secondary-color);
}
.badge-card-progress .progress {
    flex: 1;
    height: .35rem;
}
.badge-card-meta {
    margin: .4rem 0 0;
    font-size: var(--lf-text-2xs);
    color: var(--bs-secondary-color);
}
.badge-card-meta span + span::before {
    content: " · ";
}
.badge-card-feature {
    margin-top: .45rem;
}
@media (prefers-reduced-motion: reduce) {
    .badge-card-earned:hover .badge-card-art::after { transition: none; }
}

/* "Fundatores" (the first hundred authors): the purple of the site's header (.site-navbar), white on it, a deeper
   purple in dark mode, like the header. */
.user-badge.user-badge-founders {
    border-color: var(--lf-brand);
    background: var(--lf-brand);
    color: var(--lf-white);
    font-weight: var(--lf-weight-semibold);
}
:root[data-bs-theme="dark"] .user-badge.user-badge-founders {
    border-color: var(--lf-founders-fill-night);
    background: var(--lf-founders-fill-night);
}

/* "Що це?" -- a link to the knowledge base next to a field or heading (fragments/help.html). */
.help-link {
    font-weight: var(--lf-weight-regular);
    white-space: nowrap;
}
h1 .help-link, h2 .help-link {
    font-size: var(--lf-text-md);
    vertical-align: middle;
}

/* A comment's likes and dislikes (fragments/work-comments.html, blog/post.html), thumbs up and down. Each stays grey
   until the reader's own is on. */
.comment-like,
.comment-dislike {
    filter: grayscale(1);
    opacity: var(--lf-opacity-muted);
}
.comment-like.comment-liked,
.comment-dislike.comment-disliked {
    filter: none;
    opacity: 1;
}
.comment-author-liked {
    font-size: var(--lf-text-sm);
    color: var(--bs-danger-text-emphasis);
}

/* "Редактор перекладу" (manage/chapter-translate.html): two canvases side by side, the original on the left (to read)
   and the translation on the right (to write), each a continuous text with its own scrollbar, like two pages. The
   blocks of a row (.te-seg) get the same height on both (js/translation-editor.js), so a translation starts next to
   what it translates; the translation's fields are borderless, so the right canvas reads as one text. A row that needs
   work is marked in the translation's left margin; a changed one too. One under the other on a narrow screen (still
   levelled and scrolled together). */
.translation-editor .te-titles,
.translation-editor .te-canvases {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
}
.translation-editor .te-titles {
    align-items: end;
    margin-bottom: .75rem;
}
.translation-editor .te-title-original {
    font-weight: var(--lf-weight-semibold);
    padding: .375rem 0;
}
.translation-editor .te-canvas {
    height: calc(100vh - 14rem);
    min-height: 22rem;
    overflow-y: auto;
    padding: 1.25rem 1.5rem;
    border: var(--lf-border);
    border-radius: var(--bs-border-radius-lg);
    background: var(--bs-body-bg);
    font-size: var(--te-font-size);
    line-height: var(--te-line);
}
.translation-editor .te-canvas-original {
    background: var(--bs-tertiary-bg);
}
.translation-editor .te-canvas-translation {
    padding-left: 2rem;
    padding-right: 2rem;
}
.translation-editor .te-canvas-translation:focus-within {
    border-color: var(--bs-primary);
}
/* A row (a group of the alignment) is set off from the next by a thin rule, at the same height on both canvases; the
   paragraphs of one row (one of the original, two of the translation, say) are only a blank line apart. */
.translation-editor .te-seg {
    position: relative;
    padding: calc(var(--te-gap) / 2) 0;
    border-bottom: var(--lf-border);
}
.translation-editor .te-seg:last-child {
    border-bottom-color: transparent;
}
.translation-editor .te-seg.te-active {
    background: rgba(var(--bs-primary-rgb), .06);
}
.translation-editor .te-para {
    margin: 0 0 var(--te-gap);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}
.translation-editor .te-para:last-child {
    margin-bottom: 0;
}
.translation-editor .te-none {
    color: var(--bs-secondary-color);
    font-style: italic;
}
.translation-editor .te-text {
    display: block;
    width: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    outline: 0;
    resize: none;
    overflow: hidden;
    background: transparent;
    color: var(--bs-body-color);
    font: inherit;
    line-height: inherit;
}
.translation-editor .te-field {
    position: relative;
    margin-bottom: var(--te-gap);
}
.translation-editor .te-field:last-of-type {
    margin-bottom: 0;
}
/* "×" takes a paragraph away (when the row has more than one), "+ абзац" adds one; both show on the row the pointer or
   the cursor is in, in the margin and the gap below the row, so the canvas keeps its lines. */
.translation-editor .te-remove,
.translation-editor .te-add {
    position: absolute;
    border: 0;
    background: transparent;
    color: var(--bs-secondary-color);
    line-height: var(--lf-leading-none);
    opacity: 0;
    transition: opacity var(--lf-duration-fast);
}
.translation-editor .te-remove {
    right: -1.25rem;
    top: .15em;
    padding: 0 .25rem;
    font-size: var(--lf-text-lg);
}
.translation-editor .te-add {
    left: 0;
    bottom: -.55rem;
    z-index: 1;
    padding: .1rem .35rem;
    font-size: var(--lf-text-sm);
    border-radius: var(--bs-border-radius-sm);
    background: var(--bs-body-bg);
}
.translation-editor .te-add:hover,
.translation-editor .te-remove:hover {
    color: var(--bs-primary);
    background: rgba(var(--bs-primary-rgb), .08);
}
.translation-editor .te-seg:hover .te-add,
.translation-editor .te-seg:focus-within .te-add,
.translation-editor .te-seg.te-multi .te-field:hover .te-remove,
.translation-editor .te-seg.te-multi .te-field:focus-within .te-remove {
    opacity: 1;
}
.translation-editor .te-add:focus-visible,
.translation-editor .te-remove:focus-visible {
    opacity: 1;
}
.translation-editor .te-text::placeholder {
    color: var(--bs-secondary-color);
    opacity: var(--lf-opacity-muted);
    font-style: italic;
}
.translation-editor .te-canvas-translation .te-seg::before {
    content: "";
    position: absolute;
    left: -1.25rem;
    top: calc(var(--te-gap) / 2 + .2em);
    bottom: calc(var(--te-gap) / 2 + .2em);
    width: 3px;
    border-radius: var(--lf-radius-hair);
}
.translation-editor .te-seg.te-stale::before {
    background: var(--bs-warning);
}
.translation-editor .te-seg.te-untranslated::before {
    background: var(--bs-info);
}
.translation-editor .te-seg.te-orphaned::before {
    background: var(--bs-secondary);
}
.translation-editor .te-seg.te-changed::before {
    background: var(--bs-primary);
}

.translation-editor .te-mark {
    position: absolute;
    right: 0;
    top: .15em;
    font-size: var(--lf-text-2xs);
    line-height: var(--lf-leading-none);
    color: var(--bs-secondary-color);
    pointer-events: none;
}
.translation-editor .te-whole-text {
    resize: none;
    color: var(--bs-body-color);
}
.translation-editor .te-savebar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    padding: .75rem 0;
}
@media (max-width: 767.98px) {
    .translation-editor .te-titles,
    .translation-editor .te-canvases {
        grid-template-columns: 1fr;
    }
    .translation-editor .te-canvas {
        height: 38vh;
        min-height: 12rem;
        padding: .75rem 1rem;
    }
    .translation-editor .te-canvas-translation {
        padding-left: 1.5rem;
    }
    .translation-editor .te-canvas-translation .te-seg::before {
        left: -1rem;
    }
}

/* The AI helper of the text editors (js/text-assist.js): the bar over a selection, with
   "Синоніми" / "Перефразувати" and the AI's options as buttons (many synonyms: the list scrolls); wider while the
   translator writes a request for "Перефразувати". */
.te-assist {
    position: absolute;
    z-index: var(--lf-z-assist);
    /* 100%: the page's width as the phone shows it (100vw may be wider there); the bar hangs off <body>. */
    max-width: min(40rem, calc(100% - 1rem));
    padding: .4rem;
    font-size: var(--lf-text-md);
    background: var(--bs-body-bg);
    border: var(--lf-border);
    border-radius: var(--bs-border-radius);
}
/* Just the two buttons, before anything is asked: no box around them, they look like "Глосарій" beside them. */
.te-assist.te-assist-bare {
    padding: 0;
    background: none;
    border: 0;
}
.te-assist .te-assist-option {
    white-space: normal;
}
.te-assist.te-assist-wide {
    width: min(40rem, calc(100% - 1rem));
}
.te-assist .te-assist-input {
    min-height: 4.5rem;
    max-height: var(--lf-input-grow-max-tall);
    resize: vertical;
}
/* A phone zooms the page in on a field with letters under 16px as it takes the focus (the screen shifts and the field
   reads wider than it): the request's field of "Перефразувати" is 16px there. */
@media (pointer: coarse) {
    .te-assist .te-assist-input {
        font-size: var(--lf-text-base);
    }
}
.te-assist .te-assist-result {
    max-height: 50vh;
    overflow-y: auto;
}

/* ===== Touch screens (phones, tablets) =====
   A finger needs a target of about 44px; Bootstrap's defaults (16px checkboxes, the 20px "?" filter buttons,
   one-line menu items) are sized for a mouse. pointer: coarse = the primary pointer is a finger, so desktops
   with a mouse keep the compact look whatever the window's width. */
@media (pointer: coarse) {
    /* Checkboxes and radios: bigger boxes, the label moved over to make room (switches keep their own size). */
    .form-check:not(.form-switch) {
        min-height: 2rem;
        padding-left: 2rem;
    }
    .form-check:not(.form-switch) .form-check-input {
        width: 1.35em;
        height: 1.35em;
        margin-top: 0.05em;
        margin-left: -2rem;
    }
    .form-check-inline {
        margin-right: 1.25rem;
    }
    /* The header: roomier icon links and menu items. */
    .site-navbar .nav-icon-link {
        padding: 0.5rem;
    }
    .site-navbar .navbar-toggler {
        padding: 0.5rem 0.85rem;
    }
    .site-navbar .nav-link {
        padding-top: 0.65rem;
        padding-bottom: 0.65rem;
    }
    .dropdown-item {
        padding-top: 0.6rem;
        padding-bottom: 0.6rem;
    }
    /* The subgenre chips (originals/categories.html): easier to tap. */
    .genre-chip {
        padding: 0.4rem 0.75rem;
    }
}

/* ===== Glossary sections of a fandom's / universe's page (fanfiction/fragments-glossary.html, the characters too) =====
   Each section is a <details>, folded by default: its summary is the heading with the number of articles and a chevron
   that turns when it is open. js/glossary-sections.js opens the one a link points to. */
.glossary-summary {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    border: var(--lf-border);
    border-radius: var(--bs-border-radius);
    cursor: pointer;
    list-style: none;
}
.glossary-summary::-webkit-details-marker {
    display: none;
}
.glossary-summary::before {
    content: "";
    flex-shrink: 0;
    width: 0.6rem;
    height: 0.6rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(-45deg);
    transition: transform var(--lf-duration-fast);
    opacity: var(--lf-opacity-muted);
}
.glossary-section[open] > .glossary-summary::before {
    transform: rotate(45deg);
}
.glossary-summary:hover {
    background: var(--bs-tertiary-bg);
}
.glossary-section > .list-group {
    margin-top: 0.5rem;
}
@media (prefers-reduced-motion: reduce) {
    .glossary-summary::before {
        transition: none;
    }
}

/* The glossary footnotes of a chapter (catalog ChapterFootnotes, works/chapter.html): a word linked to an explanation
   is underlined with dots and carries a small "[1]" that leads to the list after the text; the explanation's title
   leads back. The place jumped to is lit for a moment. The editor's preview shows the same. */
a.gloss-ref {
    color: inherit;
    text-decoration: underline dotted;
    text-underline-offset: 0.2em;
}
a.gloss-ref .gloss-num {
    color: var(--bs-link-color);
    font-size: 0.7em;
    margin-left: 0.05em;
}
.chapter-glossary-list .gloss-explanation p {
    display: inline;
    margin: 0;
}
/* "Глосарій твору" (works/glossary.html, manage/work-terms.html): the article a footnote or an address leads to is lit
   too, and not hidden under the sticky header. */
.work-glossary-entry,
.work-glossary-manage [id] {
    scroll-margin-top: 5rem;
}
.chapter-glossary-list li:target,
a.gloss-ref:target,
.work-glossary-entry:target,
.work-glossary-manage .list-group-item:target,
.reader-jump-target {
    animation: gloss-target var(--lf-duration-flash) ease-out;
}
@keyframes gloss-target {
    from { background-color: var(--bs-warning-bg-subtle); }
    to { background-color: transparent; }
}
/* The editor's "Глосарій" button over a selected word (js/glossary-picker.js): over the editor's sticky toolbar (a word
   in the first line, as in a paragraph opened in "Робочі примітки"), under the site's sticky bar. */
.glossary-pick-button {
    position: absolute;
    z-index: var(--lf-z-editor-pick);
}
/* A glossary section in the editor's picker (js/glossary-picker.js): folded under its category until opened. */
.glossary-picker-section + .glossary-picker-section {
    margin-top: .25rem;
}
.glossary-picker-section > summary {
    cursor: pointer;
    font-size: var(--lf-text-md);
    font-weight: var(--lf-weight-semibold);
}

/* ===== Site-wide look (on top of Bootstrap's components) =====
   Headings in Inter's semibold with slightly tightened tracking (the reading page's prose keeps its
   own fonts), the titles of works in Manrope, small labels and dates as upper-case mono,
   cream surfaces with a thin warm border instead of shadows, and Bootstrap's leftover blue (form
   focus, checks, pills, pagination, progress) replaced by the purple. */
h1, .h1, h2, .h2, h3, .h3, h4, .h4, h5, .h5, h6, .h6 {
    font-weight: var(--lf-weight-semibold);
    letter-spacing: var(--lf-tracking-tight);
}
.work-title {
    font-family: var(--lf-font-title);
    font-weight: var(--lf-weight-bold);
    letter-spacing: var(--lf-tracking-snug);
}
.work-title a {
    color: var(--bs-emphasis-color);
}
.work-title a:hover {
    color: var(--bs-link-color);
}
.reader-page :is(h1, h2, h3, h4, h5, h6) {
    letter-spacing: normal;
}
::selection {
    background: var(--lf-accent-soft);
    color: var(--lf-accent-ink);
}

/* Small labels: the facts of a work, table headings, dates of comments. */
.work-facts dt,
.table > thead th {
    color: var(--bs-secondary-color);
}
.work-comments .comment-time {
    font-family: var(--lf-font-mono);
    font-size: var(--lf-text-2xs);
}

/* Surfaces. */
.card,
.list-group,
.accordion,
.modal,
.toast,
.offcanvas {
    --bs-card-bg: var(--lf-surface);
    --bs-card-border-color: var(--bs-border-color);
    --bs-card-cap-bg: var(--bs-tertiary-bg);
    --bs-card-border-radius: var(--bs-border-radius-lg);
    --bs-card-inner-border-radius: calc(var(--bs-border-radius-lg) - 1px);
    --bs-list-group-bg: var(--lf-surface);
    --bs-list-group-border-color: var(--bs-border-color);
    --bs-list-group-border-radius: var(--bs-border-radius-lg);
    --bs-accordion-bg: var(--lf-surface);
    --bs-accordion-border-color: var(--bs-border-color);
    --bs-accordion-border-radius: var(--bs-border-radius-lg);
    --bs-accordion-inner-border-radius: calc(var(--bs-border-radius-lg) - 1px);
    --bs-accordion-active-color: var(--lf-accent-ink);
    --bs-accordion-active-bg: var(--lf-accent-soft);
    --bs-accordion-btn-focus-box-shadow: var(--lf-focus-ring);
    --bs-modal-bg: var(--lf-surface);
    --bs-modal-border-color: var(--bs-border-color);
    --bs-toast-bg: var(--lf-surface);
    --bs-offcanvas-bg: var(--lf-surface);
}
/* The accordion's chevron: Bootstrap draws it in its own blue (an SVG with the colour inside), so here it is a mask
   painted in the page's grey, and in the purple while the section is open. */
.accordion-button::after {
    background-image: none;
    background-color: var(--bs-secondary-color);
    -webkit-mask: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e") center / var(--bs-accordion-btn-icon-width) no-repeat;
    mask: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e") center / var(--bs-accordion-btn-icon-width) no-repeat;
}
.accordion-button:not(.collapsed)::after {
    background-image: none;
    background-color: var(--lf-accent-ink);
}
.dropdown-menu {
    --bs-dropdown-bg: var(--lf-surface);
    --bs-dropdown-border-color: var(--bs-border-color);
    --bs-dropdown-border-radius: var(--bs-border-radius-lg);
    --bs-dropdown-link-hover-bg: var(--lf-accent-soft);
    --bs-dropdown-link-hover-color: var(--lf-accent-ink);
    --bs-dropdown-link-active-bg: var(--lf-brand);
    --bs-dropdown-box-shadow: var(--lf-shadow-menu);
}
.list-group-item-action:hover,
.list-group-item-action:focus {
    background-color: var(--bs-tertiary-bg);
}
.nav-pills {
    --bs-nav-pills-link-active-bg: var(--lf-brand);
}
.nav-tabs {
    --bs-nav-tabs-link-active-bg: var(--lf-surface);
    --bs-nav-tabs-link-active-color: var(--lf-accent-ink);
}
.pagination {
    --bs-pagination-bg: var(--lf-surface);
    --bs-pagination-active-bg: var(--lf-brand);
    --bs-pagination-active-border-color: var(--lf-brand);
    --bs-pagination-focus-box-shadow: var(--lf-focus-ring);
}
.progress,
.progress-stacked {
    --bs-progress-bar-bg: var(--lf-brand);
}

/* Form fields: on the cream surface, the purple edge on focus. */
.form-control,
.form-select {
    background-color: var(--lf-surface);
}
.form-control:focus,
.form-select:focus {
    border-color: var(--lf-brand);
    box-shadow: var(--lf-focus-ring-soft);
}
.form-check-input:checked {
    background-color: var(--lf-brand);
    border-color: var(--lf-brand);
}
.form-check-input:focus {
    border-color: var(--lf-brand);
    box-shadow: var(--lf-focus-ring-soft);
}
.form-range::-webkit-slider-thumb {
    background-color: var(--lf-brand);
}
.form-range::-moz-range-thumb {
    background-color: var(--lf-brand);
}

/* Tooltips (js/tooltips.js): room for a sentence of explanation, e.g. what a work's direction, rating or
   status means; a badge that has one shows the help cursor. */
.tooltip {
    --bs-tooltip-max-width: 18rem;
    --bs-tooltip-padding-x: .75rem;
    --bs-tooltip-padding-y: .5rem;
    --bs-tooltip-border-radius: var(--bs-border-radius);
    --bs-tooltip-opacity: 1;
}
.badge[data-bs-toggle="tooltip"] {
    cursor: help;
}

/* "Структура": the handle a chapter is dragged by, and the chapter on the move (js/work-chapters.js). */
.chapter-drag-handle {
    cursor: grab;
    touch-action: none;
    line-height: var(--lf-leading-none);
}
.chapter-drag-active,
.chapter-drag-active .chapter-drag-handle {
    cursor: grabbing;
    user-select: none;
}
#chapterList > .chapter-dragging {
    position: relative;
    z-index: 2;
    opacity: var(--lf-opacity-soft);
    box-shadow: var(--lf-shadow-float);
}
/* A part's title that folds its chapters away when pressed (js/work-chapters.js). */
#chapterList .part-fold-title-active {
    cursor: pointer;
}
#chapterList .part-fold-title-active:hover,
#chapterList .part-fold-title-active:focus-visible {
    text-decoration: underline;
}

/* The fragment an open "Варіант виправлення" would change, marked in a paragraph's editor in the chapter's "Робочі
   примітки" (js/chapter-notes.js); its title is the suggestion. */
.CodeMirror .fix-mark {
    background-color: rgba(var(--lf-warning-rgb), 0.35);
    border-bottom: 2px solid var(--lf-mark-border);
}

/* The "Коментарі" tab of an author's page (authors/profile.html; the cards are .news-item). */
.profile-comment-reply {
    margin: 0;
    overflow: hidden;
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-sm);
    text-overflow: ellipsis;
    white-space: nowrap;
}
.profile-comment-text {
    display: -webkit-box;
    margin: 0;
    overflow: hidden;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    font-size: var(--lf-text-ui);
    line-height: var(--lf-leading-normal);
    overflow-wrap: anywhere;
}
.profile-comment-subject {
    display: flex;
    align-items: flex-start;
    gap: .375rem;
    font-size: var(--lf-text-ui);
    font-weight: var(--lf-weight-medium);
    overflow-wrap: anywhere;
}
.profile-comment-subject .nav-menu-icon {
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
    margin-top: .15rem;
    color: var(--bs-secondary-color);
}

/* Blogs (templates/blog): a comment's plain text keeps its line breaks (the lists of posts are .news-item cards). */
.blog-comment-text {
    white-space: pre-line;
}

/* "Версійність" (info/versions.html): the releases, the newest first, on a vertical line, taking turns on its left and
   right, each joined to the line by a short arm with a dot where they meet; a lone release, and on a phone every
   release, stands on the right of a line running on the left. */
.versions-timeline {
    position: relative;
    list-style: none;
    margin: 2rem 0 0;
    padding: 0;
}

.versions-timeline::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 50%;
    width: 3px;
    margin-left: -1.5px;
    background: var(--lf-accent-line);
}

.version-item {
    position: relative;
    width: 50%;
    padding-right: 2.5rem;
    margin-bottom: 1.5rem;
}

.version-item:nth-child(even) {
    margin-left: 50%;
    padding-right: 0;
    padding-left: 2.5rem;
}

.version-item::before {
    content: "";
    position: absolute;
    top: 1.6rem;
    right: 0;
    width: 2.5rem;
    height: 3px;
    background: var(--lf-accent-line);
}

.version-item:nth-child(even)::before {
    right: auto;
    left: 0;
}

.version-item::after {
    content: "";
    position: absolute;
    top: calc(1.6rem - 6px);
    right: -8px;
    width: 16px;
    height: 16px;
    border-radius: var(--lf-radius-circle);
    background: var(--lf-brand);
    border: 3px solid var(--bs-body-bg);
}

.version-item:nth-child(even)::after {
    right: auto;
    left: -8px;
}

/* The points of a release, a little apart from each other. */
.version-changes li + li {
    margin-top: .5rem;
}

.versions-timeline:has(> .version-item:only-child)::before {
    left: .5rem;
}

.version-item:only-child {
    width: auto;
    padding-right: 0;
    padding-left: 2.5rem;
}

.version-item:only-child::before {
    right: auto;
    left: .5rem;
    width: 2rem;
}

.version-item:only-child::after {
    right: auto;
    left: calc(.5rem - 8px);
}

@media (max-width: 767.98px) {
    .versions-timeline::before {
        left: .5rem;
    }

    .version-item,
    .version-item:nth-child(even) {
        width: auto;
        margin-left: 0;
        padding-right: 0;
        padding-left: 2.5rem;
    }

    .version-item::before,
    .version-item:nth-child(even)::before {
        right: auto;
        left: .5rem;
        width: 2rem;
    }

    .version-item::after,
    .version-item:nth-child(even)::after {
        right: auto;
        left: calc(.5rem - 8px);
    }
}

/* ===== Private messages (templates/messages/*.html, js/messages.js) =====
   Another of the small page-scoped exceptions: everything you see is Bootstrap (a card, a flush list-group, pills,
   the support chat's bubbles); these rules only lay it out as two panes the height of the window -- the conversation
   list (fixed width) and the open conversation, each scrolling on its own, the composer at the bottom. Below the lg
   breakpoint one pane shows: the list on /messages, the conversation on its page (.pm-shell-open). */
.pm-shell {
    display: flex;
    flex-direction: row;
    height: calc(100dvh - 12rem);
    min-height: 28rem;
    overflow: hidden;
}
.pm-sidebar,
.pm-pane {
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
}
.pm-sidebar {
    width: 100%;
}
.pm-pane {
    flex: 1 1 auto;
}
.pm-shell > .pm-pane,
.pm-shell-open > .pm-sidebar {
    display: none;
}
.pm-shell-open > .pm-pane {
    display: flex;
}
@media (min-width: 992px) {
    .pm-sidebar,
    .pm-shell-open > .pm-sidebar {
        display: flex;
        flex: 0 0 21rem;
        width: 21rem;
    }
    .pm-shell > .pm-pane {
        display: flex;
    }
}
.pm-sidebar-body {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    min-height: 0;
}
.pm-list,
.pm-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
}
.pm-input {
    max-height: var(--lf-input-grow-max);
    resize: none;
}
.pm-emoji {
    position: relative;
}
.pm-emoji-panel {
    position: absolute;
    bottom: calc(100% + .25rem);
    left: 0;
    z-index: var(--lf-z-picker);
    display: grid;
    grid-template-columns: repeat(8, var(--lf-btn-side-sm));
}
.pm-emoji-panel[hidden] {
    display: none;
}
@media (max-width: 400px) {
    .pm-emoji-panel {
        grid-template-columns: repeat(6, var(--lf-btn-side-sm));
    }
}

/* A series or a collection as a card (fragments/cover-stack.html, the author page's "Серії" and "Збірники"): on a
   3:2 card the covers of its first works (up to five) overlap in the middle, the first on top (--o: a book's offset
   from the middle, from its --i of --n); on hover they spread out one after another: the first on the card's whole
   height, the others as a 2 x 2 grid beside it (.cover-stack-n1 .. n5: fewer covers, centred). Lengths are shares of
   the card, so a cover keeps 2:3: its width is 4/9 of its height's share. The badges come first, so the first book is
   the 2nd child. */
.cover-stack-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 1.25rem 1rem;
}
.cover-stack {
    display: flex;
    flex-direction: column;
    gap: .4rem;
    min-width: 0;
    color: inherit;
    text-decoration: none;
}
.cover-stack-art {
    position: relative;
    display: block;
    aspect-ratio: 3 / 2;
    overflow: hidden;
    border-radius: var(--bs-border-radius);
    background: linear-gradient(to bottom, var(--lf-surface-deep), var(--lf-surface));
    border: var(--lf-border);
}
.cover-stack-book {
    --o: calc(var(--i) - (var(--n) - 1) / 2);
    position: absolute;
    top: 12%;
    left: calc(33.1% + var(--o) * 8%);
    width: 33.8%;
    height: 76%;
    z-index: calc(var(--n) - var(--i));
    overflow: hidden;
    border-radius: var(--lf-radius-hair);
    box-shadow: var(--lf-shadow-float);
    outline: 1px solid rgba(var(--lf-black-rgb), .1);
    transition: top var(--lf-duration-slower), left var(--lf-duration-slower), width var(--lf-duration-slower), height var(--lf-duration-slower);
    transition-timing-function: var(--lf-ease-spring-soft);
    transition-delay: calc(var(--i) * var(--lf-delay-stagger));
}
.cover-stack-book img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.cover-stack:is(:hover, :focus-visible) .cover-stack-book {
    top: 4%;
    width: 19.6%;
    height: 44%;
}
.cover-stack:is(:hover, :focus-visible) .cover-stack-book:nth-child(2) {
    width: 40.9%;
    height: 92%;
}
.cover-stack:is(:hover, :focus-visible) .cover-stack-n1 .cover-stack-book { left: 29.55%; }
.cover-stack:is(:hover, :focus-visible) .cover-stack-n2 .cover-stack-book { left: 7.6%; }
.cover-stack:is(:hover, :focus-visible) .cover-stack-n2 .cover-stack-book:nth-child(3) {
    left: 51.5%;
    width: 40.9%;
    height: 92%;
}
.cover-stack:is(:hover, :focus-visible) .cover-stack-n3 .cover-stack-book { left: 62.15%; }
.cover-stack:is(:hover, :focus-visible) .cover-stack-n3 .cover-stack-book:nth-child(2) { left: 18.25%; }
.cover-stack:is(:hover, :focus-visible) .cover-stack-n3 .cover-stack-book:nth-child(4) { top: 52%; }
.cover-stack:is(:hover, :focus-visible) :is(.cover-stack-n4, .cover-stack-n5) .cover-stack-book:nth-child(2) { left: 6%; }
.cover-stack:is(:hover, :focus-visible) :is(.cover-stack-n4, .cover-stack-n5) .cover-stack-book:nth-child(3) { left: 50%; }
.cover-stack:is(:hover, :focus-visible) :is(.cover-stack-n4, .cover-stack-n5) .cover-stack-book:nth-child(4) { left: 71.5%; }
.cover-stack:is(:hover, :focus-visible) :is(.cover-stack-n4, .cover-stack-n5) .cover-stack-book:nth-child(5) { left: 50%; top: 52%; }
.cover-stack:is(:hover, :focus-visible) .cover-stack-n5 .cover-stack-book:nth-child(6) { left: 71.5%; top: 52%; }
.cover-stack-badges {
    position: absolute;
    top: .5rem;
    left: .5rem;
    z-index: 6;
    display: flex;
    gap: .25rem;
}
.cover-stack-badge {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    padding: .15rem .4rem;
    border-radius: var(--bs-border-radius-sm);
    background: rgba(var(--lf-black-rgb), .55);
    color: var(--lf-white);
    font-size: var(--lf-text-2xs);
    font-weight: var(--lf-weight-medium);
    line-height: var(--lf-leading-none);
}
.cover-stack-badge .nav-menu-icon {
    width: .8rem;
    height: .8rem;
}
.cover-stack-title {
    overflow: hidden;
    font-weight: var(--lf-weight-medium);
    font-size: var(--lf-text-ui);
    white-space: nowrap;
    text-overflow: ellipsis;
    transition: color var(--lf-duration-fast);
}
.cover-stack:hover .cover-stack-title {
    color: var(--lf-brand);
}
.cover-stack-excerpt {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-sm);
    line-height: var(--lf-leading-normal);
}
@media (prefers-reduced-motion: reduce) {
    .cover-stack-book {
        transition: none;
    }
}

/* Reviews (fragments/review.html): a card with the heading line (the work's title on an author page, the reviewer on
   the work's page) and the score with a star, the text (three lines on an author page, where the whole card is a link
   to the review) and the date in small monospace capitals; the score of the work page's form is five stars, the
   chosen one and those below it lit (the radios come from 5 down to 1, so "~" lights the lower ones). */
.review-card {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    padding: .75rem;
    border: var(--lf-border-soft);
    border-radius: var(--bs-border-radius-lg);
    background: var(--bs-tertiary-bg);
    color: inherit;
    text-decoration: none;
    transition: border-color var(--lf-duration-fast), background-color var(--lf-duration-fast);
}
@media (min-width: 768px) {
    .review-card {
        padding: 1rem;
    }
}
a.review-card:hover,
a.review-card:focus-visible {
    border-color: var(--bs-border-color);
    background: var(--bs-body-bg);
}
.review-card-own {
    border-color: var(--lf-accent-line);
}
.review-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
}
.review-card-title {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--lf-text-md);
    font-weight: var(--lf-weight-medium);
    color: var(--bs-secondary-color);
    text-decoration: none;
    transition: color var(--lf-duration-fast);
}
a.review-card:hover .review-card-title,
a.review-card-title:hover {
    color: var(--lf-brand);
}
.review-card-score,
.review-average {
    display: inline-flex;
    flex-shrink: 0;
    align-items: center;
    gap: .25rem;
    font-size: var(--lf-text-md);
    font-weight: var(--lf-weight-semibold);
}
.review-star {
    width: 1rem;
    height: 1rem;
    color: var(--lf-star);
}
.review-card-text {
    font-size: var(--lf-text-md);
    line-height: var(--lf-leading-relaxed);
    white-space: pre-line;
    overflow-wrap: anywhere;
}
.review-card-text-clamp {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    overflow: hidden;
}
.review-card-date {
    color: var(--bs-secondary-color);
}
.review-rating-input {
    display: inline-flex;
    flex-direction: row-reverse;
    gap: .125rem;
}
.review-rating-input label {
    cursor: pointer;
    color: var(--bs-border-color);
    transition: color var(--lf-duration-instant);
}
.review-rating-input label .review-star {
    width: 1.75rem;
    height: 1.75rem;
    color: inherit;
}
.review-rating-input input:checked ~ label,
.review-rating-input label:hover,
.review-rating-input label:hover ~ label {
    color: var(--lf-star);
}
.review-rating-input input:focus-visible + label {
    outline: var(--lf-focus-outline);
    outline-offset: var(--lf-focus-offset);
    border-radius: var(--lf-radius-xs);
}
.review-details > summary {
    list-style: none;
}
.review-details > summary::-webkit-details-marker {
    display: none;
}

/* The catalogue of collections (collections/catalogue.html), laid out like the blogs (.news-shell): the author pages'
   stacks of covers (.cover-stack) in a grid in the middle column, each naming its owner. */
.collections-catalogue-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.5rem 1rem;
}
@media (min-width: 576px) {
    .collections-catalogue-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}
@media (min-width: 1200px) {
    .collections-catalogue-grid {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}
.cover-stack-owner {
    overflow: hidden;
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-xs);
    opacity: var(--lf-opacity-muted);
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* ===== Genre index ("/originals", originals/categories.html) =====
   One card per top-level genre in up to three CSS columns, so a long card (fantasy) doesn't leave a hole beside a short
   one. The header is the genre with its count as a pill; the subgenres are wrapping chips with their own counts. A genre
   or subgenre with no works yet is dimmed but still a link. */
.genre-board {
    column-gap: 1rem;
}
@media (min-width: 768px) {
    .genre-board {
        columns: 2;
    }
}
@media (min-width: 1200px) {
    .genre-board {
        columns: 3;
    }
}
.genre-card {
    break-inside: avoid;
    margin-bottom: 1rem;
    padding: 1rem;
    border: var(--lf-border-soft);
    border-top: var(--lf-border-accent-strong);
    border-radius: var(--bs-border-radius-xl);
    background: var(--bs-body-bg);
    transition: box-shadow var(--lf-duration-fast);
}
.genre-card:hover {
    box-shadow: var(--lf-shadow-hover);
}
.genre-card.is-empty {
    border-top-color: var(--lf-accent-line);
}
.genre-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    color: inherit;
    text-decoration: none;
}
.genre-card-name {
    font-family: var(--lf-font-title);
    font-size: var(--lf-text-lg);
    font-weight: var(--lf-weight-bold);
    transition: color var(--lf-duration-fast);
}
.genre-card-head:hover .genre-card-name,
.genre-card-head:focus-visible .genre-card-name {
    color: var(--bs-primary-text-emphasis);
}
.genre-card-count {
    flex-shrink: 0;
    padding: .15rem .6rem;
    border-radius: var(--bs-border-radius-pill);
    background: var(--lf-accent-soft);
    color: var(--lf-accent-ink);
    font-size: var(--lf-text-xs);
    font-weight: var(--lf-weight-semibold);
    white-space: nowrap;
}
.genre-card.is-empty .genre-card-count {
    background: var(--bs-tertiary-bg);
    color: var(--bs-secondary-color);
}
.genre-chips {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    margin: .875rem 0 0;
    padding: .875rem 0 0;
    border-top: var(--lf-border-soft);
    list-style: none;
}
.genre-chip {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .25rem .4rem .25rem .7rem;
    border: var(--lf-border-accent);
    border-radius: var(--bs-border-radius-pill);
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
    font-size: var(--lf-text-sm);
    text-decoration: none;
    transition: background-color var(--lf-duration-fast), border-color var(--lf-duration-fast), color var(--lf-duration-fast);
}
.genre-chip:hover,
.genre-chip:focus-visible {
    border-color: var(--lf-brand);
    background: var(--lf-accent-soft);
    color: var(--lf-accent-ink);
}
.genre-chip-count {
    min-width: 1.4rem;
    padding: 0 .4rem;
    border-radius: var(--bs-border-radius-pill);
    background: var(--lf-brand);
    color: var(--lf-white);
    font-size: var(--lf-text-2xs);
    font-weight: var(--lf-weight-semibold);
    line-height: 1.25rem;
    text-align: center;
}
.genre-chip.is-empty {
    border-color: var(--bs-border-color-translucent);
    color: var(--bs-secondary-color);
}
.genre-chip.is-empty .genre-chip-count {
    background: var(--bs-tertiary-bg);
    color: var(--bs-secondary-color);
}
.genre-chip.is-empty:hover,
.genre-chip.is-empty:focus-visible {
    border-color: var(--lf-accent-line);
    background: var(--lf-accent-soft);
    color: var(--lf-accent-ink);
}

/* ===== Fandoms ("/fanfiction", fanfiction/categories.html) =====
   The section chips at the top (the .genre-chip of the genre index) stay in view under the site's sticky bar while
   scrolling, from a tablet's width up (on a phone their rows would take half the screen). The universes are
   .genre-card cards in up to two columns, their fandoms as chips; the fandoms of a category are linked tiles in up to
   three, each with its name and the start of its description cut to three lines. */
.fandom-jump {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    margin: 0 -.5rem 1.5rem;
    padding: .5rem;
    border-radius: var(--bs-border-radius-xl);
    background: color-mix(in srgb, var(--bs-body-bg) 88%, transparent);
    backdrop-filter: blur(6px);
}
.fandom-section {
    margin-bottom: 2.5rem;
    scroll-margin-top: 5rem;
}
@media (min-width: 768px) {
    .fandom-jump {
        position: sticky;
        top: 3.75rem;
        z-index: var(--lf-z-page-sticky);
    }
    .fandom-section {
        scroll-margin-top: 8rem;
    }
}
.fandom-jump .fandom-jump-universes {
    border-color: var(--lf-brand);
}
.fandom-section-head {
    display: flex;
    align-items: center;
    gap: .75rem;
    margin-bottom: 1rem;
    padding-bottom: .5rem;
    border-bottom: var(--lf-border-soft);
}
.fandom-section-head h2 {
    margin: 0;
    font-family: var(--lf-font-title);
    font-size: var(--lf-text-3xl);
    font-weight: var(--lf-weight-bold);
}
.universe-cards {
    column-gap: 1rem;
}
@media (min-width: 768px) {
    .universe-cards {
        columns: 2;
    }
}
.fandom-excerpt {
    display: -webkit-box;
    overflow: hidden;
    margin: .5rem 0 0;
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-sm);
    line-height: var(--lf-leading-normal);
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
}
.universe-more {
    border-style: dashed;
    color: var(--lf-accent-ink);
    font-weight: var(--lf-weight-semibold);
}
.fandom-tiles {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: .875rem;
}
@media (min-width: 576px) {
    .fandom-tiles {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}
@media (min-width: 992px) {
    .fandom-tiles {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}
.fandom-tile {
    display: flex;
    flex-direction: column;
    padding: .875rem 1rem;
}
.fandom-tile-name {
    font-size: var(--lf-text-ui);
    font-weight: var(--lf-weight-semibold);
    transition: color var(--lf-duration-fast);
}
.fandom-tile:hover .fandom-tile-name,
.fandom-tile:focus-visible .fandom-tile-name {
    color: var(--bs-primary-text-emphasis);
}

/* ===== A universe's and a fandom's page (fanfiction/universe.html, fanfiction/fandom.html) =====
   A header on a soft purple wash: the breadcrumbs, the label ("Всесвіт", a fandom's category), the name (a fandom's
   universe under it and its buttons on the right), its counts as linked tiles and the glossary's contents as white
   chips. The fandoms below are the .fandom-tile grid of "/fanfiction". */
.ref-hero {
    margin-bottom: 1.75rem;
    padding: 1.25rem 1.5rem 1.5rem;
    border: var(--lf-border-accent);
    border-radius: var(--lf-radius-2xl);
    background: linear-gradient(135deg, var(--lf-accent-soft), var(--bs-body-bg) 75%);
}
.ref-hero .breadcrumb {
    margin-bottom: .75rem;
    font-size: var(--lf-text-sm);
}
.ref-kicker {
    display: inline-block;
    padding: .15rem .65rem;
    border-radius: var(--bs-border-radius-pill);
    background: var(--lf-brand);
    color: var(--lf-white);
}
.ref-title {
    margin: .5rem 0 1rem;
    font-family: var(--lf-font-title);
    font-size: var(--lf-text-display);
    font-weight: var(--lf-weight-extrabold);
    letter-spacing: var(--lf-tracking-snug);
}
.ref-hero-top {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: .75rem 1.5rem;
    margin-bottom: 1rem;
}
.ref-hero-heading {
    flex: 1 1 20rem;
    min-width: 0;
}
.ref-hero-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    padding-top: .25rem;
}
.ref-hero-actions .nav-menu-icon {
    width: 1.125rem;
    height: 1.125rem;
    flex-shrink: 0;
}
.ref-hero-top .ref-title {
    margin-bottom: 0;
}
.ref-universe {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    margin-top: .5rem;
    padding: .2rem .75rem;
    border: var(--lf-border-accent);
    border-radius: var(--bs-border-radius-pill);
    background: var(--bs-body-bg);
    color: var(--lf-accent-ink);
    font-size: var(--lf-text-md);
    font-weight: var(--lf-weight-semibold);
    text-decoration: none;
    transition: border-color var(--lf-duration-fast), background-color var(--lf-duration-fast);
}
.ref-universe:hover,
.ref-universe:focus-visible {
    border-color: var(--lf-brand);
    background: var(--lf-accent-soft);
}
.ref-stats {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
}
.ref-stat {
    display: flex;
    flex-direction: column;
    min-width: 7.5rem;
    padding: .6rem 1rem;
}
.ref-stat-value {
    color: var(--lf-accent-ink);
    font-family: var(--lf-font-title);
    font-size: var(--lf-text-3xl);
    font-weight: var(--lf-weight-extrabold);
    line-height: var(--lf-leading-tight);
}
.ref-stat-label {
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-xs);
}
@media (max-width: 575.98px) {
    .ref-hero {
        padding: 1rem;
    }
    .ref-stats {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: .5rem;
    }
    .ref-stat {
        min-width: 0;
        padding: .5rem .65rem;
    }
    .ref-stat-value {
        font-size: var(--lf-text-xl);
    }
}
.ref-hero .glossary-contents {
    margin: 1rem 0 0 !important;
}
.ref-hero .tag-chip {
    padding: .2rem .65rem;
    border: var(--lf-border-soft);
    border-radius: var(--bs-border-radius-pill);
    background: var(--bs-body-bg);
    font-size: var(--lf-text-sm);
}
.ref-hero .tag-chip:hover {
    border-color: var(--lf-brand);
    background: var(--lf-accent-soft);
}
/* The universe's characters by the first letter of their names: the letters as small square links, then each letter
   (a purple tile; "Інші" for the names that don't start with one) with its names, the groups in up to four columns. */
.character-letters {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    margin: .875rem 0 1.25rem;
}
.character-letter-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2rem;
    height: 2rem;
    padding: 0 .5rem;
    border: var(--lf-border-accent);
    border-radius: var(--bs-border-radius);
    color: var(--lf-accent-ink);
    font-size: var(--lf-text-sm);
    font-weight: var(--lf-weight-semibold);
    text-decoration: none;
    transition: background-color var(--lf-duration-fast), border-color var(--lf-duration-fast), color var(--lf-duration-fast);
}
.character-letter-link:hover,
.character-letter-link:focus-visible {
    border-color: var(--lf-brand);
    background: var(--lf-brand);
    color: var(--lf-white);
}
.character-groups {
    column-gap: 2rem;
}
@media (min-width: 576px) {
    .character-groups {
        columns: 2;
    }
}
@media (min-width: 992px) {
    .character-groups {
        columns: 3;
    }
}
@media (min-width: 1200px) {
    .character-groups {
        columns: 4;
    }
}
.character-group {
    break-inside: avoid;
    margin-bottom: 1.25rem;
    scroll-margin-top: 5rem;
}
.character-group-letter {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.25rem;
    height: 2.25rem;
    margin: 0 0 .4rem;
    padding: 0 .6rem;
    border-radius: var(--bs-border-radius);
    background: var(--lf-accent-soft);
    color: var(--lf-accent-ink);
    font-family: var(--lf-font-title);
    font-size: var(--lf-text-lg);
    font-weight: var(--lf-weight-extrabold);
}
.character-group-letter.is-other {
    background: var(--bs-tertiary-bg);
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-md);
}
.character-group-list {
    margin: 0;
    padding: 0;
    list-style: none;
}
.character-group-list li {
    padding: .15rem 0;
    font-size: var(--lf-text-md);
}
.character-group-list a {
    color: var(--bs-body-color);
    text-decoration: none;
}
.character-group-list a:hover,
.character-group-list a:focus-visible {
    color: var(--bs-primary-text-emphasis);
    text-decoration: underline;
}

/* ===== Tags ("/tags", tags/list.html) =====
   Laid out like the authors (.news-shell): the categories on the left, the heading with the count and the category's
   description, then the tags as linked tiles in up to three columns, each with its name, how many works carry it as a
   pill (grey when none), its category, its synonyms and the start of its description; "Про категорії" is a tab of the
   same page. */
.tags-head {
    margin-bottom: 1.25rem;
}
.tags-intro {
    max-width: 48rem;
    margin: .5rem 0 0;
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-md);
}
.tag-badge {
    display: inline-block;
    margin-left: .35rem;
    padding: .05rem .5rem;
    border: var(--lf-border-accent);
    border-radius: var(--bs-border-radius-pill);
    color: var(--lf-accent-ink);
    font-size: var(--lf-text-xs);
}
.tag-tiles {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: .875rem;
}
@media (min-width: 576px) {
    .tag-tiles {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}
@media (min-width: 1200px) {
    .tag-tiles {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}
.tag-tile {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    padding: .875rem 1rem;
}
.tag-tile-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: .5rem;
}
.tag-tile-name {
    min-width: 0;
    font-size: var(--lf-text-ui);
    font-weight: var(--lf-weight-semibold);
    transition: color var(--lf-duration-fast);
}
.tag-tile:hover .tag-tile-name,
.tag-tile:focus-visible .tag-tile-name {
    color: var(--bs-primary-text-emphasis);
}
.tag-tile-works {
    flex-shrink: 0;
    padding: .1rem .55rem;
    border-radius: var(--bs-border-radius-pill);
    background: var(--lf-accent-soft);
    color: var(--lf-accent-ink);
    font-size: var(--lf-text-2xs);
    font-weight: var(--lf-weight-semibold);
    white-space: nowrap;
}
.tag-tile-works.is-empty {
    background: var(--bs-tertiary-bg);
    color: var(--bs-secondary-color);
    font-weight: var(--lf-weight-medium);
}
.tag-tile-category {
    align-self: flex-start;
    color: var(--lf-brand);
}
.tag-tile-synonyms {
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-xs);
    font-style: italic;
}
.tag-tile .fandom-excerpt {
    margin-top: .1rem;
}
/* "Про категорії" ("/tags/categories"): a card per category in up to two columns, its whole description shown. */
.tag-category-cards {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: .875rem;
}
@media (min-width: 992px) {
    .tag-category-cards {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}
.tag-category-name {
    font-family: var(--lf-font-title);
    font-size: var(--lf-text-plus);
    font-weight: var(--lf-weight-bold);
    transition: color var(--lf-duration-fast);
}
.tag-tile:hover .tag-category-name,
.tag-tile:focus-visible .tag-category-name {
    color: var(--bs-primary-text-emphasis);
}
.tag-category-description {
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-sm);
    line-height: var(--lf-leading-base);
}

/* ===== "Спільнота — Топ" (community/top.html): a leaderboard of the first hundred =====
   The period switch is one segmented row (scrolls sideways on a phone). "Трійка лідерів": centred cards (the first in
   the middle and the tallest) lit in gold / silver / bronze, filled with the blurred avatar or its colour, the sharp
   avatar in the middle with the rounded corners of a profile's.
   The rest are rows with a bar measured against the first. */
.top-periods {
    display: flex;
    overflow-x: auto;
    scrollbar-width: none;
}
.top-periods::-webkit-scrollbar {
    display: none;
}
.top-periods a {
    flex: 1 0 auto;
    text-align: center;
    white-space: nowrap;
}
.top-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .5rem;
    padding: 3rem 1rem;
    color: var(--bs-secondary-color);
}
.top-section-title {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin: 0 0 1rem;
    font-family: var(--lf-font-title);
    font-size: var(--lf-text-xl);
    font-weight: var(--lf-weight-bold);
}

/* Трійка лідерів */
.top-podium {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    align-items: end;
    gap: 1rem;
    margin: 0 0 2rem;
    padding: 0;
    list-style: none;
}
.top-place-1 { order: 2; --top-face: var(--top-gold-face); --top-ring: var(--top-gold-ring); }
.top-place-2 { order: 1; --top-face: var(--top-silver-face); --top-ring: var(--top-silver-ring); }
.top-place-3 { order: 3; --top-face: var(--top-bronze-face); --top-ring: var(--top-bronze-ring); }
.top-podium-card {
    position: relative;
    isolation: isolate;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 1.75rem 1rem 1rem;
    overflow: hidden;
    border: 2px solid var(--top-ring);
    border-radius: var(--bs-border-radius-xl);
    color: var(--lf-white);
    text-align: center;
    text-decoration: none;
    box-shadow: 0 0 26px 3px color-mix(in srgb, var(--top-ring) 80%, transparent);
    transition: transform var(--lf-duration-base), box-shadow var(--lf-duration-base);
}
.top-place-1 .top-podium-card {
    padding-top: 2.5rem;
    box-shadow: 0 0 38px 6px color-mix(in srgb, var(--top-ring) 90%, transparent);
}
.top-podium-card:hover,
.top-podium-card:focus-visible {
    color: var(--lf-white);
    transform: translateY(-4px);
}
.top-podium-bg {
    position: absolute;
    inset: 0;
    z-index: -2;
    width: 100%;
    height: 100%;
    object-fit: cover;
    filter: blur(14px);
    transform: scale(1.2);
}
.top-podium-card::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(to bottom, rgba(var(--lf-black-rgb), .35), rgba(var(--lf-black-rgb), .72));
}
.top-podium-card .top-move {
    position: absolute;
    top: .75rem;
    right: .75rem;
}
.top-podium-card .top-move-up { color: var(--lf-rank-up-on-cover); }
.top-podium-card .top-move-down { color: var(--lf-rank-down-on-cover); }
.top-podium-card .top-move-same { color: rgba(var(--lf-white-rgb), .7); }
.top-podium-card .top-move-new {
    background: rgba(var(--lf-white-rgb), .18);
    color: var(--lf-white);
}
/* The rounded square of a profile's avatar (.author-hero-avatar), scaled. */
.top-frame {
    display: block;
    flex: 0 0 auto;
    width: 6rem;
    height: 6rem;
    overflow: hidden;
    border: 3px solid var(--top-face);
    border-radius: var(--lf-radius-avatar);
    box-shadow: var(--lf-shadow-float);
}
.top-place-1 .top-frame {
    width: 7.5rem;
    height: 7.5rem;
}
.top-frame .top-avatar {
    width: 100%;
    height: 100%;
    border-radius: 0;
    font-size: var(--lf-text-6xl);
}
.top-place-1 .top-frame .top-avatar {
    font-size: var(--lf-text-7xl);
}
.top-leader {
    margin-top: .75rem;
    color: var(--top-face);
}
.top-podium-card .top-name {
    margin-top: .75rem;
    font-size: var(--lf-text-plus);
    line-height: var(--lf-leading-snug);
}
.top-podium-card .top-leader + .top-name {
    margin-top: .2rem;
    font-size: var(--lf-text-xl);
}
.top-podium-card .top-handle,
.top-podium-card .top-parts {
    color: rgba(var(--lf-white-rgb), .78);
}
.top-podium-card .top-parts {
    justify-content: center;
    margin-top: .35rem;
}
.top-podium-score {
    display: flex;
    flex-direction: column;
    align-items: center;
    align-self: stretch;
    margin-top: .75rem;
    padding-top: .6rem;
    border-top: 1px solid rgba(var(--lf-white-rgb), .22);
    line-height: var(--lf-leading-tight);
}
.top-podium-score strong {
    font-family: var(--lf-font-mono);
    font-size: var(--lf-text-2xl);
    font-weight: var(--lf-weight-bold);
    font-variant-numeric: tabular-nums;
}
.top-place-1 .top-podium-score strong {
    font-size: var(--lf-text-4xl);
}
.top-podium-score span {
    color: rgba(var(--lf-white-rgb), .75);
    font-size: var(--lf-text-xs);
}

.top-avatar {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: var(--lf-radius-avatar);
    object-fit: cover;
    color: var(--lf-white);
    font-weight: var(--lf-weight-bold);
    background-color: var(--lf-avatar-bg, var(--lf-brand)); /* .avatar-tone-<n>, tokens.css */
}
.top-name {
    max-width: 100%;
    overflow: hidden;
    font-family: var(--lf-font-title);
    font-weight: var(--lf-weight-bold);
    text-overflow: ellipsis;
    white-space: nowrap;
}
.top-handle {
    max-width: 100%;
    overflow: hidden;
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-sm);
    text-overflow: ellipsis;
    white-space: nowrap;
}
.top-parts {
    display: inline-flex;
    flex-wrap: wrap;
    gap: .2rem .7rem;
    color: var(--bs-secondary-color);
    font-family: var(--lf-font-mono);
    font-size: var(--lf-text-2xs);
}
.top-parts > span {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
}
.top-parts .nav-menu-icon {
    width: .9rem;
    height: .9rem;
}

/* The move since the period before: up green, down red, unmoved a dash, "нове" a chip. */
.top-move {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    gap: .15rem;
    font-family: var(--lf-font-mono);
    font-size: var(--lf-text-2xs);
    font-weight: var(--lf-weight-semibold);
    font-variant-numeric: tabular-nums;
}
.top-move-down svg {
    transform: rotate(180deg);
}
.top-move-up { color: var(--lf-rank-up); }
.top-move-down { color: var(--lf-rank-down); }
.top-move-same { color: var(--bs-secondary-color); }
.top-move-new {
    padding: 0 .4rem;
    border-radius: var(--bs-border-radius-pill);
    background: var(--lf-accent-soft);
    color: var(--lf-accent-ink);
}
[data-bs-theme="dark"] .top-move-up { color: var(--lf-rank-up-night); }
[data-bs-theme="dark"] .top-move-down { color: var(--lf-rank-down-night); }
.top-row .top-move {
    justify-content: center;
    min-width: 2.25rem;
    margin-left: -.35rem;
}

/* Places 4-100 */
.top-list {
    display: flex;
    flex-direction: column;
    gap: .4rem;
    margin: 0;
    padding: 0;
    list-style: none;
}
.top-row {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .55rem .9rem;
    border: var(--lf-border);
    border-radius: var(--lf-bar-corner);
    background: var(--lf-surface);
    color: var(--bs-body-color);
    text-decoration: none;
    transition: border-color var(--lf-duration-fast), background-color var(--lf-duration-fast);
}
.top-row:hover,
.top-row:focus-visible {
    border-color: var(--lf-accent-line);
    background: var(--lf-accent-soft);
}
.top-rank {
    flex: 0 0 2rem;
    color: var(--bs-secondary-color);
    font-family: var(--lf-font-mono);
    font-weight: var(--lf-weight-semibold);
    font-variant-numeric: tabular-nums;
    text-align: right;
}
.top-who {
    display: flex;
    flex: 1 1 auto;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: .5rem;
    min-width: 0;
}
.top-bar {
    flex: 0 0 100%;
    height: .3rem;
    margin-top: .3rem;
    overflow: hidden;
    border-radius: var(--bs-border-radius-pill);
    background: var(--bs-tertiary-bg);
}
.top-bar > span {
    display: block;
    height: 100%;
    min-width: .3rem;
    border-radius: inherit;
    background: var(--lf-brand);
    opacity: var(--lf-opacity-muted);
}
.top-score {
    display: flex;
    flex: 0 0 auto;
    flex-direction: column;
    align-items: flex-end;
    min-width: 3.5rem;
    line-height: var(--lf-leading-tight);
}
.top-score strong {
    font-family: var(--lf-font-mono);
    font-size: var(--lf-text-plus);
    font-weight: var(--lf-weight-bold);
    font-variant-numeric: tabular-nums;
}
.top-score span {
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-xs);
}
/* A phone: the three one under another, the first on top. */
@media (max-width: 575.98px) {
    .top-podium {
        grid-template-columns: minmax(0, 1fr);
        gap: .75rem;
    }
    .top-podium-place {
        order: 0;
    }
    .top-row {
        gap: .5rem;
        padding: .5rem .6rem;
    }
    .top-rank {
        flex-basis: 1.6rem;
    }
    .top-row .top-avatar {
        width: 2rem;
        height: 2rem;
        font-size: var(--lf-text-md);
    }
}

/* ---- The "create" forms (author/work-new.html "Додати роботу", series/new.html "Створити серію", blog/form.html
   "Новий пост"): a heading with the page's icon and a lead, the fields in numbered cards (.compose-card, a CSS counter),
   choices as cards of their own (.compose-choice) and the buttons in a bar that stays at the bottom of the screen
   while the form is longer than it (.compose-actions). ---- */
.compose {
    max-width: var(--lf-page-lg);
    counter-reset: compose-step;
}
.compose-back {
    display: inline-block;
    margin-bottom: .75rem;
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-md);
    text-decoration: none;
}
.compose-back:hover {
    color: var(--lf-brand);
}
.compose-head {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    margin-bottom: 1.5rem;
}
.compose-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 3rem;
    height: 3rem;
    border-radius: var(--bs-border-radius-lg);
    background: var(--lf-accent-soft);
    border: var(--lf-border-accent);
    color: var(--lf-brand);
}
.compose-icon .nav-menu-icon {
    width: 1.5rem;
    height: 1.5rem;
}
.compose-eyebrow {
    color: var(--lf-brand);
}
.compose-title {
    margin: .1rem 0 .35rem;
    font-family: var(--lf-font-title);
    font-size: var(--lf-text-5xl);
    font-weight: var(--lf-weight-extrabold);
    line-height: var(--lf-leading-tight);
}
.compose-lead {
    max-width: 44rem;
    margin: 0;
    color: var(--bs-secondary-color);
}
.compose-card {
    margin-bottom: 1rem;
    padding: 1.25rem 1.35rem;
    background: var(--lf-surface);
    border: var(--lf-border);
    border-radius: var(--bs-border-radius-lg);
}
.compose-card-title {
    display: flex;
    align-items: center;
    gap: .6rem;
    margin: 0 0 1rem;
    font-size: var(--lf-text-plus);
    font-weight: var(--lf-weight-semibold);
}
.compose-card-title::before {
    counter-increment: compose-step;
    content: counter(compose-step);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 1.6rem;
    height: 1.6rem;
    border-radius: var(--lf-radius-circle);
    background: var(--lf-brand);
    color: var(--lf-white);
    font-family: var(--lf-font-mono);
    font-size: var(--lf-text-sm);
    font-weight: var(--lf-weight-semibold);
}
.compose-card-hint {
    margin: -.6rem 0 1rem;
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-md);
}
.compose .form-label {
    font-weight: var(--lf-weight-medium);
}
/* The title field: the most important one, in the type of the headings. */
.compose-title-input {
    font-family: var(--lf-font-title);
    font-size: var(--lf-text-lg);
    font-weight: var(--lf-weight-semibold);
}
.compose-choices {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .75rem;
}
.compose-choice {
    position: relative;
    display: block;
    height: 100%;
    margin: 0;
    padding: .9rem 1rem .9rem 2.6rem;
    border: var(--lf-border);
    border-radius: var(--bs-border-radius-lg);
    cursor: pointer;
    transition: border-color var(--lf-duration-fast) ease, background-color var(--lf-duration-fast) ease, box-shadow var(--lf-duration-fast) ease;
}
.compose-choice:hover {
    border-color: var(--lf-accent-line);
}
.compose-choice .form-check-input {
    position: absolute;
    top: 1.05rem;
    left: 1rem;
    margin: 0;
}
.compose-choice-title {
    display: block;
    font-weight: var(--lf-weight-semibold);
}
.compose-choice-hint {
    display: block;
    margin-top: .2rem;
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-md);
}
.compose-choice:has(.form-check-input:checked) {
    border-color: var(--lf-brand);
    background: var(--lf-accent-soft);
    box-shadow: var(--lf-ring-selected);
}
.compose-choice:has(.form-check-input:focus-visible) {
    outline: var(--lf-focus-outline);
    outline-offset: var(--lf-focus-offset);
}
.compose-aside {
    padding: 1.1rem 1.2rem;
    background: var(--lf-accent-soft);
    border: var(--lf-border-accent);
    border-radius: var(--bs-border-radius-lg);
}
.compose-aside-title {
    margin: 0 0 .75rem;
    font-size: var(--lf-text-ui);
    font-weight: var(--lf-weight-semibold);
    color: var(--lf-accent-ink);
}
.compose-steps {
    margin: 0;
    padding: 0;
    list-style: none;
    counter-reset: compose-aside;
}
.compose-steps li {
    position: relative;
    padding-left: 2rem;
    font-size: var(--lf-text-md);
}
.compose-steps li + li {
    margin-top: .7rem;
}
.compose-steps li::before {
    counter-increment: compose-aside;
    content: counter(compose-aside);
    position: absolute;
    left: 0;
    top: .05rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.35rem;
    height: 1.35rem;
    border-radius: var(--lf-radius-circle);
    border: 1px solid var(--lf-brand);
    color: var(--lf-brand);
    font-family: var(--lf-font-mono);
    font-size: var(--lf-text-2xs);
    font-weight: var(--lf-weight-semibold);
}
.compose-actions {
    position: sticky;
    bottom: 0;
    z-index: 5;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: .5rem .75rem;
    margin-top: 1.25rem;
    padding: .75rem 1rem;
    background: color-mix(in srgb, var(--lf-surface) 88%, transparent);
    backdrop-filter: blur(8px);
    border: var(--lf-border);
    border-radius: var(--bs-border-radius-lg);
    box-shadow: var(--lf-shadow-bar);
}
.compose-actions-note {
    margin-right: auto;
    color: var(--bs-secondary-color);
    font-size: var(--lf-text-md);
}
.compose-actions .btn {
    min-width: 10rem;
}
@media (max-width: 575.98px) {
    .compose-head {
        gap: .75rem;
    }
    .compose-icon {
        width: 2.5rem;
        height: 2.5rem;
    }
    .compose-title {
        font-size: var(--lf-text-2xl);
    }
    .compose-card {
        padding: 1rem;
    }
    .compose-choices {
        grid-template-columns: minmax(0, 1fr);
    }
    .compose-actions .btn {
        flex: 1 1 auto;
        min-width: 0;
    }
    /* The bar stays on the screen: on a phone only the buttons. */
    .compose-actions-note {
        display: none;
    }
}

/* ---- The widths the templates give their elements (tokens.css, "Widths"), instead of a style attribute. At the end
   of the file, so that a page's own width wins over its component's (.compose) and Bootstrap's .container. ---- */
.page-xs { max-width: var(--lf-page-xs); }
.page-sm { max-width: var(--lf-page-sm); }
.page-md { max-width: var(--lf-page-md); }
.page-lg { max-width: var(--lf-page-lg); }
.page-xl { max-width: var(--lf-page-xl); }
.page-2xl { max-width: var(--lf-page-2xl); }
.page-full { max-width: var(--lf-page-full); }
.mw-field { max-width: var(--lf-width-field); }
.mw-form { max-width: var(--lf-width-form); }
.mw-card { max-width: var(--lf-width-card); }
.w-num { width: var(--lf-width-num); }
/* A flex item that may shrink below its content's width, so a long word or name wraps instead of widening the row. */
.min-w-0 { min-width: 0; }
.min-w-field { min-width: var(--lf-min-field); }
.min-w-num { min-width: var(--lf-min-num); }
.min-w-label { min-width: var(--lf-min-label); }
.list-scroll {
    max-height: var(--lf-list-max-height);
    overflow-y: auto;
}
.avatar-sm,
.avatar-md,
.avatar-lg {
    object-fit: cover;
    object-position: center;
}
.avatar-sm {
    width: var(--lf-avatar-sm);
    height: var(--lf-avatar-sm);
}
.avatar-md {
    width: var(--lf-avatar-md);
    height: var(--lf-avatar-md);
}
.avatar-lg {
    width: var(--lf-avatar-lg);
    height: var(--lf-avatar-lg);
}
.message-bubble {
    max-width: var(--lf-bubble-width);
    min-width: 0;
}
/* The icons drawn in lines (tokens.css, "Icons"): a component's own rule (.library-info .nav-menu-icon) wins. */
.icon-line { stroke-width: var(--lf-icon-line); }
.icon-line-bold { stroke-width: var(--lf-icon-line-bold); }
.icon-line-heavy { stroke-width: var(--lf-icon-line-heavy); }
.icon-xs {
    width: var(--lf-icon-xs);
    height: var(--lf-icon-xs);
}
.icon-sm {
    width: var(--lf-icon-sm);
    height: var(--lf-icon-sm);
}
.icon-md {
    width: var(--lf-icon-md);
    height: var(--lf-icon-md);
}
.icon-lg {
    width: var(--lf-icon-lg);
    height: var(--lf-icon-lg);
}
/* A text whose line breaks are kept (a message, a note, a suggested fix); a long link breaks anywhere. */
.text-pre-wrap { white-space: pre-wrap; }
.text-wrap-anywhere { overflow-wrap: anywhere; }
.progress-tall { --bs-progress-height: var(--lf-progress-tall); }
