/* ==========================================================================
   Stories — the library, the story landing page and the reader.

   Loaded only on /stories pages via the layout's @stack('styles').

   Two visual registers on purpose:

     - CHROME (bars, cards, drawers) stays in the site's cyberpunk palette, so
       the library still reads as part of Our Dark Home.
     - THE PAGE ITSELF is a calm sheet with generous measure and warm text. An
       hour of neon-on-black prose is unreadable, and the whole point of this
       area is that people actually read it.

   Everything the reader can adjust — size, leading, measure, paper — is a CSS
   custom property on .story-reader, set from localStorage by story-reader.js.
   ========================================================================== */

/* Bitstream Vera Serif — self-hosted, not a Google Fonts link like the rest
   of the site's typefaces. It isn't in Google's catalogue, so this is the
   only way to serve it. Two weights only: the original Bitstream Vera
   release never shipped italic or bold-italic for the Serif face (only
   Sans got those), so an italic run falls back to the browser's synthesized
   oblique — acceptable for prose, and no worse than before this existed.
   Files + full license text: public/fonts/ (Bitstream Vera License — free,
   permits embedding as-is; see BITSTREAM-VERA-LICENSE.txt alongside them). */
@font-face {
    font-family: 'Bitstream Vera Serif';
    src: url('/fonts/VeraSe.ttf') format('truetype');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}
@font-face {
    font-family: 'Bitstream Vera Serif';
    src: url('/fonts/VeraSeBd.ttf') format('truetype');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

:root {
    --story-cyan:    #00f5ff;
    --story-magenta: #ff2d78;
    --story-dim:     rgba(200, 216, 248, 0.45);
    --story-faint:   rgba(200, 216, 248, 0.25);
}

/* ==========================================================================
   0. Tailwind stand-ins
   ==========================================================================
   public/build/ is a PREBUILT bundle (2026-06-17) and there is no local npm to
   regenerate it, so any utility Tailwind did not compile back then does not
   exist in production at all — the class is simply inert.

   These three are used by the story views and are absent from that bundle.
   (max-w-3xl and max-w-5xl are already used by several music views, which are
   silently missing their max-width on the server today.) Declared here so the
   pages lay out correctly with no build step; identical to Tailwind's own
   output, so they are harmless duplicates if the bundle is ever rebuilt.
   ========================================================================== */

.max-w-3xl { max-width: 48rem; }
.max-w-5xl { max-width: 64rem; }

@media (min-width: 640px) {
    .sm\:inline { display: inline; }
}

/* ==========================================================================
   1. Library index
   ========================================================================== */

.story-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: 1.25rem;
}

.story-card {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    text-decoration: none;
    transition: transform 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
}

.story-card:hover {
    transform: translateY(-3px);
    border-color: rgba(0, 245, 255, 0.45);
    box-shadow: 0 10px 32px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 245, 255, 0.12);
}

/* A book jacket is portrait; 2:3 is the paperback proportion. */
.story-cover {
    position: relative;
    aspect-ratio: 2 / 3;
    background:
        radial-gradient(circle at 30% 20%, rgba(0, 245, 255, 0.10), transparent 60%),
        linear-gradient(160deg, #0b0b22 0%, #14142f 100%);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border-bottom: 1px solid rgba(0, 245, 255, 0.14);
}

.story-cover img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.story-cover-fallback {
    font-size: 2.75rem;
    color: rgba(0, 245, 255, 0.6);
}

/* The spine flourish that makes a coverless story still look like a book. */
.story-cover::after {
    content: '';
    position: absolute;
    inset: 0 auto 0 0;
    width: 6px;
    background: linear-gradient(90deg, rgba(0, 0, 0, 0.55), transparent);
    pointer-events: none;
}

.story-card-body {
    padding: 0.85rem 0.95rem 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    flex: 1;
}

.story-card-title {
    font-family: 'Orbitron', 'Share Tech Mono', monospace;
    font-size: 0.9rem;
    line-height: 1.3;
    color: #e8f0ff;
    letter-spacing: 0.02em;
}

.story-card-meta {
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.75rem;
    letter-spacing: 0.06em;
    color: var(--story-dim);
    text-transform: uppercase;
}

.story-card-blurb {
    font-size: 0.78rem;
    line-height: 1.55;
    color: rgba(200, 216, 248, 0.62);
    display: -webkit-box;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.story-card-foot {
    margin-top: auto;
    padding-top: 0.5rem;
    display: flex;
    align-items: center;
    gap: 0.85rem;
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.75rem;
    color: var(--story-dim);
}

.story-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.12rem 0.45rem;
    border-radius: 3px;
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.75rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    border: 1px solid currentColor;
    line-height: 1.5;
}

.story-badge-adult   { color: var(--story-magenta); }

/*
   The long-form content warning.

   .story-badge is built for one or two words: uppercase, wide letter-spacing,
   no wrapping. A full sentence in that treatment is close to unreadable, and
   the whole value of a warning is that it gets read. This variant undoes the
   three things that stop it working at sentence length, and adds a faint fill
   so it still reads as a marked-off notice rather than as body text.

   The short "Extreme" pill on story cards keeps the original styling — it IS
   one word, and it sits in the corner of a cover image where nothing longer
   would fit.
*/
.story-badge-warning {
    text-transform: none;
    letter-spacing: 0.01em;
    white-space: normal;
    text-align: left;
    line-height: 1.4;
    padding: 0.3rem 0.6rem;
    max-width: 34rem;
    background: rgba(255, 45, 120, 0.10);
}
.story-badge-draft   { color: #ffd700; }
.story-badge-feature { color: var(--story-cyan); }

/*
   Badges that sit ON a cover image — the Draft and "Extreme" pills stacked in
   the top-right corner of a story card.

   .story-badge on its own has NO FILL: it is coloured text inside a 1px border
   of the same colour, so whatever is behind it shows straight through. Over a
   dark cover that reads well. Over a pale one the magenta all but vanishes,
   which is the one thing a content warning cannot afford to do — and unlike
   every other badge on the page, these two land on artwork nobody controls.

   So over a cover they bring their own ground instead of borrowing the image's.
   The fill is the site's own near-black rather than a tint of the badge colour,
   so the pill still reads as part of the page and not as a sticker.

   This is scoped to the cover stack deliberately. The same .story-badge class
   is used on the story page and in the featured block, where it sits on a solid
   card and is perfectly readable as it is.

   One note on the weight: it is SYNTHESISED. Share Tech Mono ships a single
   weight (400) and the browser thickens it, which is why this says 700 rather
   than 600 — 600 would round to the same synthetic result. A short uppercase
   word at 0.1em tracking has the room to carry it.
*/
.story-cover-badges {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    align-items: flex-end;
}

.story-cover-badges .story-badge {
    font-weight: 700;
    background: rgba(5, 5, 18, 0.94);
    /* Holds the glyph edges against a bright cover, and lifts the pill off the
       artwork so it reads as sitting on top rather than printed into it. */
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.85);
    box-shadow: 0 1px 5px rgba(0, 0, 0, 0.5);
}

/* A touch hotter than the base magenta. Against a solid dark ground it can
   afford the extra brightness, and that is what makes the word land at a
   glance on a busy cover. */
.story-cover-badges .story-badge-adult { color: #ff4d8d; }

/* Reading progress rail on a card and on the landing page. */
.story-progress {
    height: 3px;
    border-radius: 999px;
    background: rgba(0, 245, 255, 0.12);
    overflow: hidden;
}

.story-progress-fill {
    height: 100%;
    border-radius: 999px;
    background: linear-gradient(90deg, var(--story-cyan), var(--story-magenta));
}

/* Featured hero on the library index. */
.story-hero {
    display: grid;
    grid-template-columns: minmax(0, 12rem) minmax(0, 1fr);
    gap: 1.75rem;
    align-items: center;
}

@media (max-width: 640px) {
    .story-hero {
        grid-template-columns: 1fr;
        justify-items: center;
        text-align: center;
    }
}

.story-hero .story-cover {
    border-radius: 4px;
    border: 1px solid rgba(0, 245, 255, 0.2);
    box-shadow: 0 14px 40px rgba(0, 0, 0, 0.6);
    width: 100%;
}

/* ==========================================================================
   2. The reader shell
   ========================================================================== */

.story-reader {
    /* Reader-adjustable. Default is 16pt — the site's own baseline for new
       library content (see .story-prose below), matching what LibreOffice's
       own default 16pt renders as. Point, not rem: the request this follows
       from was literally "16pt", and pt is a real, exact CSS unit — no
       reason to convert it to an approximation. */
    --sheet-size:    16pt;      /* base font size of the prose      */
    --sheet-leading: 1.75;      /* line-height                      */
    --sheet-measure: 38rem;     /* max line length                  */
    /* Alignment is NOT a custom property: the imported per-style rules live in
       a <style> tag in the body and would win on source order over anything
       applied here. It is a [data-align] attribute instead — see below. */

    /* Paper theme, overridden by [data-paper]. */
    --sheet-bg:     linear-gradient(170deg, #12121f 0%, #0d0d18 100%);
    --sheet-ink:    #ded9d0;
    --sheet-ink-dim: rgba(222, 217, 208, 0.55);
    --sheet-rule:   rgba(222, 217, 208, 0.14);
    /* Page-action buttons. Declared per paper theme rather than derived with
       color-mix(): an unsupported color-mix drops the whole declaration, and
       for `border` that means no border at all — the buttons would lose the
       edge that makes them read as buttons, which is the entire fix. */
    --sheet-btn-bg:   rgba(222, 217, 208, 0.07);
    --sheet-btn-edge: rgba(222, 217, 208, 0.32);
    --sheet-btn-bg-hover:   rgba(222, 217, 208, 0.14);
    --sheet-btn-edge-hover: rgba(222, 217, 208, 0.55);
    --sheet-shadow: 0 18px 50px rgba(0, 0, 0, 0.6);

    position: relative;
}

.story-reader[data-paper='sepia'] {
    --sheet-bg:     linear-gradient(170deg, #2a2118 0%, #221b13 100%);
    --sheet-ink:    #efe0c8;
    --sheet-ink-dim: rgba(239, 224, 200, 0.55);
    --sheet-rule:   rgba(239, 224, 200, 0.16);
    --sheet-btn-bg:   rgba(239, 224, 200, 0.07);
    --sheet-btn-edge: rgba(239, 224, 200, 0.32);
    --sheet-btn-bg-hover:   rgba(239, 224, 200, 0.14);
    --sheet-btn-edge-hover: rgba(239, 224, 200, 0.55);
}

.story-reader[data-paper='parchment'] {
    --sheet-bg:     linear-gradient(170deg, #f3ead8 0%, #e9dec7 100%);
    --sheet-ink:    #241d14;
    --sheet-ink-dim: rgba(36, 29, 20, 0.6);
    --sheet-rule:   rgba(36, 29, 20, 0.18);
    /* Dark ink on light paper: the same alphas read heavier here, so they come
       down rather than being reused blindly from the dark themes. */
    --sheet-btn-bg:   rgba(36, 29, 20, 0.05);
    --sheet-btn-edge: rgba(36, 29, 20, 0.30);
    --sheet-btn-bg-hover:   rgba(36, 29, 20, 0.10);
    --sheet-btn-edge-hover: rgba(36, 29, 20, 0.55);
    --sheet-shadow: 0 18px 50px rgba(0, 0, 0, 0.5);
}

.story-reader[data-paper='void'] {
    --sheet-bg:     #05050c;
    --sheet-ink:    #c9d4e8;
    --sheet-ink-dim: rgba(201, 212, 232, 0.5);
    --sheet-rule:   rgba(201, 212, 232, 0.14);
}

.story-reader[data-paper='contrast'] {
    --sheet-bg:     #000000;
    --sheet-ink:    #ffffff;
    --sheet-ink-dim: rgba(255, 255, 255, 0.7);
    --sheet-rule:   rgba(255, 255, 255, 0.3);
}

/* --- Top bar -------------------------------------------------------------- */

.reader-bar {
    position: sticky;
    top: 0;
    z-index: 30;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 0.9rem;
    background: linear-gradient(to bottom, rgba(5, 5, 18, 0.97), rgba(9, 9, 28, 0.92));
    border: 1px solid rgba(0, 245, 255, 0.16);
    border-radius: 4px;
    backdrop-filter: blur(6px);
    flex-wrap: wrap;
}

.reader-bar-title {
    flex: 1;
    min-width: 8rem;
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.75rem;
    letter-spacing: 0.08em;
    color: var(--story-dim);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.reader-bar-title strong {
    color: #e8f0ff;
    font-weight: 600;
}

.reader-tool {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    min-width: 2rem;
    height: 2rem;
    padding: 0 0.5rem;
    background: rgba(0, 245, 255, 0.05);
    border: 1px solid rgba(0, 245, 255, 0.22);
    border-radius: 3px;
    color: var(--story-cyan);
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.75rem;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
}

.reader-tool:hover,
.reader-tool[aria-expanded='true'] {
    background: rgba(0, 245, 255, 0.14);
    border-color: rgba(0, 245, 255, 0.5);
}

.reader-tool.is-liked {
    color: var(--story-magenta);
    border-color: rgba(255, 45, 120, 0.5);
    background: rgba(255, 45, 120, 0.12);
}

.reader-rail {
    width: 100%;
    height: 2px;
    background: rgba(0, 245, 255, 0.12);
    border-radius: 999px;
    overflow: hidden;
}

.reader-rail-fill {
    height: 100%;
    background: linear-gradient(90deg, var(--story-cyan), var(--story-magenta));
    transition: width 0.35s ease;
}

/* --- The stage and its sheets --------------------------------------------- */

.reader-stage {
    position: relative;
    perspective: 2400px;
    perspective-origin: 50% 40%;
    margin: 1.25rem 0;
    transition: height 0.4s ease;
}

.page-sheet {
    position: relative;
    background: var(--sheet-bg);
    color: var(--sheet-ink);
    border: 1px solid var(--sheet-rule);
    border-radius: 3px;
    box-shadow: var(--sheet-shadow);
    padding: clamp(1.5rem, 5vw, 3.25rem) clamp(1.25rem, 5vw, 3.5rem);
    transform-style: preserve-3d;
    backface-visibility: hidden;
}

/* The bound edge — a subtle gutter shadow, like a page in an open book. */
.page-sheet::before {
    content: '';
    position: absolute;
    inset: 0 auto 0 0;
    width: 2.25rem;
    background: linear-gradient(90deg, rgba(0, 0, 0, 0.22), transparent);
    pointer-events: none;
    border-radius: 3px 0 0 3px;
}

.story-reader[data-paper='parchment'] .page-sheet::before {
    background: linear-gradient(90deg, rgba(120, 96, 60, 0.18), transparent);
}

/* Sheets stacked mid-flip.

   Pinned by top/left only, never `inset: 0`. With a bottom edge as well, the
   stage's locked height would stretch the sheet and its measured offsetHeight
   would come back as the OUTGOING page's height — so the stage could never ease
   to the height of the page arriving. */
.page-sheet.is-flipping {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    z-index: 3;
}

.page-sheet.is-incoming {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    z-index: 2;
}

@keyframes sheet-flip-forward {
    from { transform: rotateY(0deg);     box-shadow: var(--sheet-shadow); }
    to   { transform: rotateY(-172deg);  box-shadow: 0 0 60px rgba(0, 0, 0, 0.7); }
}

@keyframes sheet-flip-back {
    from { transform: rotateY(172deg);   box-shadow: 0 0 60px rgba(0, 0, 0, 0.7); }
    to   { transform: rotateY(0deg);     box-shadow: var(--sheet-shadow); }
}

.page-sheet.flip-forward {
    transform-origin: left center;
    animation: sheet-flip-forward 620ms cubic-bezier(0.42, 0.02, 0.32, 1) forwards;
}

.page-sheet.flip-back {
    transform-origin: left center;
    animation: sheet-flip-back 620ms cubic-bezier(0.42, 0.02, 0.32, 1) forwards;
}

/* A sheet turning through the light picks up shade — that is what sells the
   illusion. The two directions need opposite curves: the page leaving darkens
   as it goes, the page arriving brightens as it lands. One shared keyframe left
   the incoming page sitting at 90% black. */
.page-sheet.flip-forward::after,
.page-sheet.flip-back::after {
    content: '';
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.35);
    opacity: 0;
    pointer-events: none;
    border-radius: 3px;
}

.page-sheet.flip-forward::after {
    animation: sheet-shade 620ms ease forwards;
}

.page-sheet.flip-back::after {
    animation: sheet-unshade 620ms ease forwards;
}

@keyframes sheet-shade {
    from { opacity: 0; }
    60%  { opacity: 0.7; }
    to   { opacity: 0.9; }
}

@keyframes sheet-unshade {
    from { opacity: 0.9; }
    40%  { opacity: 0.7; }
    to   { opacity: 0; }
}

/* Readers who ask for less motion get a plain crossfade instead. */
@media (prefers-reduced-motion: reduce) {
    .page-sheet.flip-forward,
    .page-sheet.flip-back {
        animation: sheet-fade 200ms linear forwards;
    }

    .page-sheet.flip-forward::after,
    .page-sheet.flip-back::after {
        animation: none;
        opacity: 0;
    }

    .reader-stage { transition: none; }
}

.story-reader[data-motion='off'] .page-sheet.flip-forward,
.story-reader[data-motion='off'] .page-sheet.flip-back {
    animation: sheet-fade 180ms linear forwards;
}

.story-reader[data-motion='off'] .page-sheet.flip-forward::after,
.story-reader[data-motion='off'] .page-sheet.flip-back::after {
    animation: none;
    opacity: 0;
}

@keyframes sheet-fade {
    from { opacity: 1; }
    to   { opacity: 0; }
}

/* --- Sheet furniture ------------------------------------------------------ */

.sheet-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1.75rem;
    padding-bottom: 0.7rem;
    border-bottom: 1px solid var(--sheet-rule);
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.75rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--sheet-ink-dim);
}

.sheet-chapter {
    font-family: 'Orbitron', 'Share Tech Mono', monospace;
    font-size: 0.75rem;
    letter-spacing: 0.16em;
    color: var(--sheet-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sheet-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-top: 2.25rem;
    padding-top: 0.9rem;
    border-top: 1px solid var(--sheet-rule);
    flex-wrap: wrap;
}

.sheet-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* Sheet-local buttons inherit the paper's ink rather than the site's cyan, so
   they do not glow out of a parchment page. */
/*
   These were missable, and a reader reported reading a whole page without
   noticing them. Three things were working against them: they were drawn in
   --sheet-ink-dim, which is the colour of furniture rather than of controls;
   they were icon-and-number only, which reads as a statistic rather than an
   action; and they sat at 0.75rem in the same row as the folio, so they looked
   like part of the page numbering.

   Now: full ink instead of dim, a real border, a faint fill so they read as
   raised, more padding, and a word next to each icon. Still quiet enough for a
   page of prose — this is a book, not a toolbar — but unmistakably buttons.
*/
.sheet-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.5rem 0.95rem;
    background: var(--sheet-btn-bg);
    border: 1px solid var(--sheet-btn-edge);
    border-radius: 3px;
    color: var(--sheet-ink);
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.82rem;
    letter-spacing: 0.04em;
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
}

.sheet-btn:hover {
    color: var(--sheet-ink);
    border-color: var(--sheet-btn-edge-hover);
    background: var(--sheet-btn-bg-hover);
}

.sheet-btn:focus-visible {
    outline: 2px solid var(--story-magenta);
    outline-offset: 2px;
}

.sheet-btn.is-liked {
    color: var(--story-magenta);
    border-color: rgba(255, 45, 120, 0.55);
    background: rgba(255, 45, 120, 0.12);
}

/* The count is secondary to the verb, so it steps back rather than competing.
   Zero is hidden entirely — "Comment 0" invites nobody, and an empty count is
   the most common state on a new page. */
.sheet-btn-count {
    opacity: 0.7;
    font-size: 0.92em;
}
.sheet-btn-count:empty { display: none; }

/*
   The label is the whole point of this change, so it survives to a much
   narrower screen than a nicety would. It only drops below 380px, where the
   two buttons plus the folio genuinely cannot share a line — and by then the
   icons have the row to themselves and are no longer competing for attention.
*/
@media (max-width: 380px) {
    .sheet-btn-label { display: none; }
    .sheet-btn { padding: 0.5rem 0.75rem; }
}

.sheet-folio {
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.75rem;
    letter-spacing: 0.12em;
    color: var(--sheet-ink-dim);
}

/* --- Flip controls -------------------------------------------------------- */

.reader-nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-top: 0.5rem;
}

.flip-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.55rem 1.1rem;
    background: rgba(0, 245, 255, 0.06);
    border: 1px solid rgba(0, 245, 255, 0.25);
    border-radius: 3px;
    color: var(--story-cyan);
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.75rem;
    letter-spacing: 0.08em;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, transform 0.15s;
}

.flip-btn:hover:not(:disabled) {
    background: rgba(0, 245, 255, 0.16);
    border-color: rgba(0, 245, 255, 0.55);
}

.flip-btn:disabled {
    opacity: 0.3;
    cursor: default;
}

/* Invisible edge targets: clicking the edge of the sheet turns the page, the way
   tapping the side of an e-reader does. Kept narrow enough to sit inside the
   sheet's own margin, so it never swallows a click meant to select text. */
.reader-edge {
    position: absolute;
    top: 0;
    bottom: 0;
    width: clamp(1.25rem, 4%, 2.75rem);
    z-index: 4;
    border: none;
    background: none;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.2s;
}

.reader-edge:hover:not(:disabled) { opacity: 1; }
.reader-edge:disabled { cursor: default; }

.reader-edge-prev {
    left: 0;
    background: linear-gradient(90deg, rgba(0, 245, 255, 0.10), transparent);
}

.reader-edge-next {
    right: 0;
    background: linear-gradient(270deg, rgba(0, 245, 255, 0.10), transparent);
}

/* --- Drawers -------------------------------------------------------------- */

.reader-drawer {
    display: none;
    margin-top: 1rem;
    padding: 1.1rem 1.25rem;
    background: linear-gradient(160deg, rgba(9, 9, 30, 0.96), rgba(5, 5, 18, 0.96));
    border: 1px solid rgba(0, 245, 255, 0.2);
    border-radius: 4px;
}

.reader-drawer.is-open { display: block; }

.reader-drawer-title {
    font-family: 'Orbitron', 'Share Tech Mono', monospace;
    font-size: 0.75rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--story-cyan);
    margin-bottom: 0.9rem;
}

/* Table of contents. */
.toc-list {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    max-height: 24rem;
    overflow-y: auto;
}

.toc-item {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    padding: 0.45rem 0.6rem;
    border-radius: 3px;
    text-decoration: none;
    color: rgba(200, 216, 248, 0.75);
    font-size: 0.82rem;
    transition: background 0.15s, color 0.15s;
}

.toc-item:hover {
    background: rgba(0, 245, 255, 0.08);
    color: #e8f0ff;
}

.toc-item.is-current {
    background: rgba(0, 245, 255, 0.12);
    color: var(--story-cyan);
    box-shadow: inset 2px 0 0 var(--story-cyan);
}

.toc-num {
    flex-shrink: 0;
    width: 2rem;
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.75rem;
    color: var(--story-faint);
}

.toc-pages {
    margin-left: auto;
    flex-shrink: 0;
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.75rem;
    color: var(--story-faint);
}

/* Reader settings. */
.setting-row {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    padding: 0.5rem 0;
    border-bottom: 1px solid rgba(0, 245, 255, 0.07);
    flex-wrap: wrap;
}

.setting-row:last-child { border-bottom: none; }

.setting-label {
    flex: 0 0 7.5rem;
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.75rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--story-dim);
}

.setting-options {
    display: flex;
    gap: 0.35rem;
    flex-wrap: wrap;
}

.setting-btn {
    padding: 0.25rem 0.6rem;
    background: rgba(0, 245, 255, 0.04);
    border: 1px solid rgba(0, 245, 255, 0.18);
    border-radius: 3px;
    color: rgba(200, 216, 248, 0.7);
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.75rem;
    cursor: pointer;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
}

.setting-btn:hover { border-color: rgba(0, 245, 255, 0.45); }

.setting-btn.is-active {
    background: rgba(0, 245, 255, 0.16);
    border-color: var(--story-cyan);
    color: var(--story-cyan);
}

/* --- Immersive mode ------------------------------------------------------- */

/* Hides the site chrome and centres the sheet, for reading without the HUD. */
body.story-immersive .sticky-nav,
body.story-immersive footer,
body.story-immersive .story-reader-aside {
    display: none !important;
}

body.story-immersive {
    padding-top: 0 !important;
    background: #03030a;
}

body.story-immersive .story-reader-shell {
    max-width: none;
    padding: 0.75rem clamp(0.5rem, 4vw, 3rem) 3rem;
}

/* ==========================================================================
   3. The prose itself — the classes the ODT converter emits
   ==========================================================================
   USED TO SAY: "The document's own styles are layered ON TOP of these... so
   anything the writer set in LibreOffice wins." That's flipped now, on
   request — new library posts should read as Bitstream Vera Serif 16pt
   REGARDLESS of what font/size an imported .odt (or a hand-typed chapter
   inheriting one via Story::typographyTemplate()) actually carries. The
   per-class rules OdtStyles::stylesheet() generates and injects per chapter
   (read.blade.php's #chapter-style) still exist and still carry everything
   else about the source formatting — alignment, indents, margins, colour,
   borders — untouched. Only font-family and font-size are now forced.

   MECHANISM: those generated rules are plain, unscoped classes with no
   !important (confirmed in OdtStyles.php), so a plain !important here beats
   them outright regardless of their selector being more specific — that's
   how !important vs. non-important always resolves, specificity only
   decides ties BETWEEN !important rules. The one place specificity between
   two !important declarations matters here is deliberate: a member who
   explicitly picks the 'reader' or 'sans' typeface in Reading Settings
   below still wins over this, because THAT rule's selector
   (.story-reader[data-typeface='reader'] .story-prose) is more specific
   than this bare .story-prose. This is the new default, not a hard floor —
   an explicit reader preference still gets the final say.
   ========================================================================== */

.story-prose {
    max-width: var(--sheet-measure);
    margin: 0 auto;
    font-family: 'Bitstream Vera Serif', Georgia, 'Times New Roman', serif !important;
    font-size: var(--sheet-size) !important;
    line-height: var(--sheet-leading);
    color: var(--sheet-ink);
    /* Long-form prose needs hyphenation to justify without rivers. */
    hyphens: auto;
    overflow-wrap: break-word;
}
.story-prose * {
    /* Catches character-level "run" classes the ODT converter can emit
       nested inside a paragraph (a mid-sentence font change, for example) —
       without this, a plain .story-prose rule would lose to a more specific
       nested .odt-run-xxx class even though neither uses !important, simply
       because the nested one is more specific among NON-important rules. */
    font-family: 'Bitstream Vera Serif', Georgia, 'Times New Roman', serif !important;
    font-size: inherit !important;
}

/* "Use the reading font" — overrides the document's font choices for readers
   who would rather not have the manuscript's face imposed on them. */
.story-reader[data-typeface='reader'] .story-prose,
.story-reader[data-typeface='reader'] .story-prose * {
    font-family: Georgia, 'Iowan Old Style', 'Palatino Linotype', 'Times New Roman', serif !important;
}

.story-reader[data-typeface='sans'] .story-prose,
.story-reader[data-typeface='sans'] .story-prose * {
    font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif !important;
}

.story-prose .odt-p {
    margin: 0 0 0.95em;
}

.story-prose .odt-p:last-child { margin-bottom: 0; }

/* Reader alignment override. Deliberately scoped to body paragraphs only:
   forcing every element would drag centred chapter titles, captions and figures
   left as well, which is not what "ragged right" means. "As written"
   (data-align="document") sets nothing and lets the manuscript decide. */
.story-reader[data-align='justify'] .story-prose .odt-p {
    text-align: justify !important;
}

.story-reader[data-align='left'] .story-prose .odt-p {
    text-align: left !important;
    text-indent: 0 !important;
}

.story-prose .odt-empty {
    color: var(--sheet-ink-dim);
    font-style: italic;
    text-align: center;
}

.story-prose h1, .story-prose h2, .story-prose h3,
.story-prose h4, .story-prose h5, .story-prose h6 {
    font-family: 'Orbitron', 'Share Tech Mono', monospace;
    font-weight: 600;
    line-height: 1.3;
    letter-spacing: 0.04em;
    color: var(--sheet-ink);
    margin: 1.9em 0 0.75em;
}

.story-prose h1:first-child, .story-prose h2:first-child,
.story-prose h3:first-child, .story-prose h4:first-child { margin-top: 0; }

.story-prose .odt-h1 { font-size: 1.6em; }
.story-prose .odt-h2 { font-size: 1.35em; }
.story-prose .odt-h3 { font-size: 1.18em; }
.story-prose .odt-h4 { font-size: 1.06em; }
.story-prose .odt-h5,
.story-prose .odt-h6 { font-size: 1em; text-transform: uppercase; letter-spacing: 0.12em; }

.story-prose .odt-list {
    margin: 0 0 1em 1.4em;
    padding: 0;
}

.story-prose .odt-list li { margin-bottom: 0.35em; }
.story-prose .odt-list .odt-list { margin-bottom: 0.2em; }

.story-prose .odt-link {
    color: inherit;
    text-decoration: underline;
    text-decoration-color: var(--story-cyan);
    text-underline-offset: 0.15em;
}

.story-prose .odt-link:hover { color: var(--story-cyan); }

/* A tab stop is roughly a 4-space indent once the page is reflowed; the
   document's own measure no longer applies. */
.story-prose .odt-tab {
    display: inline-block;
    width: 2.5em;
}

.story-prose .odt-image {
    max-width: 100%;
    height: auto;
    display: inline-block;
    border-radius: 2px;
}

.story-prose .odt-figure {
    margin: 1.6em 0;
    text-align: center;
}

.story-prose .odt-caption {
    display: block;
    margin-top: 0.55em;
    font-size: 0.85em;
    font-style: italic;
    color: var(--sheet-ink-dim);
    text-align: center;
}

/* A text box on its own — a pull-quote, an epigraph, an aside. */
.story-prose .odt-textbox-p {
    display: block;
    margin: 1.4em 0;
    padding: 0.85em 1.1em;
    border-left: 2px solid var(--story-cyan);
    background: rgba(0, 245, 255, 0.045);
    font-style: italic;
}

.story-reader[data-paper='parchment'] .story-prose .odt-textbox-p {
    background: rgba(120, 96, 60, 0.10);
    border-left-color: rgba(120, 96, 60, 0.6);
}

/* Tables scroll inside themselves rather than making the page scroll. */
.story-prose .odt-table-wrap {
    margin: 1.5em 0;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.story-prose .odt-table {
    border-collapse: collapse;
    width: 100%;
    font-size: 0.92em;
}

.story-prose .odt-cell {
    border: 1px solid var(--sheet-rule);
    padding: 0.45em 0.65em;
    vertical-align: top;
    text-align: left;
}

.story-prose .odt-table thead .odt-cell {
    background: rgba(0, 245, 255, 0.06);
    font-weight: 600;
}

.story-prose .odt-cell .odt-p { margin-bottom: 0.35em; }
.story-prose .odt-cell .odt-p:last-child { margin-bottom: 0; }

/* --- Footnotes ------------------------------------------------------------ */

.story-prose .odt-noteref {
    font-size: 0.7em;
    line-height: 0;
    vertical-align: super;
}

.story-prose .odt-noteref a {
    color: var(--story-cyan);
    text-decoration: none;
    padding: 0 0.15em;
}

.page-footnotes {
    max-width: var(--sheet-measure);
    margin: 2rem auto 0;
    padding-top: 0.9rem;
    border-top: 1px solid var(--sheet-rule);
    font-size: calc(var(--sheet-size) * 0.82);
    line-height: 1.6;
    color: var(--sheet-ink-dim);
}

.page-footnotes .odt-footnote-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.page-footnotes .odt-footnote {
    display: flex;
    gap: 0.6rem;
    margin-bottom: 0.5rem;
}

.page-footnotes .odt-footnote-back {
    flex-shrink: 0;
    color: var(--story-cyan);
    text-decoration: none;
    font-family: 'Share Tech Mono', monospace;
}

.page-footnotes .odt-footnote-body .odt-p { margin-bottom: 0.35em; }

/* ==========================================================================
   4. Comments inside the reader
   ========================================================================== */

.reader-comments-body {
    min-height: 4rem;
}

.reader-comments-loading {
    padding: 1.5rem 0;
    text-align: center;
    color: var(--story-dim);
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.75rem;
}

/* The comment partials are shared with the music section, which styles itself
   inline; only the drawer's own wrapper needs anything here. */
.reader-drawer .music-comment:first-of-type {
    border-top: none;
}

/* ---- Shelf links ----
   The shelf name on a story card and on a story page both filter the library.
   They used to be plain text, which is why nobody clicked them: nothing said
   they were anything but a label.

   The card one keeps the muted meta colour and gets a dotted underline — enough
   to read as a link inside a line that is otherwise not one, without turning a
   quiet meta row into a bright blue streak. */
.story-card-shelf {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px dotted currentColor;
    transition: color 0.15s ease, border-color 0.15s ease;
}

.story-card-shelf:hover,
.story-card-shelf:focus-visible {
    color: var(--story-cyan);
    border-bottom-color: var(--story-cyan);
}

/* The story page renders its shelf as a badge. As a link it must keep the badge
   shape exactly — the pill already reads as interactive enough; all it needs is
   the underline suppressed and something to happen on hover. */
a.story-badge {
    text-decoration: none;
    transition: background 0.15s ease;
}

a.story-badge:hover,
a.story-badge:focus-visible {
    background: rgba(0, 245, 255, 0.14);
}
