/* ══════════════════════════════════════════════════════════════════════════════
   PPEA — Intro (Front-Matter) Page Stylesheet
   ══════════════════════════════════════════════════════════════════════════════
   Loaded on intro (front-matter) pages only — those identified by the presence
   of <nav class="contents"> (i.e. doc-type="editorial").  Loaded conditionally
   via textnav:page-assets() so it never burdens text or other page types.

   Provides:
     §19   Intro sidebar (nav.contents) — L-shaped drawer + glass tab
     §19b  Desktop sidebar (≥1200px) — always-visible sticky sidebar
     §19a  Intro-page prose — reading width cap, justified text, gutters
   ══════════════════════════════════════════════════════════════════════════════ */


/* ── §19  Intro sidebar (nav.contents) ────────────────────────────────────── *
 * Drawer + tab rendered as a SINGLE L-shaped element via clip-path.
 * One element → one backdrop-filter pass → one bg layer → no seam
 * between drawer pane and tab.  The tab protrusion (26px) sticks out
 * on the right, centered on --tab-top (written by intro.js).
 *
 * The outline follows the L via a drop-shadow filter on the ::before
 * pseudo-element (which carries the glass bg).  This avoids filter
 * artefacts on child text — the nav itself has no filter.           */
nav.contents {
    --pane-w: 300px;
    --tab-w: 26px;
    /* 100dvh tracks the visual viewport as iOS Safari's URL bar
       expands/contracts, so the drawer matches whatever page area
       Safari has allotted us.  (We previously tried 100lvh to extend
       behind iOS chrome strips, but on devices where viewport-fit=cover
       isn't activating there's no chrome region to paint into and
       100lvh just overshoots the bottom.)  Visual-viewport alignment
       for the sticky scroll-fade is handled on .contents-scroll below. */
    top: 0;
    height: 100dvh;
    text-indent: 0.5ex hanging;
    display: block;
    position: fixed;
    left: calc(-1 * var(--pane-w));
    width: calc(var(--pane-w) + var(--tab-w));
    overflow: clip;
    /* L-shaped clip via CSS clip-path: path().  JS (intro.js)
       writes the path string with SVG arc commands for rounded tab
       corners on init, drag, and resize.  Falls back to the full
       rectangle if JS hasn't run yet. */
    /* clip-path set by JS — see intro.js updateClipPath() */
    /* Glass bg + blur live directly on nav so backdrop-filter
       composites correctly against the page behind. */
    background-color: var(--color-bg-glass-mid);
    /* Reduced blur radius (was var(--glass-blur-heavy) = blur(5px)) to
       ease iOS Safari per-frame backdrop re-sampling cost — the heavier
       blur caused a shift-and-snap lag between the fixed tab and its
       glass background while scrolling the page behind. */
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
    transition: left var(--transition-slow), visibility var(--transition-slow);
    z-index: 30;   /* nav.contents sidebar tier — above toolbar (20) */
}
/* On narrow viewports, cap the pane at 75vw so the tab never
   reaches the screen edge and some page content stays visible. */
@media (max-width: 899px) {
    nav.contents {
        --pane-w: min(300px, 75vw);
    }
}

/* L-shape outline: two pseudo-elements trace the content-facing
   edges (panel right edge + tab protrusion).  Top/left/bottom are
   flush with the viewport and need no outline.  No background or
   backdrop-filter on either pseudo → no double-glass compositing.
   Both read --tab-top (set by JS for draggable repositioning). */

/* Panel right edge: 1px vertical line with a gap for the tab. */
nav.contents::before {
    content: '';
    position: absolute;
    top: 0; bottom: 0;
    left: calc(var(--pane-w) - 1px);
    width: 1px;
    background: var(--color-border);
    clip-path: polygon(
        0 0, 1px 0,
        1px calc(var(--tab-top, 50%) - 41px),
        0   calc(var(--tab-top, 50%) - 41px),
        0   calc(var(--tab-top, 50%) + 41px),
        1px calc(var(--tab-top, 50%) + 41px),
        1px 100%,
        0 100%
    );
    pointer-events: none;
}

/* Tab protrusion outline: bordered box with rounded right corners.
   border-left: none so it joins flush with the panel right edge. */
nav.contents::after {
    content: '';
    position: absolute;
    top: calc(var(--tab-top, 50%) - 41px);
    left: calc(var(--pane-w) - 1px);
    width: calc(var(--tab-w) + 1px);
    height: 82px;
    border: 1px solid var(--color-border);
    border-left: none;
    border-radius: 0 6px 6px 0;
    box-sizing: border-box;
    pointer-events: none;
}

/* Scrollable content wrapper: isolates overflow from the nav so the
   tab button and outline pseudos (positioned on the nav) stay fixed
   while the TOC list scrolls.  Height fills the nav; vertical padding
   provides breathing room at top and bottom. */
.contents-scroll {
    max-width: var(--pane-w);
    /* Height is set by intro.js from window.visualViewport.height
       so the scroll area matches the *visible* window between the
       status bar/notch on top and the iOS address bar on the bottom.
       Sticky-bottom children (e.g. .scroll-fade) then pin to the
       actual visible bottom as the address bar slides, while the
       nav's own full-screen backdrop (top:0/bottom:0) still covers
       the entire device.

       iOS Safari's 100dvh is specced to do this but updates only at
       end-of-transition in practice — visualViewport.scroll/resize
       events fire continuously during the slide. */
    height: var(--visual-vh, 100dvh);
    box-sizing: border-box;
    /* +env() for iOS safe areas so TOC entries don't render behind
       the notch or home indicator when viewport-fit=cover extends the
       panel's backdrop into those zones.  env() resolves to 0 when
       viewport-fit isn't active. */
    padding-top:    calc(50px + env(safe-area-inset-top));
    padding-bottom: calc(50px + env(safe-area-inset-bottom));
    overflow-y: auto;
}

/* Scroll-fade hint: gradient at the bottom of the scroll area that
   signals "more TOC below".  Mirrors the comparison panel's
   .scroll-fade (edition_compare.css §3).  Injected into .contents-scroll
   by intro.js; class .at-bottom added when scrolled to the end
   (or when the list is short enough that it all fits). */
/* Base scroll-fade styles in main.css §13b; set token to container bg. */
.contents-scroll {
    --scroll-fade-bg: var(--color-bg-glass-solid);
}

nav.contents > .contents-scroll > ul {
    /* Constrain TOC text to the drawer pane so long entries
       (e.g. Hm I.7.2) don't bleed into the tab protrusion. */
    max-width: var(--pane-w);
    box-sizing: border-box;
}

nav.contents ul {
    padding-left: 1em;
    list-style: none;
}

nav.contents ul li { position: relative; }

nav.contents a { text-decoration: none; }

nav.contents a:hover { text-decoration: underline; }

/* Scroll-spy current entry (set by intro.js). */
nav.contents a.current {
    font-weight: 600;
    color: var(--color-accent, #0066cc);
}

nav.contents ul li.end-contents-preface { margin-bottom: 1.5ex; }

nav.contents input[type='checkbox'].contents-expand-box {
    position: absolute;
    top: 0;
    left: -2ex;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    height: 1.5ex;
    width: 1.5ex;
    font-size: initial;
}

nav.contents input[type='checkbox'].contents-expand-box:not(:checked) ~ ul {
    display: none;
}

input.contents-expand-box::after {
    width: 0;
    height: 0;
    display: inline-block;
    content: "";
    margin-left: 0.25ex;
    margin-top: 0.25ex;
}

input.contents-expand-box:not(:checked)::after {
    border-top: 0.5ex solid transparent;
    border-left: 1ex solid var(--color-border-strong);
    border-bottom: 0.5ex solid transparent;
}

input.contents-expand-box:checked::after {
    border-left: 0.5ex solid transparent;
    border-right: 0.5ex solid transparent;
    border-top: 1ex solid var(--color-border-strong);
}

/* Edition navigation footer: links to other front-matter items,
   text passus, and supplements.  Sits below the document TOC. */
.intro-nav-footer {
    margin-top: 1.5em;
    padding-top: 1em;
    border-top: 1px solid var(--color-border);
    max-width: var(--pane-w);
    box-sizing: border-box;
}
.intro-nav-footer h4 {
    margin: 0.8em 0 0.3em;
    font-size: 0.82em;
    font-weight: 600;
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}
.intro-nav-footer h4:first-child { margin-top: 0; }
.intro-nav-footer ul {
    padding-left: 1em;
    list-style: none;
    font-size: 0.9em;
}
.intro-nav-footer a { text-decoration: none; }
.intro-nav-footer a:hover { text-decoration: underline; }

/* Tab label: positioned in the protrusion zone of the L-shape.
   Carries its own glass bg with rounded outer corners so the tab
   reads as a softened protrusion from the rectangular drawer. */
#contents-button {
    position: absolute;
    z-index: 1;
    /* Centered on --tab-top by subtracting half the tab height (41px),
       matching the ::before/::after pseudo-element math above.  Using
       calc() instead of a transform keeps the button from being
       promoted to its own iOS compositor layer — previously the
       button layer updated ahead of the nav's backdrop-filter layer
       during scroll, causing a visible shift-and-snap. */
    top: calc(var(--tab-top, 50%) - 41px);
    right: 0;
    writing-mode: vertical-rl;
    width: var(--tab-w);
    padding: 0.9em 0;
    margin: 0;
    /* No bg — nav's own glass bg covers the tab protrusion via the
       CSS clip-path (which provides rounded corners via arc commands).
       Adding a second bg here would double-composite in the tab zone,
       making it visibly more opaque than the drawer pane. */
    background: transparent;
    border: none;
    color: var(--color-text);
    font-family: var(--font-ui);
    font-size: 0.78em;
    letter-spacing: 0.04em;
    cursor: grab;
    touch-action: none;
    user-select: none;
    text-align: center;
}
#contents-button.dragging {
    cursor: grabbing;
}

nav.contents.active {
    left: 0;
    visibility: visible;
}


/* ── §19b  Desktop sidebar (≥1200px) ──────────────────────────────────────────
   At widths where the 42em centered reading column (see §19a) leaves
   enough left margin to host the TOC without overlapping content,
   nav.contents becomes an always-visible sticky sidebar instead of a
   sliding drawer.  Threshold: 42em ≈ 672px reading column needs at
   least a 260px sidebar + some breathing room → ≥1200px.
   Below this breakpoint the drawer (§19) continues to apply. */
@media (min-width: 1200px) {
    nav.contents {
        /* Override drawer: always visible, no slide.  Revert to a
           plain rectangle — no clip-path, no L protrusion.  Base
           rule uses height:100dvh anchored at top:0; here we inset
           by the fixed header (50px) and subtract that from height. */
        top: 50px;
        height: calc(100dvh - 50px);
        left: 0;
        width: clamp(200px, 18vw, 260px);
        /* !important overrides the inline clip-path set by JS
           (intro.js updateClipPath), which otherwise wins over
           this media-query rule. */
        clip-path: none !important;
        background: var(--color-bg-alt);
        border-right: 1px solid var(--color-border);
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
        transition: none;
        overflow: auto;
        padding: 1em 1em 2em;
        box-sizing: border-box;
    }

    /* L-shape outline pseudos not needed on desktop. */
    nav.contents::before,
    nav.contents::after { display: none; }

    /* Scroll wrapper is only needed for the drawer; on desktop the
       nav itself scrolls and the wrapper should be transparent. */
    .contents-scroll {
        height: auto;
        max-width: none;
        padding: 0;
        overflow-y: visible;
    }

    /* Desktop sidebar has a solid background — fade into that. */
    nav.contents {
        --scroll-fade-color: var(--color-bg-alt);
    }

    /* Toggle button is redundant when sidebar is always visible. */
    #contents-button {
        display: none;
    }
}


/* ── §19a  Intro-page prose ──────────────────────────────────────────────────
   Intro (front-matter) pages are identified structurally by the presence of
   nav.contents.  Text pages use the passus grid + toolbar and never carry
   nav.contents, so these rules don't leak into them. */
body:has(nav.contents) main {
    line-height: 1.6;
}

/* Cap reading width at ~84 CPL for scholarly prose — wider than the
   passus grid's 32em text column to accommodate inline folio thumbs,
   reference-heavy lines, and the density typical of front-matter
   palaeographic descriptions.  margin-inline: auto centers within main.
   The existing 68rem cap from §4a (article.passage-container) stays in place
   for text-page grid compatibility; on intro pages this narrower cap wins. */
body:has(nav.contents) article.passage-container {
    max-width: 42em;
    margin-inline: auto;
}

/* More horizontal breathing room on phone/tablet intro pages.  The grid-based
   text pages keep the tight gutter so the passus grid can hit the edge.
   Applied to article.passage-container (not main) to beat the Stage D (< 500px)
   rule in §27 that zeros article.passage-container's horizontal padding for
   the passus grid's negative-margin trick — a text-page concern that
   accidentally caught intro pages (which reuse the passage-container class). */
@media (max-width: 899px) /* < --bp-desktop */ {
    body:has(nav.contents) article.passage-container {
        /* Symmetric gutters sized to just-clear the ~26px #contents-button
           edge tab (tab width ≈ 1.6rem + ~1px of breathing gap ≈ 1.75rem).
           The tab lives in the left gutter; the right side matches so
           the reading column stays centered and visually balanced. */
        padding-left: 1.75rem;
        padding-right: 1.75rem;
    }
}

/* Justified prose for intro pages.  WCAG 1.4.8 (no-justify) is AAA —
   our AA target is unaffected — and `hyphens: auto` further mitigates
   the "rivers of whitespace" problem by allowing the browser to break
   long words.  `text-align-last: left` keeps the final line of each
   paragraph ragged rather than stretched. */
body:has(nav.contents) article.passage-container {
    text-align: justify;
    hyphens: auto;
    text-align-last: left;
}

/* Backstop: any image inside the intro article is capped to the reading
   column width.  edition_images.css (which has .folio-thumb { max-width:
   2.5em }) is not loaded on editorial pages, so inline thumbnails also
   rely on this rule to stay in-flow. */
body:has(nav.contents) article.passage-container img {
    max-width: 100%;
    height: auto;
}

/* Block figures (figure.ed-figure) — emitted by intro.xsl for <figure>
   elements that appear at division level rather than inside a <p>.
   Centred, with breathing room above and below.  The <a> wrapper removes
   its default link decoration so the image stands alone. */
figure.ed-figure {
    margin-block: 1.5em;
    text-align: center;
}
figure.ed-figure a {
    display: inline-block;
    text-decoration: none;
}

/* Dark-mode inversion for line-art block figures.  Targeted by data-entity
   so only specific known line-art images are inverted — colour photographs
   or other non-line-art block figures should not be. */
@media (prefers-color-scheme: dark) {
    figure.ed-figure[data-entity="BxStemma"] img {
        filter: invert(1);
    }
}
