/* ══ The ported reader, in Mere Orthodoxy's colour ═══════════════════
   Skin and bones: the corpus owner's reader engine, our palette.

   THE READER CAME OUT GREY (Ian, 2026-09-14: "It's also too gray. I
   liked the warmth of the old reader"). Not because his design is grey
   — `prdl-system.css` is already built from our brand and sets the same
   #f6f3f2 page, the same #d9c6a7 tan rule and the same #c1593c accent
   we do. Two later stylesheets overwrite all of it with a neutral
   scheme:

     read.in01.css:9          :root{--bg:#F0F0ED; --fg:#1C1C1A; …}
     fr-reading-system.css:5  :root,:root[data-site]{--bg:#F0F0ED;
                              --card:#FFFFFF; --border:#D8D8D3; …}

   #F0F0ED is a cool grey-green and #FFFFFF is a white card on top of
   it, which is why the page reads colder than the rest of the library
   and why a highlighted passage sat in a grey band.

   So this restores the values his own system file already holds. It is
   loaded last in custom-faith-port-read.hbs, which is the only reason
   it wins; keep it last.

   WHAT IS DELIBERATELY NOT TOUCHED: --accent. The reading system sets
   it to #1C1C1A, near-black, and in this engine the accent is used on
   running text as well as on controls, so making it burnt orange would
   colour prose rather than furniture. The controls take the accent from
   our own rules instead. If that changes, verify on screen before
   trusting it — this file exists because someone assumed. */

:root,
:root[data-site] {
  /* The page and the paper it sits on */
  --bg: #f6f3f2;
  --bg-warm: #f1e0c9;
  --card: #fffdfa;
  --card-bg: #fffdfa;
  --panel: #fffdfa;
  --paper: #fffdfa;
  --sidebar: #f5efe1;
  --sidebar-bg: #f5efe1;

  /* Ink. #2d2927 is the library's dark; #1C1C1A is a colder black. */
  --fg: #2d2927;
  --head: #2d2927;
  --ink: #2d2927;
  --muted: #6b6660;

  /* The tan rule, everywhere a rule is drawn */
  --border: #d9c6a7;
  --line: #d9c6a7;

  /* A marked passage sits on cream, not on grey */
  --hl: #f5efe1;
  --highlight: #f5efe1;

  /* ONE TOKEN CARRIES MOST OF THE "THIS ISN'T OUR SITE" FEELING.
     --sans was -apple-system/Segoe UI, and it paints every label in the
     tool layer: the Aa row labels, the Outline tabs, the sidebar chips,
     the mobile dock, the century badges, the selection chip. A system
     sans on a page of Garamond is the single loudest tell. */
  --sans: "Source Serif Pro", Georgia, serif;

  /* The white slab behind the About dialog's header and the related
     passages bar. One token, two surfaces. */
  --masthead: #f5efe1;
  --masthead-ink: #2d2927;

  /* Was #B0B0AA, a cool grey, on volume pills and related-passage
     hovers. Our tan rule. */
  --accent-soft: #d9c6a7;
}

/* ── The reader inside the shell ────────────────────────────────────
   Both mastheads are position:fixed at top:0 — ours at z-index 20, the
   reader's toolbar at 40 — so until the toolbar is pushed down it
   covers the site nav completely.

   --mo-head is measured by assets/js/page/faith-port-read-boot.js
   rather than written here as a number, because our header is 61px on a
   desktop and taller when it wraps. The fallback is the desktop height,
   which is what an unmeasured first paint should assume. */
.ph.fr-reader-head,
.ph {
  top: var(--mo-head, 61px);
}

/* .app is `height:100dvh; padding-top:var(--phh)` — a full-viewport app
   shell with its own internal scroller. --phh is published at runtime by
   the engine's setPhh() and now carries our masthead as well as the
   reader's toolbar (see the MereO delta in reader-core.js), so the
   padding clears both and the app still measures exactly one viewport.
   border-box is what keeps that true. */
.app {
  box-sizing: border-box;
}

/* The footer sits after the app. The app is exactly one viewport tall,
   so the page itself scrolls to reach it once the reader's own scroller
   hits its end — which is ordinary scroll chaining, not a second
   scrollbar competing with the first. `overscroll-behavior: auto` on the
   reader's scroller is what allows the chain; the reader sets `contain`
   in places to trap it, and on this page we want it to pass through. */
.faith-port-read-page #scroll { overscroll-behavior-y: auto; }
/* ── Our shell is not his chrome ────────────────────────────────────
   read.in01.css styles `.brand` and `.nav`. So does Mere Orthodoxy's
   masthead. Sharing a document, his rules land on our header: measured
   live, they take header.site-header from 61px to 89px by giving our
   brand block 16px of top padding, 11.2px of bottom, and a rule under
   it, and they hand our nav `overflow-y: auto`.

   HIS STYLESHEET IS NOT LAYERED AND THIS FILE IS NOT LAST. An earlier
   version of this comment claimed both, from a cascade-layer experiment
   that was reverted in 30cd646; the claim outlived the code and would
   have sent the next person writing rules that silently lose. The
   rendered order is: his port sheets, then this file, then
   screen.min.css, then faith-received.min.css. So this file BEATS his
   sheets on a tie and LOSES to ours. Anything that has to outrank our
   own site CSS needs specificity, not position — which is why the rules
   below name `header.site-header` and `#app` rather than bare classes.

   These are the shell's own computed values, read off a clean page
   rather than guessed, and verified to restore 61px exactly. */
header.site-header .brand,
header.site-header .nav,
footer.site-footer .brand,
footer.site-footer .nav {
  padding: 0;
  border-bottom: 0;
  position: static;
  flex: 0 1 auto;
  overflow-y: visible;
  scrollbar-width: auto;
  font-family: var(--font-body);
}

/* `.hero` is the third generic class name both sides use, after `.brand`
   and `.nav`. Ours is the dark band with the cathedral behind it —
   screen.min.css paints it `linear-gradient(#2d2927e6, #2d2927 50%)` and
   sets white text. His index.in01.css sets `.hero h1 { color: var(--head) }`,
   and our skin points --head at the library's dark ink, so the heading
   came out #2d2927 on #2d2927: "The library" was there, in the right
   place, and invisible.

   Inside our hero the band's own colour wins. This is not a palette
   choice, it is the rule that light text belongs on a dark ground. */
.hero { color: #fff; }
.hero h1,
.hero h2,
.hero p,
.hero span,
.hero small,
.hero label { color: inherit; }

/* ── Our shell's rules must not reach into his reader ──────────────
   The collision runs both ways. `.nav` and `.brand` are our site
   masthead's class names AND his reader's: `.nav` is the contents
   sidebar, `.brand` the work card. Our screen.min.css loads after his
   read.in01.css, so on a tie our rule wins — which is what we want in
   our header and exactly what we do not want in his sidebar.

   `.nav { display: flex; align-items: center; gap: 10px }` is our site
   menu, and applied to his outline it laid 276 chapter titles out as a
   horizontal row, collapsing each to a 60px column one letter wide
   (Ian, 2026-09-15: "the TOC on the reader is a mess").

   Only the properties that actually collide are restored, and only
   inside #app. Anything more — a cascade layer over his whole
   stylesheet — demotes every rule he has, including the ones that are
   more specific than ours and ought to win, and that cost the outline
   its typography as well as its layout. Check `#app` first when
   something in the reader inherits a site style it should not. */
#app .nav {
  display: block;
  align-items: normal;
  gap: 0;
}
#app .brand {
  display: block;
  align-items: normal;
}

/* The contents belong in the sidebar, not stacked above the text as
   well. The engine prints an inline contents block at the head of any
   work whose outline runs to four entries or more, which for these is
   the whole outline a second time (Ian: "we need it to all be
   sidebar"). The sidebar carries the same tree, with a filter and a
   Show current control the inline copy does not have. */
#reading .inctoc { display: none; }

/* Our site-menu link treatment — 11px, uppercase, letter-spaced — is
   `.nav a`, and his outline entries are links inside a `.nav`. His own
   rules set the family, size and colour, so those came right once the
   layer went; these two he never declares, so there is nothing of his
   to win and ours simply applies. Two hundred and seventy-six chapter
   titles in spaced small caps is not a list anyone reads. */
#app .nav a {
  text-transform: none;
  letter-spacing: normal;
}

/* THE SIDEBAR STAYS OPEN ON A WIDE SCREEN (Ian, 2026-09-15: "we need it
   to all be sidebar and the sidebar to be really really good").

   His sidebar is a drawer: absolutely positioned over the text with a
   scrim behind it, which is right on a phone and wrong on a desktop,
   where there is room for the outline and the text at once and no
   reason to dim what you are reading to look at where you are in it.

   Above 1100px it joins the flex row instead, so the reading column
   starts where the sidebar ends and nothing is covered. Below that it
   remains the drawer it was. */
@media (min-width: 1100px) {
  .app:not(.nosb) #sbScrim { display: none !important; }
  .app:not(.nosb) .sidebar {
    position: relative;
    top: 0;
    /* Stated explicitly. The drawer's translateX(-100%) is applied while
       .nosb is on the element at first paint, and it survives the class
       being removed a moment later — computed transform stayed at -300px
       with no rule matching it. The open state says what open means
       rather than relying on the closed state's rule stopping. */
    transform: none;
    flex: 0 0 300px;
    box-shadow: none;
  }
  .app:not(.nosb) .main { min-width: 0; }
}

/* .app is `height: 100dvh` — one viewport — which was true when the
   reader owned the document. Inside our shell it starts below the
   masthead, so a full viewport of height runs past the fold by exactly
   the masthead's height, and the sidebar and the reading column both
   ended up with their last rows under the bottom edge.

   It gets the viewport MINUS our masthead. --mo-head is measured at
   runtime by faith-port-read-boot.js, so this follows the header when it
   wraps at narrow widths. */
.faith-port-read-page {
  height: calc(100dvh - var(--mo-head, 61px));
}

/* ── Dark mode is not shipped, so it must not arrive by accident ────
   fr-reading-system.css applies its whole dark token set under
   `@media (prefers-color-scheme: dark)` to
   `:root:not([data-theme="light"]):not([data-theme="sepia"])`. On a
   first visit there is no data-theme attribute at all, so A READER
   WHOSE OS IS DARK LANDS ON THE DARK READER WITHOUT TOUCHING A CONTROL
   — under our cream masthead, above our cream footer. Dark content,
   light chrome, no seam between them that anyone chose.

   The reading theme is a property of the reading surface, and a
   masthead carrying the wordmark and a terracotta member button is not
   part of that surface. Light and sepia are both warm and sit under
   that masthead without a seam, which is why they are fine scoped to
   the reader. Dark is different in kind: someone who asks for a dark
   screen is not asking for an 85px cream band at the top of it. Doing
   it properly means taking our own shell dark in OUR dark
   (--color-dark ground, --color-cream ink, the band our heroes already
   use), and that is its own piece of work with its own review.

   Until then the OS preference does not get to choose it. The explicit
   toggle still works — `[data-theme="dark"]` is untouched — so nothing
   is removed, it just stops being the default for a third of visitors.
   The theme control itself should drop to Light/Sepia; that is a
   template change, tracked separately. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="dark"]),
  :root[data-site]:not([data-theme="dark"]) {
    color-scheme: light;
    --bg: #f6f3f2;
    --bg-warm: #f1e0c9;
    --card: #fffdfa;
    --card-bg: #fffdfa;
    --panel: #fffdfa;
    --paper: #fffdfa;
    --sidebar: #f5efe1;
    --sidebar-bg: #f5efe1;
    --fg: #2d2927;
    --head: #2d2927;
    --ink: #2d2927;
    --muted: #6b6660;
    --border: #d9c6a7;
    --line: #d9c6a7;
    --hl: #f5efe1;
    --highlight: #f5efe1;
  }
}

/* THE READER SUBTREE TAKES THE THEME'S INK, NOT THE SITE'S.
   Any element in here that sets no colour of its own inherits from
   `body`, and `body { color: var(--color-dark) }` comes from
   screen.min.css, which loads after this file. In dark that put
   #2d2927 ink on a #242521 sidebar — the work title measured about
   1.1:1 against its own background, which is invisible, not merely
   low-contrast. Asserting --fg at the roots of the embedded surfaces
   fixes every un-coloured descendant at once. */
#app,
#aboutOv,
aside.relpanel,
#selpop,
#notebook {
  color: var(--fg);
}

/* THE VOLUME LIST NEEDED SAYING TWICE.
   `#volnav` sits inside `#app`, so the rule above should have reached it, and
   `read.in01.css` sets `.vnrow { color: var(--fg) }` besides. It still came out
   #2d2927 on the #242521 sidebar — 1.07:1, which is not low contrast, it is
   invisible — for every one of the forty-six rows in PL 140. Whatever wins that
   cascade, an id in the selector settles it. The list is the way from one work in
   a Migne volume to the rest of it and it is now the first thing under the
   outline, so it cannot be the one part of the sidebar a reader cannot see. */
#app #volnav,
#app #volnav .vnrow,
#app #volnav .vnt,
#app #volnav .vnhead,
#app #volnext {
  color: var(--fg);
}
#app #volnav .vni,
#app #volnav .vnc {
  color: var(--muted);
}

/* ── Every overlay was anchored to a viewport it no longer owns ─────
   One defect, five instances. The facsimile pane, the contents scrim,
   the related-passages panel, the research drawer and its scrim are all
   `top: 0` — correct when the reader was the whole document, wrong now
   that our masthead occupies the first 61px. Four of the five carry a
   z-index above our masthead's 20, so they do not merely reach it, they
   cover it: with the contents drawer open at 375px, elementFromPoint at
   our logo returns the scrim, and the hamburger cannot be tapped.

   .facs stays inside the media query on purpose. Above 1100px it is
   position:relative and a bare `top` would shift it in normal flow. */
#sbScrim,
.nb-scrim,
.notebook,
aside.relpanel {
  top: var(--mo-head, 61px);
}
@media (max-width: 1100px) {
  #app .facs { top: var(--mo-head, 61px); }
}

/* ── A MODAL SHEET COVERS; A DOCKED RAIL SITS INSIDE THE WINDOW ─────
   The rule above gives the research drawer our masthead's height, which
   is right for the sheet it is below 1100px: that one is aria-modal,
   it takes the screen, and covering the reader's toolbar is what a
   modal is for.

   Docked it is not a modal. read-tools.js sets role="complementary" and
   drops aria-modal, the text narrows to make room, and the rail is
   furniture standing beside the book. So it has to start where the book
   starts. `html.fr-research-docked #notebook { top: var(--phh) }` in
   read.in01.css gives it the READER TOOLBAR's height alone — 65px —
   which was the whole offset back when the reader owned the viewport.
   Our masthead is 85px, so the rail began twenty pixels ABOVE the
   masthead's own bottom edge and, carrying z-index 10020 against the
   masthead's 20, covered it; below that it ran straight over the
   reader's toolbar, which is what Ian saw (2026-09-18: "it's not
   aligned at the top... needs to sit in the TFR window and not overlap
   the navbar"). Both bars, not one.

   Same specificity as his (1,1,1) and this file loads after, so this
   wins on source order. When the chrome hides on scroll both bars leave
   together — see the transform block at the end of this file — and the
   rail takes the space with them rather than leaving a band of nothing
   where the toolbar used to be. */
@media (min-width: 1100px) {
  html.fr-research-docked #notebook {
    top: calc(var(--mo-head, 61px) + var(--phh, 64px));
    transition: transform .18s ease-out, top .28s var(--ease, ease);
  }
  html.fr-research-docked.mh-hide #notebook,
  html.fr-research-docked.mh-mini #notebook {
    top: env(safe-area-inset-top, 0px);
  }
  @media (prefers-reduced-motion: reduce) {
    html.fr-research-docked #notebook { transition: transform .18s ease-out; }
  }
}

/* The section title in the toolbar never shrinks — `white-space:nowrap`
   with `flex:none` and no overflow rule, while the sibling link beside
   it got the full ellipsis treatment. At 375px it measures 566px inside
   a 260px container and runs 344px past the right edge, straight under
   the Aa button, which is why "Aa" is unreadable on a phone. Only
   `html{overflow-x:hidden}` hides it from the scrollbar.

   It is removed rather than clipped: it prints the same heading that
   the reading column prints 60px below it, and removing it gives the
   author's name back the room it was being crushed out of. */
#app .fr-reader-head #reader-location { display: none; }

/* The settings panel's height cap subtracts the reader's toolbar and
   nothing else — not our masthead, not the mobile dock. At 320x640 the
   last row, the text-size slider, renders 35px underneath the dock and
   the panel does not scroll, so it cannot be reached at all. */
#app .fr-reader-head .aapop {
  max-height: calc(100dvh - var(--mo-head, 61px) - var(--phh, 62px) - 57px - 24px);
}

/* The contents drawer reserves 52px for a dock that measures 57. */
html.g-mobile #app .sidebar { bottom: calc(57px + env(safe-area-inset-bottom)); }

/* Seven dock labels at 9.6px. 14px cannot fit seven cells at 320px;
   11.5px can, and is the honest floor for a label anyone must read. */
html.g-mobile nav.frthumb button { font-size: 11.5px; }

/* The dock's visibility is decided once at load by the g-mobile class
   and never re-run, so a phone-width load resized to desktop keeps the
   dock AND shows the floating Ask pill on top of the dock's own Ask
   cell. Wherever the dock exists, it owns Ask. */
html.g-mobile .fra-launcher { display: none !important; }

/* The only way to dismiss a panel covering the whole phone screen was a
   26px target. */
#app aside.relpanel .relpanel-x { min-width: 44px; min-height: 44px; }

/* ── The font control pointed at the wrong element entirely ─────────
   It changed our masthead and never touched the work. Both halves of
   that are explainable and one of them is this file's fault.

   OUT: the containment rule above restores `font-family: var(--font-body)`
   on our header and footer — but his sheets REDEFINE --font-body at
   :root for each data-font value, so that line was not restoring our
   face, it was piping his toggle straight into our masthead. Our chrome
   now states its own value for the variable rather than reading a token
   he owns. (That is also why the masthead was EB Garamond at rest on
   this page and Source Serif everywhere else on the site.)

   IN: the work's prose inherits from `body { font-family }` in
   screen.min.css, which the control has no way to outrank, so the
   setting had no path to the text. #reading now reads a variable only
   this block sets, keyed off his data-font attribute, so his control
   drives our element. The serif value is unchanged from today's
   default, so nothing moves until a reader asks it to. */
header.site-header,
footer.site-footer {
  --font-body: "Source Serif Pro", Georgia, serif;
  --font-display: "IM Fell Great Primer", "Source Serif Pro", Georgia, serif;
}
:root[data-font="serif"] { --fr-read-font: "Source Serif Pro", Georgia, serif; }
:root[data-font="sans"]  { --fr-read-font: "Source Sans 3", -apple-system, BlinkMacSystemFont, sans-serif; }
:root[data-font="easy"]  { --fr-read-font: "Atkinson Hyperlegible", "Source Sans 3", sans-serif; }
#app #reading { font-family: var(--fr-read-font, "Source Serif Pro", Georgia, serif); }

/* ── Parchment, not pixels. Stone, not glass. ───────────────────────
   Five surfaces in the tool layer were frosted glass with saturation
   boosts. The brand voice says the opposite in those words. The toolbar
   in particular was a translucent page colour under a 16px blur, so it
   read as a floating pane; opaque cream makes it a band belonging to
   the masthead above it. */
#app .ph,
#app .aapop,
#app .fh,
aside.relpanel .relpanel-h,
html nav.frthumb {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
#app .ph { background: var(--color-cream, #f5efe1); }
#app .aapop { background: var(--card, #fffdfa); }

/* ── The outline is a list, not a wall ──────────────────────────────
   276 entries at weight 600. Weight now means "you are here" and
   nothing else, and the current entry is marked by the tan rule down
   its edge rather than a floating white tile — the editorial row, which
   is what the rest of the library uses.

   His selector is (0,3,0); #app is required or this loses. */
#app .nav-node.nd1 .nn-t { font-weight: 400; }
#app .nav-node.on .nn-t { font-weight: 600; }
#app .nav-node.on {
  background: transparent;
  border-radius: 0;
  border-left-color: var(--color-secondary, #c1593c);
}
#app .nav-node:hover { background: var(--hl); }

/* ── The settings panel, in a column ────────────────────────────────
   The label track was `flex: 0 0 3.4rem` — 54.4px — and "FOOTNOTES"
   renders 74px, so that one row's control started 20px right of the
   other five. A grid guarantees the column instead of hoping every
   label fits the basis. The label itself becomes our eyebrow. */
#app .aapop .aarow {
  display: grid;
  grid-template-columns: 104px minmax(0, 1fr);
  align-items: center;
  column-gap: 14px;
  padding: 6px 0;
}
#app .aapop .aal {
  font: 600 11px/1 var(--font-ui, "Source Serif Pro", Georgia, serif);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Selected means terracotta, not near-black. His --accent is #1C1C1A
   and paints every active state in the reader; it is left alone at the
   root because it also reaches footnote markers and drop caps in the
   prose, which is a house-style decision rather than a bug fix. */
#app .aapop .aaseg .tgl[aria-pressed="true"],
#app .aapop .aaseg .tgl.on {
  background: var(--color-secondary, #c1593c);
  border-color: var(--color-secondary, #c1593c);
  color: #fff;
}
#app .nav-vt button.on { color: var(--color-secondary, #c1593c); }
html nav.frthumb button.on { color: var(--color-secondary, #c1593c); }

/* ── One radius, no shadows ─────────────────────────────────────────
   Twelve pill shapes and nine card shadows across the tool layer. The
   site system is a 6px radius and flat surfaces with a tan rule. */
#app .aapop,
#aboutOv .about-box,
aside.relpanel a.rsr {
  border-radius: 4px;
  box-shadow: none;
}
#aboutOv .about-box { border: 1px solid var(--border); }
aside.relpanel { box-shadow: none; border-radius: 0; }
aside.relpanel a.rsr:hover { transform: none; }
#app .nav-vt,
#app .nav-vt button { border-radius: 0; box-shadow: none; }

/* ── The settings panel stops being a second toolbar ────────────────
   Two blocks at the top of a menu labelled "Reading settings" are not
   settings: Previous/Next duplicate the toolbar arrows eight pixels
   away, About and Related belong on the toolbar, and "Go to section" is
   the THIRD way to move within a work after the outline and the
   toolbar's own number field. Together they are 167px of a panel that
   is supposed to be about type.

   His own stylesheet already hides .aamob; a later rule of his turns it
   back on at every width, which is a rule-order accident rather than a
   decision. It stays on the phone, where the toolbar has no room for
   those controls. */
#app .fr-reader-head .aamob,
#app .fr-reader-head .reader-jump-form { display: none; }
html.g-mobile #app .fr-reader-head .aamob { display: flex; }

/* The welcome banner names three things that do not exist on this
   page: a Latin lane and a Scan button that are both display:none on an
   English-only work, and a command palette whose script this template
   deliberately does not load. It is also set in italic EB Garamond — a
   display face on a working surface — and opens with an em dash. An
   open contents list teaches the interface better than a paragraph
   about controls that are not there. */
#app .coach { display: none; }

/* A lane toggle with one lane. On an English-only work it cannot be
   toggled, and it was rendering underlined as though it were a live
   control you had selected.

   Hiding it was the wrong answer and lasted one commit: it is the only
   thing on screen that says WHICH LANE YOU ARE READING, and a reader
   who opens a parallel work and then an English-only one should not
   have that fact silently disappear (Ian, 2026-09-15). It stays, and
   reads as the statement of fact it is: no underline, no pressed
   state, not a button. */
#app.only-en .fr-reader-head #m-en {
  text-decoration: none;
  cursor: default;
  color: var(--muted);
  pointer-events: none;
}

/* The sidebar's default-open lives in faith-port-read-boot.js, NOT
   here. It was briefly done in CSS by neutralising the transform on
   .nosb — which is the CLOSED state, so open and closed rendered
   identically and the toggle silently stopped working (Ian,
   2026-09-15: "this does not actually collapse/expand the TOC"). Never
   style a state class to look like its opposite; drop the class. */

/* ── The work is named once ─────────────────────────────────────────
   The sidebar printed "Confessions / Augustine of Hippo · 276 sections
   · English · digital text" directly beneath a toolbar printing
   "Confessions / Augustine of Hippo · c. 397–400 AD" — the same work,
   named twice, forty pixels apart, and the lower one clipped by the
   band above it (Ian, 2026-09-15).

   The toolbar's copy wins because it is always visible; this one only
   exists while the sidebar is open. Removing it also gives the outline
   back the 81px it was costing, which is most of a first entry.

   The count line goes with it: "276 contents entries" restates the
   toolbar's "/ 276" and the scrollbar says as much again. */
#app .sidebar .work { display: none; }
#app .sidebar .cc-count { display: none; }

/* ── Rows for things this work does not have ────────────────────────
   MY BUG, AND THE ANSWER TO "these tools don't make sense". His
   stylesheet hides the scan-zoom row on a work with no facsimile
   (`.app.nopdf .aarow:has(.izc)`), but the grid rule above sets
   `display: grid` on every .aarow and carries an id, so it outranks his
   hiding and put the row back. Confessions is a born-digital English
   text: there is no scan to zoom and no second lane to balance, so
   those sliders moved nothing at all.

   His conditions are re-asserted at this file's specificity. */
#app.nopdf .aapop .aarow:has(.izc),
#app.no-facs .aapop .aarow:has(.izc) { display: none; }

/* The two lane-size sliders set the size of one lane RELATIVE to the
   other. With a single lane there is no other, so "En" was a control
   with nothing on the far side of it. The text-size slider above is the
   one that does this job on an English-only work. */
#app.only-en .aapop .aarow:has(.lnz),
#app.only-en .aapop #source-size { display: none; }

/* ── Type size changes the type, not the page ───────────────────────
   `max-width: min(40em, 88%)` is measured in em, so it tracks the
   reading font-size: turning the text down pulled the column in with
   it and left a widening band of empty page on the right, and the
   layout appeared to move when only the type should have (Ian,
   2026-09-15: "it should just change the actual font size in the doc
   but keep it formatted to the page").

   rem is rooted on the document, and the slider sets font-size on
   #reading, so the column now holds its width while the type inside it
   grows and shrinks. 46rem is the same 736px the em measure gave at the
   default size, so nothing moves until someone asks it to. The 88% cap
   is kept: it is what guarantees a margin at every width. */
#app.only-la #reading,
#app.only-en #reading { max-width: min(46rem, 88%); }

/* Wider once the sheet is gone. 46rem was right when the column sat on a
   lit sheet of paper: the paper drew the eye and the space around it read
   as a mat. With the surface removed the same column reads as a narrow
   ribbon stranded in a field, which is what Ian saw. The measure opens on
   wide screens only, so phones and tablets keep the line length they
   have, and 60rem at the reader's body size is still inside a sane
   measure rather than a full-bleed wall of text. */
@media (min-width: 1200px) {
  #app.only-la #reading,
  #app.only-en #reading { max-width: min(60rem, 92%); }
}

/* ── The toolbar's right cluster ────────────────────────────────────
   Four groups with four different rhythms: the arrows cramped against
   each other, a white input box on a cream band, a rule with unequal
   air either side, then two text buttons at a third spacing. Read left
   to right it has no beat (Ian, 2026-09-15).

   One gap between groups, one height for everything in the row, and
   the page field belongs to the toolbar rather than looking like a form
   dropped onto it. */
#app .ph .ctr {
  display: flex;
  align-items: center;
  gap: 18px;
}
#app .ph .ctr .seg {
  display: flex;
  align-items: center;
  gap: 2px;
}
#app .ph .ctr #pPrev,
#app .ph .ctr #pNext {
  min-width: 34px;
  min-height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
#app .ph .ctr #pgJump {
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 3px;
  min-height: 30px;
  padding: 0 6px;
  text-align: right;
  font: inherit;
  color: var(--fg);
}
#app .ph .ctr .pg-total { color: var(--muted); }
#app .ph .ctr #rdFlow,
#app .ph .ctr #rdRel { min-height: 34px; }

/* ── Where the reader stops and the site resumes ────────────────────
   The reading pane is exactly one viewport, so the footer sits below it
   and the join had nothing marking it: text ran to the pane's edge and
   the footer began, with no line to say one had ended (Ian, 2026-09-15:
   "the footer is showing up here weird").

   A rule is what the rest of the library uses between a reading surface
   and what follows it. The Ask pill also stops floating over the
   footer — it belongs to the reader, not to the page below it. */
.faith-port-read-page { border-bottom: 1px solid var(--color-card-border, #d9c6a7); }
body:has(.faith-port-read-page) .fra-launcher { position: absolute; bottom: 18px; }

/* ═══════════════════════════════════════════════════════════════════
   THE TEXT SITS ON THE PAGE, NOT ON A SHEET OF PAPER

   His FR-EDITION pass (read.in01.css, "the Fine Press pass") floats the
   reading column on a paper surface: --card-bg a step lighter than the
   desk, a hairline edge, a soft drop shadow. Ian's call, 2026-09-15: the
   words should be on the page background itself, not on a document-like
   object sitting on top of it.

   Skin, not bones. His file is left alone and the values are stated
   here; our stylesheet loads after his, so equal specificity is enough
   and no !important is needed. The measure and the padding are his and
   stay his — this removes the surface, not the typography.
   ═══════════════════════════════════════════════════════════════════ */
html:not(.fr-plain) .app.only-en #reading,
html:not(.fr-plain) .app.only-la #reading,
html:not(.fr-plain) .app:not(.only-en):not(.only-la) #reading,
html:not(.fr-plain) .app.teiflow:not(.no-facs):not(.only-en):not(.only-la) #reading {
  background: transparent;
  border-inline: 0;
  box-shadow: none;
}

/* ───────────────────────────────────────────────────────────────────
   THE TOOLS POPOVER

   Wears the Aa popover's dress, because it is the same kind of object
   in the same toolbar and a second visual language there would be one
   too many. Rows are buttons rather than menu items: each does its
   thing and says so in the status line at the foot.
   ─────────────────────────────────────────────────────────────────── */
#app .aapop.rdtools {
  display: none;
  width: min(19rem, calc(100vw - 24px));
  padding: 6px;
}
#app .aapop.rdtools.is-open { display: block; }

#app .aapop.rdtools .rdt {
  display: block; width: 100%;
  appearance: none; background: none; border: 0; border-radius: 4px;
  /* 44px of target even at the smaller label size. */
  padding: 9px 10px;
  text-align: left; cursor: pointer;
  font-family: var(--sans); font-size: 0.86rem; line-height: 1.25;
  color: var(--fg);
}
#app .aapop.rdtools .rdt .rdt-l { display: block; }
#app .aapop.rdtools .rdt span:not(.rdt-l) {
  display: block; margin-top: 2px;
  font-size: 0.72rem; line-height: 1.3; color: var(--muted);
}
#app .aapop.rdtools .rdt:hover:not(:disabled) { background: var(--hl); }
#app .aapop.rdtools .rdt:disabled { opacity: 0.5; cursor: default; }
#app .aapop.rdtools .rdt[aria-pressed="true"] { color: var(--accent); }

/* The status line only takes room once it has something to report, so
   the popover does not carry an empty gap at its foot. */
#app .aapop.rdtools .rdt-say {
  margin: 0; padding: 0 10px;
  font-family: var(--sans); font-size: 0.72rem; line-height: 1.35;
  color: var(--muted);
}
#app .aapop.rdtools .rdt-say:empty { display: none; }
#app .aapop.rdtools .rdt-say:not(:empty) {
  padding: 8px 10px; margin-top: 4px;
  border-top: 1px solid var(--border);
}
#app .aapop.rdtools .rdt-say.is-bad { color: var(--accent); }

/* ── The paragraph rail (assets/js/page/faith-port-read-tools.js) ───
   Follows the pointer down the column and acts on the block beside it.
   It lives in the margin, which after the sheet came off is space the
   page had going spare. Pointer devices only: it is a hover affordance,
   and on a touch screen it would either never show or never leave. */
.fr-para-rail {
  position: absolute; left: 0; z-index: 3;
  display: flex; flex-direction: column; gap: 2px;
  opacity: 0; transition: opacity 0.12s ease;
}
#reading:hover .fr-para-rail { opacity: 1; }
.fr-para-rail[hidden] { display: none; }
.fr-para-rail button {
  appearance: none; cursor: pointer;
  width: 24px; height: 24px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: 4px;
  font: 0.82rem/1 var(--sans); color: var(--muted);
}
.fr-para-rail button:hover { background: var(--hl); color: var(--accent); }
@media (prefers-reduced-motion: reduce) { .fr-para-rail { transition: none; } }

/* Touch-only: a hover affordance there either never appears or never
   leaves. This is the gate; the script no longer asks at load, because a
   wrong answer in that one instant was permanent and silent. */
@media (hover: none) { .fr-para-rail { display: none !important; } }

/* ── No site footer in the reader ───────────────────────────────────
   The reader is a full-height application: the toolbar is fixed, the
   column scrolls inside #scroll, and the page itself does not scroll.
   A site footer under that is unreachable by scrolling and only ever
   appeared as a band of marketing wedged beneath the text.

   :has() on the body rather than a class from JS, so it is right on the
   first paint and there is no moment where the footer flashes. The
   footer is a sibling of <main>, not of #app, so no combinator from
   #app can reach it. */
body:has(#app.faith-port-read-page) .site-footer { display: none; }

/* ═══ MOBILE: two layout faults that are arithmetic, not guesswork ═══ */

/* 1. The last lines of a page sat UNDER the thumb bar. nav.frthumb is a
      fixed 57px strip at the foot of the screen and #reading's bottom
      padding was 48px, so the closing paragraph of every page was
      unreachable however far you scrolled. Enough room for the bar, the
      home indicator, and a line of air. */
@media (max-width: 880px) {
  html.g-mobile .app #reading,
  #app.faith-port-read-page #reading {
    padding-bottom: calc(57px + env(safe-area-inset-bottom, 0px) + 28px);
  }
}

/* 2. The contents panel left a band of reading text showing between the
      toolbar and its own top edge.
      
      The panel is positioned from --phh, the toolbar's height, and any
      disagreement between that number and the toolbar's real height
      opens a gap the text shows through. Rather than keep chasing the
      number, the panel now starts ABOVE where the toolbar ends and pads
      its content back down: a gap is then geometrically impossible, and
      the overlap hides behind the toolbar.

      That requires the panel to sit UNDER the toolbar in the stacking
      order. It shipped at z-index 60 against the toolbar's 40, so it
      would have covered it. 30 keeps it above the scrim (25) — which is
      what it must overlay on a phone — and below the toolbar. */
@media (max-width: 1099px) {
  #app .sidebar {
    top: calc(var(--phh, 64px) - 28px);
    padding-top: 28px;
    z-index: 30;
  }
}

/* 3. The toolbar's right-hand strip, on a phone.
      
      Measured at 375px: the row wants 394px and Aa's right edge lands at
      406, so it sits outside the screen with no way to reach it. That is
      the BEST case — a single-lane work. A work with English · Latin ·
      Scan, or one carrying a date under the page box, is far wider
      again, which is why tightening the spacing alone cannot be the
      whole answer.

      So: tighter spacing to make the common case fit outright, and the
      strip scrolls when it still does not. A control that has run off
      the edge is then reachable rather than lost. The scrollbar is
      hidden because this is chrome, not content. */
@media (max-width: 880px) {
  #app .ph .ctr {
    gap: 10px;
    /* overflow-x cannot produce a scroll until the box is actually
       CONSTRAINED. The strip is a flex child, and it was sizing to its
       own content and overflowing .ph instead of scrolling inside it:
       394px of controls reported as a 394px box, so scrollWidth equalled
       clientWidth and nothing scrolled. flex-shrink plus min-width: 0 is
       what lets it be narrower than its contents. */
    flex: 1 1 auto;
    min-width: 0;
    max-width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    /* The strip must be able to scroll to its own end, and the last
       control needs air after it rather than being flush to the edge. */
    padding-inline: 8px;
    scroll-padding-inline: 8px;
  }
  #app .ph .ctr::-webkit-scrollbar { display: none; }
  /* Nothing in the strip may be squeezed: a compressed page box or a
     lane label clipped to three letters is worse than a scroll. */
  #app .ph .ctr > * { flex: 0 0 auto; }
}

/* ═══ DARK MODE: the text was on the wrong surface ═══════════════════
   
   Measured on a phone in dark scheme: the reader's own palette switches
   correctly — --fg is #E9E9E5 and the text computes to rgb(233,233,229)
   — but the page underneath it stayed rgb(246,243,242), because the site
   shell has no dark mode of its own. Near-white type on near-white
   paper: eleven points of luminance between them. Unreadable, exactly as
   reported.

   Removing the paper sheet is what exposed it. Until then #reading
   painted --card-bg, which in dark scheme is #2B2C29, so the text sat on
   a dark card and could be read. Taking the sheet away let the light
   body through.

   The reading surface follows the reader's own --bg from here, which is
   the variable the reader's Light/Sepia/Dark control writes too — so
   this answers the OS setting and the in-reader toggle with one rule.
   In light scheme --bg is #f6f3f2, the colour the page already was, so
   nothing moves. */
body:has(#app.faith-port-read-page) { background: var(--bg); }
#app.faith-port-read-page,
#app.faith-port-read-page .main,
#app.faith-port-read-page #scroll { background: var(--bg); }

/* The reader's toolbar is ours, not his, and it was pinned to the cream
   masthead colour in every scheme — a bright band across the top of an
   otherwise dark reader. It takes the dark surface with everything else,
   and only in dark scheme, so the light reader is untouched. */
@media (prefers-color-scheme: dark) {
  #app.faith-port-read-page .ph {
    background: var(--card-bg);
    color: var(--fg);
    border-bottom-color: var(--border);
  }
  #app.faith-port-read-page .ph button,
  #app.faith-port-read-page .ph a,
  #app.faith-port-read-page .ph input { color: var(--fg); }
  #app.faith-port-read-page .ph .pg-total,
  #app.faith-port-read-page .ph .sub,
  #app.faith-port-read-page .ph .ploc { color: var(--muted); }
}

/* ── The contents panel's header scrolls away with everything else ───
   
   Two sticky blocks were stacked at the top of the panel: the
   Outline/Library tabs (56px, pinned at 0) and the tools box beneath
   them (135px, pinned at 57px) — the search field, Expand all / Collapse
   all, and the entry count. 191px of chrome held permanently above the
   list.
   
   On a phone that is a quarter of the screen gone before a single
   contents entry is shown, and it is what was covering the outline.
   Ian's call: it should not stick. You scroll past it to read the
   contents, and scroll back up to search. */
#app .sidebar .nav-vt,
#app .sidebar .rc-contents-tools {
  position: static;
  top: auto;
}

/* The masthead does not change for dark mode. Ian's call, and it is the
   right one: the nav bar is the site's, not the reader's, and it should
   read the same on every page of Mere Orthodoxy whatever the reader
   underneath it is doing. A cream band over a dark reader is a seam
   between two things that genuinely are two things.

   This is also why the logo swap and the hamburger rule that used to sit
   here are gone: both existed only to prop up a dark masthead. The
   shipped logo PNG has the wordmark's dark ink baked in, so if this is
   ever revisited, mere-o-logo-white.png is the light cut, and
   .nav-toggle-bar is painted with background rather than color. */

/* The thumb bar's Research button has nothing to open: its panel was
   removed as dead markup (see custom-faith-port-read.hbs). Hidden rather
   than left as a fifth control that does nothing when tapped. Restore it
   together with a panel that is actually mounted. */
/* The Research panel is mounted again; its dock button uses __frOpenNotebook. */

/* ── The reader, embedded in someone else's panel ───────────────────
   The Ask workspace opens a cited work in an iframe. The masthead and
   the footer are the frame around a PAGE; inside a preview panel they
   are a second Mere Orthodoxy nav bar sitting in the middle of an
   answer. The reader itself is the whole point of the embed, so it
   takes the full height it is given. */
html.mo-embedded header.site-header,
html.mo-embedded .site-footer,
html.mo-embedded #mobile-nav { display: none !important; }
html.mo-embedded { --mo-head: 0px; }

/* ── The curated authors the ported room has no roster for ───────
   Drawn by assets/js/page/faith-author-curated.js only when the shell
   found nothing. Deliberately plain: a name, a line of context, and the
   works. It is a gap-filler, not a second author room, and looking like
   one would invite it to grow into one. */
.fr-curated-author {
  max-width: 720px;
  margin: 0 auto;
  padding: 56px 24px 96px;
  font-family: var(--font-body, "Source Serif Pro", Georgia, serif);
}
.fr-ca-name {
  font-family: var(--font-display, "EB Garamond", Georgia, serif);
  font-weight: 400;
  font-size: clamp(30px, 4vw, 44px);
  line-height: 1.1;
  margin: 0 0 10px;
  color: var(--fg, #2d2927);
}
.fr-ca-meta {
  font-size: 14px;
  letter-spacing: 0.04em;
  color: var(--muted, #6b6660);
  margin: 0 0 28px;
}
.fr-ca-count {
  font-size: 12px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--color-secondary, #c1593c);
  margin: 0 0 14px;
}
.fr-ca-works { list-style: none; padding: 0; margin: 0; }
.fr-ca-works li { border-top: 1px solid var(--border, #d8d8d3); }
.fr-ca-work {
  display: block;
  padding: 16px 0;
  font-family: var(--font-display, "EB Garamond", Georgia, serif);
  font-size: 21px;
  line-height: 1.3;
  color: var(--fg, #2d2927);
  text-decoration: none;
}
.fr-ca-work:hover { color: var(--color-secondary, #c1593c); text-decoration: none; }

/* ── Highlights that cover the words, not the paragraph ──────────
   read-tools.js wraps the selected characters in <mark class="fr-hlx">.
   The port's own .row[data-hl] tints an entire row, which is what Ian
   was objecting to; a row that has marks in it no longer gets that
   tint, so these two never paint at once.

   A <mark> arrives with a browser default of black-on-yellow. Both are
   overridden: the ink is inherited so the text keeps the page's colour
   and its italics, and the grounds are the same three the row tint
   used, so a highlight made before this change looks the same after it.
   Inline padding with a negative margin would misalign wrapped lines,
   so the mark takes its ground and nothing else. */
mark.fr-hlx {
  background: var(--mark-amber, #fbe9c8);
  color: inherit;
  border-radius: 2px;
  padding: 0;
}
mark.fr-hlx[data-hl="sage"] { background: #e7efe0; }
mark.fr-hlx[data-hl="slate"] { background: #e5ecf3; }

[data-theme="dark"] mark.fr-hlx { background: #4d3b1c; }
[data-theme="dark"] mark.fr-hlx[data-hl="sage"] { background: #2c3a2a; }
[data-theme="dark"] mark.fr-hlx[data-hl="slate"] { background: #26313f; }

/* The one highlight control, when it is offering to take the mark back.
   It stops being a swatch and becomes a plain labelled button, because
   an amber chip that means "remove amber" reads as "apply amber". */
#selpop button.sw.is-clear {
  background: transparent;
  border: 1px solid var(--fg);
  color: var(--fg);
}
#selpop button.sw.is-clear:hover {
  border-color: var(--color-secondary, #c1593c);
  color: var(--color-secondary, #c1593c);
}
html.mo-embedded .faith-port-read-page { height: 100dvh; }
html.mo-embedded .ph { top: 0; }

/* ── The reader's own chrome, in dark ──────────────────────────────────
   Putting the theme control back in the bar made dark reachable by a click,
   and dark had never been finished here. The toolbar's ground is painted
   with a literal cream rather than a token, so in dark it held #f5efe1
   under #e9e9e5 ink: the work's own title measured 1.06 against its own
   background, which is invisible rather than merely faint. The tokens were
   right all along — --bg resolves to #222320 in dark — so it is the paint
   that is corrected here and nothing else.

   Our masthead is deliberately left alone. Taking the site shell dark is
   its own piece of work with its own review, as the note above says; this
   is the reader's own furniture only. */
:root[data-theme="dark"] #app .ph.fr-reader-head,
:root[data-theme="dark"] #app .ph {
  background: var(--card-bg, #232320);
  border-bottom-color: color-mix(in srgb, var(--fg) 20%, transparent);
}
:root[data-theme="dark"] #nbSaveReadingPlace {
  background: var(--card-bg, #232320);
  color: var(--fg);
  border-color: color-mix(in srgb, var(--fg) 24%, transparent);
}

/* ── The reader bar on a phone ─────────────────────────────────────────
   At 390px the bar was laying out nine controls in a nowrap row and simply
   running off the side: Tools sat at x=373 and the Aa button at x=449, both
   past the edge of the screen, which is why tapping them did nothing. The
   title block, which is flex:1 against them, was squeezed to nought and the
   work's name disappeared with it.

   The corpus site's reader shows four controls at this width -- home, where
   you are, which text, and Aa -- and hides forty-five. Everything it hides
   is already reachable: reader-core.js proxies Previous, Next, About and
   Related into the Aa menu (see "fold the header controls" there), the lane
   buttons are the same choice as the source picker, and the theme lives in
   that menu as #th. So the bar keeps the four and the title gets its room
   back. Nothing is removed, only moved to where there is space for it. */
@media (max-width: 880px) {
  /* `#app .ph .ctr #pPrev` above is two ids; a selector that means to overrule it
     has to carry two of its own, or it loses however many classes it stacks. */
  html.g-mobile:not(.fr-plain) #app .ph .ctr #pPrev,
  html.g-mobile:not(.fr-plain) #app .ph .ctr #pNext,
  html.g-mobile:not(.fr-plain) #app .ph .ctr #pgJump,
  html.g-mobile:not(.fr-plain) #app .ph .ctr .seg,
  html.g-mobile:not(.fr-plain) #app .ph #m-en,
  html.g-mobile:not(.fr-plain) #app .ph #m-par,
  html.g-mobile:not(.fr-plain) #app .ph #rdTools,
  html.g-mobile:not(.fr-plain) #app .ph #thTop {
    display: none;
  }
  /* The title block is what the freed width is for. */
  html.g-mobile:not(.fr-plain) #app .ph .phl { min-width: 0; flex: 1 1 auto; }
  html.g-mobile:not(.fr-plain) #app .ph .ctr { flex: 0 0 auto; gap: 2px; }
}

/* ── Reading, uninterrupted: the part that is ours ────────────────────────────
   The gesture itself now lives where it belongs, in assets/css/port/read.in01.css,
   ported from the corpus site along with the fix that made it work: the masthead
   restyle there used to end in transform:none, which is exactly the property the
   hide needs. What is left here is the part that is only true of this site.

   This site has its own masthead above the reader's bar, so the bar has to clear
   both itself and that masthead, and the masthead has to leave with it. The port
   moves the reader bar by -101% and knows nothing about ours; these two rules are
   more specific, so they win, and they say the same thing plus the offset.
   Anyone who has asked not to see motion sees them cut instead of slide. */
html header.site-header { transition: transform .28s var(--ease, ease); }
html body,
html #app { transition: padding-top .28s var(--ease, ease); }
html.mh-hide header.site-header,
html.mh-mini header.site-header { transform: translateY(-101%); }
html.mh-hide #app .ph.fr-reader-head,
html.mh-mini #app .ph.fr-reader-head {
  transform: translateY(calc(-101% - var(--mo-head, 61px)));
}
/* Both bars are fixed, so the page is padded to start below them: the body by
   this site's own boot script, which writes the masthead's height as an inline
   style, and #app by the port, which reserves the reader bar's. Leave those in
   place and the departing chrome uncovers a blank band 150px deep instead of the
   text it was covering. They go with it and the prose glides up into the space,
   which is what the corpus site does on a phone and now what both do everywhere.
   !important only because an inline style outranks every selector without one. */
html.mh-hide body,
html.mh-mini body { padding-top: env(safe-area-inset-top, 0px) !important; }
html.mh-hide #app,
html.mh-mini #app { padding-top: env(safe-area-inset-top, 0px); }
/* The reader box is a screen minus our masthead, so moving it up without
   growing it only moves the blank band from the top of the page to the bottom.
   It takes the whole screen while the masthead is away. */
html .faith-port-read-page { transition: height .28s var(--ease, ease); }
html.mh-hide .faith-port-read-page,
html.mh-mini .faith-port-read-page { height: 100dvh; }
@media (prefers-reduced-motion: reduce) {
  html header.site-header, html body, html #app,
  html .faith-port-read-page { transition: none; }
}


/* ── THE READER TOOLBAR, BOXED ──────────────────────────────────────
   Ian, 2026-09-18: "Box the toolbar buttons too."

   The bar was the last surface in the library still speaking in bare
   text. read.in01.css:2049 flattens `.seg button`, `.tgl` and `.sbt` to
   `border: 0` on a transparent ground, which is a defensible reading of
   a reading toolbar and is not the one we are using: every other control
   in The Faith Received is now an outlined box, and a row of unboxed words
   at the top of the page read as labels rather than as the controls they
   are. Same three steps as everywhere else — off-black outlined at rest,
   terracotta outlined when pointed at, terracotta filled when chosen. See the TFR BUTTON block at the
   end of assets/css/faith-received.css; this is that button, stated for
   the one surface whose sheets are too specific for it to reach.

   HIS SELECTOR IS (0,3,1) — `html:not(.fr-plain) .fr-reader-head .tgl`.
   A bare class loses to it however late it is stated, so every rule here
   carries `#app`. That is also why this lives in the skin rather than in
   faith-received.css: it needs the reader's own ids.

   A SEGMENTED GROUP IS ONE BOX WITH DIVIDERS, not boxes in a row — but
   only where the group really is one control. `‹ ›` and `1 / 19` are,
   and they get the group box. THE LANES ARE NOT: their own tooltips say
   "a toggle; combine freely with Latin and the scan", so English, Latin
   and Scan are three independent switches that happen to sit together,
   and a single box around them would promise a single choice. They are
   boxed one by one.

   AND ONE OF THEM IS NOT A BUTTON AT ALL. `#app.only-en … #m-en` is
   `pointer-events: none` and muted — see "the lane you are reading is a
   state, not a button" earlier in this file. Boxed and filled it became
   terracotta ink on a terracotta ground, about 2.3:1, on a control that
   cannot be pressed: a button that looks pressable and is not, which is
   the thing this whole sweep exists to stop. The inert lane keeps its
   quiet label.

   NOT BOXED, deliberately: `.mh-home` ("‹ Library"), which is a back
   link and belongs with the title beside it rather than with the tools,
   and everything inside `.aapop` — the Aa and Tools popovers are panels
   with their own segment design, and they already fill in terracotta
   when a choice is on. */
#app .ph.fr-reader-head .phl > .sbt,
#app .ph.fr-reader-head .ctr > .seg.pnav,
#app .ph.fr-reader-head .ctr > .seg.pgjump,
#app .ph.fr-reader-head .ctr > .seg.lanes > button,
#app .ph.fr-reader-head .ctr > .tgl,
#app .ph.fr-reader-head .ctr > button,
#app .ph.fr-reader-head .ctr > .aaw > .tgl,
#app .ph.fr-reader-head #nbCount {
  box-sizing: border-box;
  height: 40px;
  min-height: 40px;
  border: 1px solid var(--fg);
  border-radius: 3px;
  background: transparent;
  color: var(--fg);
}

/* One box, dividers inside it. The parts give up their own radius so the
   group's corners are the only ones in the row. */
#app .ph.fr-reader-head .ctr > .seg.pnav,
#app .ph.fr-reader-head .ctr > .seg.pgjump {
  gap: 0;
  overflow: hidden;
  padding: 0;
}
#app .ph.fr-reader-head .ctr > .seg.pnav > button {
  height: 100%;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: inherit;
}
#app .ph.fr-reader-head .ctr > .seg.pnav > button + button {
  border-left: 1px solid var(--fg);
}
#app .ph.fr-reader-head .ctr > .seg.lanes { gap: 6px; }
/* The page field IS the group's box. A bordered input inside a bordered
   group is a box in a box, and his `border-right … !important` on .pgjump
   has to be answered in kind or three sides come out terracotta and the
   fourth stays tan. */
#app .ph.fr-reader-head .ctr > .seg.pgjump {
  border-right: 1px solid var(--fg) !important;
  padding: 0 8px;
  gap: 4px;
}
#app .ph.fr-reader-head .ctr .pgjump #pgJump {
  min-height: 0;
  height: 26px;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--fg);
}
/* --fg, not the site's --color-dark: that ink is light-only, and in the dark scheme it set the page
   box and its total near-black on a near-black bar — '/ 698' unreadable (owner, 2026-09-19). The
   skin's own ink flips with the scheme; in light it is the same #2d2927. Same for the borders above. */
#app .ph.fr-reader-head .ctr .pgjump .pg-total {
  color: var(--muted);
}
/* The arrows sat in a group pulled 10px into its neighbour, which reads
   as kerning when the group has no edge and as a collision once it has. */
#app .ph.fr-reader-head .ctr > .seg.pnav { margin-right: 0; }

/* POINTED AT: the terracotta outline the box used to wear at rest.
   Only the two colours move, so the row does not shift under the cursor. */
#app .ph.fr-reader-head .phl > .sbt:hover,
#app .ph.fr-reader-head .ctr > .tgl:hover,
#app .ph.fr-reader-head .ctr > button:hover,
#app .ph.fr-reader-head .ctr > .aaw > .tgl:hover,
#app .ph.fr-reader-head .ctr > .seg.pnav > button:hover,
#app .ph.fr-reader-head .ctr > .seg.lanes > button:hover,
#app .ph.fr-reader-head #nbCount:hover {
  background: transparent;
  border-color: var(--color-secondary, #c1593c);
  color: var(--color-secondary, #c1593c);
}

/* CHOSEN: filled, one step past hover. Flow, the lane you are reading, an
   open popover. Stated after hover so that pointing at the chosen control
   does not un-fill it. */
#app .ph.fr-reader-head .ctr > .seg.lanes > button[aria-pressed="true"],
#app .ph.fr-reader-head .ctr > .tgl[aria-pressed="true"],
#app .ph.fr-reader-head .ctr > .aaw > .tgl[aria-expanded="true"],
#app .ph.fr-reader-head #nbCount[aria-expanded="true"] {
  background: var(--color-secondary, #c1593c);
  border-color: var(--color-secondary, #c1593c);
  color: var(--color-white, #fff);
}

#app .ph.fr-reader-head .phl > .sbt:focus-visible,
#app .ph.fr-reader-head .ctr > .tgl:focus-visible,
#app .ph.fr-reader-head .ctr > button:focus-visible,
#app .ph.fr-reader-head .ctr > .aaw > .tgl:focus-visible,
#app .ph.fr-reader-head .ctr > .seg.pnav > button:focus-visible,
#app .ph.fr-reader-head .ctr > .seg.lanes > button:focus-visible {
  outline: 2px solid var(--color-secondary, #c1593c);
  outline-offset: 2px;
}

/* Three switches, three boxes, and the one that cannot be switched keeps
   the quiet label it already had.

   LAST IN THE BLOCK, AND STATED TWICE. The inert lane also carries
   aria-pressed="true" — it is the lane you are reading — so it matches
   the filled state above. On paper this rule outranks that one (two ids
   against one); in the browser it did not, and terracotta ink sat on a
   terracotta ground at about 2.3:1 until this moved below it. Source
   order settles what a specificity argument did not, and the pressed
   form is spelled out so neither can be read as the closer match. */
#app.only-en .ph.fr-reader-head .ctr > .seg.lanes > #m-en[aria-pressed="true"],
#app.only-la .ph.fr-reader-head .ctr > .seg.lanes > #m-par[aria-pressed="true"],
#app.only-en .ph.fr-reader-head .ctr > .seg.lanes > #m-en,
#app.only-la .ph.fr-reader-head .ctr > .seg.lanes > #m-par {
  border-color: transparent;
  background: transparent;
  color: var(--muted);
}

/* Aa ▸ Spacing has to reach the paragraphs, not just the lane.
 *
 * WHAT WAS BROKEN. Compact / Normal / Relaxed set aria-pressed and wrote
 * html[data-lh], and fr-reading-system.css turns that into
 *
 *     html[data-lh] .app #reading :is(.row:not(.rapp)>.en, … ){line-height:var(--reading-lh)}
 *
 * which styles the LANE. The prose inside it is <p>, and read.in01.css:1754
 * gives every one of them a line-height of its own:
 *
 *     #reading .row .la p,#reading .row .en p{line-height:1.74; … }
 *
 * A declaration on the element always beats a value inherited from its
 * parent, however specific the parent's rule is, so the paragraphs never
 * moved. Measured on the live reader (eebo-266, 2026-09-18): the lane's
 * computed line-height went 31.7px → 33.9px → 40.4px across the three
 * settings while every paragraph stayed at 38.0016px and #reading's height
 * was byte-identical at 399,335px in all three. The control was inert.
 *
 * WHY THIS IS SAFE. It is keyed off [data-lh], and applyLH() REMOVES that
 * attribute for "normal" — so the default reading surface is untouched and
 * keeps read.in01's 1.74. Only a reader who has explicitly asked for
 * compact or relaxed sees anything change. The same lanes the port's own
 * rule names, so the two cannot disagree about scope. */
html[data-lh] .app #reading :is(.row:not(.rapp) > .en, .row:not(.rapp) > .la,
                                .stk > .en, .stk > .la,
                                .zipcol > .en, .zipcol > .la) :is(p, li, blockquote) {
  line-height: var(--reading-lh);
}

/* The research sheet was a viewport tall and did not start at the top.
 *
 * read.in01.css:2224 gives the sheet `height:100dvh` at 880px and below,
 * which was right when the reader owned the window. The block above moves
 * its top edge down by our masthead (`.notebook{top:var(--mo-head,61px)}`),
 * so a full viewport of height now begins below the masthead and ends
 * that far past the fold: measured at 390×844, #notebook's bottom edge
 * sat at 905.
 *
 * Something WAS lost outright. #notebook is `overflow:hidden` and a
 * flex column, and at 390×844 its scrollHeight equalled its clientHeight
 * at 844 — the sheet has no scroll of its own to recover the overhang
 * with. The last child, .nb-destinations, measured y 848 to 905: below
 * the fold entirely, on the Work tab and the Saved tab alike, with
 * elementFromPoint at its centre returning nothing. Those are three
 * links — Personal research, Notebooks, Open Desk — that no reader at
 * or below 880px could reach at all. The .nb-settings row lost its last
 * 4px on the Saved tab on top of that.
 *
 * Not only phones: the masthead is 85px above the 640px breakpoint, so
 * 820px and 880px overhung by 85, not 61. Measured across six widths —
 * 390, 640, 820, 880 all overhung and none could reach the links; 1024
 * and 1440 already sat at 0 and are unchanged by this rule.
 *
 * Its inset is already `61px 0 0 0`; only the explicit height stopped the
 * top and bottom edges from settling it. Handing the height back to them
 * makes the sheet exactly the space below the masthead, and keeps it
 * correct if --mo-head ever changes. */
@media (max-width: 880px) {
  #notebook { height: auto; }
}


/* ── THE ABOUT PANEL: THE WORK, THEN THE PERSON WHO WROTE IT ────────
   Ian, 2026-09-18, on franciscus-junius-opera-theologica-vol-1: "These
   About blocks on works are an absolute mess. They need to be incredibly
   simplified."

   The markup changed with it (reader-core.js, openAbout) — the panel no
   longer emits .about-sec / .about-tags / .about-prose / .about-sig /
   .about-kw, so read.in01.css:123-132 now styles nothing. Those rules
   are left where they are: that file is the corpus owner's and forking
   it costs us his next drop. This block styles the four elements the new
   panel does emit.

   WHAT THE TYPE IS DOING. The h2 is a WORK'S OWN NAME, which is the one
   place the library spends its display face, so it keeps --font-display
   (EB Garamond in this reader, measured) and the blurb keeps --font-body
   — the panel reads in the same voice as the page behind it. Everything
   that is furniture rather than reading goes to Source Serif: the meta
   line under the title was italic EB Garamond at 14.4px, which is a
   display face set in italic on a caption, and the author's name was
   EB Garamond at 17.92px, which made a person's name look like a second
   work title. Hierarchy from size, weight and space instead.

   THE HAIRLINE IS THE ONLY DIVIDER. One work, one author, one rule
   between them; a panel with two items in it does not need a grid. */
#aboutOv .about-box { width: min(600px, 94vw); }

/* THE PANEL SHARED TWO CLASS NAMES WITH /about/ AND LOST BOTH.
   screen.min.css loads after every port sheet, so at equal specificity
   its bare classes win, and Mere Orthodoxy's own About page owns
   `.about-body` (10 rules) and `.about-prose` (12). Measured on the live
   dialog: the panel body was taking `padding: 56px 0 32px` and
   `background-color: #f1e8d5` from screen.css:6009 rather than
   read.in01.css:122's `1.3rem 1.4rem 1.5rem` — which is where the 56px
   of dead space under the title came from, why the body sat on a
   different cream than the box, and why the prose ran flush to the box's
   left edge, 22px outside the title above it. It had been true of every
   About panel in the reader. The new markup drops `.about-prose`
   altogether and this restates the body's own box; `#aboutOv` is (1,1,0)
   and settles it for good. See [[project_port-and-shell-share-class-names]]. */
#aboutOv .about-body {
  padding: 1.35rem 1.4rem 1.5rem;
  background: transparent;
  max-width: none;
  margin: 0;
}

#aboutOv .about-h .ah-meta {
  font: 0.82rem/1.45 var(--sans, "Source Serif Pro", Georgia, serif);
  font-style: normal;
  color: var(--muted, #6b6660);
  margin-top: 0.35rem;
}

/* The close control was rgba(255,255,255,.12) — a white wash designed
   for a dark masthead, invisible on our cream one. Same three steps as
   every other control in the library. */
#aboutOv .about-x {
  background: transparent;
  border: 1px solid transparent;
  color: var(--muted, #6b6660);
}
#aboutOv .about-x:hover { color: var(--tfr-btn, var(--color-secondary, #c1593c)); }
#aboutOv .about-x:focus-visible {
  outline: 2px solid var(--tfr-btn, var(--color-secondary, #c1593c));
  outline-offset: 2px;
}

/* The work. First, because a reader opened a work. */
#aboutOv .about-work p {
  margin: 0;
  font: 1rem/1.62 var(--font-body, Georgia, serif);
  color: var(--fg, #2d2927);
}

/* The author. Context, not a second entry: a name, the two facts that
   place him, one sentence, and the door to the rest. */
#aboutOv .about-who {
  margin-top: 1.35rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--border, #d9c6a7);
}
#aboutOv .about-who:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
#aboutOv .about-who h3 {
  margin: 0;
  font: 600 0.95rem/1.3 var(--sans, "Source Serif Pro", Georgia, serif);
  letter-spacing: 0.01em;
  color: var(--fg, #2d2927);
}
#aboutOv .about-life {
  margin: 0.2rem 0 0;
  font: 0.8rem/1.45 var(--sans, "Source Serif Pro", Georgia, serif);
  color: var(--muted, #6b6660);
}
/* .about-bio and not .about-note — read.in01.css:133 styles .about-note at .72rem under a
   dashed rule, which landed on this line before it was renamed. */
#aboutOv .about-bio {
  margin: 0.55rem 0 0;
  font: 0.94rem/1.58 var(--font-body, Georgia, serif);
  color: var(--fg, #2d2927);
}

/* TFR BUTTON, stated for a surface that sits outside #app.
   The overlay is appended to document.body, so none of the
   `#app .ph.fr-reader-head …` rules above can reach it. Same system,
   same three steps: off-black outlined at rest, terracotta outlined when
   pointed at. See the TFR BUTTON block at the end of
   assets/css/faith-received.css.

   Ink and line read --fg rather than --color-dark so the one rule holds
   in the reader's explicit dark theme, where an off-black outline would
   be an invisible box; --tfr-btn is the lifted terracotta that theme
   needs, and it falls back to the daylight colour when unset. */
#aboutOv .about-more {
  display: inline-block;
  margin-top: 0.9rem;
  padding: 0 13px;
  height: 34px;
  box-sizing: border-box;
  border: 1px solid var(--fg, #2d2927);
  border-radius: 3px;
  background: transparent;
  color: var(--fg, #2d2927);
  font: 13px/32px var(--sans, "Source Serif Pro", Georgia, serif);
  text-decoration: none;
}
#aboutOv .about-more:hover {
  border-color: var(--tfr-btn, var(--color-secondary, #c1593c));
  color: var(--tfr-btn, var(--color-secondary, #c1593c));
}
#aboutOv .about-more:focus-visible {
  outline: 2px solid var(--tfr-btn, var(--color-secondary, #c1593c));
  outline-offset: 2px;
}
/* Dark is scoped to this overlay, never :root — dark-mode.js sets
   data-theme="dark" on every page for an OS-dark reader, and a :root
   token would repaint terracotta controls on a cream page. */
:root[data-theme="dark"] #aboutOv {
  --tfr-btn: color-mix(in srgb, var(--color-secondary, #c1593c) 65%, #fff);
}

/* A long author name is a two-line button, not a clipped one. */
@media (max-width: 640px) {
  #aboutOv .about-more {
    height: auto;
    min-height: 34px;
    line-height: 1.5;
    padding: 6px 13px;
    font-size: 13px;
  }
}

/* ── The Research drawer in the dark scheme (09-19) ──────────────────
   Ian's site sheet inks every notebook control — "Search this work",
   "Ask about this book", "Start a conversation", the work-overview
   links, the destinations, the selection popover — with --tfr-btn and
   falls back to --color-dark (#2d2927), a token the dark scheme never
   changes. On the near-black drawer that ink is invisible (contrast
   1.03). Scoped to the drawer and the popover, never :root (see the
   note above #aboutOv): the drawer only exists on reader pages, whose
   dark scheme is real. */
:root[data-theme="dark"] #notebook,
:root[data-theme="dark"] #selpop {
  --tfr-btn: var(--fg);
  --tfr-btn-ink: var(--bg);
  --color-dark: var(--fg);
}
:root[data-theme="dark"] #notebook a:not([class]) { color: var(--fg); }

/* Toolbar scrolling must not clip its open settings or tools. Keep the
   toolbar above the contents/research panes while one of its menus is open. */
#app .ph.fr-reader-head .ctr:has(.aapop.on, .aapop.is-open) {
  overflow: visible;
}
#app .ph.fr-reader-head:has(.aapop.on, .aapop.is-open) {
  z-index: 10030;
}

/* Tablet readers use the same compact toolbar as phones. Position its
   settings below both mastheads, within the available reading viewport. */
@media (max-width: 1099px) {
  #app .ph.fr-reader-head .aapop {
    --fr-menu-head: var(--mo-head, 61px);
    position: fixed;
    top: calc(var(--fr-menu-head) + var(--phh, 58px) + 8px);
    right: 12px;
    left: auto;
    width: 340px;
    max-width: calc(100vw - 24px);
    max-height: calc(100dvh - var(--fr-menu-head) - var(--phh, 58px) - 80px);
    overflow: auto;
  }
  body:has(> .site-header.is-hidden) #app .ph.fr-reader-head .aapop { --fr-menu-head: 0px; }
}
@media (max-width: 640px) {
  #app .aapop .aarow { grid-template-columns: 80px minmax(0, 1fr); column-gap: 8px; }
  #app .aapop .aaseg { flex-wrap: wrap; }
  #app .aapop .aaseg .tgl { min-height: 44px; }
}

/* Phone reading: retain each existing Latin/original-English pair at full width. */
@media (max-width: 640px) {
  #app:not(.grcwork):not(.en-only):not(.only-en):not(.only-la) #reading {
    font-size: var(--rdszm, 17px) !important;
  }
  #app:not(.grcwork):not(.en-only):not(.only-en):not(.only-la) #reading .row,
  #app:not(.grcwork):not(.en-only):not(.only-en):not(.only-la) #reading .row.seg .sp {
    grid-template-columns: minmax(0, 1fr);
    gap: 12px;
  }
  #app:not(.grcwork):not(.en-only):not(.only-en):not(.only-la) #reading :is(.row, .sp) > :is(.la, .en) {
    grid-column: 1;
    padding-inline: 0;
    border-inline: 0;
    min-width: 0;
    max-width: none;
    overflow-wrap: break-word;
  }
  #app:not(.grcwork):not(.en-only):not(.only-en):not(.only-la) #reading .row.soloen > .la,
  #app:not(.grcwork):not(.en-only):not(.only-en):not(.only-la) #reading .row.solola > .en {
    display: none;
  }
  #app:not(.grcwork):not(.en-only):not(.only-en):not(.only-la) #reading :is(.row:not(.rhead):not(.rapp), .sp) > .la::before {
    content: var(--source-label, "Original");
    display: block;
    color: var(--muted);
    font: 12px/1.5 var(--font-body);
    margin: 8px 0;
  }
  #app:not(.grcwork):not(.en-only):not(.only-en):not(.only-la) #reading :is(.row:not(.rhead):not(.rapp), .sp) > .en::before {
    content: "English";
    display: block;
    color: var(--muted);
    font: 12px/1.5 var(--font-body);
    margin: 8px 0;
  }
}

/* One mobile interaction layout through the reader's own 880px breakpoint.
   Keep the open-section link separate from a full-size expansion target. */
@media (max-width: 880px) {
  #app #nav .nav-node { align-items: center; gap: 8px; min-height: 56px; margin-block: 4px; padding: 4px 4px 4px calc(8px + var(--nav-depth, 0) * 12px) !important; }
  #app #nav .nav-node .nav-open { order: 1; min-height: 48px; padding: 8px 4px; font-size: 16px; line-height: 1.45; color: var(--fg); }
  #app #nav .nav-node button.cv { order: 2; align-self: center; flex: 0 0 48px; width: 48px; height: 48px; min-height: 48px; margin: 0; padding: 14px; border: 1px solid var(--border); border-radius: 6px; background: var(--card-bg); color: var(--fg); touch-action: manipulation; }
  #app #nav .nav-node button.cv svg { width: 18px; height: 18px; pointer-events: none; }
  #app #nav .nav-node .cv.leaf { display: none; }
  #app #nav .nav-node .nn-la { order: 3; }
  #app #nav .nav-vt button { min-height: 48px; }
  html.g-mobile nav.frthumb { min-height: 60px; padding: 3px 4px calc(3px + env(safe-area-inset-bottom)); gap: 2px; }
  html.g-mobile nav.frthumb button { min-width: 0; min-height: 54px; padding: 5px 1px; gap: 3px; font-size: 12px; line-height: 1.15; border-radius: 4px; }
  html.g-mobile nav.frthumb button .lb { font-size: 12px; line-height: 1.15; }
  html.g-mobile nav.frthumb button .ic { display: flex; align-items: center; justify-content: center; width: 22px; height: 22px; font-size: 20px; line-height: 1; }
  html.g-mobile nav.frthumb button .ic svg { display: block; width: 20px; height: 20px; }
  html.g-mobile nav.frthumb button.on { background: var(--hl); color: var(--fg); }
  html.g-mobile nav.frthumb button:focus-visible { outline: 2px solid var(--fg); outline-offset: -2px; }
  html.g-mobile #app .sidebar { bottom: calc(60px + env(safe-area-inset-bottom)); }
  html.g-mobile #app .reader-reference { min-width: 0; overflow: hidden; }
  html.g-mobile #app #reader-volume { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; white-space: normal; overflow-wrap: anywhere; }
}
#app .contents-toolbar { display: flex; align-items: center; justify-content: space-between; flex: none; gap: 12px; padding: 0 12px 0 16px; border-bottom: 1px solid var(--border); color: var(--fg); }
#app .contents-toolbar button { width: 48px; height: 48px; padding: 0; border: 0; background: none; color: inherit; font-size: 28px; touch-action: manipulation; }
@media (min-width: 1100px) { #app .contents-toolbar { display: none; } }
#app.fr-work-unavailable .sidebar,
#app.fr-work-unavailable .ph .ctr,
#app.fr-work-unavailable #sbT,
#app.fr-work-unavailable #rdAbout,
#app.fr-work-unavailable #fac,
#app.fr-work-unavailable #dragbar { display: none !important; }
