/* Bootstrap 5.3, pulled in via @import rather than a <link> in every
   template's own <head> (there's no shared head fragment, only
   fragments/nav.html) -- this one line puts it before all of this
   file's own rules in the cascade, so anything below can still override
   it same as it always could. CDN, not vendored -- no npm/build step in
   this project, and this keeps it that way; the tradeoff is a runtime
   dependency on jsdelivr being reachable. Its JS bundle (offcanvas,
   dropdowns, collapse -- what this site actually uses) loads from
   fragments/nav.html instead, the same one-fragment-reaches-every-page
   trick theme.js already uses.

   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, justified and indented paragraphs, dialogue
   alignment, 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/fanfiction.html, works/originals.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/fanfiction.html, works/originals.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), not a custom token system
   -- so this file carries no color tokens of its own outside the sepia
   override below. */
@import url("https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css");

/* ===== Works filters/results layout (works/fanfiction.html,
   works/originals.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/fanfiction.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: 1000;
    max-height: 16rem;
    overflow-y: auto;
}

/* ===== Author picker (works/fanfiction.html, works/originals.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: 1000;
    max-height: 16rem;
    overflow-y: auto;
}

/* ===== Tag picker (works/fanfiction.html, works/originals.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: 1000;
    max-height: 16rem;
    overflow-y: auto;
}

/* ===== Pairing picker (works/fanfiction.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: 1000;
    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: 1000;
    max-height: 16rem;
    overflow-y: auto;
}

/* ===== Filter info button (works/fanfiction.html, works/originals.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%);
    width: 1.25rem;
    height: 1.25rem;
    padding: 0;
    font-size: 0.75rem;
    line-height: 1;
    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: Georgia, "Times New Roman", Times, serif;
    line-height: 1.7;
    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 0.2s ease-in-out;
}
.reader-toolbar.reader-toolbar-hidden {
    transform: translateY(-100%);
}
/* The toolbar's buttons: a little larger than Bootstrap's .btn-sm text (0.875rem). */
.reader-toolbar .btn {
    --bs-btn-font-size: 1.05rem;
}

/* 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 {
    width: 1.2em;
    height: 1.2em;
    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: 1.3rem;
    line-height: 1;
    transform: translateY(-0.03em);
}

/* The work's title in the toolbar: larger than the buttons' text. */
.reader-toolbar-title {
    font-size: 1.25rem;
}

/* 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%;
    }
}
@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;
}

/* 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: "Roboto", "Helvetica Neue", Arial, sans-serif;
}
.reader-page[data-font="verdana"] {
    font-family: Verdana, Geneva, sans-serif;
}
.reader-page[data-font="arial"] {
    font-family: Arial, Helvetica, sans-serif;
}
.reader-page[data-font="alegreya"] {
    font-family: "Alegreya", Georgia, serif;
}
.reader-page[data-font="times"] {
    font-family: "Times New Roman", Times, serif;
}
.reader-page[data-font="comfortaa"] {
    font-family: "Comfortaa", "Trebuchet MS", sans-serif;
}
.reader-page[data-font="fira-sans"] {
    font-family: "Fira Sans", "Segoe UI", sans-serif;
}
.reader-page[data-font="pt-sans"] {
    font-family: "PT Sans", "Trebuchet MS", sans-serif;
}

/* 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. */
/* On <body> (set by reader.js), so the page background itself turns sepia, not just the content. */
body[data-reader-theme="sepia"] {
    --bs-body-bg: #f4ecd8;
    --bs-body-color: #3b2f1c;
    --bs-emphasis-color: #3b2f1c;
    --bs-secondary-color: #7a6a4f;
    --bs-border-color: #d8c9a3;
    --bs-border-color-translucent: #d8c9a3;
    --bs-tertiary-bg: #ece0c4;
}

/* Light sepia: the plain light theme with only a faint warm tint (barely off-white page, near-black
   brown text), for those who find full sepia too yellow. */
body[data-reader-theme="sepia-light"] {
    --bs-body-bg: #fbf7ee;
    --bs-body-color: #2e2a24;
    --bs-emphasis-color: #2e2a24;
    --bs-secondary-color: #766c5c;
    --bs-border-color: #e6dec9;
    --bs-border-color-translucent: #e6dec9;
    --bs-tertiary-bg: #f4eee0;
}

/* Base paragraph typography -- justified, hyphenated, first-line indent,
   the conventional look for long-form prose. */
.reader-page article p {
    margin: 0 0 0.95rem;
    text-align: justify;
    text-wrap: pretty;
    hyphens: auto;
    hanging-punctuation: first;
    text-indent: 1.25em;
}

/* A chapter's paragraph is a Markdown block: a div with its own paragraphs. Dialogue (see ParagraphPresenter --
   detected server-side from a dash at the start, the Ukrainian/Russian convention) reads left-aligned with no
   first-line indent instead, and an aligned block (:::center and so on) overrides the justified text inside it. */
.reader-page article .chapter-md.dialogue p {
    text-indent: 0;
    text-align: left;
    margin-left: 1.1em;
}
.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: 1040;
    display: flex;
    gap: .25rem;
    padding: .25rem;
    border-radius: .5rem;
    background: var(--bs-dark);
    box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .25);
}
.reader-selection-menu .btn {
    color: #fff;
    white-space: nowrap;
}
.reader-selection-menu .btn:hover,
.reader-selection-menu .btn:focus-visible {
    background: rgba(255, 255, 255, .15);
    color: #fff;
}
/* "Повідомлення про помилки" (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: 3px solid var(--bs-border-color);
    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-hyphens="off" (see reader.js) -- hyphenation defaults to on
   (matches the base rule above), this just un-sets it. */
.reader-page[data-hyphens="off"] article p {
    hyphens: none;
}

/* 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: #000;
    }

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

/* ===== Relationship-category colors (works/fanfiction.html,
   works/originals.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. */
.relationship-gen {
    --relationship-bg: #8d5a35;
    --relationship-fg: #fff;
}
.relationship-het {
    --relationship-bg: #2e9d57;
    --relationship-fg: #fff;
}
.relationship-slash {
    --relationship-bg: #4aa8e8;
    --relationship-fg: #06243b;
}
.relationship-femslash {
    --relationship-bg: #8a5cd0;
    --relationship-fg: #fff;
}
.relationship-multi {
    --relationship-bg: #ffc107;
    --relationship-fg: #212529;
}
.relationship-other {
    --relationship-bg: #12a99a;
    --relationship-fg: #fff;
}
/* No category set: the card still gets the stripe, in neutral grey. */
.relationship-none {
    --relationship-bg: #adb5bd;
    --relationship-fg: #212529;
}
.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. */
.work-badge {
    background-color: var(--bs-body-bg);
    border: 1px solid var(--badge-border);
    color: var(--badge-text);
}
.rating-g {
    --badge-border: #1b5e20;
    --badge-text: #1b5e20;
}
.rating-pg_13 {
    --badge-border: #8fd14f;
    --badge-text: #4c8a14;
}
.rating-r {
    --badge-border: #ffc107;
    --badge-text: #8a6500;
}
.rating-nc_17 {
    --badge-border: #dc3545;
    --badge-text: #dc3545;
}
.rating-nc_21 {
    --badge-border: #7b1e3a;
    --badge-text: #7b1e3a;
}
.status-in_progress {
    --badge-border: #ffc107;
    --badge-text: #8a6500;
}
.status-completed {
    --badge-border: #2e9d57;
    --badge-text: #1f7a42;
}
.status-frozen {
    --badge-border: #4aa8e8;
    --badge-text: #1d6fa8;
}

/* ===== 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: 1;
}
.work-description-toggle[hidden] {
    display: none;
}
.work-description-toggle .toggle-chevron {
    width: 1.25rem;
    height: 1.25rem;
    transition: transform 0.15s;
}
.work-description-toggle[aria-expanded="true"] .toggle-chevron {
    transform: rotate(180deg);
}

/* ===== Work card tags: one box per tag (like Ficbook), with the tag's
   rating in a small outlined box on its right. The rating box reuses the
   .rating-<name> colour classes (--badge-border / --badge-text). */
.tag-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.1rem 0.5rem;
    border-radius: 0.25rem;
    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);
}
.tag-chip-rating {
    padding: 0 0.3rem;
    border: 1px solid var(--badge-border);
    border-radius: 0.2rem;
    background-color: var(--bs-body-bg);
    color: var(--badge-text);
    font-size: 0.75rem;
    line-height: 1.3;
}

/* 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;
}

/* 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: 600;
    color: var(--bs-secondary-color);
    font-size: 0.85rem;
    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: extra bottom padding lifts the centred block a bit above the exact middle. */
.home-hero {
    padding-bottom: 18vh;
}

/* 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;
    /* The navbar's purple as a barely-there tint (a deeper tint in dark mode). */
    background-color: #ece4f8;
    border-top-color: #d9ccee !important;
}
:root[data-bs-theme="dark"] .page-footer {
    background-color: #2d2347;
    border-top-color: #3d3260 !important;
}
.page-footer .footer-nav a {
    color: var(--bs-secondary-color);
    text-decoration: none;
}
.page-footer .footer-nav a:hover {
    color: var(--bs-body-color);
    text-decoration: underline;
}
/* The footer theme button names the theme it switches TO. */
[data-bs-theme="dark"] .page-footer .css-is-light-theme,
:root:not([data-bs-theme="dark"]) .page-footer .css-is-dark-theme {
    display: none;
}

/* Purple navbar (fragments/nav.html). The nav carries data-bs-theme="dark", so its text is
   white in both site themes (white on #8260c4 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 {
    background-color: #8260c4;
    border-bottom-color: rgba(0, 0, 0, 0.15) !important;
}
:root[data-bs-theme="dark"] .site-navbar {
    background-color: #55388a;
}
/* 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: #fff;
    --bs-navbar-hover-color: #fff;
    --bs-navbar-active-color: #fff;
    --bs-navbar-brand-color: #fff;
    --bs-navbar-brand-hover-color: #fff;
}
.navbar.site-navbar .nav-link {
    color: #fff;
}
.navbar.site-navbar .nav-link:hover,
.navbar.site-navbar .nav-link:focus {
    color: #fff;
    text-decoration: underline;
}
/* Dropdown menus: translucent purple "glass" -- the page behind is blurred, the text stays
   white (the fill is kept ~80% opaque so it reads over any content). Items are highlighted
   with a translucent white wash. */
.site-navbar .dropdown-menu {
    --bs-dropdown-bg: rgba(118, 78, 190, 0.8);
    --bs-dropdown-border-color: rgba(255, 255, 255, 0.28);
    --bs-dropdown-link-color: #fff;
    --bs-dropdown-link-hover-color: #fff;
    --bs-dropdown-link-hover-bg: rgba(255, 255, 255, 0.18);
    --bs-dropdown-link-active-color: #fff;
    --bs-dropdown-link-active-bg: rgba(255, 255, 255, 0.26);
    --bs-dropdown-divider-bg: rgba(255, 255, 255, 0.25);
    -webkit-backdrop-filter: blur(12px) saturate(140%);
    backdrop-filter: blur(12px) saturate(140%);
    box-shadow: 0 0.5rem 1.25rem rgba(20, 8, 45, 0.35);
}
:root[data-bs-theme="dark"] .site-navbar .dropdown-menu {
    --bs-dropdown-bg: rgba(58, 36, 98, 0.82);
}
/* 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 (mobile) menu keeps its normal flow. */
@media (min-width: 992px) {
    .site-navbar .dropdown-menu {
        --bs-dropdown-spacer: 0.7rem;
    }
}
/* 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: 0.6rem;
    background: #dc3545;
    color: #fff;
    font-size: 0.65rem;
    font-weight: 600;
    line-height: 1.05rem;
    text-align: center;
    pointer-events: none;
}
.site-navbar .nav-icon-link svg {
    width: 1.4rem;
    height: 1.4rem;
}

/* Signed-in user menu (fragments/nav.html): the profile icon is drawn in the text colour and a bit
   larger than the name; the long menu scrolls inside itself on short screens; its sections
   collapse, and the logout button (a POST form) looks like any other item. */
.site-navbar .user-icon {
    width: 1.5rem;
    height: 1.5rem;
    flex-shrink: 0;
}
/* The signed-in user's avatar in the header: a round thumbnail the same size as the profile
   icon. 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: 50%;
    object-fit: cover;
    object-position: center;
}
.site-navbar .user-menu {
    min-width: 17rem;
    max-height: calc(100vh - 6rem);
    overflow-y: auto;
}
/* The collapsible sections: the title row is a full-width menu item with a chevron that flips
   while the section is open; the items inside are indented under it. */
.site-navbar .user-menu .user-menu-group {
    font-weight: 600;
}
.site-navbar .user-menu .user-menu-chevron {
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
    transition: transform 0.15s;
}
.site-navbar .user-menu .user-menu-group[aria-expanded="true"] .user-menu-chevron {
    transform: rotate(180deg);
}
.site-navbar .user-menu .user-menu-sub .dropdown-item {
    padding-left: 2rem;
}
.site-navbar .user-menu form .dropdown-item {
    width: 100%;
}

.site-navbar .btn-nav {
    /* Inverted: a button in the footer's light-purple tint (#ece4f8) with purple text, so it
       doesn't melt into the purple bar. */
    --bs-btn-color: #5a3a96;
    --bs-btn-bg: #ece4f8;
    --bs-btn-border-color: #d9ccee;
    --bs-btn-hover-color: #4a2f80;
    --bs-btn-hover-bg: #e0d4f3;
    --bs-btn-hover-border-color: #cdbde8;
    --bs-btn-active-color: #4a2f80;
    --bs-btn-active-bg: #d4c5ee;
    --bs-btn-active-border-color: #c3b0e3;
    --bs-btn-focus-shadow-rgb: 236, 228, 248;
    /* The label is set like the header's menu links (size and weight), not in .btn-sm's smaller type. */
    --bs-btn-font-size: 1rem;
    --bs-btn-font-weight: 400;
}
.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 palette (navbar #8260c4, footer tint #ece4f8) ----
   Bootstrap's primary / secondary / outline-primary / outline-secondary buttons re-coloured
   through their own --bs-btn-* variables, so every page picks it up without touching markup.
   .btn-nav (navbar) keeps its own rule above. Semantic colours (danger, success, ...) are left
   alone. Dark-mode values follow below: any element in a dark colour mode ([data-bs-theme="dark"],
   on <html> or e.g. on <body> of the reading page), with white text and icons on the buttons. */
.btn-primary {
    --bs-btn-color: #fff;
    --bs-btn-bg: #8260c4;
    --bs-btn-border-color: #8260c4;
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #7550b5;
    --bs-btn-hover-border-color: #6a45ab;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #6a45ab;
    --bs-btn-active-border-color: #62429c;
    --bs-btn-focus-shadow-rgb: 130, 96, 196;
    --bs-btn-disabled-color: #fff;
    --bs-btn-disabled-bg: #8260c4;
    --bs-btn-disabled-border-color: #8260c4;
}
.btn-outline-primary {
    --bs-btn-color: #7550b5;
    --bs-btn-border-color: #8260c4;
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #8260c4;
    --bs-btn-hover-border-color: #8260c4;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #7550b5;
    --bs-btn-active-border-color: #7550b5;
    --bs-btn-focus-shadow-rgb: 130, 96, 196;
    --bs-btn-disabled-color: #7550b5;
    --bs-btn-disabled-border-color: #8260c4;
}
.btn-secondary {
    --bs-btn-color: #4a2f80;
    --bs-btn-bg: #ece4f8;
    --bs-btn-border-color: #d9ccee;
    --bs-btn-hover-color: #3f2672;
    --bs-btn-hover-bg: #e0d4f3;
    --bs-btn-hover-border-color: #cdbde8;
    --bs-btn-active-color: #3f2672;
    --bs-btn-active-bg: #d4c5ee;
    --bs-btn-active-border-color: #c3b0e3;
    --bs-btn-focus-shadow-rgb: 117, 80, 181;
    --bs-btn-disabled-color: #4a2f80;
    --bs-btn-disabled-bg: #ece4f8;
    --bs-btn-disabled-border-color: #d9ccee;
}
.btn-outline-secondary {
    --bs-btn-color: #5d4a82;
    --bs-btn-border-color: #bcaedb;
    --bs-btn-hover-color: #3f2672;
    --bs-btn-hover-bg: #ece4f8;
    --bs-btn-hover-border-color: #a995d0;
    --bs-btn-active-color: #3f2672;
    --bs-btn-active-bg: #e0d4f3;
    --bs-btn-active-border-color: #a995d0;
    --bs-btn-focus-shadow-rgb: 117, 80, 181;
    --bs-btn-disabled-color: #5d4a82;
    --bs-btn-disabled-border-color: #bcaedb;
}
[data-bs-theme="dark"] .btn-primary {
    --bs-btn-bg: #8760c6;
    --bs-btn-border-color: #8760c6;
    --bs-btn-hover-bg: #7550b5;
    --bs-btn-hover-border-color: #6a45ab;
    --bs-btn-active-bg: #6a45ab;
    --bs-btn-active-border-color: #62429c;
    --bs-btn-disabled-bg: #8760c6;
    --bs-btn-disabled-border-color: #8760c6;
}
[data-bs-theme="dark"] .btn-outline-primary {
    --bs-btn-color: #fff;
    --bs-btn-border-color: #8760c6;
    --bs-btn-hover-bg: #8760c6;
    --bs-btn-hover-border-color: #8760c6;
    --bs-btn-active-bg: #7550b5;
    --bs-btn-active-border-color: #7550b5;
    --bs-btn-disabled-color: #fff;
    --bs-btn-disabled-border-color: #8760c6;
}
[data-bs-theme="dark"] .btn-secondary {
    --bs-btn-color: #fff;
    --bs-btn-bg: #3d3260;
    --bs-btn-border-color: #4d4078;
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #4a3c75;
    --bs-btn-hover-border-color: #5a4b8a;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #54447f;
    --bs-btn-active-border-color: #64539a;
    --bs-btn-disabled-color: #fff;
    --bs-btn-disabled-bg: #3d3260;
    --bs-btn-disabled-border-color: #4d4078;
}
[data-bs-theme="dark"] .btn-outline-secondary {
    --bs-btn-color: #fff;
    --bs-btn-border-color: #5b4c86;
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #3d3260;
    --bs-btn-hover-border-color: #7160a3;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #4a3c75;
    --bs-btn-active-border-color: #7160a3;
    --bs-btn-disabled-color: #fff;
    --bs-btn-disabled-border-color: #5b4c86;
}
/* A light colour mode scoped inside a dark one (the reading page's own "light" theme on a site in
   dark mode) puts the light palette back: this rule is more specific than the dark ones above. */
[data-bs-theme="dark"] [data-bs-theme="light"] .btn-primary {
    --bs-btn-color: #fff;
    --bs-btn-bg: #8260c4;
    --bs-btn-border-color: #8260c4;
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #7550b5;
    --bs-btn-hover-border-color: #6a45ab;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #6a45ab;
    --bs-btn-active-border-color: #62429c;
    --bs-btn-focus-shadow-rgb: 130, 96, 196;
    --bs-btn-disabled-color: #fff;
    --bs-btn-disabled-bg: #8260c4;
    --bs-btn-disabled-border-color: #8260c4;
}
[data-bs-theme="dark"] [data-bs-theme="light"] .btn-outline-primary {
    --bs-btn-color: #7550b5;
    --bs-btn-border-color: #8260c4;
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #8260c4;
    --bs-btn-hover-border-color: #8260c4;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #7550b5;
    --bs-btn-active-border-color: #7550b5;
    --bs-btn-focus-shadow-rgb: 130, 96, 196;
    --bs-btn-disabled-color: #7550b5;
    --bs-btn-disabled-border-color: #8260c4;
}
[data-bs-theme="dark"] [data-bs-theme="light"] .btn-secondary {
    --bs-btn-color: #4a2f80;
    --bs-btn-bg: #ece4f8;
    --bs-btn-border-color: #d9ccee;
    --bs-btn-hover-color: #3f2672;
    --bs-btn-hover-bg: #e0d4f3;
    --bs-btn-hover-border-color: #cdbde8;
    --bs-btn-active-color: #3f2672;
    --bs-btn-active-bg: #d4c5ee;
    --bs-btn-active-border-color: #c3b0e3;
    --bs-btn-focus-shadow-rgb: 117, 80, 181;
    --bs-btn-disabled-color: #4a2f80;
    --bs-btn-disabled-bg: #ece4f8;
    --bs-btn-disabled-border-color: #d9ccee;
}
[data-bs-theme="dark"] [data-bs-theme="light"] .btn-outline-secondary {
    --bs-btn-color: #5d4a82;
    --bs-btn-border-color: #bcaedb;
    --bs-btn-hover-color: #3f2672;
    --bs-btn-hover-bg: #ece4f8;
    --bs-btn-hover-border-color: #a995d0;
    --bs-btn-active-color: #3f2672;
    --bs-btn-active-bg: #e0d4f3;
    --bs-btn-active-border-color: #a995d0;
    --bs-btn-focus-shadow-rgb: 117, 80, 181;
    --bs-btn-disabled-color: #5d4a82;
    --bs-btn-disabled-border-color: #bcaedb;
}

/* 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, in the same purple as the primary
   buttons; the dark-mode value is the dark-mode button purple. Same scoping as the buttons above:
   any element in a dark colour mode, and a light mode nested inside a dark one puts the light
   value back. */
.list-group {
    --bs-list-group-active-color: #fff;
    --bs-list-group-active-bg: #8260c4;
    --bs-list-group-active-border-color: #8260c4;
}
[data-bs-theme="dark"] .list-group {
    --bs-list-group-active-bg: #8760c6;
    --bs-list-group-active-border-color: #8760c6;
}
[data-bs-theme="dark"] [data-bs-theme="light"] .list-group {
    --bs-list-group-active-bg: #8260c4;
    --bs-list-group-active-border-color: #8260c4;
}

/* 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;
}
.home-title .brand-mark {
    /* The big mark above the home page's name */
    width: 3.6rem;
    height: 8rem;
    color: #8260c4;
}
/* 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 1s 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 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: 1px solid var(--bs-border-color, #dee2e6);
    padding: 0.25rem 0.6rem;
}
.news-body blockquote,
.chapter-md blockquote {
    border-left: 3px solid var(--bs-border-color, #dee2e6);
    padding-left: 0.9rem;
    color: var(--bs-secondary-color, #6c757d);
}
.news-body pre,
.chapter-md pre {
    background: var(--bs-tertiary-bg, #f8f9fa);
    padding: 0.6rem 0.8rem;
    border-radius: 0.375rem;
    overflow-x: auto;
}

/* 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: 0.5rem;
}
.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: 0.375rem;
}
@media (max-width: 575.98px) {
    /* on a phone the text still needs room next to it */
    .work-cover-thumb {
        width: 140px;
    }
}
.work-cover-preview {
    max-width: 160px;
    max-height: 240px;
    height: auto;
    border-radius: 0.375rem;
}

/* ===== 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 placeholder with the
   site's icon; the direction badge sits 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 glow take
   the colour of the work's direction. */
.library-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 1rem;
}
.library-card {
    position: relative;
    display: block;
    aspect-ratio: 2 / 3;
    border-radius: 0.6rem;
    overflow: hidden;
    color: #fff;
    text-decoration: none;
    background: var(--bs-tertiary-bg, #f1ecf8);
    outline: 3px solid transparent;
    outline-offset: -3px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.18);
    transition: transform 0.15s ease, box-shadow 0.15s ease, outline-color 0.15s ease;
}
.library-card:hover,
.library-card:focus-visible {
    color: #fff;
    transform: translateY(-3px);
    outline-color: var(--relationship-bg);
    box-shadow: 0 0 0 2px var(--relationship-bg), 0 8px 20px color-mix(in srgb, var(--relationship-bg) 55%, transparent);
}
.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(160deg, #a58bd8, #6f4fb3);
}
.library-card-placeholder .brand-mark {
    width: 34%;
    height: auto;
    color: #fff;
    opacity: 0.85;
}
.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(0, 0, 0, 0.85), rgba(0, 0, 0, 0));
    overflow-wrap: anywhere;
}
.library-card-fandom {
    font-size: 0.7rem;
    line-height: 1.2;
    opacity: 0.85;
    display: -webkit-box;
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.library-card-title {
    font-size: 0.9rem;
    font-weight: 600;
    line-height: 1.2;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.library-direction {
    position: absolute;
    top: 0.45rem;
    left: 0.45rem;
    width: 1.9rem;
    height: 1.9rem;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--relationship-bg);
    color: var(--relationship-fg);
    font-size: 1rem;
    font-weight: 700;
    line-height: 1;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
}
/* 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"; }
/* 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: .55;
}
.library-unpublished-tag {
    position: absolute;
    left: 0.45rem;
    bottom: 3.2rem;
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    background: #b02a37;
    color: #fff;
    font-size: 0.65rem;
    font-weight: 600;
}
.library-shelf-tag {
    position: absolute;
    top: 0.45rem;
    right: 0.45rem;
    max-width: 60%;
    padding: 0.1rem 0.45rem;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.6);
    font-size: 0.65rem;
    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 after the pointer has rested on it for 1.5 seconds
   (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: 30;
}
.library-popover {
    display: none;
    position: absolute;
    z-index: 30;
    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, #fff);
    color: var(--bs-body-color, #212529);
    border: 3px solid var(--relationship-bg);
    border-radius: 0.6rem;
    box-shadow: 0 0 0 1px var(--relationship-bg), 0 12px 32px rgba(0, 0, 0, 0.35);
    opacity: 0;
    transform: scale(0.45);
    transition: transform 0.2s ease, opacity 0.2s ease;
}
.library-item.is-open .library-popover {
    display: block;
}
.library-item.is-open .library-popover.is-shown {
    opacity: 1;
    transform: scale(1);
}
.library-popover-description {
    max-height: 11rem;
    overflow-y: auto;
    font-size: 0.9rem;
}
@media (hover: none) {
    .library-popover {
        display: none !important;
    }
}

/* 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: 0.8rem;
    line-height: 1.3;
    color: var(--bs-secondary-color, #6c757d);
    overflow-wrap: anywhere;
}
.collection-excerpt {
    max-height: 6.5rem;
    overflow: hidden;
}

/* The mini menu ("...") 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, with the like count beside it. */
.work-menu {
    position: absolute;
    top: 0.6rem;
    right: 0.6rem;
    z-index: 6;
}
.work-menu-btn {
    width: 2.5rem;
    height: 2.5rem;
    padding: 0;
    font-size: 1.6rem;
    font-weight: 700;
    line-height: 1;
    border: 1px solid rgba(0, 0, 0, 0.15);
    border-radius: 0.6rem;
    background: color-mix(in srgb, var(--bs-body-bg, #fff) 92%, transparent);
    color: var(--bs-body-color, #212529);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}
.work-menu-btn:hover,
.work-menu-btn:focus-visible {
    background: var(--bs-body-bg, #fff);
    color: var(--bs-body-color, #212529);
}
.work-menu .work-like-count {
    padding: 0.2rem 0.55rem;
    border-radius: 999px;
    background: color-mix(in srgb, var(--bs-body-bg, #fff) 92%, transparent);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
    font-weight: 600;
}
.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: 2000;
    max-width: min(32rem, calc(100vw - 2rem));
    margin: 0;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
    transition: opacity 0.35s ease;
}
.work-toast-out {
    opacity: 0;
}
.work-card-leaving {
    opacity: 0;
    transition: opacity 0.3s ease;
}

/* The hidden works page (reader/hidden-works.html): a small cover (or the logo on a purple tile) on each line. */
.hidden-work-cover {
    display: block;
    width: 54px;
    aspect-ratio: 2 / 3;
    object-fit: cover;
    border-radius: 0.35rem;
    border-left: 4px solid var(--relationship-bg, #adb5bd);
}
.hidden-work-cover-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(160deg, #a58bd8, #6f4fb3);
    color: #fff;
}
.hidden-work-cover-empty .brand-mark {
    width: 40%;
    height: auto;
}

/* 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: 1px solid var(--bs-border-color-translucent);
    border-radius: .75rem;
    background: var(--bs-body-bg);
    box-shadow: 0 1px 2px rgba(0, 0, 0, .04);
}
/* 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 2.5s 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 6s ease-out;
}
@keyframes comment-flash {
    from { background: rgba(111, 66, 193, .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 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: 50%;
    font-weight: 600;
    font-size: 1rem;
    line-height: 1;
    color: #fff;
    text-decoration: none;
    user-select: none;
    background: #6f42c1;
}
.work-comments .comment-reply .comment-avatar {
    width: 1.75rem;
    height: 1.75rem;
    font-size: .8rem;
}
.work-comments .comment-avatar-0 { background: linear-gradient(160deg, #a58bd8, #6f4fb3); }
.work-comments .comment-avatar-1 { background: linear-gradient(160deg, #6fb3e0, #2f78b5); }
.work-comments .comment-avatar-2 { background: linear-gradient(160deg, #7cc9a0, #2f8f63); }
.work-comments .comment-avatar-3 { background: linear-gradient(160deg, #f0b27a, #c96a2b); }
.work-comments .comment-avatar-4 { background: linear-gradient(160deg, #ee8fa8, #c2456b); }
.work-comments .comment-avatar-5 { background: linear-gradient(160deg, #8fa0b8, #4f5f78); }
.work-comments .comment-avatar-deleted {
    background: var(--bs-tertiary-bg);
    border: 1px dashed var(--bs-border-color);
}

/* 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: .25rem;
}
.work-comments .comment-author {
    font-weight: 600;
    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: .8rem;
    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: .8rem;
    color: var(--bs-secondary-color);
    text-decoration: none;
    border: 1px solid var(--bs-border-color);
    border-radius: 999px;
    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: 999px;
    font-size: .7rem;
    font-weight: 600;
    letter-spacing: .02em;
    line-height: 1.4;
    color: #6f42c1;
    background: rgba(111, 66, 193, .12);
}
/* The site role of the writer: the admin (red) or a moderator (blue). */
.work-comments .comment-badge.comment-badge-admin {
    color: #b02a37;
    background: rgba(220, 53, 69, .12);
}
.work-comments .comment-badge.comment-badge-moderator {
    color: #0b5ed7;
    background: rgba(13, 110, 253, .12);
}
[data-bs-theme="dark"] .work-comments .comment-badge.comment-badge-admin {
    color: #f1aeb5;
    background: rgba(220, 53, 69, .22);
}
[data-bs-theme="dark"] .work-comments .comment-badge.comment-badge-moderator {
    color: #9ec5fe;
    background: rgba(13, 110, 253, .22);
}
[data-bs-theme="dark"] .work-comments .comment-badge {
    color: #c9b3f5;
    background: rgba(165, 139, 216, .2);
}
.work-comments .comment-body {
    border-radius: .25rem;
}

/* The actions: quiet text buttons that light up on hover. */
.work-comments .comment-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .15rem;
    margin-left: -.5rem;
}
.work-comments .comment-action {
    padding: .15rem .5rem;
    border: 0;
    border-radius: 999px;
    background: transparent;
    font-size: .8rem;
    font-weight: 500;
    color: var(--bs-secondary-color);
}
.work-comments .comment-action:hover,
.work-comments .comment-action:focus-visible {
    color: var(--bs-emphasis-color);
    background: var(--bs-tertiary-bg);
}
.work-comments .comment-action-danger:hover,
.work-comments .comment-action-danger:focus-visible {
    color: var(--bs-danger-text-emphasis);
    background: var(--bs-danger-bg-subtle);
}
.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: 999px;
    font-size: .85rem;
    font-weight: 500;
    color: #fff;
    background: #6f42c1;
    box-shadow: 0 4px 14px rgba(111, 66, 193, .35);
}
.work-comments .comments-fresh:hover {
    background: #5e35a8;
}
.work-comments .comments-fresh-dot {
    width: .5rem;
    height: .5rem;
    border-radius: 50%;
    background: #fff;
    animation: comments-pulse 1.6s 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: #fff;
    background: #6f42c1;
}

/* 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: 999px;
    background: transparent;
    font-size: .8rem;
    font-weight: 500;
    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: 50%;
    font-size: .8rem;
    line-height: 1;
}
.work-comments .comment-branch-count {
    padding: 0 .4rem;
    border-radius: 999px;
    font-size: .7rem;
    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: 1px;
    background: var(--bs-border-color);
    transition: background-color .15s;
}
.work-comments .comment-branch-close:hover::before,
.work-comments .comment-branch-close:focus-visible::before {
    background: #6f42c1;
}
.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: .8rem;
    }
    .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 .25rem .25rem 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: .25rem;
    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: .25rem;
    padding: 0 .15em;
    box-decoration-break: clone;
    -webkit-box-decoration-break: clone;
    transition: background-color .2s, color .2s;
}
.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(255, 255, 255, .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: 2px solid #6f42c1;
    outline-offset: 1px;
}
.work-comments span.spoiler.spoiler-open {
    background-color: rgba(111, 66, 193, .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: 110px;
    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: 1px solid var(--bs-border-color-translucent);
    border-radius: 1rem;
    box-shadow: 0 6px 24px rgba(111, 66, 193, .08);
}
.moderator-create-head {
    display: flex;
    align-items: center;
    gap: .9rem;
    padding: 1.1rem 1.25rem;
    color: #fff;
    background: linear-gradient(135deg, #8a63d2, #5b3fa6);
}
.moderator-create-sub {
    color: rgba(255, 255, 255, .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: 50%;
    background: rgba(255, 255, 255, .18);
}
.moderator-create .card-body {
    padding: 1.25rem;
}
.moderator-create-foot {
    padding: .85rem 1.25rem;
    background: var(--bs-tertiary-bg);
    border-top: 1px solid var(--bs-border-color-translucent);
}
.moderator-create [data-password-toggle].active {
    color: #fff;
    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: 50%;
    font-weight: 600;
    color: #0b5ed7;
    background: rgba(13, 110, 253, .12);
}
.moderator-password {
    margin-top: .25rem;
    padding: .85rem 1rem;
    border-radius: .75rem;
    background: var(--bs-tertiary-bg);
}
.moderator-password [data-password-toggle].active {
    color: #fff;
    background: var(--bs-secondary);
}
.moderator-active {
    color: #146c43;
    background: rgba(25, 135, 84, .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);
}

/* 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;
}
.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: 1px solid var(--bs-border-color);
    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;
}

/* 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;
}
.author-hero-banner {
    height: 96px;
    background: linear-gradient(120deg, #8260c4 0%, #a47fe0 55%, #d6a6e8 100%);
}
:root[data-bs-theme="dark"] .author-hero-banner {
    background: linear-gradient(120deg, #55388a 0%, #6f4bab 55%, #8a5f9e 100%);
}
.author-hero-body {
    padding-top: 0;
}
.author-hero-avatar {
    width: 112px;
    height: 112px;
    margin-top: -56px;
    border-radius: 50%;
    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: 2.75rem;
    font-weight: 600;
    color: #fff;
    background: linear-gradient(135deg, #8260c4, #b07ad6);
}
.author-hero-name {
    min-width: 12rem;
}
.author-hero-actions {
    margin-left: auto;
}
.author-role {
    display: inline-block;
    padding: 0.2rem 0.65rem;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 600;
    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: 1.65;
}
.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: 1.5rem;
    font-weight: 700;
    line-height: 1.1;
}
.author-stat-label {
    font-size: 0.8rem;
    color: var(--bs-secondary-color);
}
.author-facts {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.35rem 1rem;
    font-size: 0.925rem;
}
.author-facts dt {
    font-weight: normal;
    color: var(--bs-secondary-color);
}
.author-facts dd {
    margin: 0;
    overflow-wrap: anywhere;
}
@media (max-width: 575.98px) {
    .author-hero-actions {
        margin-left: 0;
        width: 100%;
    }
}

/* 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: 999px;
    font-size: 0.85rem;
    font-weight: 600;
    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-card .min-w-0 {
    min-width: 0;
}
.assistant-avatar {
    width: 2.5rem;
    height: 2.5rem;
    flex: 0 0 2.5rem;
    border-radius: 50%;
    object-fit: cover;
}
.assistant-avatar-letter {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    color: #fff;
    background: linear-gradient(135deg, #8260c4, #b07ad6);
}
.assistant-facts {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.3rem 0.9rem;
    font-size: 0.9rem;
}
.assistant-facts dt {
    font-weight: normal;
    color: var(--bs-secondary-color);
}
.assistant-facts dd {
    margin: 0;
    overflow-wrap: anywhere;
}
.assistant-snippet {
    font-size: 0.9rem;
    overflow-wrap: anywhere;
}
.assistant-text {
    white-space: pre-line;
    line-height: 1.6;
    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: 500;
}
.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: 500;
}
.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: 500;
}
.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: 3px solid var(--bs-border-color);
}
.collab-table td {
    overflow-wrap: anywhere;
}

/* ---- Statistics (fragments/stats.html): a bar chart of plain blocks, no chart library. ---- */
.stats-chart {
    display: flex;
    align-items: flex-end;
    gap: 2px;
    height: 140px;
    padding: 4px 0;
    border-bottom: 1px solid var(--bs-border-color);
}
.stats-bar-cell {
    flex: 1 1 0;
    height: 100%;
    display: flex;
    align-items: flex-end;
}
.stats-bar {
    width: 100%;
    background: var(--bs-primary);
    border-radius: 2px 2px 0 0;
    min-height: 0;
}
.stats-axis {
    display: flex;
    justify-content: space-between;
}
.stats-number {
    font-size: 1.4rem;
    font-weight: 600;
    line-height: 1.2;
}

/* 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: 1px dashed var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    color: var(--bs-secondary-color);
    font-size: .875rem;
}

.library-item-folded .library-card,
.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: 1px solid var(--bs-border-color-translucent);
    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: .875rem;
    padding: 0 .35rem;
    border-radius: var(--bs-border-radius-pill);
    color: var(--bs-secondary-color);
}
.note-icon-none {
    opacity: .45;
}
.note-icon-open {
    background: var(--bs-warning-bg-subtle);
    color: var(--bs-warning-text-emphasis);
}
.note-thread {
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    padding: .5rem .75rem;
    background: var(--bs-body-bg);
}
.note-thread-resolved {
    opacity: .75;
}
.note-message + .note-message {
    border-top: 1px dashed var(--bs-border-color-translucent);
    margin-top: .35rem;
    padding-top: .35rem;
}

/* Badges (badge module, fragments/badge.html): a chip like the role badges, coloured by the level. */
.user-badge {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .15rem .55rem;
    border-radius: var(--bs-border-radius-pill);
    border: 1px solid var(--bs-border-color);
    background: var(--bs-tertiary-bg);
    font-size: .8125rem;
    line-height: 1.4;
}
.user-badge-level {
    font-size: .75rem;
    color: var(--bs-secondary-color);
}
.user-badge-level-1 {
    border-color: #b87333;
}
.user-badge-level-2 {
    border-color: #a8a9ad;
}
.user-badge-level-3 {
    border-color: #d4af37;
    background: rgba(212, 175, 55, .12);
}
.user-badge-level-4 {
    border-color: #8fb8c9;
    background: rgba(143, 184, 201, .15);
}
.user-badge-level-5 {
    border-color: #6f42c1;
    background: rgba(111, 66, 193, .12);
}

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

/* A comment's likes (fragments/work-comments.html). */
.comment-like.comment-liked {
    color: var(--bs-danger);
}
.comment-author-liked {
    font-size: .8125rem;
    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-font-size: 1.0625rem;
    --te-line: 1.6;
    --te-gap: 1.6em; /* one blank line: as tall as the blank line between the paragraphs of a field */
}
.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: 600;
    padding: .375rem 0;
}
.translation-editor .te-canvas {
    height: calc(100vh - 14rem);
    min-height: 22rem;
    overflow-y: auto;
    padding: 1.25rem 1.5rem;
    border: 1px solid var(--bs-border-color);
    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: 1px solid var(--bs-border-color);
}
.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: 1;
    opacity: 0;
    transition: opacity .15s;
}
.translation-editor .te-remove {
    right: -1.25rem;
    top: .15em;
    padding: 0 .25rem;
    font-size: 1.1rem;
}
.translation-editor .te-add {
    left: 0;
    bottom: -.55rem;
    z-index: 1;
    padding: .1rem .35rem;
    font-size: .8rem;
    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: .6;
    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: 2px;
}
.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: .7rem;
    line-height: 1;
    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 translator's helper (js/translation-editor.js): the bar under a selection of a translation field, 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: 1050;
    max-width: min(40rem, calc(100vw - 1rem));
    padding: .4rem;
    font-size: .875rem;
    background: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
}
.te-assist .te-assist-option {
    white-space: normal;
}
.te-assist.te-assist-wide {
    width: min(40rem, calc(100vw - 1rem));
}
.te-assist .te-assist-input {
    min-height: 4.5rem;
    resize: vertical;
}
.te-assist .te-assist-result {
    max-height: 50vh;
    overflow-y: auto;
}
