:root {
  --bg: Canvas;
  --bg-raised: Canvas;
  --fg: CanvasText;
  --border: rgba(127,127,127,0.25);
  --surface: rgba(127,127,127,0.06);
  --muted: rgba(127,127,127,0.85);
  --accent: #1166ee;
  --accent-fg: white;
  --danger: #c33;
  --ok: #2ea043;
  --warn: #d29922;
  --radius: 6px;
}
/* Theme picker. The color-scheme property drives both the browser's
   default body background/text and the Canvas/CanvasText system
   keywords used by popovers below, so most of the dark/light split
   falls out of the keyword alone. The hljs palette near the bottom
   of the stylesheet is the one place that needs explicit overrides
   keyed on the active theme. */
html.theme-auto  { color-scheme: light dark; }
html.theme-light { color-scheme: light; }
html.theme-dark  { color-scheme: dark; }
/* Warm graphite dark palette: paper-in-the-dark background, warmer
   off-white text, and a muted lifted to a SOLID grey -- the alpha
   --muted that works on white turns illegible on a dark ground (the
   read-row problem). Applied for the explicit dark theme and for
   auto-when-the-OS-is-dark; e-ink overrides later in this sheet win
   on html.eink-mode regardless. Keep both copies identical. */
html.theme-dark:not(.eink-mode) {
  --bg: #201f1d;
  --bg-raised: #262522;
  --fg: #e8e4dd;
  --muted: #a8a39a;
  --border: rgba(168,163,154,0.22);
  --surface: rgba(168,163,154,0.08);
  --accent: #5a93e8;
}
@media (prefers-color-scheme: dark) {
  html.theme-auto:not(.eink-mode) {
  --bg: #201f1d;
  --bg-raised: #262522;
  --fg: #e8e4dd;
  --muted: #a8a39a;
  --border: rgba(168,163,154,0.22);
  --surface: rgba(168,163,154,0.08);
  --accent: #5a93e8;
  }
}
* { box-sizing: border-box; }
body { font-size: var(--font-size, 15px); line-height: 1.5;
       background: var(--bg); color: var(--fg);
       /* The chrome (toolbar, settings popover, related cards) always
          uses the UI sans stack so the serif toggle reads as "article
          font," not "everything font." --font-family applies on
          <article> instead. */
       font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
       max-width: var(--content-max-width, 760px); margin: 2rem auto; padding: 0 1rem; }
/* Sticky so the toolbar (logo, nav, TOC toggle, share, settings,
   avatar) stays reachable while reading a long doc. `Canvas` keeps
   the strip opaque so article content doesn't bleed through when
   it scrolls underneath, and `z-index: 20` clears the popover
   children (z-index: 10) and the article's own stacking. Headings
   below get scroll-margin-top so TOC anchor jumps don't land their
   target underneath the sticky strip. */
header { display: flex; align-items: center; justify-content: space-between;
         margin-bottom: 2rem; gap: 1rem;
         position: sticky; top: 0; z-index: 20;
         background: var(--bg); padding: 0.5rem 0;
         border-bottom: 1px solid var(--border); }
header h1 { margin: 0; font-size: 1.25rem; letter-spacing: -0.01em; }
header h1 a { color: inherit; text-decoration: none; }
header nav { font-size: 0.85rem; color: var(--muted); display: flex; gap: 1rem; align-items: center; }
/* Nav-level icon link (currently just /docs). Matches the
   padding / hover treatment of the settings cog so the toolbar
   icons share a single visual rhythm. */
header nav a.nav-icon-link {
  display: inline-flex; align-items: center; padding: 4px;
  border-radius: 4px; line-height: 0; color: var(--muted);
}
header nav a.nav-icon-link:hover { background: var(--surface); color: var(--accent); }
header nav a { color: inherit; text-decoration: none; }
header nav a:hover { color: var(--accent); }
/* Nav-level buttons (currently `+ New doc`) match the avatar's 28px
   height so the header line stays visually balanced. The default
   .btn padding builds ~39px-tall buttons, which crowd everything
   else in the header. */
header nav .btn { height: 28px; padding: 0 0.75rem;
                  display: inline-flex; align-items: center; }
details.settings { position: relative; flex-shrink: 0;
                   display: inline-flex; align-items: center; }
details.settings > summary { list-style: none; cursor: pointer;
                             padding: 4px; color: var(--muted);
                             border-radius: 4px; line-height: 0;
                             user-select: none; }
details.settings > summary::-webkit-details-marker { display: none; }
details.settings > summary::marker { content: ""; }
details.settings > summary:hover,
details.settings[open] > summary { background: var(--surface); color: inherit; }
.settings-icon, .nav-icon { display: block; width: 18px; height: 18px; }
.settings-panel { position: absolute; right: 0; top: calc(100% + 4px);
                  background: var(--bg-raised); border: 1px solid var(--border);
                  border-radius: 8px; padding: 0.6rem 0.75rem; z-index: 10;
                  min-width: 200px;
                  display: flex; flex-direction: column; gap: 0.5rem;
                  box-shadow: 0 4px 16px rgba(0,0,0,0.18); }
.settings-panel label { display: grid; grid-template-columns: 1fr 6.5rem;
                        align-items: center; gap: 0.75rem; font-size: 0.85rem;
                        color: var(--muted); }
.settings-panel select {
  width: 100%;
  font: inherit; font-size: 0.85rem; color: inherit;
  background: transparent; border: 1px solid var(--border);
  border-radius: var(--radius); padding: 0.15rem 0.4rem; cursor: pointer;
}
.settings-panel input.switch {
  -webkit-appearance: none; appearance: none;
  width: 32px; height: 18px; margin: 0;
  background: var(--border); border: none; border-radius: 999px;
  position: relative; cursor: pointer; justify-self: start;
  transition: background 0.15s;
}
.settings-panel input.switch::before {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--bg); transition: transform 0.15s;
}
.settings-panel input.switch:checked { background: var(--accent); }
.settings-panel input.switch:checked::before { transform: translateX(14px); }
/* User menu: avatar acts as the popover trigger; opening it
   anchors a panel of account links below. Same chrome as the
   gear settings popover, just with vertical link items. */
details.user-menu { position: relative; flex-shrink: 0;
                    display: inline-flex; align-items: center; }
details.user-menu > summary {
  list-style: none; cursor: pointer; user-select: none;
  padding: 0; line-height: 0; border-radius: 50%;
  box-shadow: 0 0 0 1px var(--border);
  transition: box-shadow 0.12s;
}
details.user-menu > summary::-webkit-details-marker { display: none; }
details.user-menu > summary::marker { content: ""; }
details.user-menu > summary:hover,
details.user-menu[open] > summary { box-shadow: 0 0 0 2px var(--accent); }
.avatar { display: block; width: 28px; height: 28px;
          border-radius: 50%; object-fit: cover; }
.avatar-fallback { background: var(--surface); color: var(--muted);
                   font-weight: 600; font-size: 13px; line-height: 28px;
                   text-align: center; user-select: none; }
/* Generic popover panel used by any <details>-anchored header menu
   (user menu, share menu). The summary styling is per-trigger;
   the dropdown chrome is shared. */
.popover-menu { position: absolute; right: 0; top: calc(100% + 6px);
                background: var(--bg-raised); border: 1px solid var(--border);
                border-radius: 8px; padding: 0.25rem; z-index: 10;
                min-width: 200px;
                box-shadow: 0 4px 16px rgba(0,0,0,0.18); }
.popover-menu a,
.popover-menu button { display: block; width: 100%; text-align: left;
                       background: none; border: none; padding: 0.5rem 0.75rem;
                       font: inherit; color: inherit; cursor: pointer;
                       border-radius: 4px; text-decoration: none; }
.popover-menu a:hover,
.popover-menu button:hover { background: var(--surface); }
.popover-menu form { margin: 0; }
.user-menu-header { padding: 0.5rem 0.75rem 0.4rem;
                    border-bottom: 1px solid var(--border);
                    margin-bottom: 0.25rem; line-height: 1.25;
                    overflow: hidden; text-overflow: ellipsis; }
.btn { display: inline-block; padding: 0.5rem 1rem; font: inherit;
       border: 1px solid var(--border); border-radius: var(--radius);
       background: transparent; color: inherit; text-decoration: none; cursor: pointer; }
.btn:hover { background: var(--surface); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-primary { background: var(--accent); color: var(--accent-fg); border-color: var(--accent); }
.btn-primary:hover { background: #0d56cc; }
.btn-small { padding: 0.25rem 0.55rem; font-size: 0.85rem; }
article { line-height: 1.65;
  /* The serif/sans toggle in Settings writes --font-family on <html>;
     reading it here scopes the toggle to article prose. Default is the
     sans stack so first paint matches the chrome. The .related cards
     nested inside article are UI navigation, not prose, and override
     back to the chrome sans stack just below. */
  font-family: var(--font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif);
  /* Break long unbreakable runs (URLs, hashes, base64 blobs, stack
     traces) only when they'd otherwise push the line past the
     article width. `break-word` (not `anywhere`) is the right
     lever: both wrap long words at render time, but `anywhere`
     also counts every character as a min-content break, which
     collapses table columns to a single character per line and
     defeats `.table-scroll`. */
  overflow-wrap: break-word;
  /* Book typography in continuous mode too, matching paginated mode
     (paginator.css carries the same rules scoped to its page boxes).
     Hyphenation keeps narrow viewports from opening rivers between
     words; it needs the document's lang attribute, which both apps
     set. Headings, code, tables, and the related-cards trailer are
     exempted below, mirroring the paginated exemptions. */
  text-align: justify;
  -webkit-hyphens: auto;
  hyphens: auto;
  /* Explicit ligature + kerning opt-in (mostly default-on, but some
     UA stylesheets disable ligatures at small sizes), and hanging
     punctuation where supported (Safari) for cleaner justified edges. */
  font-variant-ligatures: common-ligatures;
  font-kerning: normal;
  hanging-punctuation: first last; }
article h1, article h2, article h3,
article h4, article h5, article h6 {
  line-height: 1.3;
  text-align: start;
  -webkit-hyphens: none;
  hyphens: none;
}
/* Compact heading scale with a quiet h3: browser defaults put an h1
   at 2em, which is out of proportion at an 18px/66ch measure. h3-h6
   sit at body size, bold only. */
article h1 { font-size: 1.6em;  letter-spacing: -0.01em; }
article h2 { font-size: 1.25em; letter-spacing: -0.005em; }
article h3, article h4, article h5, article h6 { font-size: 1em; }
/* Related-articles trailer: UI cards, not prose. Pin to the chrome
   sans stack so the serif toggle reads as "article font" only, and
   keep the cards ragged-right. */
article > section.related {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  text-align: start;
  -webkit-hyphens: none;
  hyphens: none;
}
/* Scale wide images down to the article width instead of letting
   them force horizontal page scroll. `height: auto` preserves the
   aspect ratio; max-width doesn't upscale smaller images. */
article img { max-width: 100%; height: auto; }
/* Figures center, book-style: image paragraphs (the common newsletter
   shape -- an <img> alone in a <p>) center their inline content along
   with any caption text; <figure> elements and bare block images
   center as boxes. */
article p:has(> img) { text-align: center; }
article figure { margin-inline: auto; text-align: center; }
article > img { display: block; margin-inline: auto; }
article pre { position: relative;
  background: var(--surface); padding: 0.85rem; border-radius: 8px; overflow-x: auto; }
/* Inline code: more visible than the body --surface tint so the
   chips read as distinct tokens, GitHub-style. `--surface` alone
   was barely perceptible in dark mode. Padding + radius bump up
   to match the visual weight of the new background. */
/* Embedded code face (fonts.css); ligatures ship enabled in Fira's
   default calt. Falls back to the platform mono stack. */
article pre, article code {
  font-family: "Fira Code", ui-monospace, SFMono-Regular, Menlo, monospace;
  -webkit-hyphens: none;
  hyphens: none; }
article code { background: rgba(127,127,127,0.18); padding: 0.15em 0.4em; border-radius: var(--radius); font-size: 0.9em; }
article pre code { background: none; padding: 0; border-radius: 0; }
article h1, article h2, article h3, article h4, article h5, article h6 {
  /* Clears the sticky header height so TOC and heading-anchor
     jumps land their target below the toolbar, not under it. */
  scroll-margin-top: 4rem;
}
/* GFM task lists: drop the list marker so the checkbox sits where
   the bullet would and flows inline with the first text run via a
   negative left margin. Leave the checkbox as the platform-native
   control -- the page's `color-scheme: light dark` already steers
   the UA stylesheet into rendering the box appropriately for the
   current theme, and `accent-color` colors the checked state with
   the brand accent. The `ul` selector covers nested lists whose
   siblings are plain bullet items -- only items that carry
   `.task-list-item` lose the marker. */
article ul:has(> li.task-list-item) { list-style: none; padding-left: 1.5em; }
article li.task-list-item > input[type="checkbox"] {
  /* GitHub's recipe for putting the checkbox where a bullet would
     sit. `vertical-align: middle` aligns the *margin box* center
     with the parent's x-height line; the 0.25em bottom margin makes
     the margin box taller below the checkbox, which shifts the
     visible box upward by ~0.125em -- the difference between
     x-height center (where `middle` would put it) and cap-height
     center (where `::marker` bullets render). Pattern cited in
     facelessuser/pymdown-extensions and the github-markdown-css
     repo (issue #10). */
  margin: 0 0.4em 0.25em -1.4em;
  vertical-align: middle;
  accent-color: var(--accent);
}
/* Tables are data, not prose: justification opens gaps inside narrow
   cells and hyphenation perturbs intrinsic column widths. */
article table { border-collapse: collapse; width: 100%; margin: 1rem 0;
  text-align: start; -webkit-hyphens: none; hyphens: none; }
article table th, article table td {
  padding: 0.4rem 0.6rem; border-bottom: 1px solid var(--border);
  text-align: left; vertical-align: top;
}
article table th { font-weight: 600; }
a.heading-anchor, a.section-copy {
  margin-left: 0.4em;
  color: var(--muted);
  text-decoration: none;
  font-weight: normal;
  font-size: 0.85em;
  opacity: 0;
  transition: opacity 0.1s ease-out;
}
a.heading-anchor:hover, a.heading-anchor:focus,
a.section-copy:hover, a.section-copy:focus { color: var(--accent); }
article h1:hover a.heading-anchor,
article h2:hover a.heading-anchor,
article h3:hover a.heading-anchor,
article h4:hover a.heading-anchor,
article h5:hover a.heading-anchor,
article h6:hover a.heading-anchor,
article h1:hover a.section-copy,
article h2:hover a.section-copy,
article h3:hover a.section-copy,
article h4:hover a.section-copy,
article h5:hover a.section-copy,
article h6:hover a.section-copy,
a.heading-anchor:focus, a.section-copy:focus { opacity: 1; }
/* Paginated reading mode. The body becomes a fixed-height flex
   column (header + paginated article); the article itself flows
   into viewport-sized CSS columns and scrolls horizontally one
   page at a time. Slides docs already paginate themselves, so the
   `.slides-mode` body opt-out keeps those alone. The TOC sidebar
   and similar-docs footer are hidden because they don't translate
   to the page-turn UX; the popover TOC stays available in the
   header. */
/* Paginated reading mode -- measure-and-split layout. The article
   becomes a horizontal flex row of `<section class="ebook-page">`
   wrappers, each pinned to a viewport-sized box; paginated.js
   walks the article's children, distributes them into pages by
   measuring overflow, and starts a new page on every H1. We tried
   CSS multi-column first (the technique foliate-js / epub.js use)
   but `break-before: column` doesn't fire reliably in Firefox with
   `column-fill: auto` -- DevTools shows the rule applied but the
   browser ignores it. Wrapping into explicit pages sidesteps the
   whole break-control machinery.
   `:has(> article)` keeps non-doc pages alone. */
html.reading-paginated body:not(.slides-mode):has(> article) {
  max-width: none;
  margin: 0;
  padding: 0;
  /* 100dvh = current visual viewport height, accounting for the
     URL bar whenever it's visible. This keeps the article (and
     therefore each ebook-page) sized to what the user can
     actually see; content never gets clipped behind iOS Safari's
     URL bar. iOS won't auto-hide the bar without a body scroll,
     and the JS workarounds for forcing the hide are unreliable
     on modern Safari, so we accept the bar's presence and let
     dvh handle the layout.
     `display: grid; grid-template-rows: auto 1fr` is the explicit
     "header takes its natural height, article fills the rest"
     layout. We tried flex-column first; pairing it with a sticky
     header inside an overflow:hidden parent left iOS Safari
     mis-positioning the article so the doc's H1 rendered behind
     the header. Grid resolves the row sizes deterministically
     before sticky positioning kicks in, so the article always
     starts below the header.*/
  height: 100dvh;
  overflow: hidden;
  display: grid;
  /* The header is a fixed overlay in paginated mode (below), so the
     article is the only in-flow child and owns the whole viewport.
     Pages lay out for the full height regardless of chrome state --
     toggling the chrome never re-paginates. */
  grid-template-rows: 1fr;
}
html.reading-paginated body:not(.slides-mode):has(> article) > header {
  margin-bottom: 0;
  padding: 0.5rem 1rem;
  /* Overlay, not a layout row: the toolbar floats over the page's top
     margin when revealed and slides away when hidden. The base header
     rule's z-index: 20 + opaque background carry over, so the running
     head (z-index: 5) disappears behind it while it's shown. NOTE: no
     transform when visible -- a transformed header becomes the
     containing block for the TOC sheet's position: fixed and would
     drag the sheet along. */
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
}
html.reading-paginated body:not(.slides-mode):has(> article) aside.toc-sidebar,
html.reading-paginated body:not(.slides-mode):has(> article) .similar-docs {
  display: none;
}
/* The sidebar TOC is hidden in paginated mode (above), but each app's
   sidebar gating also hides the header popover whenever the sidebar
   fits (html.toc-mode-sidebar.toc-sidebar-fits details.toc-toggle) --
   a sidebar-preference user would lose TOC access entirely. Bring the
   popover back; same shape as the slides-mode override in mds's
   layout.css. */
html.reading-paginated.toc-mode-sidebar.toc-sidebar-fits body:not(.slides-mode):has(> article) details.toc-toggle {
  display: inline-flex;
}
/* In paginated mode the open TOC is a contents page, not a popover: a
   fixed sheet that swaps in for the article, e-reader style. The sheet
   lives inside the header's stacking context (header is the sticky
   z-index: 20 strip with an opaque background), so z-index: -1 paints
   it under the toolbar -- the toggle stays visible and clickable as
   the close affordance -- and above everything outside the header.
   The padding-top clears the strip; scrolled content slides under it
   like normal page flow. settings.js closes the details when a TOC
   link is picked. */
html.reading-paginated body:not(.slides-mode):has(> article) details.toc-toggle .toc-popover {
  position: fixed;
  inset: 0;
  z-index: -1;
  /* The popover is a nav inside the header, so `header nav`'s flex +
     align-items: center applies and would vertically center the list
     in the sheet. */
  display: block;
  border: none;
  border-radius: 0;
  box-shadow: none;
  background: var(--bg);
  min-width: 0;
  max-width: none;
  padding: 4rem 1.5rem 2rem;
  overflow-y: auto;
  font-size: 1rem;
}
html.reading-paginated body:not(.slides-mode):has(> article) details.toc-toggle .toc-popover > ul {
  max-height: none;
  overflow: visible;
  max-width: 36rem;
  margin: 0 auto;
}
/* Contents-page touch targets: roomier than the popover's. */
html.reading-paginated body:not(.slides-mode):has(> article) details.toc-toggle .toc-popover li {
  padding-top: 0.3rem;
  padding-bottom: 0.3rem;
}
/* The page indicator (z-index: 30) would float over the contents
   page; it describes the article underneath, so hide it while the
   sheet is open. */
html.reading-paginated body:not(.slides-mode):has(details.toc-toggle[open]) .ebook-pagina {
  display: none;
}
html.reading-paginated body:not(.slides-mode):has(> article) > article {
  --ebook-margin: max(2rem, 6vw);
  /* Book-like block (top/bottom) margins paired with the inline
     margin above. The previous 1.5rem flat value left too little
     breathing room at the top and bottom of the page; 6vh scales
     with viewport so paperback-ish proportions hold from phone to
     desktop, and the 2rem floor keeps it from collapsing on very
     short viewports. */
  --ebook-margin-block: max(2rem, 6vh);
  /* As the 1fr grid track the article gets the remaining height
     automatically. min-height/min-width: 0 stop the grid item
     from growing past its track when long pages or wide images
     try to push it out. */
  min-height: 0;
  min-width: 0;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: row;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  /* `scroll-behavior: auto` so tap-to-turn lands instantly. The
     animation looked ok with arrow keys but felt sluggish on a
     phone where the user expects the page to snap on touch. */
  scroll-behavior: auto;
  scroll-snap-type: x mandatory;
  /* Suppress iOS Safari's grey flash on tap. */
  -webkit-tap-highlight-color: transparent;
}
html.reading-paginated body:not(.slides-mode):has(> article) > article::-webkit-scrollbar {
  display: none;
}
html.reading-paginated article > section.ebook-page {
  flex: 0 0 100vw;
  height: 100%;
  overflow: hidden;
  padding: var(--ebook-margin-block) var(--ebook-margin);
  scroll-snap-align: start;
  /* Inline-margin separation between pages is the padding above;
     no margin-left because flex-row + scroll-snap puts the boundary
     at the page edge. */
}
/* The inner content div carries the user's content-width setting
   (Narrow / Normal / Wide / Full from the Settings panel). Each
   page stays full-viewport-width for scroll-snap, but the readable
   text column inside obeys --content-max-width like in continuous
   mode. width:100% means the column shrinks to fit narrow phones
   where the viewport is already below the chosen max. */
html.reading-paginated article > section.ebook-page > .ebook-page-content {
  width: 100%;
  max-width: var(--content-max-width, 760px);
  margin: 0 auto;
}
/* In continuous mode both wrappers exist in the DOM (paginated.js
   leaves them alone across mode toggles to avoid rebuild churn)
   but `display: contents` makes them transparent to layout:
   children act as direct children of article. */
html:not(.reading-paginated) article > section.ebook-page,
html:not(.reading-paginated) article > section.ebook-page > .ebook-page-content {
  display: contents;
}
/* Page-position indicator overlay -- injected by paginated.js
   only when paginated mode is active. The CSS just locks the
   default-hidden state so a stray element from a previous mode
   doesn't show through. */
.ebook-pagina {
  position: fixed;
  bottom: 1rem; right: 1rem;
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  background: var(--surface);
  padding: 0.25rem 0.6rem;
  border-radius: 4px;
  border: 1px solid var(--border);
  z-index: 30;
  pointer-events: none;
  user-select: none;
}
html:not(.reading-paginated) .ebook-pagina { display: none; }

/* === Chromeless reading ===================================================
   settings.js toggles body.chrome-hidden on reading pages: paginated mode
   hides the chrome on a page turn and toggles it on a center-third tap
   (the paginator's onCenterTap dispatches mds:chrome-toggle); continuous
   mode hides on scroll down and reveals on scroll up or at the top.
   Escape always reveals. The header slides rather than display-toggling
   so the reveal reads as the e-reader gesture it is. */
body:has(> article) > header { transition: transform 0.25s ease; }
body.chrome-hidden:has(> article) > header { transform: translateY(-110%); }

/* Running head: the document title, set quietly in the page's top
   margin, book-style. Rendered by the chrome on reading pages; only
   paginated mode shows it (continuous mode has the sticky header for
   orientation). Sits under the header overlay (z 5 vs 20), so revealing
   the chrome covers it instead of stacking on it. The line-height
   matches --ebook-margin-block to center it in the margin band. */
.running-head { display: none; }
html.reading-paginated body:not(.slides-mode):has(> article) .running-head {
  display: block;
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 5;
  line-height: max(2rem, 6vh);
  text-align: center;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Cap the title at roughly two-thirds of the viewport; long titles
     ellipsize. */
  padding: 0 17vw;
  pointer-events: none;
  user-select: none;
}

/* Folio: restyle the page indicator from a floating pill to a quiet
   centered "3 / 14" in the bottom margin -- part of the page, not UI.
   The line-height centers it in the margin band like the running head. */
html.reading-paginated body:not(.slides-mode):has(> article) .ebook-pagina {
  left: 0; right: 0; bottom: 0;
  background: none;
  border: none;
  border-radius: 0;
  padding: 0;
  text-align: center;
  line-height: max(2rem, 6vh);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
}

/* === Book typesetting (paginated mode) ====================================
   Tighter leading than the web default, and book paragraphs: first-line
   indent with no blank line between paragraphs. Flush exceptions follow
   book convention -- openers after headings/rules, the first paragraph
   of a blockquote, list paragraphs, figure-style image paragraphs, and
   split continuations (mid-paragraph by definition). All the exceptions
   are page-position-independent, so the measurement probe and the
   rendered page always agree. */
html.reading-paginated body:not(.slides-mode):has(> article) > article {
  line-height: 1.5;
}
/* :where keeps the class exclusions out of the specificity so the
   flush exceptions below (plain type selectors) can win. */
html.reading-paginated body:not(.slides-mode):has(> article) article p:where(:not(.meta):not(.article-subtitle)) {
  margin-block: 0;
  text-indent: 1.4em;
}
html.reading-paginated body:not(.slides-mode):has(> article) article :is(h1, h2, h3, h4, h5, h6, hr) + p,
html.reading-paginated body:not(.slides-mode):has(> article) article .meta + p,
html.reading-paginated body:not(.slides-mode):has(> article) article p:has(> img) + p,
html.reading-paginated body:not(.slides-mode):has(> article) article p[data-split-from],
html.reading-paginated body:not(.slides-mode):has(> article) article blockquote > p:first-child,
html.reading-paginated body:not(.slides-mode):has(> article) article li p {
  text-indent: 0;
}
/* Image paragraphs are figures, not prose: no indent, keep their air. */
html.reading-paginated body:not(.slides-mode):has(> article) article p:has(> img) {
  text-indent: 0;
  margin-block: 1em;
}
/* E-ink high-contrast overrides. Two activation paths:
   - `@media (update: slow), (monochrome)`: standardized signal
     for displays that can't repaint fast enough for animation
     (e-ink's defining trait). Vendor adoption is inconsistent
     though -- NeoBrowser on Boox doesn't match.
   - `html.eink-mode`: a class set by the inline pre-paint
     script when the user picks "E-ink" from the Theme picker,
     OR when the media query above matches. The manual override
     is the bypass when vendor detection fails.
   The default palette leans on low-alpha rgba(127,127,127,...)
   values that read as faint gray on LCD; on e-ink those alpha
   blends collapse to barely-distinguishable mid-tones and
   secondary text washes out. Override the variables to solid,
   high-contrast values and force a light color scheme (e-ink
   is reflective like paper; dark mode looks inverted and dim).
   The rules are duplicated under both selectors because CSS
   doesn't support OR between media queries and class selectors
   in one rule. */
html.eink-mode {
  --border: #000;
  --surface: #ededed;
  /* `--muted` was #444 in the first pass; on Boox / Kindle that
     still reads grey-on-grey for secondary text (RECENT /
     WORKSPACES section labels, doc dates). #222 keeps the
     hierarchy distinct from #000 primary text while staying
     inside the e-ink readability band. */
  --muted: #222;
  --accent: #000;
  color-scheme: light;
}
html.eink-mode article code {
  background: #ededed !important;
  color: #000 !important;
}
html.eink-mode select,
html.eink-mode option {
  color: #000;
  background: #fff;
}
html.eink-mode ::placeholder {
  color: #555;
  opacity: 1;
}
html.eink-mode a.heading-anchor,
html.eink-mode a.section-copy {
  opacity: 1;
}
html.eink-mode *,
html.eink-mode *::before,
html.eink-mode *::after {
  transition: none !important;
  animation: none !important;
}
@media (update: slow), (monochrome) {
  :root {
    --border: #000;
    --surface: #ededed;
    --muted: #222;
    --accent: #000;
  }
  html { color-scheme: light; }
  article code {
    background: #ededed !important;
    color: #000 !important;
  }
  select, option {
    color: #000;
    background: #fff;
  }
  ::placeholder {
    color: #555;
    opacity: 1;
  }
  a.heading-anchor, a.section-copy {
    opacity: 1;
  }
  *, *::before, *::after {
    transition: none !important;
    animation: none !important;
  }
}

/* TTS playback cluster. Hidden until tts.js flips body.tts-active;
   while active, the rest of the header nav is hidden so the four
   playback buttons own the toolbar. Shared by mds and the reader. */
.tts-controls { display: inline-flex; gap: 0.25rem; align-items: center; }
.tts-controls[hidden] { display: none; }
body.tts-active header nav > *:not(.tts-controls) {
  display: none !important;
}
body.tts-active header nav .tts-controls[hidden] {
  display: inline-flex;
}
body.tts-active [data-tts-toggle].nav-button-loading svg {
  animation: reading-tts-spin 0.9s linear infinite;
}
@keyframes reading-tts-spin {
  to { transform: rotate(360deg); }
}
/* Respect the OS-level motion preference everywhere: kill smooth
   scrolling and transitions. Pages opt back in only for motion that
   carries information. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
