/* ANCHOR JUMPS ARE INSTANT, NEVER ANIMATED.

   Bootstrap's reboot sets `scroll-behavior: smooth` on :root inside a
   (prefers-reduced-motion: no-preference) query. That makes every jump to a
   #fragment glide the whole way down the document — which on About, Terms,
   Privacy and the commentary introduction means picking a section from the
   contents list sends the page rolling past every section above it (user,
   2026-07-31). On a page eighteen screens tall that is a long ride, and the
   reader arrives disoriented rather than placed.

   This is the same root cause the reader already fought: js/app.js flips the
   property to 'auto' around its programmatic scrollIntoView so a verse is not
   glided to. That workaround could only cover scrolls the app itself performs —
   a fragment jump is the browser's own navigation and no script is involved, so
   it needed fixing where it is set. Overriding it here does both jobs at once;
   the guard in app.js stays as it is, harmless and self-documenting.

   Set unconditionally rather than inside a media query of our own: this restores
   the platform default, and a reader who has asked for reduced motion was
   already getting it. Landing position is handled separately, by the
   scroll-margin-top on .prose-heading, .prose-subheading and .verse-row. */
:root {
    scroll-behavior: auto;
}

/* The search box's filter menus. The part-of-the-Bible menu is ~560px of choices —
   taller than a phone leaves below the box — so it is capped to the viewport and
   scrolls within itself; the version menu shares the rule harmlessly, four rows
   never reaching the cap. overscroll-behavior keeps a scroll that hits the menu's
   end from spilling into the page behind it. Popper (no data-bs-display="static"
   on these buttons) handles the rest, flipping the menu upward when the box sits
   low on a scrolled results page. */
.input-group > .dropdown-menu {
    max-height: min(35rem, 70vh);
    overflow-y: auto;
    overscroll-behavior: contain;
    /* Tighter than the surrounding prose (1.4): these are one-line menu rows, not
       sentences, and the tightening also buys the tall part-of-the-Bible list a
       little more room on a short screen. Set on the menu so rows and group
       headers inherit it together. */
    line-height: 1.2;
}

/* The worked example under the search box. The query is a button so it can be put
   into the field with one click (and reached by keyboard), but it has to READ as
   part of the sentence around it, not as a control — hence the full reset. The
   monospace and weight come from .prose code, which the search page already sits
   inside, so the query is set apart from the prose without a rule of its own. */
.search-hint-try {
    border: 0;
    background: none;
    padding: 0;
    font: inherit;
    color: inherit;
    cursor: pointer;
}
/* Underlining only the code keeps the affordance on the part that is clickable.
   The resume link ("pick up where you left off") dresses the same way, so a
   clickable query looks like one thing everywhere on the page. */
.search-resume-link {
    text-decoration: none;
    color: inherit;
}
.search-hint-try:hover code,
.search-hint-try:focus-visible code,
.search-resume-link:hover code,
.search-resume-link:focus-visible code {
    text-decoration: underline;
}
/* "Clear" beside the result count. No underline at rest — it sits inside a small
   uppercase line and a rule under it read as clutter — but one on hover and on
   keyboard focus, so the thing is still visibly a link at the moment of using it.
   Colour alone carries it the rest of the time. */
.search-clear {
    text-decoration: none;
    /* Reads as the word "Clear", not as another SHOUTED heading fragment: it is the
       one thing on the line you can act on, and sentence case sets it apart from the
       label it sits beside. Declared here to override the uppercase inherited from
       the heading around it. */
    text-transform: none;
}
.search-clear:hover,
.search-clear:focus-visible {
    text-decoration: underline;
}

/* The query echoed above the result count, inside a text-uppercase line. Shown
   exactly as it was typed: see the note in search.php — the search operators are
   case-sensitive, so uppercasing the echo could report a query that never ran. */
.search-echo {
    text-transform: none;
}

/* The place band, above the verse results. It answers a different question from
   the verses beneath it — where a place is, rather than where it is spoken of —
   so it is set apart by a rule and a tint rather than dressed as a first result.
   Quiet on purpose: for Jerusalem, on thirteen plates, this still has to read as
   an aside the eye can pass over on its way to Scripture. */
/* The measures band — a unit, a month or a feast the query names — is the same
   object with different content, so it shares every rule here rather than
   copying them: one look, and the two cannot drift apart. Extend these selector
   lists for any third band rather than adding a block. */
/* The commentary band is the third, added 2026-07-31, and it joins these lists
   rather than getting a block of its own — exactly as the note above asks. It
   holds up to three comments instead of one line, which is the only reason it
   has any rules of its own further down. */
/* The ad band is the fourth (2026-08-02), and it joins the lists the same way.
   Its one distinction is deliberate and small: a neutral grey edge instead of
   the site's primary blue, so a paid band never borrows the exact dress of the
   three editorial ones — the "Sponsored" tag says it in words, the edge says it
   at a glance. */
.search-topic,
.search-place,
.search-measure,
.search-commentary,
.search-ad {
    border-left: 3px solid var(--bs-primary, #0d6efd);
    background: rgba(13, 110, 253, 0.04);
    border-radius: 0 0.25rem 0.25rem 0;
    padding: 0.5rem 0.75rem;
}
.search-ad {
    border-left-color: var(--bs-secondary, #6c757d);
    background: rgba(108, 117, 125, 0.05);
    color: inherit;
}
/* OUR OWN BAND IS GOLD (Daniel, 2026-08-15) — the same rule and wash as an
   advertiser's band, in the app's own colour instead of the neutral grey, so a
   Biblion announcement is told from an advertisement at a glance and before the
   label is read.

   ⚠ THIS IS THE ONE PLACE GOLD DOES NOT MEAN "HERE". The standing rule
   ([[bible-gold-system]]) is that `rgba(201,162,39,.22)` marks the selected
   thing; this is identity, not selection, and it is kept apart from that rule by
   being a different value — --biblion-gold-tint, 0.16, which exists for exactly
   this kind of quiet ground and is used by no selection state. Do not "unify"
   the two alphas: the day they match, a band starts looking like it is chosen. */
.search-ad-own {
    border-left-color: var(--biblion-gold);
    background: var(--biblion-gold-tint, rgba(201, 162, 39, 0.16));
}
.search-ad:hover .search-ad-name { text-decoration: underline; }
/* Two place bands stacking is the only adjacency there is: no word is both a
   place on the plates and a measure on that page — checked, 140 terms against
   465 place names — so a measure band under a place band is left to its own
   mt-3 rather than given a selector here for a case that cannot arise. If one
   ever does, this is the line that wants ".search-place + .search-measure". */
.search-place + .search-place { margin-top: 0.4rem; }
/* THE PEOPLE & PLACES BAND IS VIOLET, alone on this page, and the reason is the
   app's own colour language rather than variety: the blue bands are Biblion
   telling you something (our plates, our measures, our commentary), and this is
   a reference work QUOTED — the same act as the lexicon article behind a
   Strong's number, which is where a reader first meets this hue
   ([[bible-gold-system]], [[bible-lexicon-articles]]). Same rule, same wash,
   same shape as its blue siblings above; only the channel differs. */
.search-who {
    border-left: 3px solid #7a5ea8;
    background: rgba(122, 94, 168, 0.05);
    border-radius: 0 0.25rem 0.25rem 0;
    padding: 0.5rem 0.75rem;
}
.search-who + .search-who { margin-top: 0.4rem; }
/* ⚠ THE LINK IS DARKENED INSIDE THIS BAND, and the reason is the wash under it.
   Bootstrap's #0d6efd is 4.63:1 on white — already only just past AA — and the
   band's 5% violet ground drops it to 4.22:1, which fails. #0a58ca is
   Bootstrap's own hover blue and measures 5.9:1 on the same ground, so the fix
   costs no new colour. Anywhere a band tints its background, re-measure the
   link rather than assuming the default still clears. */
.search-who a { color: #0a58ca; }
/* THE CREDIT UNDER THE BANDS IS SMALLER THAN THEY ARE (Daniel, 2026-08-19):
   at body size it was 1rem against the band's own 0.95rem, so the
   acknowledgement was louder than the thing being acknowledged.

   ⚠ This is a DELIBERATE EXCEPTION to the standing rule in [[bible-typography]]
   that a note is body text and never small or muted — and the distinction is
   worth keeping: that rule protects notes written TO the reader, which are
   either worth full size or should not be written. This is not one. It is a
   licence condition discharged in the reader's sight, and its job is to be
   present and legible, not to compete with the answer it sits under. The
   attribution paragraphs on who.php, place.php and people.php are notes and
   stay at body size. */
.search-who-credit {
    font-size: 0.8rem;
    color: var(--bs-secondary-color, #6c757d);
    /* IT WEARS THE BAND'S OWN DRESS AND TOUCHES IT (Daniel, 2026-08-19): "use
       the same band for credits as for results, so the user will know
       intuitively they belong together". As a bare paragraph below the bands it
       read as page text that happened to follow them — a shared boundary is the
       strongest grouping signal there is, and two things inside one violet edge
       are one thing. Same rule, same wash, and NO gap above, so the credit is
       the foot of the band rather than a neighbour of it; only the bottom
       corner rounds, because the top edge is a seam, not an end. */
    /* It is rendered INSIDE the last band rather than after it, so there is no
       seam to hide and no dependence on :has() — which sits exactly on this
       project's browser floor ([[bible_browser_compat]]) and is the wrong thing
       to spend a margin of safety on when the markup can simply say it. */
    margin-top: 0.45rem;
}
.search-place-name,
.search-measure-name,
.search-commentary-name,
.search-who-name,
.search-ad-name {
    font-weight: 600;
    text-transform: none;   /* the heading above this line shouts; this does not */
}
/* The topic band's title is the ONE thing on this page set in the reading serif
   outside the verses themselves, and that is how it tells itself apart from the
   two editorial bands beneath it without taking a colour of its own
   ([[bible-typography]] — 600 is a real cut of this face; 500 and 700 are not).
   It is also the name of the page it opens, word for word, so what the reader
   lands on is what the band promised. */
.search-topic-name {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-size: 1.05rem;
    font-weight: 600;
    text-transform: none;
}
.search-topic-blurb {
    color: var(--bs-secondary-color, #6c757d);
}
.search-topic:hover .search-topic-name { text-decoration: underline; }
/* THE COMMENTARY BAND'S HEADING IS NORMAL WEIGHT (Daniel, 2026-08-15), alone
   among the four above. A place or a measure name is a RESULT — the thing you
   were looking for — and carries the weight of one. "The commentary on this" is
   a label on a band, not a hit, and setting it like the others made the band
   announce itself more loudly than the comments inside it. Split out rather than
   removed from the rule above, so the other three keep the treatment that is
   right for them. */
.search-commentary-name { font-weight: 400; }
/* "in Macedonia" — the three names that are two different towns. Set in the
   name's own weight, because it is part of the name here and not a comment on
   it: without it the two Pellas are told apart only by doing the geography. */
.search-place-qual { font-weight: 600; }
/* "the conquest plate's name for Jerusalem". The reader typed Jebus; being
   handed Jerusalem with no explanation reads as the search having misheard. */
.search-place-alias,
/* THE MATCH HIGHLIGHT IN A VERSE — a wash in the app's gold, not a badge
   (Daniel, 2026-08-15, in two steps: normal weight first, then "the same shade
   of gold we have on command palette instead of this warning color").

   THE VALUE IS THE COMMAND PALETTE'S OWN, character for character. There it
   says "this is the line you are on"; here it says "this is the word you asked
   for". One colour, one meaning — and the two surfaces are near enough in
   background (the palette panel rgb(250,247,240), these results white) that the
   same alpha genuinely reads the same on both.

   It replaced `badge text-bg-warning`, which was wrong twice over: the warning
   role is the colour of "careful", and a matched word is the thing the reader
   came for; and a badge is a solid pill with padding and a 6px corner, which
   drops a chip into running scripture instead of marking it. Normal weight
   stays for the reason it was set — the tint finds the word, and the verse
   should still read as a verse.

   Measured before shipping: ink reads 11.5:1 on this wash against 12.9:1 on the
   old amber, so no legibility was traded for the calm. */
/* THE VERSION CODE IN FRONT OF A SEARCH RESULT IS A COLUMN, NOT A WORD
   (Daniel, 2026-08-15: "can version identifiers WEB, KJV etc. be fixed width?
   So that the verse text will start at the same point").

   Measured at this size and weight, the seven codes run from KJV at 26.0px to
   WEB at 31.1px, so two lines of the same result started 5px apart and the left
   edge of the text sawtoothed down the page. The same idea as .palette-kind,
   which is fixed-width for exactly this reason.

   MIN-WIDTH, NOT WIDTH, and that is the whole care in this rule: a code longer
   than three characters — a future translation, or the "WLC ketiv" variant the
   reader already prints — pushes the column wider instead of being clipped or
   overrunning its neighbour. Everything at three characters still lines up.

   2.25rem clears the widest code with room to spare; the me-1/ms-2 margins on
   the markup still supply the gap, so the two decisions stay separate. */
/* THE WRAPPED LINES OF A VERSE HANG UNDER THE FIRST (Daniel, 2026-08-15:
   "align the second row of verse ... with the first row"). The code sat inline,
   so line one flowed after it and line two returned to the container's left
   edge, under the label.

   ⚠ FLEX WAS TRIED HERE FIRST AND IS CATASTROPHIC — do not go back to it. The
   reasoning that it would work was wrong in a specific way worth writing down:
   only a contiguous run of TEXT becomes one anonymous flex item, while every
   ELEMENT child becomes an item of its own. A verse in these results is text,
   then a <span class="search-hit">, then text, then another mark — so the whole
   verse shattered into one narrow column per fragment, words stacked
   vertically. Daniel saw it on the page: "just the row, not all the words, this
   look awful now."

   A negative text-indent was the second attempt and also wrong: it outdents the
   whole first line, and the first line carries the label, so WEB and KJV hung
   out to the left of "Genesis 14:2" and past the row's shading. Absolute
   positioning was the third: it fixed the indent and the outdent, but a label
   out of the flow shares no baseline with the text it labels, so the code
   floated against the verse's first line.

   ⇨ THE MARKUP IS A TWO-COLUMN TABLE NOW (Daniel's suggestion), and these rules
   are all that is left to do. A cell gives the three things the CSS was
   fighting for: baseline alignment between the code and the verse's first line,
   a column that every wrapped line clears, and no way to outdent. Do not try to
   put this back into a div — the failures above are the whole history of that
   idea. See the note at the markup in search.php. */
.search-verses {
    width: 100%;
    border-collapse: collapse;
}
.search-verses td {
    /* The whole point: the code and the verse's first line sit on one baseline. */
    vertical-align: baseline;
    padding: 0;
}
/* Scoped to td.search-source so it outranks the `padding: 0` above on
   specificity — (0,2,1) against (0,1,1) — rather than with an !important. */
.search-verses td.search-source {
    /* Shrink-to-fit against a width this small, so the column comes out as wide
       as the widest code and no wider; nowrap because a two-line "WLC ketiv"
       would set the row's height from the label rather than from the verse. */
    width: 2.5rem;
    white-space: nowrap;
    padding-inline-end: 0.35rem;
}

/* The commentary band's reference, set as a USFM code — "DAN 5:10-16" — so the
   headings after it start on one line (Daniel, 2026-08-15; the markup note is
   in search.php). The code fixes the book at three characters; this column
   absorbs what is left, which is only how many digits a chapter and a verse
   range take.

   min-width again, not width: a reference like "1CH 12:23-40" is wider than
   "JOB 1:1" by digits alone, and the long one should push rather than be
   clipped. Sized to clear the common shapes; the rare very long range moves its
   own heading and nothing else. */
/* The commentary band is a two-column table like the verse results above.
   THE COLUMN SIZES ITSELF: `width: 1px` on a table cell means "as narrow as the
   content allows", so the reference column comes out exactly as wide as the
   longest reference in THIS band and every heading lines up on it. The 7.5rem
   min-width this used to carry is gone — it was a guess at the widest possible
   reference, and once the references shortened to USFM codes it was far too
   wide for most bands.

   NO UNDERLINE, AND THAT MEANS ON HOVER TOO (Daniel, 2026-08-15). A flat visual
   rule is absolute here, not a starting point — keeping the hover underline is
   the exact mistake made on the article map links, shipped as a "softened"
   compromise and called out on the live page. The reference is blue and sits in
   a list of links; it needs no rule under it to be found. */
.search-commentary-hits {
    width: 100%;
    border-collapse: collapse;
}
.search-commentary-hits td {
    vertical-align: baseline;
    padding: 0 0 0.5rem;
}
.search-commentary-hits td.search-commentary-ref {
    width: 1px;
    white-space: nowrap;
    padding-inline-end: 0.6rem;
    /* Black (Daniel, 2026-08-15). It stopped being a link when the link moved to
       the title, and it was muted grey for an hour on my reading that "not the
       thing you press" meant "quieter" — it does not. The range is a fact about
       the comment and a reader scanning the band uses it to place the passage,
       so it is set in the same black as the book buttons rather than in the grey
       Bootstrap keeps for things that look disabled. The blue it used to wear
       was the misdirection; grey was an overcorrection. */
    color: #000;
}

.search-hit {
    background-color: rgba(201, 162, 39, 0.22);
    border-radius: 0.15em;
    padding: 0.05em 0.15em;
    font-weight: 400;
}

.search-place-kind,
.search-topic-kind,
.search-measure-kind,
.search-commentary-kind,
.search-who-kind,
.search-ad-kind {
    font-weight: 400;
    font-size: 0.85rem;
    color: var(--bs-secondary-color, #6c757d);
    text-transform: none;
}
.search-place-kind::before,
.search-topic-kind::before,
.search-measure-kind::before,
.search-commentary-kind::before,
.search-who-kind::before,
.search-ad-kind::before { content: ' · '; }
/* EVERY BAND'S ANSWER IS ONE SIZE (Daniel, 2026-08-19): 0.95rem, which is what
   the commentary band's point already used — "let's say just like commentary
   band uses". The sizes had drifted apart, 0.9 here and 0.95 there, and the
   difference read as a hierarchy nobody intended: the people & places sentence
   looked like a footnote beside a commentary point doing the identical job.

   These five lines are the ANSWER each band exists to give, one per band, so
   they are set alike and kept in one rule where they cannot drift again. What
   stays smaller stays smaller on purpose and is NOT an answer — the ·kind label
   above (0.85rem) and the licence credit below (0.8rem), which Daniel checked
   separately and left alone the same day. */
.search-who-line,
.search-topic-blurb,
.search-place-maps,
.search-measure-link,
.search-commentary-point { font-size: 0.95rem; }
.search-place-sep,
.search-measure-sep { color: var(--bs-secondary-color, #6c757d); }

/* The commentary band's own three rules — it carries up to three hits where the
   other two carry one line, so it needs a little internal structure.

   The HEADING is the comment's title and the POINT is its one-sentence answer.
   The point is the reason the band exists, so it is given the room of its own
   line rather than being run in after the title, where it would read as a
   subtitle and be skipped. It stays at body colour: a muted grey here would
   say "small print" about the only sentence on the band worth reading. */
/* The comment's title, and since 2026-08-15 it is THE LINK of the row — the
   reference beside it is plain metadata. Bold because it is the heading of the
   thing being offered, blue because it is what opens, and never underlined, on
   hover as well as at rest, by the same flat rule the reference link followed.

   No left margin: it used to hold the heading off an inline reference, and the
   reference is a cell of its own now with its own padding. Left in place it
   indented the title 0.35rem past the point beneath it. */
.search-commentary-heading { font-weight: 600; }
a.search-commentary-heading,
a.search-commentary-heading:hover,
a.search-commentary-heading:focus {
    text-decoration: none;
}
/* Its size lives with the other band answers, above — it was the one they were
   raised to match. */
.search-commentary-point {
    margin-top: 0.1rem;
}
/* Last hit sits flush with the band's padding, so the box does not gain a
   stray half-line of air under it. As a table row this is the CELL's padding —
   margin does not apply to a row, so the old margin-bottom rule here became a
   no-op the moment the band became a table. */
.search-commentary-hits tr:last-child td { padding-bottom: 0; }

/* Where a hint is about one of the buttons rather than something to type, its icon
   stands in the query's place. Given the same weight and colour as the <code> it
   replaces, so the line keeps one rhythm — and it is NOT underlined on hover,
   because unlike the examples it is a picture of a control, not a click target. */
.search-hint-icon {
    color: #000;
    font-size: 0.95em;
}

/* Prompt — the site's UI/display face, full weight range (Thin 100 … Black 900). */
@font-face { font-family: 'Prompt'; font-style: normal; font-weight: 100; font-display: swap; src: url('../fonts/prompt-100.woff2') format('woff2'); }
@font-face { font-family: 'Prompt'; font-style: normal; font-weight: 200; font-display: swap; src: url('../fonts/prompt-200.woff2') format('woff2'); }
@font-face { font-family: 'Prompt'; font-style: normal; font-weight: 300; font-display: swap; src: url('../fonts/prompt-300.woff2') format('woff2'); }
@font-face { font-family: 'Prompt'; font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/prompt-400.woff2') format('woff2'); }
@font-face { font-family: 'Prompt'; font-style: normal; font-weight: 500; font-display: swap; src: url('../fonts/prompt-500.woff2') format('woff2'); }
@font-face { font-family: 'Prompt'; font-style: normal; font-weight: 600; font-display: swap; src: url('../fonts/prompt-600.woff2') format('woff2'); }
@font-face { font-family: 'Prompt'; font-style: normal; font-weight: 700; font-display: swap; src: url('../fonts/prompt-700.woff2') format('woff2'); }
@font-face { font-family: 'Prompt'; font-style: normal; font-weight: 800; font-display: swap; src: url('../fonts/prompt-800.woff2') format('woff2'); }
@font-face { font-family: 'Prompt'; font-style: normal; font-weight: 900; font-display: swap; src: url('../fonts/prompt-900.woff2') format('woff2'); }
/* ⚠ THE ONLY REAL ITALICS ON THE SITE — added 2026-08-15. Until then not one
   @font-face declared an italic style, so EVERY <em> anywhere was a SYNTHETIC
   OBLIQUE: the browser shearing the upright glyphs. It shows worst in the
   commentary, where the Greek transliterations are set in emphasis
   (*koinōneō* in the corpus, rendered as <em>) and came out as slanted Prompt
   Light rather than a drawn italic — Daniel took it for a font substitution.

   HOW TO PROVE WHICH YOU ARE LOOKING AT, since a shear is easy to mistake for a
   face: measure the same word upright and italic. A synthetic oblique keeps the
   advance widths exactly — "koinōneō" measured 439.3px both ways before this —
   while a real italic redraws the letterforms and cannot match to the pixel.

   TWO WEIGHTS, NOT NINE: 300 is .prose's body weight and 500 is what .prose
   strong takes, so those are the only two an <em> can inherit today. If a
   heading weight ever needs emphasis, fetch that cut too rather than letting it
   fall back to synthesis.

   ⚠ TWO FILES PER WEIGHT, AND THE unicode-range IS LOAD-BEARING. Google serves
   this face in subsets, and the `latin` one stops at U+00FF. The transliterations
   this was added for live ABOVE that line — ō is U+014D, ē is U+0113, ḥ is
   U+1E25 — so a latin-only italic would have covered "k o i n e" and not "ō",
   and rendered koinōneō from two different fonts mid-word: the very fault this
   is meant to cure, made worse. The `-ext` files carry Latin Extended-A and the
   ranges below are copied from Google's own CSS. Do not "simplify" this to one
   file per weight unless the file genuinely carries both ranges — the upright
   cuts already do, which is why they need no such split. */
@font-face { font-family: 'Prompt'; font-style: italic; font-weight: 300; font-display: swap; src: url('../fonts/prompt-300italic-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: 'Prompt'; font-style: italic; font-weight: 300; font-display: swap; src: url('../fonts/prompt-300italic.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Prompt'; font-style: italic; font-weight: 500; font-display: swap; src: url('../fonts/prompt-500italic-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: 'Prompt'; font-style: italic; font-weight: 500; font-display: swap; src: url('../fonts/prompt-500italic.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
/* Roboto Serif (Regular / SemiBold / ExtraBold) — the scripture itself and the
   running header above it, and the headings on the about page. 400 and 600 are
   preloaded in head.php: they are wanted for the first paint of every chapter,
   and discovered here they would be requested too late to make it. */
@font-face { font-family: 'Roboto Serif'; font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/roboto-serif-400.woff2') format('woff2'); }
@font-face { font-family: 'Roboto Serif'; font-style: normal; font-weight: 600; font-display: swap; src: url('../fonts/roboto-serif-600.woff2') format('woff2'); }
@font-face { font-family: 'Roboto Serif'; font-style: normal; font-weight: 800; font-display: swap; src: url('../fonts/roboto-serif-800.woff2') format('woff2'); }

/* The face that stands in for Roboto Serif until it arrives — Georgia, or Times
   where there is no Georgia, adjusted to occupy exactly the space Roboto Serif
   will. Without this, a chapter renders in a font 11.7% narrower with a smaller
   x-height, and then everything below reflows the instant the real one lands:
   measured at 943px of jump on Psalm 119, which throws the reader off the verse
   they followed a link to. With it, the swap changes the letterforms and moves
   nothing at all.
   The numbers are measured, not guessed. Average character width per em gives
   113.2% as a first approximation, but average width is not how a paragraph
   actually breaks; sweeping the value against a real chapter and comparing page
   heights lands on 115.5%, which leaves Psalm 119 within 22px over its 18,888 —
   about a tenth of one percent, and nothing a reader can see. Ascent and descent
   are Roboto Serif's own 0.927 and 0.244, each divided by the size-adjust so the
   line box comes out the same height. Re-measure the same way if the reading
   face is ever changed.

   ONE NUMBER PER FACE, measured 2026-07-28. This was a single rule carrying
   115.5% for all seven names, which was right only for Georgia: the others were
   wearing Georgia's number. Times New Roman needs 125.8%, Noto Serif — the
   stock Android serif, so the one most readers off Windows actually get —
   106.7%, DejaVu Serif 99.3%. Noto at 115.5% was rendering about 8% too wide,
   which is the reflow this whole mechanism exists to prevent.

   How they were measured, and how to redo it: set one long line of scripture
   in 'Roboto Serif' at a large size with white-space: nowrap, measure it, then
   measure the same line in the candidate face; size-adjust is target ÷ candidate.
   Ascent and descent stay Roboto Serif's own 0.927 and 0.244, each divided by
   that face's size-adjust so every line box comes out the same height.
   TWO TRAPS in doing it again:
     - Measure in the browser against the LOADED webfont, never from a font file
       downloaded upstream. Roboto Serif ships here as a static 400 instance;
       the upstream file is variable, and its default instance has different
       advance widths, which puts the answer out by ~6%. (x-height agrees
       between the two, because sxHeight is one static table value — so an
       x-height-based check will look fine while the widths are wrong. Width is
       the basis that matters: reflow is line breaking.)
     - Georgia's 115.5% was left exactly as swept. An independent width
       measurement gives 116.2%, i.e. it confirms the original sweep rather than
       replacing it, and 0.7% is not worth changing a value that was looked at
       and accepted.

   DECLARATION ORDER IS REVERSE PREFERENCE, and this is the trap that makes the
   split work at all. When several @font-face rules share a family name and
   differ only in src, the LAST one whose local() resolves wins — verified in
   this browser, both directions. So the order below runs least-preferred first
   and Georgia last, which reproduces the old src list's order exactly. That is
   also why the Times metrics appear twice: Liberation/Tinos have to sit before
   Noto Serif and Times/Times New Roman after it, or the old preference order
   would change on machines carrying more than one of them.

   WHERE THIS DOES NOT REACH, and it is worth knowing rather than rediscovering:

   1. Safari 16 and earlier — including iOS 16 — ignore all four override
      descriptors. There is no polyfill and no fallback: on those the stand-in
      renders at its own metrics and the reflow above is back. Nothing to fix
      here; it goes away as iOS 17 finishes rolling through.
   2. The src list is what makes the mechanism engage at all, and Georgia/Times
      are Microsoft/Apple fonts. On stock Android none of them exist, so the
      rule matched nothing, 'Reading Fallback' dropped out of the stack, and the
      reflow was back for a different reason. Hence the Android and Linux names
      — Liberation Serif and Tinos being metric-compatible with Times New Roman,
      though fontconfig often aliases those to Times before it gets this far. */
@font-face {
    font-family: 'Reading Fallback';
    src: local('DejaVu Serif');
    size-adjust: 99.3%;
    ascent-override: 93.4%;
    descent-override: 24.6%;
    line-gap-override: 0%;
}
/* Metric-compatible with Times New Roman, so they share its number. Declared
   before Noto Serif to keep the old order, in which Noto was preferred. */
@font-face {
    font-family: 'Reading Fallback';
    src: local('Liberation Serif'), local('Tinos');
    size-adjust: 125.8%;
    ascent-override: 73.7%;
    descent-override: 19.4%;
    line-gap-override: 0%;
}
@font-face {
    font-family: 'Reading Fallback';
    src: local('Noto Serif');
    size-adjust: 106.7%;
    ascent-override: 86.9%;
    descent-override: 22.9%;
    line-gap-override: 0%;
}
@font-face {
    font-family: 'Reading Fallback';
    src: local('Times New Roman'), local('Times');
    size-adjust: 125.8%;
    ascent-override: 73.7%;
    descent-override: 19.4%;
    line-gap-override: 0%;
}
/* Last, so it wins wherever it exists — which is every Windows and every Mac. */
@font-face {
    font-family: 'Reading Fallback';
    src: local('Georgia');
    size-adjust: 115.5%;
    ascent-override: 80.3%;
    descent-override: 21.1%;
    line-gap-override: 0%;
}

/* Noto Sans Hebrew / Greek — the original-language texts (WLC and the Greek TR)
   and the Strong's lemmas. Self-hosted like every other font so the rendering
   is the same on every OS instead of leaning on whatever the system provides.
   Each face only covers its own script, so a combined stack lets the browser
   pick the right one per glyph — Hebrew from the Hebrew face, Greek from the
   Greek face — which is why the lemma (either script) can list both. */
@font-face { font-family: 'Noto Sans Hebrew'; font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/noto-sans-hebrew-400.woff2') format('woff2'); }
@font-face { font-family: 'Noto Sans Greek';  font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/noto-sans-greek-400.woff2')  format('woff2'); }
/* POLYTONIC. The file above is Google's Noto Sans 'greek' subset — verified
   byte-identical to it — and that subset is MONOTONIC ONLY (U+0370-03FF). The
   Textus Receptus is polytonic throughout: breathings, circumflexes and iota
   subscripts all live in Greek Extended, U+1F00-1FFF, and without this face
   every one of them fell out of the family and was drawn by Segoe UI on
   Windows and by each other OS's own serif elsewhere — inside a word, so
   'ἀρχῇ' was rendered from two faces at once. Measured 2026-08-13: 1,078 of
   the Greek characters on John 1 are polytonic.

   It is declared AFTER the face above and carries an explicit unicode-range,
   which is what makes it win for exactly this block and nothing else: when
   two @font-face rules of one family overlap, the LAST is checked first, and
   this one's range does not contain the monotonic letters. The rule above is
   deliberately left without a range so nothing else about it changes. */
@font-face { font-family: 'Noto Sans Greek';  font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/noto-sans-greek-ext-400.woff2') format('woff2'); unicode-range: U+1F00-1FFF; }
/* Suez One for the Shema on the landing page — a heavy, square Hebrew slab
   serif, one weight (already black by design). Only the landing page pulls it,
   so its weight is not on the reader's critical path. Hebrew subset only. */
@font-face { font-family: 'Suez One'; font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/suez-one-400.woff2') format('woff2'); }
/* Google Sans (Bold) for the Greek quote on the landing page. Greek subset
   only; landing page only. */
@font-face { font-family: 'Google Sans'; font-style: normal; font-weight: 700; font-display: swap; src: url('../fonts/google-sans-greek-700.woff2') format('woff2'); }

@font-face {
    font-family: 'Gochi Hand';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/gochi-hand-v27-latin-regular.woff2') format('woff2'),
         url('../fonts/gochi-hand-v27-latin-regular.ttf') format('truetype');
}
/* Roboto Mono — for code/monospace. */
@font-face {
    font-family: 'Roboto Mono';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/roboto-mono-400.woff2') format('woff2');
}
@font-face {
    font-family: 'Roboto Mono';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('../fonts/roboto-mono-700.woff2') format('woff2');
}

/* Use Roboto Mono for all monospace (code, kbd, samp, pre via Bootstrap). */
:root {
    --bs-font-monospace: 'Roboto Mono', SFMono-Regular, Menlo, Consolas, monospace;

    /* THE GOLD. One accent, shared on purpose (Daniel, 2026-08-02): the
       player's scrubber and auto-advance switch, the verse being spoken, and
       the scroll progress line at the top of the page all carry it, so that
       "something is running" reads the same wherever it appears. Named because
       it is now five places rather than one, and a hue this specific should be
       changed in one edit.

       IT IS A CONSTANT, NOT THE MOOD'S GOLD. The brand mark's --brand-roller is
       tinted per colorway from each wallpaper's own palette; this deliberately
       is not, or the progress line and the player would drift apart from each
       other every time the reader changed the background.

       The tint is spelled out rather than derived: color-mix() would express it
       from the line above, but it only reached Chrome 111 and the floor here is
       108 ([[bible-browser-compat]]), and a fallback-less color function fails
       to an invalid declaration rather than to a sensible color. */
    --biblion-gold: #c9a227;
    --biblion-gold-tint: rgba(201, 162, 39, 0.16);
    /* The app's one red, for the destructive controls (Daniel, 2026-08-15:
       "pink-red-tomato"). A warmer red than Bootstrap's crimson #dc3545, and
       CHOSEN BY MEASUREMENT rather than by eye: white on it is 4.73:1, which
       clears AA at button-label size. Tomato itself (#ff6347) is 2.95:1 and
       fails outright — it looks right and is unreadable. If this value is ever
       warmed further, re-measure the white. */
    --biblion-tomato: #cf4125;

    /* The rounded corners, the notch and the home indicator, as four lengths
       anything pinned to an edge can add to its own offset.

       THESE ARE ZERO TODAY, ON EVERY DEVICE, AND THAT IS DELIBERATE. An inset
       only becomes non-zero when the viewport meta in includes/head.php carries
       viewport-fit=cover; without it iOS keeps the layout viewport inside the
       safe area, so nothing can land under the notch and every calc() below
       reduces to the plain offset it had before. The plumbing is in place so
       that turning cover on is a one-attribute change rather than a hunt
       through the stylesheet — and so it can be previewed here: override these
       four on :root in devtools (iPhone 14 Pro portrait is 59/0/34/0) and the
       whole page lays out as it would on the phone, without a phone.

       Kept as variables rather than repeating env() at each site so that the
       preview above works at all: env() cannot be overridden, a variable can. */
    --safe-t: env(safe-area-inset-top, 0px);
    --safe-r: env(safe-area-inset-right, 0px);
    --safe-b: env(safe-area-inset-bottom, 0px);
    --safe-l: env(safe-area-inset-left, 0px);
}

.strongnum, .morph {
    font-family: 'Prompt', sans-serif;
    margin-left: 2px;
    padding: 0 1px;
}

.strongnum {
    cursor: pointer;
}

/* Brand wordmark in the site's display font. */
.navbar-brand {
    font-family: 'Prompt', sans-serif;
}

/* The fixed bar spans the full width, so under viewport-fit=cover its contents
   would run beneath the notch and the rounded corners. Padding, not offsets:
   the bar's background should still reach every edge — that is the point of
   going edge to edge — while what it holds stays inside the safe area.
   Zero today; see the note on --safe-* at the top of this file.

   NOT DONE HERE, and it is the remaining half of the job: the CLEARANCE under
   this bar. The reader's own is below (.verses.reader-flow), but every other
   page clears the bar with Bootstrap's pt-5 written into its markup, which no
   stylesheet rule can reach. Those pages need their own top padding before
   viewport-fit=cover goes on, or their first line will sit under a bar that
   just grew by the height of the notch. */
.navbar.fixed-top {
    /* Through Bootstrap's own variable, not a repeated 0.5rem, so a change to
       the framework's navbar padding is not silently overridden here. */
    padding-top: calc(var(--bs-navbar-padding-y, 0.5rem) + var(--safe-t));
    padding-left: var(--safe-l);
    padding-right: var(--safe-r);
}

/* The chapter picker only holds numbers, so keep it just wide enough for
   three digits plus the dropdown caret rather than the placeholder text. */
.chapter-select {
    width: 4.75rem;
}

/* Navbar dropdowns (Options, account menu) open on click same as ever, just
   without Bootstrap's caret glyph. */
.navbar-nav .dropdown-toggle::after {
    display: none;
}

/* The items in those menus are set Light. They are a list of places to go
   rather than anything to be read, and at this size Prompt's regular weight
   makes a short menu look heavier than the bar above it. The headers between
   the bands ("Mood", "Admin") keep their medium weight, which is what now
   separates a label from the items under it. */
.navbar .dropdown-menu .dropdown-item {
    font-weight: 300;
}

/* NOTHING IN THE BAR OR ITS MENUS TAKES A SECOND ROW — it is shortened with an
   ellipsis instead (Daniel, 2026-08-07). The items that can grow are all the
   dynamic ones: "Continue reading Song of Solomon 1", "Back to the reading for
   August 6", the Resume line the player writes, and the account menu carrying a
   reader's own name. Any of them can outrun the menu on a narrow screen, and a
   menu entry that becomes two lines reads as two entries.

   Bootstrap already sets white-space: nowrap on .dropdown-item, which alone
   makes a long label widen the MENU rather than wrap — off the side of a phone.
   The cap is what turns that into an ellipsis: wide enough that every fixed
   label in the menus fits untouched, and bounded by the viewport so it can
   never overhang. min() rather than a bare rem for the same reason .auth-card
   uses one — a fixed width wider than a 375px phone runs off it. */
.navbar .dropdown-menu {
    max-width: min(22rem, calc(100vw - 1.5rem));
}

/* THE SELECTED AND THE JUST-CLICKED ITEM ARE GOLD, NOT BOOTSTRAP BLUE (Daniel,
   2026-08-15: "use gold in here and for that brief moment the blue color shows
   now when menuitem is clicked").

   Both are one variable's job. Bootstrap draws `.dropdown-item.active` and
   `.dropdown-item:active` from the same --bs-dropdown-link-active-bg, so the
   standing mark on the current mood and the flash under a finger are the same
   colour by construction — override the variable and there is no specificity
   fight to lose, and no rule to keep in step with a future Bootstrap.

   #0d6efd was the last loud blue left in the chrome: on a near-black menu it
   was the brightest thing on the screen, for an item whose whole job is to say
   quietly which mood is already on.

   TWO SURFACES, TWO STRENGTHS. The navbar's menus are near-black, where 0.22
   nearly disappears, so they take 0.28 with white type. Every other dropdown in
   the app — the search filters, the map switch — sits on white and takes 0.22
   with the menu's own ink, because white type on a pale wash would be
   unreadable. Same colour either way. */
.dropdown-menu {
    --bs-dropdown-link-active-bg: rgba(201, 162, 39, 0.22);
    --bs-dropdown-link-active-color: inherit;
}
.navbar .dropdown-menu {
    --bs-dropdown-link-active-bg: rgba(201, 162, 39, 0.28);
    --bs-dropdown-link-active-color: #fff;
}
.navbar .dropdown-menu .dropdown-item {
    overflow: hidden;
    text-overflow: ellipsis;
}
/* The top-level items too — "Navigator", "Articles", and the account toggle,
   which is a name and so is the one that can surprise. */
.navbar .nav-link {
    white-space: nowrap;
}

/* Sign-in / register / account pages. */
/* Sign in, register, recover. A narrow card rather than a reading column, so it
   is deliberately not .prose — but it was inheriting nothing from it either, and
   these three pages were the only ones on the site set in the browser's default
   sans. The type is taken from .prose and the width is not: same voice, own
   shape. Kept in step by hand, which is worth a glance if .prose ever changes. */
.auth-card {
    /* min(), not a bare 25rem: at 400px the card was wider than a 375px phone
       and ran off the edge of it. */
    width: min(25rem, 100%);
    font-family: 'Prompt', sans-serif;
    font-weight: 300;
    line-height: 1.4;
    color: #2c2926;
}

/* Sign in, register, recover and the recovery-code screen are laid out the same
   way: the business of the page in two thirds, an aside in the remaining third.
   The card no longer sets its own width — the column does that now — but keeps a
   ceiling so a form does not sprawl across a wide screen. */
.auth-layout .auth-card {
    width: 100%;
    max-width: 30rem;
}

/* The contact form is the one card here that wants the whole column. The 30rem
   cap suits the sign-in pages, where every field is a single short line and a
   wider box would only stretch the gap between label and value — but this form is
   mostly an eight-row textarea, and someone writing several paragraphs into a
   30rem box is being made to work in a slot when there is a column of free space
   beside it. Applied per page rather than raising the shared cap, because the
   reason is this form's shape and not a change of mind about the others. */
.auth-layout .auth-card-wide {
    max-width: none;
}

/* The aside beside the form. Set as scripture is set everywhere else — Roboto
   Serif, the same x-height correction the reader uses — and kept quiet: this is
   something to rest on while signing in, not a second thing to do. */
.auth-verse {
    margin: 2.5rem 0 0;
    padding-top: 1.5rem;
    border-top: 1px solid rgba(0, 0, 0, 0.08);
    text-align: center;
}

/* Once the columns are side by side, the aside stops being a footnote and
   becomes a margin note: no rule above it, aligned with the text rather than
   centred, and held down the page a little so it sits against the form rather
   than floating at the top of an empty column. */
@media (min-width: 992px) {
    .auth-layout .auth-verse {
        margin: 0;
        padding-top: 5.5rem;
        padding-right: 1.5rem;
        border-top: 0;
        text-align: left;
    }
}

/* Typing a recovery code back in. Monospace and letter-spaced like the slip it
   was copied from, so the two read as the same thing and a mistyped character is
   easier to spot. */
.recovery-input {
    font-family: 'Roboto Mono', monospace;
    letter-spacing: 0.06em;
}

/* The disclosure that folds away optional fields — the register form's
   last-name/email pair, and the article editor's address/author/editor.
   A link, not a button — it reveals rather than does. */
.auth-optional-toggle {
    /* Never underlined, hover included (user, 2026-07-26): the plus/minus
       and the link color already say it acts. */
    text-decoration: none;
    font-size: 0.9rem;
}
.auth-optional-toggle::before {
    content: "\f067";                 /* plus */
    font-family: 'Font Awesome 6 Pro', 'Font Awesome 6 Free';
    font-weight: 900;
    font-size: 0.7em;
    margin-right: 0.5em;
}
.auth-optional-toggle[aria-expanded="true"]::before { content: "\f068"; }  /* minus */

/* The warning under the recovery slip. Amber rather than red — it is a caution
   about care, not an error, and the page is not reporting that anything has gone
   wrong. The icon hangs in the margin so the sentence keeps a straight left edge
   as it wraps, rather than the second line tucking under the triangle. */
.auth-warning {
    position: relative;
    padding-left: 1.75rem;
    font-size: 0.9rem;
    color: #7a5c1e;
}
.auth-warning i {
    position: absolute;
    left: 0;
    top: 0.15em;
    font-size: 1.1rem;
    color: #c9922b;
}
.auth-verse blockquote {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-weight: 400;
    font-size: calc(1.05rem * var(--reader-face-scale, 0.914));
    line-height: 1.5;
    color: #3f3a2f;
    margin: 0;
}
/* THE GOLD LIVES ON THE CAPTION, NOT THE LINK — moved 2026-08-15 when the
   translation code joined the reference ("Jeremiah 33:3 · WEB"). The code sits
   OUTSIDE the anchor deliberately, since a translation is not a place to go, so
   with the colour on the anchor alone it fell back to body black beside a gold
   reference. Setting it here and letting the link inherit keeps ONE definition
   of the colour for both halves of one line. */
.auth-verse figcaption {
    font-family: 'Prompt', sans-serif;
    font-size: 0.65rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    margin-top: 0.75rem;
    color: #a08c62;
}
.auth-verse figcaption a {
    color: inherit;
    text-decoration: none;
}
.auth-verse figcaption a:hover,
.auth-verse figcaption a:focus-visible {
    text-decoration: underline;
}

/* The sign-in, register and recovery pages are drawn on the reader's own sheet
   rather than on a white panel, so arriving at them feels like opening the book
   rather than leaving it. .bible-paper is otherwise the reader's alone — it is
   scoped to a direct child of .container there — so these pages ask for it by
   name. The toning vignette comes with it, and both are unconditional here: they
   are the page's design, not a preference, and a signed-out visitor has no
   preferences to consult anyway. */
.container > .bg-white.bible-paper.age-toning {
    background-color: #eceae2 !important;
}

/* The one-time recovery code — must stay selectable despite body user-select:none. */
/* The recovery slip. This is the only screen in the app that is shown once and
   never again, so it is drawn as something to keep rather than as a message to
   read: warm paper stock like the reader's, a torn lower edge, and the code set
   large enough to copy off the screen by hand without squinting at it. */
.recovery-slip {
    position: relative;
    background-color: #f7f5f1;                    /* the reader's paper */
    border: 1px solid #e6dcc4;
    border-bottom: 0;
    border-radius: 0.375rem 0.375rem 0 0;
    padding: 1.25rem 1.25rem 1.5rem;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
}
/* The torn edge: a row of half-circles bitten out of the foot of the slip. A
   repeating radial gradient rather than an image, so it costs nothing and takes
   the page's own background colour behind it. */
.recovery-slip::after {
    content: "";
    position: absolute;
    left: -1px;
    right: -1px;
    bottom: -10px;
    height: 10px;
    background:
        radial-gradient(circle at 6px 0, transparent 0 6px, #e6dcc4 6px 6.6px, transparent 6.7px) repeat-x,
        radial-gradient(circle at 6px -1px, #f7f5f1 0 6px, transparent 6.1px) repeat-x;
    background-size: 12px 10px;
}
.recovery-slip-label {
    font-family: 'Prompt', sans-serif;
    font-size: 0.65rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: #a08c62;
    text-align: center;
    margin-bottom: 0.5rem;
}
.recovery-code {
    font-family: 'Roboto Mono', monospace;
    font-size: 1.5rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-align: center;
    color: #3f3a2f;                                /* ink on the paper */
    /* Selecting it selects all of it — the code is one thing, not four words. */
    user-select: all;
    -webkit-user-select: all;
    word-break: break-word;
}
.recovery-slip-actions {
    display: flex;
    justify-content: center;
    gap: 0.5rem;
    margin-top: 1rem;
}
/* THE APP ICON, on /app and /press.
   ROUNDED HERE, NOT IN THE FILE. iOS masks the icon itself and Apple has
   changed the shape of that mask before; a radius baked into the pixels would
   be wrong the day it changes, and could not be undone by anyone reusing the
   asset. 22.37% is the proportion Apple's own superellipse works out to, so
   the corner matches a home screen closely enough at these sizes. */
.app-icon {
    width: 88px;
    height: 88px;
    flex: 0 0 auto;
    border-radius: 22.37%;
    /* The icon's own ground is dark; a hairline keeps it from bleeding into a
       dark page rather than decorating it. */
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
}
@media (max-width: 30rem) {
    .app-icon { width: 64px; height: 64px; }
}
.app-title .prose-title { line-height: 1.15; }

/* THE AVAILABILITY LINE ON /app — "All three apps are on sale…", the paragraph
   directly under the lead.
   IT MATCHES THE LEAD'S SIZE AND NOTHING ELSE. The two sit together as one
   opening block, and at 16px against the lead's 16.8 the difference was too
   small to read as hierarchy and just large enough to read as a mistake.
   Only font-size is set: both already inherit the same 1.4 line-height ratio,
   so the leading follows on its own, and the darker body colour is left alone
   deliberately — this sentence says what you can buy and should stay the more
   emphatic of the two, which is also why it opens bold. */
.app-availability { font-size: 1.05rem; }

/* THE APP SCREENSHOTS, on /app and /press.
   A scrolling row rather than a grid: five phone screens are tall, and a grid
   of them on a narrow window becomes a column of postage stamps. Scrolling
   keeps each one big enough to read a verse in, which is the only reason to
   show a screenshot of a reader at all. */
.app-shots-row {
    display: flex;
    gap: 1rem;
    overflow-x: auto;
    padding-bottom: 0.5rem;
    /* Each shot lands squarely rather than half-shown, which reads as a
       gallery instead of as a cut-off row. */
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
}
.app-shot {
    flex: 0 0 auto;
    width: 15rem;
    height: auto;
    scroll-snap-align: center;
    border: 1px solid #e0d9c8;
    border-radius: 1.25rem;      /* the corner radius of the device it came off */
    background-color: #f7f5f1;
}
@media (max-width: 40rem) {
    .app-shot { width: 60vw; }
}

/* THE MAC SCREENSHOTS, on /app.
   STACKED, NOT SCROLLED, and that is the whole difference from the row above.
   A desktop window is wide and its content is small: the point of each of these
   is that three texts are layered under one verse, or that a pane is following
   the reading, and none of that survives being shown at 15rem in a row. So each
   one takes the full measure of the column and they run down the page.
   The corner is squarer than the phone's for the same reason the phone's is
   round — it is the corner of the thing photographed. */
.mac-shot {
    display: block;
    width: 100%;
    height: auto;
    margin-bottom: 1rem;
    border: 1px solid #e0d9c8;
    border-radius: 0.5rem;
    background-color: #f7f5f1;
}
.mac-shot:last-of-type { margin-bottom: 0; }

/* APPLE'S "DOWNLOAD ON THE APP STORE" BADGE on /app (2026-08-19, the day the
   Mac went on sale). The artwork is Apple's and arrives unmodified; these rules
   only place it.

   ⚠ 44px, NOT THE 40 THE FILE IS DRAWN AT, AND BOTH RULES WANTED IT.
   Apple's 40px is a FLOOR for the web, not a fixed size — scaling the lockup
   proportionally above it is allowed, distorting or recolouring it is not. At
   40 the badge measured 40px tall, which is under this site's own 44px touch
   target ([[bible-ux-audit]]). 44 clears Apple's minimum and our floor at once,
   and the width follows from the aspect rather than being typed.

   ⚠ THE HEIGHT IS DECLARED AND THE WIDTH IS auto, never the reverse: a
   stylesheet that only holds while the file keeps its intrinsic 119.66×40 is
   one edit away from a squashed trademark.

   ⚠ AND THE CLEAR SPACE IS APPLE'S TOO: no other element may come nearer than
   10% of the badge's height, which is 4px here. 0.75rem is comfortably past it
   and matches the paragraph rhythm around it, so nothing has to be remembered.

   NO HOVER, NO SHADOW, NO ROUNDING, NO FILTER. Every one of those is a
   modification of a mark we do not own. The link's own focus ring is the whole
   of the interaction. */
.app-store-cta {
    margin-top: 0.75rem;
    margin-bottom: 0.75rem;
}
.app-store-cta a {
    display: inline-block;
    line-height: 0; /* kills the inline descender gap under the badge */
}
.app-store-badge {
    display: block;
    height: 44px;
    width: auto;
}

/* THE CONTENTS OF /app — one pill per app (2026-08-18). About's contents list
   is a stacked column because it carries seventeen entries; these sit on a line,
   and stacked they read as the beginning of a list rather than the whole of one.

   ⚠ TIGHTENED FOR A FOURTH PILL (Daniel, 2026-08-25) — Android made the row
   wrap on a wide screen. The width came out of the PADDING, THE GAP AND THE
   TYPE, and never out of min-height: that 44px is a touch-target floor, and it
   is declared rather than inferred precisely so a later squeeze cannot quietly
   eat it. Four fit now; a fifth app will wrap, and wrapping is the correct
   answer at that point rather than a smaller target.

   ⚠ THE PAPER WASH, NOT THE GOLD. One colour means HERE and nothing else
   ([[bible-gold-system]]), so these are the same wash as the hero's topic pills
   — which are the same object in the same job, a quiet row of ways in. The
   border is what separates a pale pill from the white panel behind it, which
   the hero does not need because its ground is the wallpaper.

   ⚠ THE 44px FLOOR IS DECLARED, NOT INFERRED FROM THE PADDING. The hero's
   pills reach it by padding alone, so this rule was written the same way and
   MEASURED AT 40px — the padding maths there does not transfer here, because
   the two sit in different type contexts and this one adds a border. A floor
   arrived at by arithmetic is a floor nobody checked; min-height is the thing
   that cannot drift when the font changes. */
.app-toc-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.45rem;
}
.app-toc-pill {
    display: inline-flex;
    align-items: center;
    /* The gap is what makes room for the icon; without it the glyph and the
       first letter touch, because inline-flex gives neither a side bearing. */
    gap: 0.4rem;
    /* ⚠ THE FLOOR STAYS 44px. See the note above — everything else here got
       smaller and this did not, because it is the touch target. */
    min-height: 44px;
    font-family: 'Prompt', sans-serif;
    font-size: 0.85rem;
    padding: 0.5rem 0.75rem;
    background: rgba(243, 235, 207, 0.7);
    border: 1px solid #e0d9c8;
    border-radius: 2rem;
    color: #4a3f2a;
    text-decoration: none;
}

/* A HAIR SMALLER AND A SHADE LIGHTER THAN THE LABEL. The icon says which
   machine and the words say which app, so the words lead; an icon at full
   weight competes with the name instead of introducing it. It takes its colour
   from the pill so the hover state carries it along with no second rule. */
.app-toc-pill i {
    font-size: 0.95em;
    opacity: 0.75;
}
.app-toc-pill:hover,
.app-toc-pill:focus-visible {
    background: rgba(243, 235, 207, 1);
    color: #4a3f2a;
}

/* The ready-to-send recovery message on the admin page. Monospace and wrapped
   at its own line breaks, because it is a letter being read for checking rather
   than prose being read for pleasure — and the line breaks are the ones that
   will arrive in the mail. */
.reissue-message {
    background-color: #fff;
    border: 1px solid #e0d9c8;
    border-radius: 0.375rem;
    padding: 0.75rem 1rem;
    font-size: 0.85rem;
    line-height: 1.5;
    white-space: pre-wrap;
    max-height: 22rem;
    overflow-y: auto;
}

/* Typing the code back. Set like the slip it answers — same face, same spacing,
   centred — so the two read as one object rather than as a form appearing after
   a document. It stands where the slip stood, which is why it takes the slip's
   margin rather than a new one. */
.recovery-confirm {
    margin: 1.5rem 0;
}
.recovery-confirm input {
    font-family: 'Roboto Mono', monospace;
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-align: center;
    color: #3f3a2f;
}

/* Long-form prose (about page): Prompt throughout — a light body, heavier
   headings, on a constrained measure for a calm, reference feel. */
/* THE READING COLUMN, AND THE TWO LINES THAT KEEP IT INSIDE ITS CARD.
   `width: 45rem` on its own is a trap here, because .prose is a flex item of a
   `d-flex justify-content-center` card: a flex item shrinks from its width only
   as far as its automatic minimum, and past that it OVERFLOWS. The card centers,
   so the overflow lands on both sides at once and the white padding vanishes
   left and right together — reported on comm.php 2026-07-31 while narrowing the
   window, and reproducible on any .prose page below roughly 45rem + the card's
   padding.
   max-width caps it at the card's content box; min-width lets it shrink there
   instead of stopping at whatever its widest child demands. Both are needed:
   without min-width a single nowrap child re-establishes the floor. */
.prose {
    width: 45rem;
    max-width: 100%;
    min-width: 0;
    font-family: 'Prompt', sans-serif;
    font-weight: 300; /* Light for normal text */
    line-height: 1.4;
    color: #2c2926;
}

.prose strong,
.prose b {
    font-weight: 500; /* Medium for bold text */
}

.prose code {
    font-weight: 700;
    color: #000;
}

/* h1 / h2 headings — Roboto Serif ExtraBold. Color comes from the utility class. */
.prose-title {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-weight: 800;
    font-size: 2.1rem;
    line-height: 1.15;
}

.prose-heading {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-weight: 800;
    font-size: 1.6rem;
    line-height: 1.25;
    margin-top: 2.75rem;
    margin-bottom: 0.6rem;
    /* Jumped to from the About contents list — keep the heading clear of the
       fixed navbar instead of landing underneath it. */
    scroll-margin-top: 5rem;
}

/* Arriving at a verse from a link elsewhere in the app. The verse is placed at
   the top of the reading area rather than centred: it is what an anchor does,
   the reader's eye is already at the top of the screen, and it keeps the rest of
   the chapter below it where there is something to read. The margin holds it
   clear of the fixed navbar, as on .prose-heading above. */
.verse-row {
    scroll-margin-top: 6rem;
}

/* The chapter while its verse is being found. index.php renders this class only
   when the URL names a verse, and the inline script there removes it the instant
   the verse is in place — before the first paint, so nothing is ever seen to
   move. It covers the one case the script cannot: a long chapter the browser
   begins painting before the parser has reached the script at its foot. A
   noscript rule in index.php uncovers it where there is no script to do so. */
.verses-placing {
    visibility: hidden;
}

/* Per-item options on the notes and favorites lists: a horizontal-ellipsis
   button pinned to the row's top-right corner. Clicking it (see app.js) reveals
   the Remove button in a new row at the foot of the item and turns the icon
   vertical. */
/* A row in the notes / favorites lists. The padding is here rather than in
   utilities on the element so every state covers the same box, and the right
   side is wide enough to keep the text clear of the tick box pinned in the
   corner. The rows sit flush against each other — no margin between them — so a
   row's background runs from the rule above it to the rule below with nothing
   showing through; a gap there reads as a fault in the banding. */
.item-row {
    position: relative;
    padding: 0.5rem 2rem 0.5rem 0.75rem;
}
/* A row in the search, Strong's and cross-reference result lists. Same idea as
   .item-row above and the same two rules that matter — padding here rather than
   in utilities so the tint covers the whole row, and no bottom margin, or a band
   would stop short of the rule beneath it and the gap would read as a fault.
   Narrower on the right than .item-row: these rows carry no tick box.

   The band itself is applied in the markup rather than by :nth-child, because
   refs.php puts a phrase heading in the same list as the results — counting
   siblings there would tint the headings and break the alternation for every row
   after each one. A counter in the loop skips them by construction. */
/* ─────────────────────────────────────────────────────────────────────────
   THE TOPICAL COLUMN in the hero's right half — pills, chosen by Daniel
   2026-08-15 from four candidates shown side by side at /?topics=a|b|c|d. The
   other three (serif list, the card's Prompt idiom, two columns) and the switch
   were deleted with the decision, as the ?grid=1 rules were.

   Pills were the shape I argued against and he picked them having seen all four,
   which is the right way round. Two things make them sit quietly enough: they
   take the CARD'S paper wash rather than the gold itself — so the page still
   holds exactly one saturated mark, the search button — and they carry no
   border, so they read as labels on paper rather than as controls.

   Column head and links keep the hills' ink, because this half of the hero
   floats on the sky exactly as the verse does. */
.welcome-topics {
    color: var(--hills-near, #25707e);
    padding-top: 0.4rem;
    /* ⚠⚠ THE 2.5rem BOTTOM MARGIN IS GONE — 2026-08-23, and removing it is worth
       40px of the hero's height at every width. It was "air before the card when
       the row stacks": below lg the topics used to land between the search and
       the favorites card, and the card's auto margin collapsed to nothing once
       the content filled the stage, so they sat on top of one another.

       The card is INSIDE this column now, under the verse of the day, so the
       margin was holding the column apart from whatever came after it — which is
       nothing. It sat below the card rather than above it, and on a wide screen
       it made the row 40px taller than its own tallest column for no reason at
       all. The air it was for is `.welcome-mostread { margin-top }`, which is on
       the correct side of the card and works at both widths.

       ⚠ A MARGIN TUNED FOR AN ELEMENT THAT HAS SINCE MOVED IS NOT NEUTRAL. This
       one cost height on every screen and nothing reported it; the row simply
       measured taller than anything in it. */
}
.welcome-topics-head {
    font-family: 'Prompt', sans-serif;
    font-size: 0.8rem;
    font-weight: 500;
    letter-spacing: 0.08em;
    margin-bottom: 0.6rem;
}
.welcome-topics-link,
.welcome-topics-all {
    color: inherit;
    text-decoration: none;
    transition: opacity 0.2s ease;
}
.welcome-topics-link:hover,
.welcome-topics-all:hover {
    color: inherit;
    opacity: 0.68;
}
.welcome-topics-all {
    font-family: 'Prompt', sans-serif;
    font-size: 0.85rem;
    font-weight: 400;
    text-transform: uppercase;
    margin-top: 1rem;
}

/* ⚠ "ALL TOPICS" GOES PALE WHEN THE ROW STACKS (Daniel, 2026-08-15). Below lg
   the topics land in the lower half of the screen, and the hills climb higher
   on a narrow viewport — so this line, alone in --hills-near, ends up dark ink
   on a dark ridge. The PILLS above it are unaffected: they carry their own
   paper wash and bring their own contrast with them. This one is bare text.

   The remedy is the one the most-read band used before it became a card: each
   colorway's own pale tone, not one grey for all four. Spelled out per mood
   rather than derived — there is no custom property for "the pale one"
   (--hills-sky is the sky's TOP stop, which is itself near-black at night), and
   color-mix() is below the browser floor ([[bible-browser-compat]]).

   ⚠ NIGHT IS DELIBERATELY ABSENT. Its --hills-near is ALREADY the pale moonlit
   tone (#cdd9e6) precisely because its sky is dark, so it needs no override and
   forcing one would make it the only mood whose link disagrees with its verse.
   Ordered so the three light colorways come first and cannot be overridden by
   the base rule. */
@media (max-width: 991.98px) {
    .bg-hills .welcome-topics-all       { color: #eaf5f6; }  /* day   — over the teal ridges */
    .bg-hills.dusk .welcome-topics-all  { color: #f6e6de; }  /* dusk  — over the violet ridges */
    .bg-hills.dawn .welcome-topics-all  { color: #eef1f8; }  /* dawn  — over the blue ridges */
    .bg-hills.night .welcome-topics-all { color: var(--hills-near); }
}

.welcome-topics ul {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}
/* ⚠ SEVEN PILLS WHEN THE ROW STACKS (Daniel, 2026-08-15). There are twenty-four
   topics, and on a phone they wrapped to seven rows between the search and the
   card — a directory, which is exactly what the column was agreed not to be
   ([[bible-topical-layer]]). Seven is what fits two rows on a 375px screen.

   HIDDEN, NOT UNRENDERED: every topic stays in the markup, so the links are
   still there for a crawler and the list does not change shape with the
   viewport. "All topics" underneath is what carries a reader to the rest, which
   is the whole reason the hub exists. */
@media (max-width: 991.98px) {
    .welcome-topics li:nth-child(n + 8) { display: none; }
}
.welcome-topics .welcome-topics-link {
    display: inline-block;
    font-family: 'Prompt', sans-serif;
    font-size: 0.9rem;
    /* 0.55rem of vertical padding, not the 0.35 the preview carried: at 0.35 a
       pill stood 31px tall, and six of them in a wrapped row are the page's
       smallest touch targets. This puts them at the 44px floor without changing
       how they look on a desktop. */
    padding: 0.55rem 0.95rem;
    background: rgba(243, 235, 207, 0.7);
    border-radius: 2rem;
    color: #4a3f2a;
}

/* ─────────────────────────────────────────────────────────────────────────
   THE VERSE OF THE DAY — the trigger under the topical column, and the dialog
   it opens (2026-08-15). The button is the fourth quiet thing in the hero and
   must stay quiet: the page holds ONE saturated mark, the gold search button
   ([[bible-gold-system]]), so this takes the card's paper wash like the pills
   above it rather than a colour of its own. */
.welcome-votd {
    /* Well clear of the topics above it (Daniel, 2026-08-15). At 1.5rem the card
       read as the last item of the pill list rather than as its own offer; this
       is the same 3rem the hero uses between the search and the card at the foot. */
    margin-top: 3rem;
    padding: 1.1rem 1.25rem;
    background: rgba(243, 235, 207, 0.7);
    border-radius: var(--bs-border-radius);
    color: #4a3f2a;
    width: fit-content;
    max-width: 100%;
}
/* THE SAME HEADING AS THE CARD AT THE OTHER CORNER (Daniel, 2026-08-15) —
   "FAVORITE PASSAGES" and this one are the same kind of thing, a label naming
   what its card holds, and two cards on one screen labelled two different ways
   read as two systems. Values copied from .welcome-mostread-head rather than
   shared through a class, because the two cards are otherwise independent and a
   shared class would tie every future change of one to the other. */
.welcome-votd-head {
    font-family: 'Prompt', sans-serif;
    font-size: 0.8rem;
    font-weight: 500;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    margin-bottom: 0.35rem;
}
/* The invitation under the heading, in THE READING SERIF (Daniel, 2026-08-15) —
   the same face and size the card at the other corner gives its chapters, so the
   two cards in the hero say their piece in one voice.

   ⚠ THE WEIGHT HAD TO MOVE WITH THE FAMILY. This line was Prompt 300, and 300
   is not a cut Roboto Serif ships here — only 400, 600 and 800 do — so changing
   the family alone would have left the browser to SYNTHESISE a light weight,
   which is the shear the italics carried until they were fixed
   ([[bible-typography]]). 400 is the lightest this face can honestly draw. */
.welcome-votd-line {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-size: 0.9rem;
    font-weight: 400;
    margin-bottom: 0.9rem;
}
/* The button is `.btn-gold` — black label on gold, measured at 8.68:1, and the
   one control on this card. Nothing else here competes with it. */
.welcome-votd-open {
    border-radius: 2rem;
    padding-left: 1.1rem;
    padding-right: 1.1rem;
}

/* The dialog. `dialog` brings the backdrop, the focus trap and Escape with it,
   so all this adds is the shape. */
.votd {
    max-width: min(92vw, 34rem);
    border: 0;
    border-radius: var(--bs-border-radius);
    padding: 1.25rem;
    background: #fff;
    color: #2c2926;
}
.votd::backdrop {
    background: rgba(0, 0, 0, 0.45);
}
.votd-close-form {
    display: flex;
    justify-content: flex-end;
    margin-bottom: 0.25rem;
}
.votd-close {
    border: 0;
    background: none;
    font-size: 1.5rem;
    line-height: 1;
    color: inherit;
    padding: 0 0.25rem;
}
/* ⚠ THE CANVAS IS 1080 SQUARE AND MUST BE SHOWN SMALLER WITHOUT DISTORTION.
   width:100% alone would stretch it to whatever height the box allowed; the
   aspect-ratio keeps the square square, and `height:auto` stops the intrinsic
   2160px height from setting the box. */
.votd-canvas {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 1 / 1;
    border-radius: var(--bs-border-radius);
    /* The card is painted one frame AFTER the dialog opens, so the box is
       briefly empty. A dark ground means that frame reads as the card arriving
       rather than as a white hole punched in the dialog. */
    background: #0d1626;
}
/* The verse as real text under the picture — never `small`, never muted: it is
   what a screen reader reads and what a reader copies ([[bible-typography]]). */
.votd-text {
    margin: 0.9rem 0 0;
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    line-height: 1.5;
}
.votd-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 1rem;
}
/* Playing. The button says so by filling with the same gold wash that means
   "this one" everywhere else, which is exactly what it means here. */
.votd-listen.is-on {
    --bs-btn-bg: rgba(201, 162, 39, 0.22);
    --bs-btn-border-color: rgba(201, 162, 39, 0.55);
}

/* ─────────────────────────────────────────────────────────────────────────
   THE TOPICAL HUB — /topics (2026-08-15). Grouped bands with the topic word as
   the label and a short line beside it; chosen from three candidates previewed
   at ?v=a|b|c, and the two losers were deleted with the decision.

   What it fixes: the page as first built repeated "Bible verses about" in every
   one of twelve headings, so the distinguishing word sat at the end of a phrase
   the eye had already read, and twelve ~30-word blurbs made 380 words of
   near-identical prose. The word is the label now, the sentence is said once in
   the h1, and the list is down to ~160 words. */
/* ⚠ A BAND IS DRAWN LIKE A QUOTED VERSE, BUT NEVER IN THE GOLD (Daniel,
   2026-08-15). Same shape as `.article-verse blockquote` and
   `.devotional-verse` — a 3px rule down the left edge, a pale wash behind, the
   radius on the two right corners only — because that shape already means "a
   block that belongs together" on this site. The COLOUR is what differs: gold
   means "this one, here" ([[bible-gold-system]]), and three gold bands on an
   index page would spend the site's one saturated signal on nothing.

   THE HUES COME FROM THE COLORWAYS, one band each: dawn's blue, dusk's violet,
   day's teal, then night's slate. The rule takes each mood's darkest ridge and
   the wash is that hue carried to near-white, so the ink over it stays the
   page's ordinary #2c2926 rather than needing its own colour.

   ⚠ THEY CYCLE ON POSITION, not on the group's name, so a fourth or fifth group
   is coloured without touching this file. Four is the whole palette; a fifth
   band starts again at dawn, which is correct — the colours are there to tell
   the bands apart on one screen, not to mean anything in themselves. */
.topic-band {
    border-left: 3px solid var(--band-rule);
    background: var(--band-wash);
    border-radius: 0 var(--bs-border-radius) var(--bs-border-radius) 0;
    padding: 1.25rem 1.5rem;
    margin-bottom: 1.25rem;
}
.topic-band-1 { --band-rule: #3c4a6b; --band-wash: #eef1f8; }  /* dawn  */
.topic-band-2 { --band-rule: #3f3663; --band-wash: #f3eff5; }  /* dusk  */
.topic-band-3 { --band-rule: #25707e; --band-wash: #eaf5f6; }  /* day   */
.topic-band-4 { --band-rule: #40607a; --band-wash: #eef2f6; }  /* night */

.topic-group-head {
    font-family: 'Prompt', sans-serif;
    font-size: 0.8rem;
    font-weight: 500;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    /* No top margin inside a band — the band's own padding is the air now, and
       the 2.5rem this carried when the groups were bare headings would have
       pushed the first line away from its own rule. */
    margin: 0 0 0.75rem;
}
.topic-group li {
    padding: 0.35rem 0;
}
/* The topic word, in the reading serif, large enough to be found by scanning
   rather than by reading. */
/* ⚠ 800, AND IT SAYS 800 BECAUSE THAT IS THE FILE. Roboto Serif ships as three
   subset weights — 400, 600, 800 — with no 700 face, so a request for 700 is
   resolved UPWARD by CSS font matching and rendered by ExtraBold anyway.
   Measured: at 17.6px the same string is 198.33px at 400, 205.84 at 600, and
   213.41 at BOTH 700 and 800. Asking for 700 was therefore a number that never
   reached the page; writing 800 makes the declaration and the glyphs agree.
   The weights that exist are the only ones worth writing here — anything else
   either resolves elsewhere or falls to a synthetic bold, which is a shear
   ([[bible-typography]]). 600 is the lighter real option. */
.topic-word {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-size: 1.1rem;
    font-weight: 800;
    color: inherit;
    text-decoration: none;
}
/* Hover is the colour alone, no underline (Daniel, 2026-08-15) — the same way
   the hero's own links answer, and on a page of twelve one-word links an
   underline appearing under each in turn is a lot of movement for a small
   confirmation.

   ⚠ NOT #0d6efd, THE SITE'S ORDINARY LINK BLUE. Measured on these washes it
   reaches only 3.96–4.05:1, which is below the 4.5:1 that AA wants for ordinary
   text, so it fails on all three bands. #0a58ca — Bootstrap's own hover blue, a
   step darker — measures 5.66–5.79:1 and passes.

   ⚠ AND THE EXEMPTION STILL DOES NOT APPLY AT 1.1rem/800 (Daniel, 2026-08-15).
   "Large text" drops the bar to 3:1, and BOLD lowers the threshold that earns it
   — 14pt (18.66px) bold, against 18pt (24px) at normal weight. These words are
   now bold, which is the direction of the exemption, but 1.1rem is 17.6px and
   misses 18.66px. They are ordinary text for contrast purposes and 4.5:1 still
   governs. Do not read the bold as licence to go back to #0d6efd.

   If the washes are ever warmed, measure again rather than assuming a blue that
   worked before still does. */
.topic-word:hover,
.topic-word:focus-visible {
    color: #0a58ca;
    text-decoration: none;
}
/* The short line sits on the same row as the word on a wide screen and wraps
   under it on a narrow one — left inline and given air, rather than made a flex
   row that would then have to be argued back into wrapping. */
.topic-blurb {
    font-family: 'Prompt', sans-serif;
    font-size: 0.95rem;
    font-weight: 300;
    margin-left: 0.6rem;
}

/* ─────────────────────────────────────────────────────────────────────────
   A TOPIC PAGE — /topics/grief (2026-08-15). Read top to bottom by someone who
   is upset, so it is prose in the 45rem measure with air between passages, not
   a grid of cards.

   ⚠ THE VERSE ITSELF IS `.article-verse`, THE SITE'S OWN QUOTATION STYLE, and
   there is deliberately no rule for it here. Inventing a topic-page verse style
   is how the same verse comes to look like a quotation in an article and a
   citation on a topic page — the exact drift the devotional's key verse was
   corrected for earlier the same day. What is left below is only what this page
   adds around that quotation. */
.topic-passage {
    padding-bottom: 1.75rem;
}
/* Our own sentence about the passage, under the quotation it belongs to. Italic,
   which this site can finally draw properly as of 2026-08-15 — before the real
   italic cuts arrived it would have been a synthetic shear. */
.topic-point {
    font-style: italic;
}
.topic-version {
    margin-top: 2rem;
}

.result-row {
    padding: 0.5rem 0.75rem;
}

/* THE REST OF A READING PLAN'S DAYS, behind a disclosure (reading.php, Daniel,
   2026-08-15). A year-long plan is 365 rows down a half-width column; five show
   and the rest wait here.

   A native <details> rather than a Bootstrap collapse — no JavaScript, keyboard
   and screen reader support for free, and every day stays in the markup. All
   this needs from CSS is for the summary to look like something you can press:
   the marker is Bootstrap's own text with a pointer, sharing the rows' padding
   so it lines up under them rather than floating in the column. */
.day-more > summary {
    cursor: pointer;
    padding: 0.5rem 0.75rem;
    font-size: 0.875rem;
}
.day-more > summary:hover {
    background: var(--bs-light);
}

/* A PLAN DAY IS A LINK NOW (reading.php, 2026-08-24). It was inert text, so the
   only route from "Day 1 — Genesis 1–4" to Genesis 1 was to work the book and
   chapter selects by hand.

   THE PADDING MOVES OFF THE <li> AND ONTO THE <a>, which is the whole point:
   an inline link is only as tall as its words, and the row has to be the hit
   area. At the rows' own 0.5rem padding a two-line row clears 44px comfortably
   at every width. */
.day-row { padding: 0; }
.day-link {
    display: block;
    padding: 0.5rem 0.75rem;
    color: inherit;
    text-decoration: none;
}
.day-link:hover { background: var(--bs-light); }
.day-link:focus-visible {
    outline: 2px solid var(--biblion-gold);
    outline-offset: -2px;
}

/* THE NEXT DAY, and it deliberately takes NO BACKGROUND.

   Gold here is the standing "this one" sense ([[bible-gold-system]]) — the next
   reading is the single thing this page exists to point at. But the wash that
   normally carries that meaning, rgba(201,162,39,.22), is already spoken for on
   these very rows by `.result-row:target`, and the 0.16 tint beside it means
   IDENTITY and is used by no selection state. Rather than pick one of the two
   alphas and blur what it means, this marks the row's own box with geometry —
   a rule and a tag — and leaves both fills alone. It also keeps the zebra
   legible underneath, since banding says "position" and this says "start here",
   and the two must not look like the same signal. */
.day-next { border-left: 3px solid var(--biblion-gold); }

/* The day number and the pill share a line, so the pill can sit at the row's
   right edge instead of trailing the number. Every row uses it — only the next
   one has a second child to push over. */
/* ⚠ "Day 1" TAKES THE READING FACE (Daniel, 2026-08-24). The standing rule is
   that the thing being NAMED is set in the serif and the interface around it
   stays in the UI face at the one label size ([[bible-typography]]) — the day
   is what this row is called, so it is a name, not a label. The pill beside it
   is the interface and stays in Prompt, which is why it declares its own family
   rather than inheriting this one. */
.day-line {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-weight: 800;
}

/* ⚠ THE REFERENCE STAYS IN THE UI FACE (Daniel, 2026-08-24) — it inherits, and
   deliberately declares no family. Only the day is set in the serif: the row
   carries ONE name, and putting the reference in the reading face as well made
   two, which flattens the thing the serif was doing.

   So the hierarchy of the row is weight alone: 800 serif for the day, 500 sans
   for the reference, 400 for the aside. No size step anywhere in it — weight
   and the muted colour carry every distinction ([[bible-typography]]).

   ⚠ BOTH NUMBERS ARE FACES THAT SHIP, not values a browser has to invent.
   Roboto Serif has 400/600/800 only, so the day's 800 is real; Prompt has the
   full 100–900, so 500 is real too. A weight with no file behind it gets
   synthesised or snapped to a neighbour, and then the step you chose is not
   the step on screen. */
.day-label {
    display: block;
    font-weight: 500;
}

/* The "1 of 4 read" note drops to the regular weight: it is an aside on the
   reference, not part of its name, and it already has the muted colour. */
.day-part { font-weight: 400; }

/* AN OUTLINED PILL, NOT A FILLED ONE. Gold at full strength is already how this
   row draws its left rule, so a stroke matches it; a solid gold fill would be a
   third gold value beside the two alphas [[bible-gold-system]] keeps apart
   (.22 selection, .16 identity) and would start an argument about which it is.

   ⚠ WEIGHT 400 ON PURPOSE, against the 600 it would otherwise inherit from the
   row. The row's own bold is what says "this one"; the pill only has to name
   it. Bold inside bold is two shouts where the border already carries the
   emphasis. */
/* ⚠ inline-flex + align-items:center + line-height:1, AND ALL THREE ARE THE
   FIX FOR ONE THING: a plain inline pill sits its text on a BASELINE, so the
   font's descent stays empty under the caps and the pill looks bottom-heavy —
   the same descender gap `.app-store-badge` kills with line-height:0 and that
   `.app-toc-pill` avoids by being inline-flex. UPPERCASE MAKES IT WORST,
   because no glyph ever reaches into the descent to balance it. Centring on
   the box instead of the baseline is what makes the padding read as equal. */
.day-next-tag {
    display: inline-flex;
    align-items: center;
    line-height: 1;
    font-family: 'Prompt', sans-serif;   /* the UI face — it is a label, not a name */
    font-weight: 400;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--biblion-gold);
    border: 1px solid var(--biblion-gold);
    border-radius: 999px;
    /* ⚠ THE TOP AND BOTTOM ARE NOT EQUAL, AND THE DIFFERENCE IS MEASURED.
       Canvas TextMetrics for "NEXT" in 14px Prompt: cap ascent 10px, ink
       descent 0 — uppercase puts nothing below the baseline, so the font's 6px
       descent stays empty and the ink sits high. That leaves 5px above the ink
       and 6px below it, and the eye reads the 1px as a bottom-heavy pill.
       Half of it on each side is the correction: 0.25rem over 0.19rem is 4px
       over 3px. Re-measure before changing the face or the size — this number
       belongs to Prompt at this size and to nothing else. */
    padding: 0.25rem 0.5rem 0.19rem;
    white-space: nowrap;
}

/* THE ZEBRA STRIPES STAY BOOTSTRAP'S GREY `bg-light`, and this note is here so
   nobody warms them again. They were briefly gold on 2026-08-15, from my
   misreading of "the selected part should be the gold one" — Daniel meant the
   HIGHLIGHTED WORD inside a row, not the banding under it. His answer:
   "the striped rows were ok as before - gray."

   It is also the right call on its own terms: banding is not meaning. Gold in
   this app says "here is the thing you asked for", and spending it on every
   second row would leave the highlight competing with its own background. */

/* The phrase heading on refs.php, which opens each new word's list of references.
   Its rule was black until 2026-07-31, to mark a beginning against the pale rules
   that merely separate rows. Daniel levelled them: the heading no longer needs the
   contrast, because the rule after a phrase's last reference is gone, so nothing
   competes with it where a group ends. Take the colour from the same token the
   rows use rather than repeating a hex, or the two drift apart the day a theme
   moves it. The padding keeps the rule off the descenders; the margin below it
   closes the gap to the first reference so heading and list read as one block. */
.refs-phrase {
    border-bottom: var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);
    padding-bottom: 0.35rem;
    margin-bottom: 0;
}

/* ---- Covenant law (public/rituals.php) ------------------------------
   A sacrifice is a sequence with branches, so it is drawn as one: a column of
   cards joined by connectors. CARDS ARE HTML, NOT SVG, and that is the whole
   design — the draft this grew from was a fixed 720px drawing with its words
   inside it, which at the 303px the column measures on a 375px phone would have
   rendered 17px type at 7.2px. Real text reflows; a picture of words does not.

   The connectors are drawn with pseudo-elements rather than an overlay, so
   there is no geometry to keep in step with the cards as they reflow. */
/* The index. A GRID OF SCENES, not the topic hub's list of words: what tells
   one chart from another at a glance is a picture — a bull at the altar reads
   differently from a bowl of flour, where "Burnt" and "Grain" as bare words do
   not. auto-fill with a floor rather than a fixed column count, so the shelf
   grows into whatever width it is given and collapses to one column on a phone
   without a breakpoint to maintain. */
.rit-index {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: 1.25rem;
    margin: 1.25rem 0 2.75rem;
}
/* The sentence under a band's heading. Muted and close to the heading, so the
   two read as one unit and the cards below start cleanly. */
.shelf-band-note {
    color: var(--bs-secondary-color, #6c757d);
    margin: -0.25rem 0 0;
}
/* ⚠ A BAND IS A <details> AND ONLY THE FIRST IS OPEN — 2026-08-23. Eighty-eight
   cards measured forty screens on a phone; closed, the same shelf is its own
   table of contents and about four. The summary carries the heading AND the
   note, so a closed band still says which chapters are inside it — the whole
   point of collapsing, if the reader has to open each one to find out.

   The disclosure triangle is drawn here rather than left to the browser: the
   native marker is a different shape, colour and position in every engine, and
   it sits before the heading where it pushes an 800-weight serif off the
   margin. `list-style: none` kills it in Firefox, the ::-webkit rule in Safari
   and older Chrome. */
.shelf-band {
    border-top: 1px solid var(--bs-border-color, #dee2e6);
}
/* ⚠ NO BOTTOM BORDER ON THE LAST BAND. Whatever follows a shelf on these pages
   — the credit line, the unplaced-count guard — opens with a rule of its own, and
   the two together drew a double hairline with a sliver of white between them. */
.shelf-band:last-of-type {
    margin-bottom: 2rem;
}
.shelf-band-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 0.15rem;
    position: relative;
    padding: 0 2.25rem 1rem 0;
    cursor: pointer;
    list-style: none;
}
/* The note is the second line of the summary whatever the title's length. */
.shelf-band-head .shelf-band-note { flex: 0 0 100%; }
.shelf-band-head::-webkit-details-marker { display: none; }
/* The heading's own top margin is what separates one band from the next, and it
   belongs to the summary now that the heading lives inside one. */
.shelf-band-title { margin-top: 1.75rem; }
.shelf-band-head::after {
    content: '';
    position: absolute;
    right: 0.5rem;
    top: 2.5rem;
    width: 0.6rem;
    height: 0.6rem;
    border-right: 2px solid var(--bs-secondary-color, #6c757d);
    border-bottom: 2px solid var(--bs-secondary-color, #6c757d);
    transform: rotate(45deg);
}
.shelf-band[open] > .shelf-band-head::after {
    transform: rotate(-135deg);
    /* the arm that was the bottom edge is now the top one: keep it optically
       on the same line as the closed state rather than half a chevron higher */
    margin-top: 0.2rem;
}
@media (prefers-reduced-motion: no-preference) {
    .shelf-band-head::after { transition: transform 0.2s ease; }
}
.shelf-band-head:hover .shelf-band-title,
.shelf-band-head:hover::after {
    color: var(--biblion-gold, #c9a227);
    border-color: var(--biblion-gold, #c9a227);
}
.shelf-band-head:focus-visible {
    outline: 2px solid var(--bs-primary, #0d6efd);
    outline-offset: 2px;
    border-radius: 0.25rem;
}
/* Open-all / close-all. Sits under the contents list, where a reader who has
   just read twenty band names is deciding whether to scan or to search.

   ⚠ NO NEGATIVE MARGIN. It had -2rem, tuned against the ritual page's `mb-5`
   contents list; on /prophecy, whose list is `mb-3`, the same rule pulled the
   button up ON TOP of the last line of the list. A component shared by two pages
   cannot carry a margin that assumes one of them. */
.shelf-bands-control { margin-top: 0; }
/* The tally beside a band in the contents list. It answers "is this the long
   one?" before a reader commits to scrolling there, which is the only question
   a contents list is asked on a page of thirty-three cards. */
.toc-count {
    display: inline-block;
    min-width: 1.5rem;
    margin-left: 0.35rem;
    padding: 0 0.35rem;
    border-radius: 0.7rem;
    background: var(--bs-tertiary-bg, #f8f9fa);
    color: var(--bs-secondary-color, #6c757d);
    font-size: 0.75rem;
    text-align: center;
}
.rit-index-card {
    display: flex;
    flex-direction: column;
    height: 100%;
    background: #fff;
    border: 2px solid var(--bs-border-color, #dee2e6);
    border-radius: 0.75rem;
    overflow: hidden;
    text-decoration: none;
    color: inherit;
}
.rit-index-card:hover, .rit-index-card:focus-visible {
    border-color: var(--biblion-gold, #c9a227);
    outline: none;
}
/* ⚠ object-position RIGHT, not centre. Every one of these scenes is composed
   with its subject in the right half and quiet charcoal on the left — that is
   what the banner needs for its title to sit on. Cropped from the centre, a
   card thumbnail shows the empty half. */
/* ⚠⚠ `height: auto` IS LOAD-BEARING, AND ITS ABSENCE FAILS QUIETLY. The <img>
   carries width="1440" height="660" so the browser can reserve the space before
   the file arrives, and those attributes act as a presentational hint — which
   wins whenever the stylesheet declares no `height` of its own. Without this
   line `aspect-ratio` is computed and then ignored, and the thumbnail renders
   660px tall inside a 299px card: five times too big, and it looks like a
   cropping mistake rather than a units one. Measured, not reasoned about. */
/* THE CARD HEADER IS A STRIP AND ITS PICTURE ALREADY FITS IT, because the crop
   happens in the build rather than here — tools/crop_ritual_cards.py cuts a
   16:7 thumbnail from each scene, centred on its own subject and anchored to
   the TOP of it.

   ⚠ TWO CSS ATTEMPTS FAILED FIRST, and the reason is worth keeping. `object-fit:
   cover` cannot zoom: it only ever FITS an image to a box, so the sole way to
   magnify with it is to make the box taller — the one thing this header must
   not do. Laying the image out oversized inside an `overflow: hidden` frame DOES
   zoom, and still failed, because a single crop cannot serve both scenes: the
   burnt offering's subject starts 42.5% across and the grain offering's priest
   has his head at 9.2% from the top, so any window tight enough to drop the
   first one's dead charcoal cuts the second one's head off. Measured, both
   times. A per-image crop is the only thing that satisfies both, and it is a
   build step, not a stylesheet.

   ⚠ `height: auto` stays load-bearing: the <img> carries width/height
   attributes so space is reserved before it loads, and those act as a
   presentational hint that wins whenever the stylesheet declares no height. */
.rit-thumb {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 7;
    background: #212529;
}
.rit-index-text {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 0.85rem 1rem 1rem;
    flex: 1;
}
/* scroll-margin: the contents panel jumps to these titles (app.js gives them
   ids), and without clearance a jumped-to card lands with its name under the
   fixed navbar — invisible, which reads as the jump having failed. Same 6rem
   the prose headings carry. */
.rit-index-title { font-size: 1.15rem; margin: 0; scroll-margin-top: 6rem; }
.rit-index-passage { font-size: 0.8125rem; color: var(--bs-link-color, #0d6efd); }
.rit-index-summary { margin-top: 0.25rem; }
/* Pushed to the foot so the meta line sits on the card's bottom edge whatever
   the summary above it wraps to — a row of cards with their last lines at four
   different heights reads as four different components. */
.rit-index-meta {
    margin-top: auto;
    padding-top: 0.6rem;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--bs-secondary-color, #6c757d);
}

/* fs-6 on the link itself, as article.php sets it: this is a control, not a
   fact about the chart, and it should not shrink into the surrounding prose. */
.rit-crumb { margin-bottom: 1rem; }

/* Next and previous, side by side on a wide screen and stacked on a phone.
   The label sits above the title so the two cards read as a pair of
   destinations rather than as a sentence broken in half. */
.rit-seq {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: 0.75rem;
    margin-top: 2rem;
}
.rit-seq a {
    display: flex; flex-direction: column;
    padding: 0.7rem 0.9rem;
    border: var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);
    border-radius: 0.5rem;
    text-decoration: none;
}
.rit-seq a:hover, .rit-seq a:focus-visible {
    border-color: var(--biblion-gold, #c9a227);
    outline: none;
}
/* ⚠⚠ THE LABEL ONLY — NOT `.rit-seq span`, which is what it was until
   2026-08-24 and why the three shelves did not match. That selector hit ANY
   span in the strip, so /prophecy and /stories — which wrap the destination's
   title in `.rit-seq-name` — had the TITLE greyed and capitalised along with
   the "Previous". /rituals left its title as bare text, so there it stayed a
   normal link, and Daniel's call is that the rituals one is right: the title is
   where you are going and should read as a link. Scoped to the class, the rule
   now says what it means rather than catching whatever is nearby. */
.rit-seq .rit-seq-dir {
    display: block;
    font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--bs-secondary-color, #6c757d);
}
.rit-seq-next { text-align: right; }

.rit-banner {
    position: relative;
    border-radius: 0.5rem 0.5rem 0 0;
    overflow: hidden;
    background: #212529;                       /* matches the artwork's own ground */
    border-bottom: 4px solid var(--biblion-gold, #c9a227);
    margin-bottom: 1.5rem;
}
.rit-banner img { display: block; width: 100%; height: auto; }
/* The words sit ON the picture, never in it. The scrim is a gradient rather
   than a flat wash so it protects the title and then lets the scene go. */
.rit-banner-text {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 0.15rem;
    padding: 0 1.25rem;
    background: linear-gradient(90deg, rgba(33,37,41,.92) 0%, rgba(33,37,41,.75) 34%, rgba(33,37,41,0) 66%);
}
.rit-eyebrow {
    font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.12em;
    color: var(--biblion-gold, #c9a227);
}
.rit-title { color: #fff; margin: 0; font-size: clamp(1.35rem, 4.6vw, 2.1rem); line-height: 1.1; }
.rit-passage { color: rgba(255,255,255,.82); font-size: 0.875rem; }

/* One step. The whole card is the link — a card is a target nobody misses
   where a reference line is a few characters — but only the reference row
   takes the link colour, so the card reads as text instead of a wall of blue.
   The gold outline on hover and focus is what says the whole thing is live,
   the same gold the picker's items take under a pointer. */
.rit-card {
    display: block;
    position: relative;
    margin: 0;
    padding: 0.85rem 1rem 0.85rem 1.4rem;
    background: #fff;
    border: 2px solid var(--bs-border-color, #dee2e6);
    border-left: 7px solid #495057;
    border-radius: 0.75rem;
    text-decoration: none;
    color: inherit;
}
.rit-card:hover, .rit-card:focus-visible {
    border-color: var(--biblion-gold, #c9a227);
    border-left-color: var(--biblion-gold, #c9a227);
    outline: none;
}
/* The connector, drawn downward out of every card that has one following it.
   ⚠ A GAP EITHER END IS DELIBERATE — a line touching a card reads as part of
   its border rather than as a link between two of them. */
.rit-step::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 100%;
    width: 2px;
    height: 1.35rem;
    margin: 0.35rem 0 0 -1px;
    background: #a9b2c3;
}
.rit-step::before {
    content: "";
    position: absolute;
    left: 50%;
    top: calc(100% + 1.55rem);
    margin-left: -5px;
    border: 5px solid transparent;
    border-top-color: #a9b2c3;
    border-bottom: 0;
}
/* Nothing flows out of the last card on the page, or out of the last step of a
   path — a path ends at the shared outcome, and an arrow into it would say the
   third route is the one that gets there. */
.rit-step:has(.rit-outcome)::after, .rit-step:has(.rit-outcome)::before,
.rit-lane .rit-step:last-child::after,
.rit-lane .rit-step:last-child::before { content: none; }

.rit-kicker {
    display: block;
    font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--bs-secondary-color, #6c757d);
    margin-bottom: 0.2rem;
}
.rit-body { display: block; }
/* The reference is its own row under a rule, never beside the words.

   0.9em, not the 0.8125rem it started at (Daniel, 2026-08-23): this is the
   scripture reference, the one thing on the card a reader might copy out or read
   aloud, and it was set smaller than the caption conventions elsewhere warrant.
   `em` rather than `rem` so it keeps its proportion to the step it belongs to
   if the card's own size is ever changed. */
/* THE HEAD OF THE BUBBLE — prophecy's scene row: what the step is on the left,
   the passage it rests on at the right (Daniel, 2026-08-24). The reference used
   to be a footer under the body with a divider above it; the quotation took that
   place, and a reference below its own quotation reads as a caption on the
   verse rather than as the step's source. */
.rit-card-head {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
}

/* ⚠ `margin-left: auto`, NOT `justify-content: space-between` — some steps have
   no kicker, and with a single child space-between leaves the reference at the
   LEFT. The auto margin puts it right either way. */
.rit-ref {
    margin-left: auto;
    font-size: 0.9rem;
    white-space: nowrap;
    color: var(--bs-link-color, #0d6efd);
}

/* THE VERSE INSIDE A STEP CARD — ritualStepQuote() in rituals.php. Daniel,
   2026-08-24: the quotation sits in the bubble, banded with its reference.

   ⚠⚠ `display: block` IS LOAD-BEARING AND WAS THE WHOLE OF THE BREAKAGE.
   `.devotional-verse` is written for a block-level <blockquote>; worn by a
   <span> inside this card it stays INLINE, and an inline box accepts padding
   and a left border while reserving no height for either. The band therefore
   drew straight across the body text with its gold rule floating free, which
   looks like a sizing problem and is not one.

   ⚠ THE SIZE IS PROPHECY'S `.pro-scene-quote`, on Daniel's word — 0.9rem, not
   the 1.05rem `.devotional-verse` carries. A step card is a small object, and
   at full size the quotation reads as the page rather than as Scripture set
   inside it. */
/* ⚠⚠ TWO CLASSES, AND THE SECOND ONE IS NOT DECORATION. `.devotional-verse` is
   defined ~4,300 lines BELOW this, so at equal specificity it wins on order
   alone and the band came out at its 1.05rem and 1rem padding — the size looked
   untouched while the rule was being read and applied. `.pro-scene-quote` never
   met this only because it happens to sit after `.devotional-verse` in the file.
   Keying on the card makes this rule win wherever either block moves to, which
   is the fix rather than shuffling the file. Same family as the standing note
   that a media query is not more specific, only later. */
.rit-card .rit-quote {
    display: block;
    font-size: 0.9rem;
    line-height: 1.5;
    padding: 0.5rem 0.7rem;
    margin: 0.6rem 0 0;
}

/* The version marking is the tail of the quotation and wears its ink, not the
   link colour: it inherits `.devotional-verse`'s #2c2926 rather than being told
   a colour here. Exactly `.pro-scene-quote-ref`. */
.rit-quote-v { white-space: nowrap; }

/* ⚠ #f6f0dc IS THE COMPOSITE, NOT AN rgba — the same shade the family tree's
   "here" card takes and for the same reason: a translucent gold would let the
   connector above it show through the ground. */
.rit-outcome {
    background: #f6f0dc;
    border-color: var(--biblion-gold, #c9a227);
    border-left-color: var(--biblion-gold, #c9a227);
    margin-bottom: 1rem;
}
.rit-outcome .rit-body { font-weight: 600; }

.rit-choice {
    display: flex; align-items: center; gap: 0.75rem;
    margin: 0 0 1.75rem;
    color: var(--bs-secondary-color, #6c757d);
    font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.08em;
}
.rit-choice::before, .rit-choice::after {
    content: ""; flex: 1; height: 1px; background: var(--bs-border-color, #dee2e6);
}
.rit-lane { margin-bottom: 0.5rem; }
.rit-lane-title { display: flex; align-items: center; gap: 0.6rem; font-size: 1.15rem; margin-bottom: 0.1rem; }
.rit-lane-num {
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.6rem; height: 1.6rem; flex: 0 0 1.6rem;
    border-radius: 50%; background: #495057; color: #fff;
    font-size: 0.8rem;
}
.rit-lane-meta {
    color: var(--bs-secondary-color, #6c757d);
    font-size: 0.8125rem;
    margin: 0 0 1rem 2.2rem;
}
/* A lane that is a RULE FOR ALL THE PATHS rather than one of them — Leviticus 2
   states two of those in the middle of the chapter. Its badge carries a word
   instead of a number, so it needs to stop being a circle; a word in a circle
   reads as a number that has gone wrong. The dashed rule above it says the
   column has changed register without spending a colour. */
.rit-lane-shared { border-top: 1px dashed var(--bs-border-color, #dee2e6); padding-top: 1.25rem; }
.rit-lane-shared .rit-lane-num {
    width: auto; flex-basis: auto;
    border-radius: 0.75rem;
    padding: 0 0.55rem;
    background: var(--bs-secondary-color, #6c757d);
    font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.06em;
}

/* The two directions on refs.php — what this verse points to, and what points
   back at it — as a pair of links, never a scripted tab widget: everything else
   on that page works with scripting off and this is navigation, which a link
   already is.

   NO COLOUR ON THE SELECTED TAB. Gold in this app says "here is the thing you
   asked for" and violet says "a reference work is speaking"; a tab is neither,
   it is a control, and spending either meaning on it would leave the page with
   three things claiming the same voice. Ink and a rule under the live one carry
   it — the same way the phrase heading below uses weight rather than hue to say
   a new thing begins.

   They WRAP rather than shrink: at 375px the two labels together run past the
   column, and a tab strip that has quietly become two rows still reads, where
   one that has become 11px does not. */
.refs-views {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 1.25rem;
    border-bottom: var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);
}
.refs-view {
    /* 0.65rem, not 0.5: the same 44-ish tap target the quote disclosure below
       takes, and this is the page's primary control rather than an aside. */
    padding: 0.65rem 0;
    margin-bottom: -1px;                      /* the live rule sits ON the strip's own */
    border-bottom: 2px solid transparent;
    color: var(--bs-secondary-color, #6c757d);
    text-decoration: none;
    font-size: 0.9375rem;
}
.refs-view:hover { color: var(--bs-body-color, #212529); }
.refs-view.active {
    color: var(--bs-body-color, #212529);
    font-weight: 600;
    border-bottom-color: var(--bs-body-color, #212529);
}

/* The strip of often-linked references above the list. A VIOLET BAND, which on
   this site is not decoration: blue means editorial, grey an ad, gold the house,
   violet a reference work speaking ([[bible-search-results-layout]]). This is
   the cross-reference apparatus, so violet, and the left rule is the same
   3px #7a5ea8 the lexicon panel opens with.

   The references themselves stay LINK BLUE rather than taking the band's violet.
   Every reference on this page is blue and goes somewhere; making these five the
   exception would say they are a different kind of thing, when the only
   difference is that they are worth reading first. */
.refs-top {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.15rem 0.9rem;
    margin-bottom: 1.25rem;
    padding: 0.5rem 0.85rem;
    background: #f8f6fb;
    border-left: 3px solid #7a5ea8;
    border-radius: 0.25rem;
}
.refs-top-label {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #7a5ea8;
    white-space: nowrap;
}
.refs-top a {
    color: var(--bs-link-color, #0d6efd);
    text-decoration: none;
    /* Padding, not line height: five links on one line at phone width are five
       tap targets, and they need to be reachable without magnifying the band. */
    padding: 0.35rem 0;
}
.refs-top a:hover { text-decoration: underline; }

/* "often linked" — the mark on the references of a verse that readers follow
   most, from OpenBible.info's votes. Quiet in weight: it sits beside a
   reference in a list of sixty and its job is to be findable while skimming,
   not to compete with the reference it qualifies.

   VIOLET, ON DANIEL'S WORD (2026-08-23). It was built grey on the argument that
   this is a third voice — readers, counted — where violet had meant a reference
   work speaking, and [[bible-gold-system]] says a new thing to say takes a new
   hue rather than borrowing one. He ruled violet, and the reading that follows
   from it is the simpler one: the mark is part of the same apparatus as the
   figure and the picker, not a separate speaker. So violet on this page means
   the cross-reference apparatus entire, whoever supplied the number, and gold
   still means the verse being studied. Do not re-grey it.

   The same pairing the lex panel and the picker use — #f8f6fb ground under the
   saturated #7a5ea8 — rather than a fifth shade of it. */
.refs-often {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #7a5ea8;
    background: #f8f6fb;
    border: var(--bs-border-width, 1px) solid #7a5ea8;
    border-radius: 0.75rem;
    padding: 0.05rem 0.5rem;
    white-space: nowrap;
    vertical-align: 0.05em;
    /* A word space is what separates it from the reference otherwise, and a
       word space before a bordered thing reads as crowding rather than as a
       gap. */
    margin-left: 0.25rem;
}

/* The OpenBible.info credit at the foot of refs.php, under the pager. Smaller
   than the reading text and quieter than it, because it is owed to a licence
   rather than addressed to a reader — the note about which translation is
   quoted, higher up the same page, stays at full size for the opposite reason.
   The rule above it separates it from the list without needing a heading. */
.refs-credit,
.source-credit {
    margin-top: 2.25rem;
    margin-bottom: 2rem;
    padding-top: 1.25rem;
    border-top: var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);
    font-size: 0.8125rem;
    color: var(--bs-secondary-color, #6c757d);
}

/* The disclosure under a long quotation on refs.php. A reference may name a
   dozen verses, and one of those quoted whole pushes the nine other references
   on the page below the fold; past three verses the row keeps two and folds the
   rest in here.

   ⚠ NO BACKGROUND ON HOVER, where .day-more's summary takes --bs-light. Every
   second row on this page is already `bg-light`: a light hover would be
   invisible on half the list and would read as the banding shifting on the
   other half. Colour and an underline carry it instead.

   It takes the LINK colour although it is not a link, because it is the only
   control inside a reference row that is not one — every other clickable thing
   here is blue, and a grey line that happens to be operable is a thing readers
   do not find. */
/* ⚠ THE PADDING IS THE TAP TARGET, not decoration. At 0.15rem the control
   measured 24px tall on a 375px viewport — a thumb-sized miss on the page where
   it appears most, since a phone is where a folded quotation matters most. The
   line itself is 21px; the rest is what brings the box to 44. It runs the full
   width of the row, so height was the only dimension in question. */
.refs-quote-more > summary {
    cursor: pointer;
    font-size: 0.875rem;
    color: var(--bs-link-color, #0d6efd);
    padding: 0.72rem 0;
}
.refs-quote-more > summary:hover { text-decoration: underline; }
.refs-quote-more[open] > summary { margin-bottom: 0.15rem; }

/* ---- The family tree and the life timeline (includes/lifedraw.php) ---------
   Two pictures of things a list flattens: a family is a shape, and an event has
   a length. Both are server-rendered SVG that scales with the column, and both
   speak the app's own two colours — GOLD is the subject of the page, VIOLET is
   the reference work speaking ([[bible-gold-system]]).

   Type sizes are in SVG user units, not rem: the drawing scales as a whole, so
   a label that grew with the reader's font size would collide with its
   neighbours at some widths and not others. The end labels beneath a timeline
   ARE html for exactly that reason — they must stay readable on a phone. */
.family-tree,
.life-timeline { display: block; width: 100%; height: auto; }

.ft-card { fill: #fff; stroke: var(--bs-border-color, #dee2e6); stroke-width: 1; }
/* ⚠ THE CARD TAKES THE COMPOSITE, NOT THE rgba — the same rule #strongs-popup
   follows, and for the same reason one step further on. The joining lines are
   drawn BEFORE the cards so the cards cover them; a translucent fill undoes
   that, and a line runs visibly through the name. #f6f0dc is
   --biblion-gold-tint's 0.16 composited over white: identical shade, no
   transparency. Hover takes it too, or the same line reappears under the
   pointer (Daniel, 2026-08-19). */
.ft-card-here { fill: #f6f0dc; stroke: var(--biblion-gold); stroke-width: 2; }
.ft-role { font-size: 9.5px; letter-spacing: 0.6px; fill: var(--bs-secondary-color, #6c757d); }
/* ⚠ NO font-size HERE. Each name carries its own as a presentation attribute,
   shrunk to fit its card — and a stylesheet rule beats a presentation
   attribute, so declaring it here would silently undo the fitting and put
   "Maher-shalal-hash-baz" back outside its box. Weight and colour only. */
.ft-name { font-weight: 600; fill: var(--bs-link-color, #0d6efd); }
.ft-name-here { fill: var(--bs-body-color, #212529); }
.ft-years { font-size: 9.5px; fill: var(--bs-secondary-color, #6c757d); }
.ft-rule { stroke: var(--bs-border-color, #dee2e6); stroke-width: 1.5; }
/* ⚠ AN <a> INSIDE AN SVG STILL TAKES THE PAGE'S LINK UNDERLINE, and on a card
   whose whole box is the target it reads as clutter — eight underlined names
   in a diagram. The card itself is the affordance; the hover says so. */
/* ⚠ NO UNDERLINE, EVER, INCLUDING ON HOVER (Daniel, 2026-08-19). An <a> inside
   an SVG still takes the page's link underline, and eight underlined names turn
   a diagram into a list of links. The whole card is the target and the card
   itself is the affordance — the gold on hover is what says so. */
.family-tree a,
.family-tree a:hover { text-decoration: none; }
.family-tree a:hover .ft-card { fill: #f6f0dc; stroke: var(--biblion-gold); }

.tl-life { fill: var(--biblion-gold); opacity: 0.22; }
/* ⚠ THE KNOCKOUT UNDER "176 years", AND IT HAS TO BE OPAQUE. The axis, the
   ticks and the badges are all painted before the caption, so without a ground
   the rule ran straight through the words on every page carrying a lifespan.
   Page background, not white: in a dark mood a white patch would be a hole in
   the drawing. The gold is laid over it again by the drawing itself where the
   caption stands inside the band, so the window matches the band exactly. */
.tl-life-cap { fill: var(--bs-body-bg, #fff); }
.tl-life-label { font-size: 11.5px; fill: var(--bs-body-color, #212529); }
.tl-span { fill: #7a5ea8; opacity: 0.5; }
.tl-dot { fill: #7a5ea8; }
.tl-stem { stroke: #7a5ea8; stroke-width: 1; opacity: 0.55; }
.tl-axis { stroke: var(--bs-body-color, #212529); stroke-width: 1; }
/* The silence between two segments is dashed, so it reads as time skipped
   rather than as a rule drawn. */
.tl-gap { stroke: var(--bs-secondary-color, #6c757d); stroke-width: 1; stroke-dasharray: 2 4; }
.tl-gap-label { font-size: 10px; fill: var(--bs-secondary-color, #6c757d); }
.tl-label { font-size: 10.5px; fill: var(--bs-body-color, #212529); }
/* A MENTION IS NOT A LOCATION, and place.php now carries both pictures on one
   page: the events the graph puts here, and the events whose passages merely
   name the place. Same violet, because it is the same reference work speaking
   — the difference is in the MARK, a hollow dot for the weaker claim, because a
   legend is a thing nobody reads and a colour would have said "different
   source" instead of "different claim". */
/* The dated axis an uncaptioned drawing gets instead of captions. Quieter
   than the axis itself: it is there to be consulted, not read across. */
.tl-tick { stroke: var(--bs-secondary-color, #6c757d); stroke-width: 1; opacity: 0.55; }
.tl-tick-label { font-size: 9px; fill: var(--bs-secondary-color, #6c757d); }
/* A NUMBER STANDS IN FOR A TITLE that would not fit, so it has to be small
   enough to sit beside its own mark and quiet enough not to be read as part
   of the drawing. It is a key into the list, not a label. */
/* The badge a number rides in. White ground so the axis and the bars do not
   read through the digits, a violet ring so it is still part of the drawing
   rather than something laid on top of it. */
/* ⚠ THE FILL IS OPAQUE AND STAYS OPAQUE. The evidence ramp is applied to
   stroke-opacity by the drawing, never to the element, so a faint mention
   still gets a solid ground under its number. */
.tl-badge { fill: var(--bs-body-bg, #fff); stroke: #7a5ea8; stroke-width: 1.2; }
.tl-index { font-size: 9px; fill: var(--bs-body-color, #212529); }
.tl-key {
    display: inline-block; min-width: 1.6em; margin-right: 0.15em;
    font-size: 0.8em; color: var(--bs-secondary-color, #6c757d);
    font-variant-numeric: tabular-nums; text-align: right;
}
.tl-mentioned .tl-dot { fill: none; stroke: #7a5ea8; stroke-width: 1.3; }
/* Only for a mentioned drawing whose caller weighs nothing: place.php gives
   every mark a weight, and that arrives as an inline style, which outranks
   this. Setting the ramp here instead would be silently ignored. */
.tl-mentioned .tl-span { opacity: 0.28; }
/* The two end years, in HTML so they keep a readable size at any width. */
.tl-ends {
    display: flex;
    justify-content: space-between;
    font-size: 0.8rem;
    color: var(--bs-secondary-color, #6c757d);
}

/* ---- People & places (people.php) -----------------------------------------
   The A-Z strip and the name chips. A letter with nobody behind it is drawn
   dead rather than dropped: the alphabet keeps its shape, and the gap says
   something true (no one in the Bible is called X). The live letters take the
   app's gold on hover and to mark the current one — one colour, meaning HERE,
   exactly as everywhere else. */
/* PEOPLE AND PLACES ARE TWO KINDS OF THING AND MUST LOOK IT (Daniel,
   2026-08-19). Stacked bare, the page was two identical A–Z strips and you
   could not tell which one you were reading. A shared boundary is the
   strongest grouping signal available, so each half gets a region of its own;
   the heading icons are the ones the search bands already use for a person and
   a place, so the vocabulary is not new. Kept deliberately quiet — a hairline
   and a tint, not a card with a shadow. */
.pp-group {
    border: var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);
    border-radius: var(--bs-border-radius, 0.375rem);
    padding: 0.85rem 1rem 1rem;
    /* ONE TONE FOR ALL THREE REGIONS, AND IT IS VIOLET (Daniel, 2026-08-20).
       The wash was 0.012 — measured at 1.022:1 against the white panel, which
       is to say invisible, so the border was separating them on its own.

       ⚠ THE HUE IS NOT DECORATION, IT IS THE VOCABULARY. Violet means "a
       reference work speaking" ([[bible-gold-system]]), and this whole page is
       Theographic quoted — the same act, and the same data, as the violet
       search bands. So this is `.search-who`'s exact ground rather than a new
       value; the two must not drift.

       ⚠ THE SAME TONE ON EACH OF THE THREE, deliberately. People, places and
       excavations are three indexes into ONE source; a colour per region would
       say they were three kinds of thing, and would spend hues from a closed
       set where each already means something. What separates them is the
       bounded box and the icon, settled 2026-08-19 and unchanged. */
    background: rgba(122, 94, 168, 0.05);
}
/* ⚠ THEY STACK, AND SIDE-BY-SIDE WAS TRIED AND MEASURED WORSE. Two columns
   ought to say "two kinds of thing" by being parallel, but the prose column is
   720px, so each half gets 352 — and the A–Z strip then wraps to FOUR rows
   instead of two, making the whole block taller (527px against 487px) as well
   as harder to scan. The bounded regions and the icons do the separating on
   their own. Don't reintroduce the columns without widening the measure first. */
.pp-split { display: grid; gap: 1rem; }

.pp-alpha { display: flex; flex-wrap: wrap; gap: 0.3rem; margin: 0.25rem 0 1rem; }
/* ⚠ WHITE, NOT TRANSPARENT, SINCE THE REGION BEHIND WENT VIOLET (Daniel,
   2026-08-20: "give back white background to pills and letter buttons"). A
   control that takes its ground from the panel stops reading as a control the
   moment the panel is tinted. It also keeps the link contrast honest: the
   violet ground measures 4.22:1 under Bootstrap's link blue and FAILS, which
   is why `.search-who a` had to be darkened — on white the default clears. */
.pp-letter {
    display: inline-block;
    min-width: 2rem;
    padding: 0.25rem 0.4rem;
    text-align: center;
    border: 1px solid rgba(0, 0, 0, 0.25);
    border-radius: var(--bs-border-radius-sm, 0.25rem);
    background-color: #fff;
    color: var(--bs-body-color, #212529);
    text-decoration: none;
}
/* ⚠ THE GOLD ARRIVES AS AN IMAGE LAYER, NOT AS background-color. The tint is
   translucent, so replacing the colour would let the violet ground show
   through it and the state would read differently here than anywhere else in
   the app. A gradient of one colour paints ON TOP of the white beneath, which
   is what makes this identical to gold-on-white — and keeps the token rather
   than hardcoding the composite. */
.pp-letter:hover,
.pp-letter-on {
    background-image: linear-gradient(var(--biblion-gold-tint, rgba(201, 162, 39, 0.16)),
                                      var(--biblion-gold-tint, rgba(201, 162, 39, 0.16)));
    color: inherit;
}
/* ⚠ THE CURRENT LETTER IS NOT MARKED BY COLOUR ALONE. The gold tint against
   white is a 1.14:1 difference — real to a sighted reader glancing at the row,
   invisible to anyone who cannot separate those two, and nothing at all in high
   contrast. The weight and the darker edge carry it too, and people.php sets
   aria-current="page" so it is announced rather than merely shown. */
.pp-letter-on {
    border-color: var(--biblion-gold);
    border-width: 2px;
    font-weight: 700;
}
.pp-letter-off {
    display: inline-block;
    min-width: 2rem;
    padding: 0.25rem 0.4rem;
    text-align: center;
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: var(--bs-border-radius-sm, 0.25rem);
    color: #adb5bd;
}
/* A name and its verse count. The count is the only ranking a reader gets, so
   it rides inside the chip rather than being hidden behind a hover. */
.pp-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.pp-chip {
    display: inline-block;
    padding: 0.3rem 0.75rem;
    border: 1px solid rgba(0, 0, 0, 0.18);
    border-radius: 2rem;
    /* White for the same two reasons as .pp-letter above: a pill has to sit ON
       the region rather than dissolve into it, and this link colour does not
       clear AA on the violet ground. */
    background-color: #fff;
    text-decoration: none;
    color: var(--bs-link-color, #0d6efd);
}
.pp-chip:hover {
    background-image: linear-gradient(var(--biblion-gold-tint, rgba(201, 162, 39, 0.16)),
                                      var(--biblion-gold-tint, rgba(201, 162, 39, 0.16)));
}
.pp-chip-n {
    margin-left: 0.4rem;
    font-size: 0.82em;
    color: var(--bs-secondary-color, #6c757d);
}
/* THE TROWEL SAYS "THIS ONE HAS BEEN DUG" on a letter page, where 33 places out
   of 1,274 carry an excavation and nothing else distinguishes them. Quiet on
   purpose and in the count's own grey: the difference that matters is that a
   mark is THERE at all, and one in three-hundred-odd is rare enough for that to
   register without a colour. It is emphatically NOT gold — gold means HERE, the
   thing you have selected ([[bible-gold-system]]) — and not violet either, which
   would be defensible as "a reference work speaking" but shouts across a row of
   chips in a way a category mark should not. */
.pp-chip-dig {
    margin-right: 0.35rem;
    font-size: 0.8em;
    color: var(--bs-secondary-color, #6c757d);
}
/* A finger is not a cursor: at desktop size 32px is a comfortable letter, on a
   phone it is a miss. 44px is the platform floor and the gap keeps two letters
   from sharing an edge — this is a new control, so it starts on the right side
   of that line rather than joining the pager already noted in the UX audit.

   ⚠ THE CHIPS DID NOT ACTUALLY CLEAR IT, and the comment above said they did.
   Measured at 375px on 2026-08-19: the letters came out at 45px, the chips at
   40. 0.5rem of padding on a 22.4px line plus the border lands at 40.4, four
   short — close enough to look right in a screenshot and wrong under a thumb.
   0.65rem puts them at 45, the same height the letters already were, so the two
   controls agree. The gap goes to 8px for the same reason it did on the strip.

   The measurement is the point: this block reasoned its way to 44 twice and
   missed by four both times. Measure the rendered box, do not compute it. */
@media (max-width: 575.98px) {
    .pp-alpha { gap: 0.4rem; }
    .pp-letter,
    .pp-letter-off {
        min-width: 2.75rem;
        padding: 0.65rem 0.4rem;
    }
    .pp-chips { gap: 0.5rem; }
    .pp-chip { padding: 0.65rem 0.9rem; }
}

/* ---- The canon figure on refs.php (canonFigure) ----------------------------
   The Bible as one strip of 66 book cells, an arc from the studied verse to
   every referenced passage. Two voices, per the app's colour language: gold is
   the verse being studied, violet is a reference work speaking — the same
   saturated #7a5ea8 the .lex-panel rule settled, on cells rather than a rule.
   Geometry scales through the viewBox; the labels under the strip are HTML
   precisely so they do NOT scale, or at phone width they would shrink below
   reading size. Cell tints ride on the rects as opacity attributes (scaled by
   count in refs.php), which these hover rules outrank without !important —
   CSS beats a presentation attribute by definition. */
.refs-canon svg { display: block; width: 100%; height: auto; }
.rc-off      { fill: #f1eef6; }
.rc-on       { fill: #7a5ea8; }
.rc-arc      { fill: none; stroke: #7a5ea8; stroke-width: 1.1; opacity: 0.5; }
/* The invisible twin drawn under each arc: a 1.1-unit stroke is no click
   target, so this one widens the same path to nine. Transparent paint still
   catches the pointer — visiblePainted looks at visibility, not alpha. */
.rc-hit      { fill: none; stroke: transparent; stroke-width: 9; }
.rc-dot      { fill: #7a5ea8; }
/* The testament seam, crossing the strip in the Malachi–Matthew gap. Ink, not
   the label grey — it has to survive crossing a saturated cell. */
.rc-nt       { stroke: #1b1b1b; stroke-width: 1; }
.rc-here     { fill: none; stroke: var(--biblion-gold); stroke-width: 2; }
.rc-here-dot { fill: var(--biblion-gold); stroke: #fff; stroke-width: 1.5; }
.refs-canon a:hover .rc-arc { opacity: 1; stroke-width: 1.6; }
.refs-canon a:hover .rc-on  { opacity: 1; }
.refs-canon-labels { position: relative; height: 1.3em; font-size: 0.8rem; color: #6c757d; }
/* The mirrored figure — the verses pointing AT this one — puts its strip at the
   top and hangs its arcs beneath, so its labels move above the strip with it.
   They name the strip, and left below they would sit a hundred units of arc
   away from the thing they name. */
.refs-canon-labels-top { margin-bottom: 0.2rem; }
.rcl-start { position: absolute; left: 0; }
.rcl-end   { position: absolute; right: 0; }
.rcl-nt    { position: absolute; transform: translateX(-50%); white-space: nowrap; }
/* refs.php drops an end's word when the verse lives near that end of the
   canon — "Genesis 1:1" names the left edge better than a label beside it —
   and the verse's label then takes that corner itself, flush rather than
   centered on its position, which would hang it half off the card. */
.rcl-here  { position: absolute; transform: translateX(-50%); white-space: nowrap;
             color: var(--bs-body-color, #212529); font-weight: 600; }
.rcl-here.rcl-left  { left: 0; transform: none; }
.rcl-here.rcl-right { right: 0; transform: none; }

/* The picker a multi-reference book cell opens (see the canon-figure block in
   app.js): the same floating-panel manners as #strongs-popup — card, fixed,
   a bottom sheet on a phone — but on the lex-panel's violet ground, because
   what it holds is the Treasury speaking, not scripture. Items take the gold
   wash on hover: the one you are pointing at. */
#refs-picker {
    display: none;
    background-color: #f8f6fb;
    border-left: 3px solid #7a5ea8;
    max-width: 300px;
    z-index: 1040;
}
#rp-list { max-height: 45vh; overflow-y: auto; }
.refs-picker-item { display: block; padding: 0.3rem 0.5rem; border-radius: 0.25rem; }
.refs-picker-item:hover { background-color: rgba(201, 162, 39, 0.22); }

@media (max-width: 575.98px) {
    /* The bottom-sheet geometry #strongs-popup takes at this width, for the
       same reason: nothing this wide can sit beside what opened it on a phone.
       isBottomSheet() in app.js tracks this breakpoint for both panels. */
    #refs-picker {
        left: 0;
        right: 0;
        bottom: 0;
        top: auto;
        max-width: none;
        max-height: 70vh;
        overflow-y: auto;
        border-bottom-left-radius: 0;
        border-bottom-right-radius: 0;
        padding-bottom: calc(1rem + var(--safe-b)) !important;
        animation: sheet-rise 0.18s ease-out;
    }
    #rp-list { max-height: none; }
}

/* Arriving at a row from the canon figure (?page=N#ref-i). The wash is the
   app's "here is the thing you asked for" gold at its over-light alpha;
   !important only because the row's own .bg-light band is !important and
   would win otherwise. The scroll margin keeps the landing row clear of the
   fixed navbar, exactly as .verse-row does for a verse link. */
.result-row { scroll-margin-top: 6rem; }
.result-row:target { background-color: rgba(201, 162, 39, 0.22) !important; }

/* Banded, so the eye can hold a line across a wide row. */
.item-row:nth-child(even) {
    background-color: var(--bs-light, #f8f9fa);
}
/* A row marked for removal, in whatever color this reader picked for a selected
   verse (Preferences → verse selection; --verse-select is emitted on :root by
   head.php only when they chose one, so the fallback here is the default sky
   blue). Selection is a different kind of thing from banding and has to beat it:
   same specificity, so this must stay below the rule above. */
.item-row.item-picked {
    background-color: var(--verse-select, #d9edf7);
}
/* Anywhere in the row ticks it (initBulkSelect in app.js), so the whole row
   should look live. Hover is laid over whatever the row's background already is
   — white, banded or selected — rather than replacing it, which on a banded row
   would look like the band had dropped out. */
[data-bulk-form] .item-row { cursor: pointer; }
[data-bulk-form] .item-row:hover {
    background-image: linear-gradient(rgba(0, 0, 0, 0.03), rgba(0, 0, 0, 0.03));
}
/* Dragging rows onto the collection chips (initCollections in app.js). The row
   in flight dims; every chip that can take the drop lifts slightly so the
   targets announce themselves the moment the drag starts; the one under the
   pointer takes the reader's own verse-selection color — the same "this is
   picked" language the ticked rows already speak. */
.item-row.col-dragging { opacity: 0.45; }
/* The drag handle — the ⋮⋮ at a row's left edge, and the ONLY place a drag can
   start (see initCollections), so text anywhere in the row still selects.
   Rendered only when groups exist; has-grips is what makes room for it, keeping
   the padding in CSS per the tint rules above so a ticked row's tint still
   covers the same box. Aligned to the first line rather than centered: on a
   tall note a centered handle floats beside the middle of the text, attached
   to nothing the eye can name. */
.has-grips .item-row { padding-left: 1.6rem; }
.col-grip {
    position: absolute;
    left: 0.3rem;
    top: 0.55rem;
    color: #adb5bd;
    cursor: grab;
    padding: 0 0.25rem;   /* a finger-friendlier target than the glyph alone */
}
.col-grip:hover { color: #6c757d; }
.col-grip:active { cursor: grabbing; }
[data-col-chips] .col-target { box-shadow: 0 0 0 2px var(--verse-select, #d9edf7); }
[data-col-chips] .col-drop {
    background-color: var(--verse-select, #d9edf7);
    border-color: transparent;
    color: #212529;
}
/* The tick box that marks a note or a favorite for removal, at the row's
   top-right where the options ellipsis used to be. Its label is visually hidden
   (it names the item for screen readers), so the padding Bootstrap leaves for a
   label beside the box would only push the box off the row's edge.
   Inset from the corner rather than flush against it: the box is taken out of
   the flow, so where it sits changes no layout and cannot reopen the gap the
   row margin used to leave. The row's own right padding is wider than the inset
   plus the box, which is what keeps the text clear of it. */
.item-select {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    margin: 0;
    padding-left: 0;
    min-height: 0;
}
.item-select .form-check-input {
    float: none;
    margin: 0;
    cursor: pointer;
}

/* A preference and its control on one line — the label, then the select beside
   it — instead of the label stacked above. The names here are short and the
   selects narrow, so stacking left a column of half-empty lines. Wraps rather
   than squeezes when the two will not fit, which is what happens to the time
   zone picker on a phone. */
.setting-row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}

/* Verse-selection color palette on the account page: twelve pale swatches, the
   chosen one ringed. Each swatch is a label wrapping a visually-hidden radio, so
   the whole square is the click target and keyboard focus still works. */
.verse-swatch {
    width: 2rem;
    height: 2rem;
    border-radius: var(--bs-border-radius);
    border: 1px solid rgba(0, 0, 0, 0.15);
    cursor: pointer;
    margin: 0;
}
.verse-swatch:has(input:checked) {
    outline: 2px solid var(--bs-primary, #0d6efd);
    outline-offset: 2px;
}
.verse-swatch:has(input:focus-visible) {
    outline: 2px solid var(--bs-primary, #0d6efd);
    outline-offset: 2px;
}
/* Contents list on the About page. */
.toc li + li {
    margin-top: 0.3rem;
}

/* The About page's contents panel: a button pinned bottom-right, clear of the
   reading column, opening the offcanvas that holds the same list as the one at
   the top of the page. Built by initAboutContents() in app.js. */
.about-contents-btn {
    position: fixed;
    right: calc(1.25rem + var(--safe-r));
    bottom: calc(1.25rem + var(--safe-b));
    z-index: 1030;
    border-radius: 2rem;
    padding-left: 1rem;
    padding-right: 1rem;
    box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.25);
    transition: transform 0.25s ease;
}
/* In the reader's view the pinned chapter pager is centred on the SCREEN, not in
   the column, and on a phone the two are wide enough to meet at the bottom edge.
   The pill steps up over it rather than either being clipped — it gives way
   because the pager is what the reader has just scrolled to. Set from
   updateReaderPager() in app.js, so it moves exactly when the pager appears. */
body.reader-pager-out .about-contents-btn {
    transform: translateY(-3.25rem);
}

/* NIGHT INVERTS THE FLOATING PILLS (Daniel, 2026-08-01). Both of them are
   btn-dark, which is near-black on a near-black sky: on the Night mood they
   stopped being findable at all. Under Night — and only under Night — they turn
   white on dark, which is the same contrast the other four moods give a dark pill
   against a light sky, arrived at from the other side.

   Scoped to the two PILL classes rather than to .btn-dark. Every ordinary
   btn-dark on the site sits on a white card where it is perfectly legible; a
   blanket rule would invert buttons that were never in trouble. The pills are the
   only controls that float over the wallpaper, which is why they are the only
   ones the sky can swallow.

   body.night is set on the server by bodyClass() and kept current by app.js —
   Automatic re-picks the mood from the reader's own clock, and this follows it
   because it hangs off the same class. */
body.night .about-contents-btn,
body.night .return-trail-btn,
body.night .welcome-pill {
    --bs-btn-bg: #f4f1ea;
    --bs-btn-border-color: #f4f1ea;
    --bs-btn-color: #1b1b1f;
    --bs-btn-hover-bg: #ffffff;
    --bs-btn-hover-border-color: #ffffff;
    --bs-btn-hover-color: #1b1b1f;
    --bs-btn-active-bg: #e6e2d8;
    --bs-btn-active-border-color: #e6e2d8;
    --bs-btn-active-color: #1b1b1f;
    /* A dark halo instead of the light one: a shadow the colour of the sky is
       what separates a pale pill from a pale-lit ridge behind it. */
    box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.55);
}

/* The return control (js/app.js, initReturnTrail) — same pill as the Contents
   button and deliberately so: one visual language for "a control that floats
   above the page". It takes the OPPOSITE corner, which keeps the two from ever
   meeting on comm.php's introduction, the one page that shows both.

   Bottom rather than top because the navbar is already crowded and auto-hides
   in the reader, and because this is a thing the reader reaches for after
   reading rather than before.

   The label is a place in the reader's own words and can run long — "the
   commentary on 2 Thessalonians 2:5-7" — so it is capped and ellipsised rather
   than allowed to grow across the viewport. The arrow never shrinks. */
/* ⚠ THE LIFT IS ON `bottom`, NOT ON `transform`, and that is the whole reason
   it is a variable rather than a class. The audio bar already lifts this pill
   with a transform, and transforms do not accumulate — see the note by
   body.audio-open, where the combined case has to be spelled out by hand. Using
   the other property means the map card's lift and the player's simply add up,
   with no third rule to remember. app.js sets --map-card-lift from the card's
   MEASURED height, because that height is not fixed: the card loses its book
   list under 576px and comes out shorter. */
.return-trail-btn {
    position: fixed;
    left: calc(1.25rem + var(--safe-l, 0px));
    bottom: calc(1.25rem + var(--safe-b) + var(--map-card-lift, 0px));
    z-index: 1030;
    border-radius: 2rem;
    padding-left: 1rem;
    padding-right: 1rem;
    box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.25);
    display: inline-flex;
    align-items: center;
    max-width: min(22rem, calc(100vw - 2.5rem));
}
.return-trail-label {
    display: inline-block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* ⚠ THE CAPITAL IS DRAWN, NOT STORED. Every page's $placeLabel is written in
   lower case on purpose — the same string is the button's aria-label, where it
   reads "Back to covenant law" and a capital in the middle of that
   sentence would be wrong. The pill shows the label alone, though, so it starts
   a line and wants a capital. ::first-letter gives it one without touching the
   string, and it must be ::first-letter rather than `text-transform: capitalize`,
   which would also shout "Rituals & Offerings". `display: inline-block` is what
   makes the pseudo-element apply at all: it does not work on an inline box. */
.return-trail-label::first-letter { text-transform: uppercase; }
/* On a phone the two pills would sit on one line and fight for it, so the
   return control gives way — it is the wider of the two and the one whose text
   would be cut first. */
@media (max-width: 575.98px) {
    .return-trail-btn { max-width: calc(100vw - 9rem); }
}

/* The post-login welcome pill (js/app.js, initWelcomePill) — the same floating
   language as the return control above, same corner, since the two are never
   shown together in practice (arriving here fresh from login carries no
   return trail). Spent on the reader's first interaction with the page rather
   than on a timer or a close button: a login is a page in itself, and it is
   the very next thing they touch — a click, a tap, a key — that means they
   have moved on to actually reading. */
.welcome-pill {
    position: fixed;
    left: calc(1.25rem + var(--safe-l, 0px));
    bottom: calc(1.25rem + var(--safe-b));
    z-index: 1030;
    border-radius: 2rem;
    padding-left: 1rem;
    padding-right: 1rem;
    box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.25);
    display: inline-flex;
    align-items: center;
    max-width: min(22rem, calc(100vw - 2.5rem));
    transition: opacity 0.2s ease;
}
.welcome-pill.is-leaving {
    opacity: 0;
}

/* The sequence strip — the third navigation axis, "previous / next along a
   series". Built by seqNav() in includes/seqnav.php, which carries the reasoning
   for why it is a strip in the flow rather than a third floating pill.

   IT IS DELIBERATELY BOOTSTRAP'S .page-link AND NOTHING ELSE. Thirteen list
   pages and the reader already draw a chevron / label / chevron strip for this
   job; a second look would mean the app said "move along" two different ways.
   So border, radius, disabled state and focus ring are all inherited, and the
   only thing added here is room for a NAME where a page number used to sit.

   TWO WIDTHS. Without names the three cells stay grouped and right-aligned —
   the look .pagination has always had, because two lone chevrons spread to the
   far edges of an admin table are harder to use than a compact group, not
   easier. The spread version is switched on by .seq-nav-named, which seqNav()
   adds only when an end actually carries text to fill the width with. */
.seq-nav {
    display: flex;
    align-items: stretch;
    justify-content: flex-end;
    gap: 0.5rem;
    /* THE STRIP MUST NEVER SET ITS PARENT'S MINIMUM WIDTH.
       .prose is a flex item of a `d-flex justify-content-center` card, so its
       min-width resolves to auto and its min-content is whatever its widest
       child demands. A nowrap heading anywhere below here would push .prose past
       the card's padding — and because the card CENTERS, the overflow lands on
       both sides at once and the white margin disappears left and right
       together. Reported on comm.php while narrowing the window, 2026-07-31.
       min-width:0 here and on every flex box below breaks that chain at the
       source: the strip's own minimum is the chevrons, and the ellipsis then
       does what it was always meant to. */
    min-width: 0;
    max-width: 100%;
    /* The spacing every call site used to carry as its own mt-3/mt-4 utility.
       One value here is the point of having a component; .reader-pager still
       overrides it, because that one is pinned rather than in the flow. */
    margin-top: 1rem;
}
.seq-nav-named {
    justify-content: space-between;
}
/* Both ends claim equal width so the middle stays centred whatever the two
   names are, and neither end can push the other off the line. Only when named:
   an unnamed end is a chevron and must stay chevron-sized. */
.seq-nav-named .seq-end {
    flex: 1 1 0;
    min-width: 0;
}
.seq-end .page-link {
    height: 100%;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    border-radius: var(--bs-border-radius);
    /* The middle link of the shrink chain. Without this its default
       min-width:auto reinstates the nowrap heading's full width as a floor and
       the overflow described on .seq-nav comes straight back. */
    min-width: 0;
}
.seq-nav-named .seq-end-next {
    display: flex;
    justify-content: flex-end;
    text-align: right;
}
.seq-mid { flex: 0 0 auto; display: flex; }
.seq-mid .page-link {
    display: flex;
    align-items: center;
    border-radius: var(--bs-border-radius);
}
.seq-stack { min-width: 0; display: flex; flex-direction: column; }
/* A heading can be long — "Days two and three: separating and filling" — and on
   a wide screen it is one line or the strip stops being a strip.

   THE HOUSE RULE FOR A NAME THAT WILL NOT FIT IS CSS ELLIPSIS, NOT A SERVER-SIDE
   CUT. Truncating in PHP means guessing a character budget, and the guess is
   wrong the moment the font size, the zoom, the viewport or the language moves —
   it cuts a short name that would have fitted and overflows a long one that
   would not. The browser is the only thing that knows the actual width. The
   return-trail pill already works this way (.return-trail-label); this is the
   same rule, and it is the rule for any new truncating label. */
.seq-name {
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;   /* closes the shrink chain at the leaf */
}
.seq-where {
    display: block;
    font-size: 0.8125rem;
    font-weight: 400;
    opacity: 0.7;
}
/* On a phone the reference goes and the heading stays. The heading is what the
   reader is deciding on; the reference is confirmation, and confirmation is what
   you drop first when there is no room for both.

   THE NAME STAYS ON ONE LINE AT EVERY WIDTH. A two-line clamp was tried here on
   2026-07-31 and rejected the same day (Daniel): a name that wraps makes the two
   ends different heights, and a strip whose height changes with its contents
   stops reading as a strip. One line and an ellipsis, everywhere — the base rule
   above already does it, so this block only drops the reference and tightens the
   padding. */
@media (max-width: 575.98px) {
    .seq-name { font-size: 0.9375rem; }
    .seq-where { display: none; }
    .seq-end .page-link { padding-left: 0.5rem; padding-right: 0.5rem; }
}
/* THE CHAPTER'S SECTION RAIL — the commentary's headings standing in for the
   landmarks Scripture does not print. A dotted spine with the reader's own
   position filled in, so the panel answers "where am I" at a glance rather than
   asking to be read.

   The verse number is set in the reading serif and tabular, because it is the
   one column the eye runs down; the heading is interface sans, because it is the
   commentary talking and not the text. */
.section-rail {
    position: relative;
    padding-left: 1.1rem;
}
.section-rail::before {
    content: '';
    position: absolute;
    left: 0.28rem;
    top: 0.5rem;
    bottom: 0.5rem;
    width: 2px;
    background: var(--bs-border-color, #dee2e6);
}
.section-rail-item {
    position: relative;
    display: block;
    padding: 0.4rem 0;
    color: #6c757d;
    text-decoration: none;
    line-height: 1.35;
}
.section-rail-item::before {
    content: '';
    position: absolute;
    left: -0.95rem;
    top: 0.78rem;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--bs-border-color, #dee2e6);
}
.section-rail-item:hover,
.section-rail-item:focus-visible { color: #2c2926; }
/* The section the reader is standing in. Marked by app.js from the verse rows,
   so it tracks the page rather than the last thing clicked. */
.section-rail-item.is-here {
    color: #2c2926;
    font-weight: 500;
}
.section-rail-item.is-here::before {
    background: var(--brand-ink, #0d5c63);
    box-shadow: 0 0 0 3px rgba(13, 92, 99, 0.18);
}
.section-rail-v {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-variant-numeric: tabular-nums;
    font-size: 0.8rem;
    color: #adb5bd;
    margin-right: 0.45rem;
}
.section-rail-item.is-here .section-rail-v { color: #6c757d; }
.section-rail-t { font-size: 0.925rem; }
/* A section that opened in the chapter before: the reader walked into the middle
   of it, and saying so is more honest than an anchor that claims verse 1. */
.section-rail-from {
    display: block;
    font-size: 0.75rem;
    color: #adb5bd;
    margin-left: 1.55rem;
}

#aboutContentsNav .nav-link {
    padding: 0.3rem 0 0.3rem 0.6rem;
    color: #2c2926;
    border-left: 2px solid transparent;
}
/* The two levels take the faces their own headings take on the page: sections in
   Roboto Serif like the h2s they point at, subsections in Prompt like the h3s.
   The panel then reads as an index of this document rather than as generic
   furniture. Weight 600 rather than the headings' 800 — at this size the extra
   bold fills in and turns to a smudge, and 600 is a cut we host and preload. */
#aboutContentsNav .about-contents-section {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-weight: 600;
}
#aboutContentsNav .about-contents-sub {
    font-family: 'Prompt', sans-serif;
    font-size: 0.85em;
    color: #6c757d;
    padding-left: 1.4rem;
}
/* Scrollspy marks the section being read. This is a highlight and nothing more. */
#aboutContentsNav .nav-link.active {
    border-left-color: var(--bs-primary, #0d6efd);
    color: var(--bs-primary, #0d6efd);
}

/* Only one section shows its subsections, so the panel stays a list of ten rather
   than a wall of seventy-six. Which one is `expanded`, NOT `active` — and the
   difference matters. Hanging this on Scrollspy's class meant that once the panel
   could stay open over a scrolling page, every section it scrolled past opened and
   closed the list under the reader's pointer. Expansion is now a choice: set when
   the panel opens, and thereafter only by clicking a section. Scrolling moves the
   highlight and leaves the shape of the list alone. */
#aboutContentsNav .about-contents-subs { display: none; }
#aboutContentsNav .about-contents-section.expanded + .about-contents-subs { display: block; }

/* .about-contents-hint is DELETED (2026-08-15). It said "Click again to jump
   there" — the one thing about this panel a reader could not otherwise find
   out — and it went with the second press it explained. Nothing builds that
   element any more; if a hint is ever wanted here again, it is a new rule and
   a new reason, not this one restored. */

/* A prose heading that opens and closes the section under it. Deliberately not
   Bootstrap's accordion: no box, no chrome, no background — just the heading as
   it looks everywhere else, with a small caret to show it can be clicked. */
/* The open section is tinted so it reads as one block rather than as a heading
   with loose text under it. The heading and the panel are separate elements, so
   the tint has to run across both: the heading carries what was its bottom
   margin as padding instead, which closes the transparent gap between them
   without moving the text. The horizontal padding is pulled back out again with
   matching negative margins, so the tint is wider than the prose while the words
   stay on the same left edge as everywhere else. */
.collapse-heading {
    cursor: pointer;
    user-select: none;
    -webkit-user-select: none;
    padding: 0.5rem 0.75rem 0.6rem;
    /* Was 2.75rem on .prose-heading; less the padding-top just added, so the
       rhythm between sections is unchanged. */
    margin-top: 2.25rem;
    margin-bottom: 0;
    margin-left: -0.75rem;
    margin-right: -0.75rem;
    border-radius: var(--bs-border-radius) var(--bs-border-radius) 0 0;
    transition: background-color 0.15s ease;
}

.collapse-heading[aria-expanded="true"] {
    background-color: var(--bs-light);
}

/* .collapsing is included so the tint is there for the whole animation and does
   not snap on at the end. Vertical padding is deliberately absent: with
   border-box a panel animating down to height 0 cannot shrink below its own
   padding, which would leave a strip of tint behind after the section closed.
   The breathing room at the foot of the panel comes from a margin on the last
   child instead, which is inside the overflow and clips away cleanly. */
#accountSections .collapse.show,
#accountSections .collapsing {
    background-color: var(--bs-light);
    padding-left: 0.75rem;
    padding-right: 0.75rem;
    margin-left: -0.75rem;
    margin-right: -0.75rem;
    border-radius: 0 0 var(--bs-border-radius) var(--bs-border-radius);
}

/* ⚠ flow-root, AND IT IS THE WHOLE FIX FOR THE MISSING FOOT (Daniel, 2026-08-15:
   "some of the sections have no bottom padding… the part that has gray
   background"). The rule below gives the last child a 0.75rem bottom margin, and
   it was being applied correctly and doing nothing: with no padding, no border
   and no formatting context of its own, an open panel let that margin COLLAPSE
   THROUGH its own bottom edge, so the 12px landed under the tint rather than
   inside it and the grey ended flush against the last button.

   Measured against the real stylesheet: as shipped, the distance from the last
   child's bottom to the panel's bottom was 0px; with flow-root it is 12px.

   `.collapsing` is deliberately NOT given this — it sets `overflow: hidden`,
   which already establishes a formatting context, so the margin was contained
   for the whole animation and escaped only once the panel settled. Which is
   exactly why this looked right while opening and wrong afterwards. */
#accountSections .collapse.show {
    display: flow-root;
}

/* ⚠ !important, TO BEAT A UTILITY THAT IS ALSO !important — the second half of
   the same bug. Six of the nine sections end in an element carrying Bootstrap's
   `.mb-0` (the account-details `<dl>`, and the forms that close Search settings,
   Primary text, Your details, Password and Close your account) or `.mb-0` on the
   export paragraph. Bootstrap's spacing utilities all declare !important, so
   this rule lost to them and those six panels had no foot while the other three
   did. With !important on both sides specificity decides, and
   `#accountSections .collapse > :last-child` (1,1,0) beats `.mb-0` (0,1,0).

   The alternative was padding-bottom on the panel, and it is the wrong one: with
   border-box a panel animating down to height 0 CANNOT shrink below its own
   padding, so a strip of grey would be left behind after every section closed.
   The margin is inside the overflow and clips away cleanly.

   This also means a future `mb-0` on a last child cannot silently reopen the
   bug — which is what made it survive this long. */
#accountSections .collapse > :last-child,
#accountSections .collapsing > :last-child {
    margin-bottom: 0.75rem !important;
}

.collapse-caret {
    font-size: 0.5em;
    margin-left: 0.4em;
    vertical-align: middle;
    display: inline-block;
    transition: transform 0.15s ease;
}

/* Bootstrap's default is 0.35s, which reads as slow when the sections are only
   a few lines tall and one closes as another opens. Scoped to these headings so
   the navbar's own collapse keeps the framework timing. */
#accountSections .collapsing {
    transition-duration: 0.15s;
}

/* Bootstrap keeps aria-expanded on the toggle in step with the panel. */
.collapse-heading[aria-expanded="true"] .collapse-caret {
    transform: rotate(180deg);
}

/* The room below the last section is measured and applied in app.js, since how
   much is needed depends on how tall the open section happens to be. */

.prose-subheading {
    font-family: 'Prompt', sans-serif;
    font-weight: 600;
    font-size: 0.9rem;
    text-transform: uppercase;
    letter-spacing: 0.1rem;
    margin-top: 1.9rem;
    margin-bottom: 0.3rem;
    /* Same reason as .prose-heading above, and easy to miss because only the
       sections were being jumped to when that one was written. The About
       contents panel links to these too, and without this a subheading lands
       exactly under the 62px fixed navbar — the reader arrives at a section
       whose name they cannot see. Matched to .prose-heading so both levels
       come to rest at the same height. */
    scroll-margin-top: 5rem;
}

.prose-lead {
    font-weight: 300; /* Regular — a touch heavier than the Light body */
    font-size: 1.05rem;
    line-height: 1.4;
    color: #4a4540;
}

/* The prayer that opens the About page's Credits (it replaced the doxology,
   2026-07-26). Same rule the whole section obeys: the document's own face, size
   and left edge — its predecessor was set in the reading serif, centered and a
   size up at first, and all three made it read as a quotation lifted from
   somewhere else rather than as this page speaking. What marks it as prayer is
   the one thing that actually is prayer-like: its own line breaks and the pause
   between the two stanzas. It sits directly under the Credits heading the way
   every other section's opening paragraph does (user, 2026-07-26) — the old
   2rem push-down made it float loose of its title. */
.credits-prayer {
    margin: 0 0 2.5rem;
}

.credits-prayer p {
    line-height: 1.75;
    margin-bottom: 1.25rem; /* the stanza pause */
}

.credits-prayer p:last-child {
    margin-bottom: 0;
}

/* The names below it. A description list because that is what these are — a label
   and the person or company it points at — with the name sitting directly under
   its label and everything on the same left edge, at every width.

   It was two columns at first, which is the conventional way to set a credit list
   and was wrong here: the labels are long ("Developer & UX designer"), so the
   column had to be wide, which pushed two short names far out into the measure
   and left a corridor of white between label and value. Stacked, each entry reads
   as one thing, and the page keeps a single left edge all the way down.

   No size or alignment of its own: the whole block is the document's body text,
   and the labels take .prose-subheading in the markup rather than a look defined
   here, so they stay in step with the page's h3s if that style ever changes. */
.credits-list {
    margin-bottom: 0;
}
.credits-list dd {
    margin: 0;
}
/* The second line of an entry — a place under a company. It was set muted, to
   keep the company name at the front of the entry; the user wanted it in the
   document's own colour, so the line break alone does that work now. The rule
   stays as the hook for the markup rather than being deleted, since the span is
   what makes the place a second line rather than a run-on. */
.credits-list dd .credits-place {
    color: inherit;
}

.note-toggle {
    cursor: pointer;
}

/* The running header — "WEB · GENESIS 1" — names the text below it, so it is set
   in the same face as that text rather than in the interface sans. It appears
   three times: at the head of the study column, pinned in the reader's view
   (.reader-ref-flow), and in the bleedthrough ghost, and this covers all three.

   The same x-height correction as the scripture, for the same reason: Roboto
   Serif runs larger than the sans at a given size, and this line should stay the
   quiet label it is. The bleedthrough and study copies sit inside .verses, so em
   also carries the reader's size preference; the pinned one is outside it and
   simply follows the root. */
.reader-ref {
    letter-spacing: .06em;
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    /* The 0.875 is Bootstrap's .small, which the markup also asks for. Both are
       em against the same parent, so they do not multiply — whichever rule wins
       simply replaces the other, and this one does. Stating it here keeps the
       line the size it has always been while the face correction applies on top;
       drop the .small from the markup and nothing changes. */
    font-size: calc(0.875em * var(--reader-face-scale, 0.914));
}

/* Middle dot between the translation and the reference — the traditional
   separator for a reference line. Set lighter than the text around it so it
   reads as punctuation rather than as another piece of information, with a
   little air either side to keep the two halves distinct. */
.reader-ref-sep {
    margin: 0 0.55em;
    opacity: 0.5;
}

.dropcap {
    float: left;
    font-family: 'Roboto Serif', 'Reading Fallback', serif;
    /* em, not rem, so the cap scales with the reader font size (see .verses). */
    font-size: 4em;
    line-height: 3.4rem;
    padding: 0.1rem 0.5rem 0 0;
    font-weight: 800;
    display: flex;
    align-items: flex-start;
}

/* Flex (not vertical-align) so the quote's top lines up with the cap
   letter's top regardless of the two font-sizes' differing metrics. */
.dropcap .dropcap-quote {
    /* CAREFUL: em here compounds with the 4em on .dropcap above, so this is a
       FRACTION OF THE CAP, not of the body text — 0.4 x 4em lands at 1.6em, a
       small quote beside a big letter. Writing the intended 1.6em directly is the
       trap: it multiplies out to 6.4em and the quote dwarfs the very letter it
       introduces. */
    font-size: 0.4em;
    margin-right: 0.1rem;
}

.marginalia, .add-note-toggle, .add-note-form {
    font-family: 'Prompt', sans-serif;
}

.marginalia {
    font-size: 0.85em;
}

.note-view {
    font-family: 'Gochi Hand', cursive;
    font-size: 1.3em;
    color: #5B1CBA;
}

/* Hebrew and Greek match the KJV line: same inherited size, muted color.
   em so they scale with the reader font size (see .verses). */
.hebrew,
.greek {
    font-size: 1em;
}

/* Self-hosted Noto for the original languages, so the WLC and the Greek render
   the same everywhere rather than in each OS's own fallback. The scholarly
   system fonts stay in the stack behind them. */
.hebrew {
    font-family: 'Noto Sans Hebrew', 'Segoe UI', serif;
}
.greek {
    font-family: 'Noto Sans Greek', 'Segoe UI', serif;
}
/* The Latin badges inside those layers — the WLC/TR version labels and the
   MT/TR divergence refs. The Noto faces cover only their own script, so left
   alone these fall through to Segoe UI while the KJV label wears the verse
   serif; all three version labels must sit in the same face. */
.hebrew .text-uppercase,
.greek .text-uppercase,
.hebrew .mt-ref,
.greek .mt-ref {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
}

/* ---- The interlinear view (the third reader view, New Testament only) ----
   Each Greek word is a small stack: word, transliteration, gloss, parsing
   code (its meaning in the title), Strong's number(s) opening the same popup
   the study view uses. Rows are .verse-row, so selection highlighting and
   verse placement come from the rules those already have. */
.ilx-verse { margin-bottom: 1.25rem; }
.ilx-vnum { margin: .5rem .4rem 0 0; }
.ilx-words { display: flex; flex-wrap: wrap; gap: .1rem .3rem; }
.ilx-w { display: flex; flex-direction: column; padding: .25rem .45rem; border-radius: .35rem; }
.ilx-w:hover { background: rgba(0, 0, 0, .05); }
.ilx-gr { font-size: 1.25em; line-height: 1.5; }
.ilx-tl { font-size: .72rem; color: #6c757d; font-style: italic; }
.ilx-gl { font-size: .85rem; }
.ilx-gl.ilx-none { color: #adb5bd; }
.ilx-mo { font-size: .68rem; font-family: var(--bs-font-monospace); color: #8a6d3b; cursor: help; }
.ilx-st { font-size: .8rem; line-height: 1; }
.ilx-en { font-size: .875rem; color: #6c757d; margin: .35rem 0 0 1.5rem; max-width: 60rem; }

/* When the reader switches the main reading column to an original-language text
   (the toolbar's text picker), it takes that script's font at full reading size
   — not the muted, smaller study layers above — and the Hebrew runs right to
   left. In the flowing reader's view the whole column turns; in the study view
   it is the primary verse body that turns, verse by verse. */
.flow-text.script-hebrew,
.verse-body.script-hebrew {
    font-family: 'Noto Sans Hebrew', 'Segoe UI', serif;
    direction: rtl;
}
.flow-text.script-hebrew {
    text-align: right;
}
.flow-text.script-greek,
.verse-body.script-greek {
    font-family: 'Noto Sans Greek', 'Segoe UI', serif;
}
/* The Strong's lemma may be either script (the pop-up in the reader and the
   heading on strongs.php), so both faces are offered and the browser takes the
   one that has the glyphs. */
#sp-lemma,
.lemma {
    font-family: 'Noto Sans Hebrew', 'Noto Sans Greek', 'Segoe UI', serif;
}

/* The wallpaper itself lives in css/background.css, applied by the .bg-hills
   class on <body>. It replaced a 442 KB photograph that was washed out under a
   30% white overlay to keep text legible over it; the drawn hills are painted
   at a weight that needs no such veil, so the overlay went with it. */
/* The interface font, said once, where Bootstrap will hear it.
   Until this was set the app had never told Bootstrap what it is written in: the
   fonts were applied to .prose, to headings, to the scripture and to the navbar
   brand, and everything else fell through to the browser's own stack. That was
   invisible on ordinary pages, where the text sits inside .prose — but every
   component Bootstrap renders elsewhere came out in the system font: modals,
   toasts, tooltips, popovers, the Navigator panel, the navbar's own links and
   selects, and the pager.
   Setting the variable rather than the body rule reaches all of them at once,
   including the ones that do not exist yet when the page loads. What is meant to
   be something else still says so for itself — the scripture and the headings in
   Roboto Serif, code in Roboto Mono, .prose in Prompt Light.

   It has to be --bs-font-sans-serif, not --bs-body-font-family: tooltips and
   popovers declare font-family: var(--bs-font-sans-serif) directly and never look
   at the body variable, so setting only that one left those two in the system
   font while everything around them changed. This is the root of the pair —
   --bs-body-font-family defaults to it — so one line covers both. */
:root {
    --bs-font-sans-serif: 'Prompt', sans-serif;
}

body {
    min-height: 100vh;
    user-select: none;
    -webkit-user-select: none;
    /* The sides and the foot of the safe area, held once here rather than on
       every page's container. Horizontal padding is safe to put on the body in
       a way vertical padding is not: it cannot interact with the min-height
       above, or with the viewport-height rules further down. The foot is here
       too — border-box sizing means the padding comes out of the 100vh rather
       than adding to it, so nothing gains a scrollbar.
       Fixed elements do not inherit this and are handled where they are
       defined; that is deliberate for the wallpaper, which SHOULD reach every
       edge. Zero unless head.php's viewport meta carries viewport-fit=cover. */
    padding-left: var(--safe-l);
    padding-right: var(--safe-r);
    padding-bottom: var(--safe-b);
}

/* The map page is the exception: its stage is exactly 100dvh inside a body with
   overflow:hidden, so a padded body would push the bottom of the stage past the
   fold and it would be clipped rather than shown. The stage takes the insets
   itself instead — see .map-stage. */
body.map-fixed {
    padding-bottom: 0;
}

/* Top clearance under the fixed bar. Every page states it as Bootstrap's pt-5
   in its own markup, so this one rule reaches all 28 of them where no amount of
   editing style.css otherwise could — the alternative was touching every page.

   REDEFINING A FRAMEWORK UTILITY CARRIES A CONDITION, and it is not theoretical:
   this only works while every pt-5 in the codebase is a top-level container
   clearing the fixed navbar. Checked in the DOM, not by eye — and one exception
   turned up, search.php's form, which sat INSIDE its page's clearance container
   and so counted the notch twice (a measured 93px of clearance where 34px was
   right). It now wears .search-form-gap below instead. **Before adding a pt-5
   anywhere that is not the page's outermost container, use that class or make
   another one** — or the inset compounds, silently and only on a notched phone.

   The reader's own view does not use pt-5: its clearance is on
   .verses.reader-flow, which carries the same inset separately. */
.pt-5 {
    padding-top: calc(3rem + var(--safe-t)) !important;
}

/* Bootstrap's pt-5 by another name, for spacing that is NOT navbar clearance —
   see the condition above. Same 3rem, no safe-area inset. */
.search-form-gap {
    padding-top: 3rem;
}

.container > .bg-white {
    min-height: 100vh;
    position: relative;
    overflow: hidden;
}

/* Bible paper, reader only: warm chalky ivory rather than stark white, so the
   sheet reads as physical stock. Other pages (strongs.php, search.php) keep
   pure white so the contrast is felt when switching back to the bible.

   DESATURATED 2026-08-03 on Daniel's "reduce the yellowness a bit" — every
   warm value here moved two thirds of the way toward a neutral gray OF ITS OWN
   LUMINANCE (#faf5ea -> #f7f5f1, and the toning tint below). Holding the
   luminance is the whole trick: the yellow leaves without the paper getting
   lighter, darker or visibly gray.

   THEN, same day, THE TONED SHEET HAD TO BE DARKENED to #eceae2, because the
   first pass made Age toning look broken — and Daniel reported it as broken.
   Most of what separated the toned sheet from this one WAS the yellow, so
   desaturating both independently collapsed the gap between them: 4.24 dE
   before, 2.38 after, against a threshold of about 2.3 for two flat fields.
   Measured at the center of the page, where the text is, the switch was moving
   1.74 dE — under the threshold. It saved correctly and removed the class
   correctly the whole time; there was simply nothing left to see.
   #eceae2 restores the old strength (4.45 dE) as DEPTH rather than as hue,
   which is the honest reading of age toning anyway: paper dulls and darkens,
   and the yellow was only one expression of that.

   So the rule when retuning any of this: judge the sheets BY THE DISTANCE
   BETWEEN THEM, not one at a time. A change that looks right on each sheet
   alone can still leave a preference switch with nothing to say.

   NOTE the rule that actually wins here is the auth-pages one further up (it
   carries .bg-white as well, which every card has, so it outranks this block).
   All three declarations must be changed together. */
.container > .bible-paper {
    /* Paper on, Age toning OFF: a light, clean ivory — clearly paper, not the
       creamier toned sheet below. */
    background-color: #f7f5f1 !important;
}

/* Paper on AND Age toning on: the sheet deepens — in luminance now, not in
   warmth — and the vignette then shades it at the edges. Split from the base
   above so switching Age toning off returns to the lighter stock. */
.container > .bible-paper.age-toning {
    background-color: #eceae2 !important;
}

/* Age toning: the warm vignette darkening the sheet toward its edges. Its own
   class, separate from the paper above, because each can be switched off on its
   own from the account page.
   The center highlight is kept light (a heavy white wash here washed the warm
   ivory out to near-white exactly where the text sits, which made the whole
   vignette read as flat white); the warm edge tint carries the effect. */
.container > .age-toning {
    background-image: radial-gradient(ellipse at center,
        rgba(255, 255, 255, 0.10) 0%,
        rgba(255, 255, 255, 0) 45%,
        rgba(181, 176, 165, 0.18) 100%);
}

/* Page curvature: soft inner shading where the sheet bends toward the
   binding at either side. Overlay layer so Bootstrap's outer .shadow
   (which carries !important) is left alone.

   THIS BELONGS TO .bible-paper, NOT TO .age-toning, and that is deliberate —
   the curve is structure, the toning is age. But it means TWO switches darken
   the sheet's edges, and on 2026-08-03 that cost a diagnosis: Age toning was
   switched off and the edges still shaded, which read as the switch being
   broken. If anyone reports that again, this is the answer, and the Bible paper
   hint on account.php already names the curve as part of what it owns.

   The color was rgba(101, 74, 42) — brown, which sat in-family on the old cream
   stock but read as a stain once the paper went near-neutral: it left the gutter
   2.4x as saturated as the sheet, where it had been 1.4x. Neutralized to its own
   luminance at the same alpha, so the bend is exactly as deep (10.0 dL* either
   way) and carries almost no hue (cast 2.99 -> 0.81). */
.container > .bible-paper::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 2;
    box-shadow:
        inset 34px 0 32px -16px rgba(85, 76, 65, 0.17),
        inset -34px 0 32px -16px rgba(85, 76, 65, 0.17);
}

.bleedthrough {
    position: absolute;
    inset: 0;
    padding: 1.5rem;
    overflow: hidden;
    opacity: 0.06;
    transform: scaleX(-1);
    pointer-events: none;
    user-select: none;
    -webkit-user-select: none;
    z-index: 0;
}

.bg-white > .verses {
    position: relative;
    z-index: 1;
}

/* Reader font size. The whole reading column (both views, and the bleedthrough
   ghost so it stays aligned) scales from one factor a reader sets in
   Preferences; --reader-scale is published in head.php, defaulting to 1 — the
   current, smallest size. Everything inside is em-relative (see below), so it
   scales together rather than only the main text. */
.verses {
    font-size: calc(1rem * var(--reader-scale, 1));
}

/* The scripture itself is set in Roboto Serif — a reading face for the thing
   people came to read, leaving the sans to the interface around it. Both views:
   .flow-text is the reader's continuous column, .verse-text the study view's
   verse rows (which carry the KJV and original-language layers too, so those
   turn with it; the Hebrew and Greek keep their own faces, set above).

   The size is corrected because the two fonts are not the same size at the same
   font-size. Measured per em, the system sans has an x-height of 0.500 and
   Roboto Serif 0.547 — so at 1rem the serif looks noticeably bigger. 0.914
   equalises the x-heights, which is what the eye reads as "how big is this
   text", and leaves the default looking like the default always did. Held in a
   variable so it can be tuned without hunting through rules.

   1em, not 1rem: .verses above has already applied the reader's own size
   preference, so this composes with it instead of overriding it. */
.flow-text,
.verse-text {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-weight: 400;
    font-size: calc(1em * var(--reader-face-scale, 0.914));
}

/* The popup's paper is the command palette selector's gold (Daniel,
   2026-08-15), so the two things that say "this is the bit you are on" say it
   in one colour.

   OPAQUE, NOT THE rgba ITSELF. The palette's rgba(201,162,39,.22) is a wash
   meant to let its panel through; this floats over running scripture, and a
   translucent popup would show the verse it is explaining straight through its
   own text. #f3ebcf is that exact wash composited over white — same shade, no
   transparency. It replaced #f8eecd, a near neighbour that was nobody's
   variable. */
#strongs-popup {
    background-color: #f3ebcf;
    display: none;
    max-width: 380px;
    z-index: 1040;
}

/* A Strong's description is English prose with the odd Greek or Hebrew word
   inside it — "from ἀγαπάω; love, i.e. affection or benevolence". It had no
   font of its own, so it inherited the interface face, and Prompt carries NO
   Greek and no Hebrew at all: every one of those characters was substituted by
   the OS. That is why Strong's looked worse than the reader, where only the
   polytonic marks fell through.

   Prompt stays FIRST so the English is unchanged; the Noto faces sit behind it
   and the browser reaches them per character, for exactly the words Prompt
   cannot draw. Both the page and the reader's popup are covered here. */
.strongs-desc,
#sp-desc {
    font-family: 'Prompt', 'Noto Sans Greek', 'Noto Sans Hebrew', 'Segoe UI', sans-serif;
}

#sp-desc {
    font-size: 0.9rem;
}

/* The full lexicon articles under the gloss on strongs.php — Brown-Driver-
   Briggs behind a Hebrew number, Abbott-Smith behind a Greek one, converted
   to this HTML by tools/build_lexicon.php. Same face story as .strongs-desc
   above: English prose with source-language words inside it, and Prompt
   carries neither script. The sense tree nests by indentation alone.

   The panel is the .article-verse blockquote shape — pale tint, left rule,
   rounded right corners — because both are the same act: another book
   quoted on our page. But the hue is the distinction (Daniel, 2026-08-18):
   scripture quotes sit on warm gold-paper, scholarship sits on pale violet
   with the rule in the same hue saturated — two voices, two colour
   channels, never mistakable for each other at a glance. Violet was
   unclaimed in the app's colour language (gold HERE, blue editorial, grey
   advertiser) and now means: a reference work speaking. The text stays in
   the interface sans rather than the reading serif for the same reason.
   Kept deliberately pale — the panel is a container, not an accent. */
.lex-panel {
    background: #f8f6fb;
    border-left: 3px solid #7a5ea8;
    border-radius: 0 var(--bs-border-radius) var(--bs-border-radius) 0;
    padding: 1rem 1.25rem;
}
.lex-article {
    font-family: 'Prompt', 'Noto Sans Greek', 'Noto Sans Hebrew', 'Segoe UI', sans-serif;
}
.lex-article .lex-sense {
    padding-left: 1.25rem;
}
.lex-article .lex-sense-n {
    font-weight: 600;
}
.lex-article .lex-stem {
    font-variant: small-caps;
}

/* Sits just under the fixed navbar's 1030. The navbar is a stacking context,
   so its dropdowns can never rise above a sibling that matches its z-index —
   at 1030 this bar (later in the DOM) painted over open menus. Still above the
   book edges (1020) and the page itself. */
#scroll-progress-bar {
    position: fixed;
    left: 0;
    width: 0%;
    height: 3px;
    /* The player's gold (Daniel, 2026-08-02), replacing the coral this line
       carried on its own. Both are "how far through are you" — one through the
       page, one through the chapter — so they now say it in the same color. */
    background-color: var(--biblion-gold);
    z-index: 1029;
}

/* Art gilt: the page block seen from the side, as on a well-made Bible. Bole —
   the colored ground — laid under gold leaf reads gold face-on and shows its
   own color once the block is fanned, which is the angle these are drawn from,
   hence the bole rather than the gold. The fine vertical striping stands in for
   individual leaves; the widths are set per chapter (see the fore-edge gauge in
   index.php), so the block thickens on one side and thins on the other as you
   move through the Bible.

   The four colors below are all that separates one colorway from another, so
   they are held as custom properties and a colorway is just a set of values.
   Red is repeated as the fallback in case the class is ever missing. */
.fore-edge {
    --gilt-bole:  #e3b3ae;
    --gilt-leaf:  rgba(140, 48, 42, 0.45);
    --gilt-rule:  #b5645e;
    --gilt-shade: rgba(90, 20, 18, 0.18);

    position: fixed;
    bottom: 0;
    z-index: 1020;
    pointer-events: none;
    background-color: var(--gilt-bole);
    background-image: repeating-linear-gradient(
        90deg,
        var(--gilt-leaf) 0px,
        var(--gilt-leaf) 1px,
        rgba(255, 255, 255, 0) 1px,
        rgba(255, 255, 255, 0) 3px
    );
}

.fore-edge-red {
    --gilt-bole:  #e3b3ae;
    --gilt-leaf:  rgba(140, 48, 42, 0.45);
    --gilt-rule:  #b5645e;
    --gilt-shade: rgba(90, 20, 18, 0.18);
}

/* Blue under gold, after the Schuyler: a green-leaning teal rather than a true
   blue, which is what the bole actually looks like fanned. Held at the same
   lightness and saturation as the red so neither colorway shouts louder than
   the other beside the text. */
.fore-edge-blue {
    --gilt-bole:  #a9ced3;
    --gilt-leaf:  rgba(21, 92, 105, 0.45);
    --gilt-rule:  #5f9ba5;
    --gilt-shade: rgba(10, 58, 70, 0.20);
}

/* Pure gold — no contrasting bole at all (Daniel, 2026-08-02). Where the other
   two show a colored ground once the block is fanned, this one is gold through
   and through, which is the plainest and most expensive-looking of the three.
   The rule IS the player's gold, exactly: --biblion-gold, so the edge of the
   page block and the scrubber are the same color and stay so if that value
   ever moves.

   THE GROUND IS A PALE GOLD, NOT #c9a227 ITSELF, and that is deliberate. The
   blue was built at the red's lightness on purpose so neither colorway shouts
   louder than the other beside the text; the player's gold is a mid-dark 47%
   against those two at ~79%, so using it as the fill would have made this the
   loud one. It is held at the family's lightness instead and spends the strong
   gold on the rule, which is the line the eye actually reads as the edge. */
.fore-edge-gold {
    --gilt-bole:  #e3d5ab;
    --gilt-leaf:  rgba(140, 108, 20, 0.45);
    --gilt-rule:  var(--biblion-gold);
    --gilt-shade: rgba(90, 68, 12, 0.18);
}

.fore-edge-left {
    border-left: 2px solid var(--gilt-rule);
    box-shadow: inset -4px 0 5px var(--gilt-shade);
}

.fore-edge-right {
    border-right: 2px solid var(--gilt-rule);
    box-shadow: inset 4px 0 5px var(--gilt-shade);
}

@media (max-width: 767.98px) {
    .fore-edge { display: none; }
}

/* Two groups at ≥992px: the brand sits alone on the left, and everything else
   (book/chapter selector + Navigator/Search/Options/About/account) rides
   together as one group centered in the navbar. A 3-column grid with equal 1fr
   flanks keeps the middle group centered while the brand pins to the left. */
@media (min-width: 992px) {
    .navbar-grid {
        display: grid !important;
        grid-template-columns: 1fr auto 1fr;
        align-items: center;
    }

    .navbar-grid > .navbar-start {
        justify-self: start;
    }

    .navbar-grid > .navbar-collapse {
        grid-column: 2;
        display: flex !important;
        align-items: center;
        gap: 1rem;
    }

    /* Inside the centered group the account menu just follows the selector with
       a normal gap — no auto margin pushing it away. */
    .navbar-collapse > .navbar-end {
        margin-left: 0;
    }
}

/* On narrower screens the account menu lives inside the collapsed rollout menu
   (flex column), where this auto margin pushes it to the end of the stack. */
.navbar-end {
    margin-left: auto;
}

.verse-row {
    cursor: pointer;
    border-radius: 0.25rem;
    padding: 0.5rem;
    transition: background-color 0.15s ease;
    position: relative;
}

.vnum {
    position: absolute;
    left: -1rem;
    top: 0.6rem;
}

/* Favorite star: sits in the left gutter beside the verse, and is drawn only
   when the verse is already a favorite — so a chapter shows its favorites at a
   glance. Lives in the same negative-left gutter as .vnum so it never overlaps
   the verse text.

   PURELY AN INDICATOR since 2026-07-31, on the same instruction that paled the
   cross-reference mark: it was a <button> that added and removed the favorite,
   and that control is the picker's now. `pointer-events: none` is what makes
   the retirement real — a click passes straight through it to whatever is
   behind, which in this gutter is the paper rather than the verse row, so the
   star creates no dead zone and no longer answers a click at all — while
   app.js's click handler still fires, because the picker and the Options menu
   drive the
   favorite by calling .click() on this element and a programmatic click ignores
   pointer-events. Do not remove that listener with the button.

   Three declarations went with the button and must not come back: `cursor:
   pointer`, the `:hover` gold-and-scale, and the button reset (`padding`,
   `border`, `background`), none of which a span needs.

   The gold is paled from #e0a800 by the same step the cross-reference mark took
   from #b5645e — about a third of the way to the paper — so an indicator does
   not read as a control. It still has to be findable when a reader is scanning
   a chapter for their favorites, which is why it is a step toward the paper
   rather than a whisper. */
.fav-star {
    position: absolute;
    left: -1.15rem;
    top: 1.9rem;
    display: none;
    pointer-events: none;
    line-height: 1;
    font-size: 0.8rem;
    color: #e9c24d;
    transition: color 0.15s ease;
    z-index: 3;
}

/* The gold star STEPS ASIDE while the verse is selected: the picker below the
   verse carries the favorite state in its own star, and two gold stars for one
   fact read as a bug. Unselected, .fav-star.is-fav below still draws it, which
   is what lets a reader scan a chapter for their favorites without touching
   anything. This rule used to say `display: block` and drew a GREY star as the
   add-to-favorites control; that control is the picker's now (2026-07-31). */
.verse-row.verse-selected .fav-star {
    display: none;
}

.fav-star.is-fav {
    display: block;
}

.verse-row.verse-selected {
    /* Pale sky blue by default; a reader who picked a custom highlight sets
       --verse-select on :root (emitted in head.php).

       TWO declarations on purpose. The plain one lands first and always paints;
       the var() one overrides it wherever it resolves. If --verse-select is
       ever emitted EMPTY rather than absent — which head.php will do if
       verseColorKey() returns '' instead of null — then `var(--verse-select,
       #d9edf7)` is invalid at computed-value time and the fallback is NOT used,
       leaving the band transparent. The first line is what stops that being
       invisible instead of merely wrong. */
    /* A WASH SINCE 2026-08-02, not a solid block: the same 0.16 the narration
       mark uses, so the paper's warmth shows through both and a verse that is
       selected while being read shows the two marks together instead of one
       hiding the other. The value is the sky-blue default as ink — kept in step
       with VERSE_SELECT_DEFAULT in includes/functions.php, which is what a
       reader who has chosen a color gets emitted instead. */
    background-color: rgba(39, 147, 201, 0.16);
    background-color: var(--verse-select, rgba(39, 147, 201, 0.16));
    /* Rounded rather than a full-bleed rectangle: the band reads as a highlight
       laid on the page instead of a table row, which is what it is. */
    border-radius: var(--bs-border-radius, .375rem);
}

/* ---- The verse picker ---------------------------------------------------
   A row of actions that appears under the topmost selected verse. It sits IN
   THE FLOW rather than floating: a floating bar would cover the verse above it
   and would land exactly where a phone draws its own text-selection bubble.
   The cost accepted in exchange is that the page reflows on every selection.

   Icons only, never labels — measured at 254px for six icons against 548px
   with labels, and the reading column on a phone cannot take the second
   number. Names live in the tooltips, which are the app's own Bootstrap ones.

   Two buttons are drawn but permanently disabled: commentary and the verse
   card have no reader integration yet. They are here deliberately so the row
   does not change shape when those land — remove `pending` in app.js and each
   becomes live. */
.verse-picker {
    display: flex;
    align-items: center;
    gap: 0.1rem;
    margin: -0.35rem 0 0.9rem;
}

.verse-picker button {
    border: 0;
    background: none;
    padding: 0.42rem 0.55rem;
    border-radius: var(--bs-border-radius-sm, 0.25rem);
    color: #7a7a7a;
    font-size: 0.95rem;
    line-height: 1;
    cursor: pointer;
}

.verse-picker button:hover:not([disabled]) {
    background: rgba(0, 0, 0, 0.06);
    color: #212529;
}

.verse-picker button[disabled] {
    opacity: 0.3;
    cursor: default;
}

/* Gold exactly when every verse in the selection is already a favorite — this
   is what replaces the gutter star for the duration of the selection.

   THE :hover SELECTOR MUST BE REPEATED HERE, and it is the whole point of this
   rule. `button:hover:not([disabled])` above scores (0,3,1) against a bare
   `button.is-on` at (0,2,1), so the hover colour outranked the state colour and
   the star stayed grey for as long as the pointer sat on it — the click
   appeared to do nothing until the mouse moved away. Reported by Daniel
   2026-07-31. A state must outrank a hover, never the other way round. */
/* SCOPED TO THE FAVORITE BUTTON since 2026-07-31. It used to be a bare
   `.is-on`, which was correct while the star was the only thing that could be
   on — but gold means "this verse is a favorite" everywhere else in the app,
   and the signpost below borrows the same class for "this panel is open". A
   second meaning for the one colour would be a lie in a place a reader reads
   fast. */
.verse-picker button[data-key="fav"].is-on,
.verse-picker button[data-key="fav"].is-on:hover:not([disabled]) {
    color: #e0a800;
}

/* The signpost's open state: pressed, not coloured. It is a disclosure, so
   what it has to say is "the thing below belongs to me", and the panel's own
   tint is the other half of that sentence.

   THE :hover VARIANT IS REPEATED FOR THE SAME REASON THE FAVORITE'S IS.
   `.verse-picker button:hover:not([disabled])` scores (0,3,1) because :not()
   contributes its argument, so a plain `.is-on` at (0,2,1) loses and the
   button stays grey for as long as the pointer rests on it — a click that
   looks like it did nothing until the mouse moves away. */
.verse-picker button[data-key="sign"].is-on,
.verse-picker button[data-key="sign"].is-on:hover:not([disabled]) {
    background: rgba(0, 0, 0, 0.09);
    color: #212529;
}

/* Amen's on-state: the pressed shape, like the signpost — a toggle that is on
   reads as a button held down. NOT gold: in this row gold means "favorite"
   (see the scoping note above), and the two buttons sit side by side. The
   devotional page's standalone pill does fill with the brand gold, and that is
   deliberate rather than inconsistent — there is no favorite star on that page
   for it to be confused with.

   THE :hover VARIANT IS REPEATED for the same specificity arithmetic as the
   two rules above; without it the pressed state vanishes for as long as the
   pointer rests on the button. And the rule exists at all because the button
   proved the standing lesson a third time: it toggled `is-on` faithfully from
   the day it was built, and with no rule painting that class the click read
   as "nothing happened" — a state must not merely outrank a hover, it must
   exist. Reported by Daniel 2026-08-03. */
.verse-picker button[data-key="amen"].is-on,
.verse-picker button[data-key="amen"].is-on:hover:not([disabled]) {
    background: rgba(0, 0, 0, 0.09);
    color: #212529;
}

.verse-picker .verse-picker-sep {
    width: 1px;
    align-self: stretch;
    margin: 0.3rem 0.35rem;
    background: rgba(0, 0, 0, 0.1);
}

/* A brief pop when the favorite is toggled. Colour alone was thin feedback —
   the same glyph in a different shade — and the usual reinforcement of swapping
   a filled star for an outline one is NOT available here: only fa-solid ships,
   and fa-regular renders a tofu box (see the Font Awesome note in the
   typography memory). Short and small on purpose: a confirmation, not an
   animation.

   It rides the <i>, not the button, so it cannot fight the button's own
   padding, border-radius or hover background. */
@keyframes verse-picker-pop {
    0%   { transform: scale(1); }
    35%  { transform: scale(1.3); }
    100% { transform: scale(1); }
}

.verse-picker button.is-pulsing i {
    animation: verse-picker-pop 220ms ease-out;
}

@media (prefers-reduced-motion: reduce) {
    .verse-picker button { transition: none; }
    /* The colour change still lands; only the movement goes. */
    .verse-picker button.is-pulsing i { animation: none; }
}

/* THE TWO PAINTINGS ON measures.php, above the months and the feasts, with the
   scenes in them NAMED — pills placed over the picture from a percentage in
   measuresBandMarks(), never lettered into the artwork. Type drawn into a
   picture is the trap that retired the supplied plates entirely: a 720px plate
   puts its 9px labels at 4px in a 318px column.

   The paintings are used as painted, with no border and no rounding of their
   own — [[bible-supplied-artwork]] carries why a torn edge is redrawn rather
   than cropped or filled. */
.times-band {
    margin: 0 0 1.25rem;
}
.times-band-frame {
    position: relative;
    line-height: 0;
}
.times-band img {
    display: block;
    width: 100%;
    height: auto;
}
.times-pill {
    position: absolute;
    bottom: 5%;
    transform: translateX(-50%);
    padding: 0.3rem 0.85rem;
    border-radius: 1.2rem;
    background: rgba(33, 37, 41, 0.86);
    color: #fff;
    font-size: 0.9rem;
    font-weight: 500;
    letter-spacing: 0.02em;
    line-height: 1.2;
    white-space: nowrap;
    box-shadow: 0 0.15rem 0.5rem rgba(0, 0, 0, 0.28);
}
/* The same labels as a line of text, for the widths where they cannot sit on the
   picture. Hidden by default: on a wide screen they would be a second copy. */
.times-band-cap {
    display: none;
    margin-top: 0.4rem;
    font-size: 0.85rem;
    color: var(--bs-secondary-color, #6c757d);
}
/* ⚠ THE PILLS COME OFF THE PICTURE BELOW 992px, AND THE BREAKPOINT IS THE
   CONTAINER'S, NOT THE PHONE'S. A pill is a fixed size while the picture is
   fluid, so what matters is the width the picture actually gets: at a 992px
   viewport the prose column is its full 720px and the six feast pills clear each
   other by 6px, but one step down the container gives 648px and they overlap by
   4. Measured at 820px before this was moved from 768. The caption below carries
   exactly the same words, so nothing is lost — only the pointing. */
@media (max-width: 991.98px) {
    .times-pill { display: none; }
    .times-band-cap { display: block; }
}

/* THE YEAR AS A STRIP, above the months table: four bands of three months, in
   the order the farm year takes them.

   ⚠ THE ONLY GOLD IN HERE IS ON NISAN AND TISHRI. The season accents come from
   the supplied palette with one change — its ingathering gold is a deeper
   brown-gold here — so that gold on this figure keeps meaning "the two
   beginnings" and nothing else ([[bible-gold-system]]). */
.year-strip {
    margin: 0 0 1.5rem;
}
.year-rule {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem 0.9rem;
    margin-bottom: 0.75rem;
    padding: 0.6rem 0.9rem;
    border-radius: var(--bs-border-radius, 0.375rem);
    background: #edf1f6;
    font-size: 0.9rem;
    color: var(--bs-secondary-color, #6c757d);
}
.year-rule strong { color: var(--bs-body-color, #212529); font-weight: 700; }
.year-rule-moon { font-size: 1.1rem; line-height: 1; }
.year-rule-sep {
    width: 1px;
    align-self: stretch;
    background: var(--bs-border-color, #dee2e6);
}

/* ⚠ TWO ROWS OF SIX, NOT TWELVE ACROSS — Daniel, 2026-08-23, and the measurement
   agrees with him: twelve cells in a 720px column give each month 57px, and NINE
   OF THE TWELVE names wrapped at that width. Two bands per row gives 170px and
   every name fits on its line beside its Hebrew. The break also falls where the
   year does — Nisan opens the first row and Tishri the second. */
.year-seasons {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 0.6rem;
}
.year-season {
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: var(--bs-border-radius, 0.375rem);
    overflow: hidden;
    background: #fff;
}
.year-season-label {
    padding: 0.5rem 0.7rem 0.55rem;
    border-left: 4px solid var(--season, #6c757d);
    background: var(--season-tint, #f8f9fa);
    line-height: 1.3;
}
.year-season-label strong {
    display: block;
    font-weight: 700;
    font-size: 0.9rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--bs-body-color, #212529);
}
.year-season-label span {
    font-size: 0.9rem;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--season, #6c757d);
}
.year-month {
    display: grid;
    grid-template-columns: 1.6rem 1fr auto;
    align-items: baseline;
    gap: 0.1rem 0.5rem;
    padding: 0.5rem 0.7rem 0.6rem;
    border-top: 1px solid var(--bs-border-color, #dee2e6);
}
.year-month-n {
    grid-row: 1;
    justify-self: center;
    align-self: center;
    width: 1.4rem;
    height: 1.4rem;
    border-radius: 50%;
    background: var(--season, #6c757d);
    color: #fff;
    font-size: 0.9rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: 1.4rem;
    text-align: center;
}
/* The reading face on the month name, as on the feast names below — it is the
   word a reader is scanning the grid for, and the serif finds it without a size
   step or a run of capitals. */
.year-month-name {
    grid-row: 1;
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-weight: 700;
    font-size: 0.9rem;
}
.year-month-hebrew { grid-row: 1; justify-self: end; font-size: 0.9rem; }
.year-month-modern,
.year-month-refs {
    grid-column: 2 / -1;
    font-size: 0.9rem;
    line-height: 1.35;
    color: var(--bs-secondary-color, #6c757d);
}
/* ⚠ NISAN AND TISHRI ARE NOT OUTLINED, and were briefly. A ring around one card
   in a grid of twelve reads as a selection — a state the reader can change —
   rather than as a fact about the year. They lead their rows instead, and the
   caption carries the point in words. No gold on this figure at all. */
.year-strip-cap {
    margin: 0.5rem 0 0;
    font-size: 0.85rem;
    color: var(--bs-secondary-color, #6c757d);
}
@media (max-width: 767.98px) {
    .year-seasons { grid-template-columns: 1fr; }
}

/* THE SEVEN FEASTS AS CARDS IN THEIR TWO CLUSTERS, above the feasts table, on
   the same pattern as the year strip above it — the rule bar, the banded label,
   one type size throughout.

   ⚠ THE GAP BETWEEN THE CLUSTERS IS THE FIGURE'S SUBJECT. Four months of the
   year have no appointed time in them, and seven rows printed one under another
   hide that completely. Nothing may quietly close it up.

   ⚠ NO GOLD, AND NO OUTLINED CARD. The mockup this grew from tabbed the seventh
   month in gold and ringed Passover; here the tabs take the season accents the
   year strip already uses, and no card is outlined — a ring around one card in a
   grid reads as a selection the reader could change. */
.feast-year {
    margin: 0 0 1.5rem;
}
.feast-cluster {
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: var(--bs-border-radius, 0.375rem);
    background: var(--season-tint, #f8f9fa);
    padding: 0.55rem;
}
.feast-cluster-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.2rem 0.75rem;
    padding: 0 0.15rem 0.5rem;
}
.feast-cluster-tab {
    padding: 0.25rem 0.8rem;
    border-radius: 1.2rem;
    background: var(--season, #6c757d);
    color: #fff;
    font-size: 0.9rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}
.feast-cluster-meta {
    font-size: 0.9rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--season, #6c757d);
}
/* auto-fit rather than a fixed count: the spring cluster holds four and the
   seventh month three, and on a phone both become one column without a rule of
   their own. */
.feast-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
    gap: 0.5rem;
}
.feast-card {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    padding: 0.6rem 0.7rem 0.7rem;
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-top: 3px solid var(--season, #6c757d);
    border-radius: 0.35rem;
    background: #fff;
    font-size: 0.9rem;
}
.feast-when {
    color: var(--bs-secondary-color, #6c757d);
}
.feast-qualifier {
    display: block;
}
/* ⚠ THE READING FACE, NOT CAPITALS — Daniel, 2026-08-23, having tried both. The
   names were set in capitals first and it made a rank of seven cards shout; the
   serif distinguishes them from everything around them, which is all the caps
   were being asked to do. Same stack as .prose-heading, so the feast names are in
   the face the rest of the page reads in. */
.feast-name {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-weight: 700;
    line-height: 1.3;
}
.feast-alt {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    color: var(--season, #6c757d);
    font-weight: 700;
    line-height: 1.3;
}
.feast-marks { color: var(--bs-body-color, #212529); line-height: 1.4; }
/* Pushed to the foot of the card so the references sit on one line across a row
   of cards of different heights. */
.feast-refs {
    margin-top: auto;
    padding-top: 0.45rem;
    border-top: 1px solid var(--bs-border-color, #dee2e6);
    color: var(--bs-secondary-color, #6c757d);
}
.feast-badge {
    align-self: flex-start;
    padding: 0.1rem 0.6rem;
    border: 1px solid var(--season, #6c757d);
    border-radius: 1.2rem;
    color: var(--season, #6c757d);
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
.feast-gap {
    position: relative;
    margin: 0.85rem 0;
    text-align: center;
}
.feast-gap::before {
    content: '';
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    border-top: 1px dashed var(--bs-border-color, #dee2e6);
}
.feast-gap span {
    position: relative;
    padding: 0 0.6rem;
    background: #fff;
    font-size: 0.9rem;
    color: var(--bs-secondary-color, #6c757d);
}

/* THE BODY MEASURES DRAWN TO TRUE SCALE, above the length table on measures.php.
   The bar widths are computed in the page from the real centimetres, so nothing
   here may impose a width — this rule only says what a bar looks like, never how
   long it is.

   The three columns are fixed–flexible–fixed so every bar starts and ends on the
   same two verticals; ragged ends would make four proportional bars look
   approximate, which is the one thing this figure cannot afford. Tabular
   numerals for the same reason. */
.measure-scale {
    margin: 0 0 1.5rem;
    padding: 0.9rem 1rem;
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: var(--bs-border-radius, 0.375rem);
    background: rgba(0, 0, 0, 0.015);
}

.measure-scale-row {
    display: grid;
    grid-template-columns: 8.5rem 1fr 4.5rem;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 0.55rem;
}

.measure-scale-name { font-size: 0.9rem; }

/* THE SATURATED VIOLET, and it is the vocabulary's own (Daniel, 2026-08-20 —
   these started as half-strength page ink and he asked for the violet).
   `#7a5ea8` is the mark that means "a reference work speaking" wherever it
   appears: the lexicon panel's rule, the life timeline's spans and dots, every
   arc of the canon figure ([[bible-gold-system]]). It fits here because these
   centimetres are not Biblion's claim — they are the received figures scholarship
   gives for a cubit, which is why the column beside them is headed "≈ Modern".

   ⚠ STILL NOT GOLD, whatever else changes. Gold says "the thing you asked for",
   and a scale bar is a diagram rather than a state — the same line that keeps
   the excavation trowel out of gold. */
/* ⚠⚠ NO min-width, DELIBERATELY, AND IT MUST NOT BE ADDED BACK. A floor is the
   obvious kindness and it is the one thing that can make this figure lie: the
   bar's whole claim is that its length IS the quantity, so the moment a floor
   props a bar up, that bar is drawn longer than the truth. Measured — with a
   3px floor the log and the xestes clamped at 375px and came out 2.3× too long,
   while every other bar stayed exact.

   A sub-pixel bar is the honest rendering of a sub-pixel quantity, and it costs
   the reader nothing here: the amount is printed beside every bar, so the number
   carries the fact and the bar only carries the impression. */
.measure-scale-bar {
    height: 14px;
    border-radius: 7px;
    background: #7a5ea8;
}

.measure-scale-val {
    font-size: 0.85rem;
    color: var(--bs-secondary-color, #6c757d);
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.measure-scale-cap {
    margin: 0.65rem 0 0;
    font-size: 0.85rem;
    color: var(--bs-secondary-color, #6c757d);
}

/* On a phone the name column has to give way before the bar does — a bar under
   4rem stops being a comparison, while a name can wrap. */
@media (max-width: 575.98px) {
    .measure-scale-row { grid-template-columns: 6rem 1fr 3.9rem; gap: 0.5rem; }
    .measure-scale-name { font-size: 0.85rem; }
}

/* THE COIN LADDER, above the money table on measures.php. Money is the one set
   here that no scale bar can carry — 768,000:1 from the lepton to the talent —
   so the range is decomposed into rungs small enough to count instead. The marks
   are drawn one per coin, deliberately: the whole argument is that a reader can
   COUNT them, so nothing here may ever summarise a rung as a number and a
   flourish. See the note on $ladder in measures.php.

   The 60-mina rung wraps, and is meant to — a rung that needs three lines to
   hold its marks is saying something true about a talent. */
.coin-ladder {
    margin: 0 0 1.5rem;
    padding: 0.35rem 1rem 0.6rem;
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: var(--bs-border-radius, 0.375rem);
    background: rgba(0, 0, 0, 0.015);
}

.coin-rung {
    display: grid;
    grid-template-columns: 9rem 1fr 10rem;
    align-items: center;
    gap: 0.85rem;
    padding: 0.5rem 0;
    border-top: 1px solid var(--bs-border-color, #dee2e6);
}

.coin-rung:first-child { border-top: 0; }
.coin-rung-head { font-size: 0.9rem; }
.coin-rung-eq   { font-size: 0.9rem; color: var(--bs-secondary-color, #6c757d); }
.coin-marks     { display: flex; flex-wrap: wrap; gap: 3px; }

/* Violet, the same mark the scale bars take: these are received figures, not
   Biblion's own claim ([[bible-gold-system]]). Not gold — see measures.php. */
.coin-mark {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: #7a5ea8;
}

/* On a phone the three columns stack: 9rem of label beside 60 marks leaves the
   marks a strip too narrow to count, which is the one thing the figure is for. */
@media (max-width: 575.98px) {
    .coin-rung { grid-template-columns: 1fr; gap: 0.3rem; }
    .coin-mark { width: 8px; height: 8px; }
}

/* The star that says a row rests on an assumption, and the line that names it.
   Violet again — it is the same voice as the figures it qualifies. */
.measure-assumed {
    color: #7a5ea8;
    font-weight: 700;
}

.measure-assumed-note {
    font-size: 0.85rem;
    color: var(--bs-secondary-color, #6c757d);
}

/* The row a reader arrived at from "In this verse". A tint that stays rather
   than a flash that goes: they came here to read this line, and something that
   has already faded by the time the eye lands is a decoration rather than an
   answer.

   ⚠⚠ IT MUST BEAT THE STRIPE AT THE STRIPE'S OWN GAME, and a background-color
   does not. The tables became `table-striped` on 2026-08-20, and Bootstrap 5.3
   does not band with `background-color` — it paints
   `box-shadow: inset 0 0 0 9999px var(--bs-table-bg-type)` across the cell,
   which covers any background underneath it. Measured: with the old rule the
   marked row rendered GOLD on an even row and GREY on an odd one, silently,
   because the band was painted on top of the tint. Setting the same variable
   the band uses makes gold REPLACE the band instead of hiding under it.

   ⚠ THE SELECTOR IS LONG ON PURPOSE. Bootstrap's
   `.table-striped > tbody > tr:nth-of-type(2n+1) > *` scores (0,2,2), so a
   plain `.measure-marked > td` at (0,1,1) loses and the fix silently does
   nothing. Leading with `.table >` takes it to (0,2,3) and it wins on its own
   merits — no `!important` needed. Verified both ways round: the row now reads
   identically whichever position it falls on. */
.table > tbody > tr.measure-marked > td {
    --bs-table-bg-type: rgba(224, 168, 0, 0.13);
}

/* Both layers, edge first: the 9999px fill would otherwise replace the rule
   rather than sit behind it, because box-shadow declarations do not stack. */
.table > tbody > tr.measure-marked > td:first-child {
    box-shadow: inset 3px 0 0 #e0a800,
                inset 0 0 0 9999px rgba(224, 168, 0, 0.13);
}

/* ---- "In this verse": the signpost panel --------------------------------
   Sits under the picker, in the flow, so it covers nothing and the page
   reflows round it. Flat throughout — a hairline and a tint, no shadow and no
   gradient — because it is a part of the page rather than a thing floating
   over it, and the paper card it lands on is already the raised surface. */
.verse-signpost {
    margin: -0.55rem 0 1rem;
    padding: 0.35rem 0;
    border-top: 1px solid rgba(0, 0, 0, 0.08);
    border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}

.verse-signpost-list li.is-folded {
    display: none;
}

.verse-signpost-link {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    padding: 0.32rem 0.4rem;
    border-radius: var(--bs-border-radius-sm, 0.25rem);
    color: inherit;
    text-decoration: none;
    font-size: 0.92rem;
    line-height: 1.35;
}

.verse-signpost-link:hover,
.verse-signpost-link:focus-visible {
    background: rgba(0, 0, 0, 0.05);
    color: inherit;
}

/* Fixed width so the terms line up into a column and the eye can run down
   them — the whole list is scanned before one of them is chosen. */
.verse-signpost-link i {
    width: 1.05rem;
    flex: none;
    text-align: center;
    color: #8a8a8a;
    font-size: 0.85rem;
}

.verse-signpost-term {
    font-weight: 600;
    flex: none;
}

.verse-signpost-gloss {
    color: #6c757d;
    flex: 1 1 auto;
    min-width: 0;
}

/* The destination, set apart at the end of the row: this is the part that
   says where the click lands, and it reads as an address rather than as more
   description. */
.verse-signpost-where {
    color: #8a8a8a;
    flex: none;
    font-size: 0.85em;
    white-space: nowrap;
}

.verse-signpost-more {
    border: 0;
    background: none;
    padding: 0.3rem 0.4rem;
    color: #6c757d;
    font-size: 0.85rem;
    text-decoration: underline;
}

.verse-signpost-more:hover {
    color: #212529;
}

/* The Theographic credit, at the foot of the panel and inside its two rules —
   the same bargain .search-who-credit strikes with the violet band: a shared
   boundary is what says the credit belongs to what it is crediting, rather than
   being page text that happens to follow. Smaller than the rows for the reason
   set out there, which is why that is not a breach of the standing rule that a
   note is body text. Padding on top only: the panel's own border is the end. */
.verse-signpost-credit {
    padding: 0.35rem 0.4rem 0.1rem;
    color: var(--bs-secondary-color, #6c757d);
    font-size: 0.8rem;
    line-height: 1.45;
}

.verse-signpost-credit a {
    color: inherit;
    text-decoration: underline;
}

.verse-signpost-credit a:hover {
    color: #212529;
}

/* On a phone the three parts cannot share a line: the gloss is a clause, and
   holding it beside a nowrap destination pushes the destination off the edge.
   Term over gloss, with the destination beneath — the same three facts, read
   down instead of across. */
@media (max-width: 575.98px) {
    .verse-signpost-link {
        flex-wrap: wrap;
        row-gap: 0.05rem;
    }
    .verse-signpost-gloss {
        flex: 1 1 100%;
        padding-left: 1.55rem;
    }
    .verse-signpost-where {
        flex: 1 1 100%;
        padding-left: 1.55rem;
        white-space: normal;
    }
}

/* ---- Popular highlights --------------------------------------------------
   The community's most-starred verses, in the study view only. Two parts, and
   both whisper. The mark is Kindle's own convention — a dotted line under the
   passage — because readers already know what that grammar means without a
   legend: many hands have rested here. It is a warm paper-gray, NOT the
   narration gold (gold means "being read aloud right now") and NOT the
   selection wash (that means "chosen"); a third meaning needs a third voice,
   and this one's is the quietest of the three.

   No count rides in the text. The number appears only when the verse is
   SELECTED — the line under the picker below — so reading is never sharing a
   page with arithmetic. */
.verse-popular .verse-body {
    text-decoration-line: underline;
    text-decoration-style: dotted;
    text-decoration-thickness: 1px;
    text-underline-offset: 0.24em;
    /* The vnum/note gray family at well under half strength: present when
       looked for, absent when reading. Spelled as rgba because the derived-
       color functions are below the browser floor (see bible-browser-compat). */
    text-decoration-color: rgba(108, 117, 125, 0.55);
}

/* The communal note — "Starred by N readers", "Amen from N readers", or both
   — under the picker, in the margin's own muted voice. Set as a fact, not a
   control: nothing to hover, click or dismiss.

   Flex with a wrap, because on a phone the two facts cannot share a line and
   a nowrap pair would push the second off the edge — the same problem the
   signpost row solves the same way. */
.verse-pop-note {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0 0.4rem;
    font-size: 0.8rem;
    color: #8a8a8a;
    margin: -0.45rem 0 0.9rem;
    padding-left: 0.15rem;
}

.verse-pop-note i {
    font-size: 0.7rem;
    margin-right: 0.3rem;
    opacity: 0.75;
}

/* The separator is drawn, never typed: a screen reader hears two facts, not a
   middle dot between them. It disappears when the pair wraps onto two lines,
   where the line break is the separator. */
.verse-pop-sep {
    opacity: 0.5;
}

@media (max-width: 575.98px) {
    .verse-pop-part {
        flex: 1 1 100%;
    }
    .verse-pop-sep {
        display: none;
    }
}

/* ---- The Amen button ------------------------------------------------------
   The devotional's own control (the verse picker draws its own icon row).
   Flat, like everything else here: a hairline pill that fills when pressed,
   no shadow and no gradient. It is an answer to what was just read, so it
   sits quiet until it is used and then plainly holds its state — the standing
   rule that a state must outrank a hover. */
.devotional-amen {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: 1.25rem;
}

.amen-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.32rem 0.85rem;
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 2rem;
    background: none;
    color: #6c757d;
    font-size: 0.9rem;
    line-height: 1.3;
    transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.amen-btn:hover:not(.is-on) {
    border-color: #adb5bd;
    color: #495057;
}

/* Pressed. The gold is the site's own — one --biblion-gold, never a second —
   and it wins over the hover rule above by carrying the state further than a
   pointer ever does. */
.amen-btn.is-on {
    background-color: var(--biblion-gold, #c9a227);
    border-color: var(--biblion-gold, #c9a227);
    color: #fff;
}

.amen-btn:disabled {
    opacity: 0.65;
}

.amen-count {
    font-size: 0.82rem;
    color: #8a8a8a;
    /* Holds the line whether or not there is a number, so the button never
       moves when one arrives. */
    min-height: 1em;
}

/* ---- Verse card dialog --------------------------------------------------
   A native <dialog>, not a Bootstrap modal: it traps focus and closes on
   Escape without any script of ours, and it is inside the browser floor. The
   card itself is drawn on the canvas from the reader's mood colours, so
   nothing here sets a palette. */
.vcard {
    border: 0;
    border-radius: var(--bs-border-radius-lg, 0.5rem);
    padding: 1.25rem;
    max-width: min(92vw, 30rem);
    background: #fff;
    box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.35);
}

.vcard::backdrop {
    background: rgba(0, 0, 0, 0.55);
}

.vcard-close-form {
    display: flex;
    justify-content: flex-end;
    margin: -0.5rem -0.5rem 0.25rem 0;
}

.vcard-close {
    border: 0;
    background: none;
    font-size: 1.6rem;
    line-height: 1;
    color: #888;
    cursor: pointer;
    padding: 0 0.35rem;
}

.vcard-canvas {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--bs-border-radius, 0.375rem);
}

/* The verse as real text beside the picture: a canvas is invisible to a screen
   reader, and this is what aria-describedby points at. */
.vcard-text {
    font-size: 0.8rem;
    color: #6c757d;
    margin: 0.75rem 0 0;
    max-height: 4.5rem;
    overflow-y: auto;
    white-space: pre-line;
}

.vcard-size {
    font-size: 0.75rem;
    color: #8a8a8a;
    margin: 0.5rem 0 0;
    font-variant-numeric: tabular-nums;
}

.vcard-formats,
.vcard-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-top: 0.85rem;
}

.vcard-formats button,
.vcard-actions button {
    font: inherit;
    font-size: 0.85rem;
    padding: 0.4rem 0.8rem;
    border: 1px solid #d3d3d3;
    border-radius: var(--bs-border-radius, 0.375rem);
    background: #fff;
    cursor: pointer;
}

.vcard-formats button:hover,
.vcard-actions button:hover {
    background: #f2f2f2;
}

.vcard-formats button.is-on {
    border-color: #25707e;
    background: #eef6f7;
    font-weight: 600;
}

/* Left accent bar spanning the verses a multi-verse note covers. */
.verse-row.verse-in-note-range {
    box-shadow: inset 3px 0 0 rgba(91, 28, 186, 0.4);
}

.note-range {
    font-family: 'Prompt', sans-serif;
}

/* THE PEN IS NEVER SHOWN ANY MORE (2026-07-31). It stays in the markup because
   it is still the mechanism: its .popup-toggle-btn handler is what captures the
   selection into verse_start/verse_end, opens the form and puts the cursor in
   the box, and the picker's note button drives it with a programmatic click —
   which fires on a display:none button just as well. Deleting the element would
   mean reimplementing all of that somewhere else for nothing.

   It used to appear on .verse-note-home, the topmost selected verse. Two rules
   went with it: the one that showed it there, and the one that hid it again
   while that verse was being edited, which had nothing left to do. */
.add-note-toggle {
    display: none;
}

/* ---- Reader's-Bible mode -------------------------------------------------
   With KJV and the original languages all switched off there is nothing to set
   beside each verse, so the chapter runs as continuous prose: verse numbers
   shrink to superscripts, the per-verse box disappears, and the measure is
   held to a comfortable line length. The markup keeps .verse-row and
   data-verse throughout, so selection, ranges and the star work unchanged. */
.flow-text {
    /* em so the reading column widens with the font size, keeping roughly the
       same words per line rather than cramping as the text grows. */
    max-width: 36em;
    margin: 0 auto;
    /* Padding rather than margin: this is the first child of .verses.reader-flow,
       and a top margin on a first child collapses out through the parent unless
       the parent has padding — which here is animated between two values as the
       bar comes and goes, so the margin's behavior would depend on it. Padding
       never collapses, and it reads as space belonging to the column itself
       rather than a gap between the column and what precedes it. */
    padding-top: 5rem;
    /* Room at the foot for the pinned chapter pager, which has left the flow and
       would otherwise sit on top of the closing lines. */
    padding-bottom: 6rem;
    line-height: 1.9;
    text-align: justify;
    hyphens: auto;
}

.verse-flow {
    display: inline;
    position: static;
    padding: 0;
    border-radius: 0;
}

/* Out of the gutter and into the line, the way a printed Bible sets them. */
.verse-flow .vnum {
    position: static;
    vertical-align: super;
    font-size: 0.62em;
    margin-right: 0.1em;
    line-height: 0;
}

/* An inline highlight follows the text across line breaks; the block layout's
   rounded box would fragment line by line, so it is not used here. Selection is
   all that marks a verse in this view — the star, the note pen and the note
   underline belong to the study view and are not rendered at all. */
.verse-flow.verse-selected {
    /* Paired the same way as the study view's band above — a plain declaration
       that always paints, then the var() override. An empty custom property is
       invalid at computed-value time and does NOT fall back, which once left
       the band transparent; this view had only the single line. */
    background-color: rgba(39, 147, 201, 0.16);
    background-color: var(--verse-select, rgba(39, 147, 201, 0.16));
    box-shadow: none;
    /* Same rounding as the study view's band — the two views should not
       disagree about what a selected verse looks like. */
    border-radius: var(--bs-border-radius, .375rem);
}

/* Justification needs enough words per line to distribute the slack. On a phone
   the measure is short enough that it opens visible gaps between words, so the
   right edge goes ragged there instead. */
@media (max-width: 575.98px) {
    .flow-text {
        text-align: left;
    }
}

/* Reader's view, wider screens only: nudge the reading column a little left of
   center so the page is not perfectly symmetric, and push the bleed-through ghost
   the same amount the other way, so a strip of it clears the text and shows on the
   paper rather than hiding directly behind the words. Held to wide viewports: on
   anything narrower the column fills the card and there is no room to move it.

   ONE FIGURE, THREE PLACES. The column, the ghost and the pinned pager all move
   by this amount and they are only correct while they agree; written out three
   times they would drift the first time one of them was tuned. */
:root {
    --reader-shift: 2.5rem;
}
@media (min-width: 992px) {
    .bg-white > .verses.reader-flow .flow-text {
        transform: translateX(calc(-1 * var(--reader-shift)));
    }
    /* The ghost lives inside .bleedthrough, which is mirrored with scaleX(-1), so
       an equal leftward translate here lands rightward on screen — the opposite
       side from the real column, which is what makes more of it show. */
    .bleedthrough .flow-text {
        transform: translateX(calc(-1 * var(--reader-shift)));
    }
    /* THE PAGER FOLLOWS THE TEXT, NOT THE WINDOW (Daniel, 2026-08-01). It is
       fixed left:0/right:0 and centred, so it centred on the VIEWPORT while the
       column it belongs to sat 2.5rem to the left — near enough to look like a
       mistake rather than a choice. Moving it by the same amount puts it under
       the middle of the text, and the asymmetry that lets the ghost show stays
       exactly as it was. */
    .reader-pager {
        transform: translateX(calc(-1 * var(--reader-shift)));
    }
}

/* Reader's view, pointer devices only: the bar withdraws so nothing frames the
   page, and returns when the pointer reaches the top edge. Three gates, each
   for a reason — (hover: hover) and (pointer: fine) keep it away from touch
   screens, where there is no hover and so no way to call the bar back; the
   width matches navbar-expand-lg, below which the bar is a hamburger rather
   than a bar and hiding it would strand the menu. Everywhere else this whole
   block simply does not apply, and the navbar behaves normally.
   The reading card already runs underneath the bar, so nothing shifts when it
   goes: the page just gains the strip of paper it was covering. */
/* Clearance for the fixed bar. In the reader's view the stylesheet sets this
   rather than Bootstrap's pt-5, so it can close up when the bar retreats.

   THE TAIL AT THE FOOT IS THE OTHER HALF OF "NEVER OVER THE TEXT" (Daniel,
   2026-08-01). Lowering the reveal threshold alone is not enough: at full scroll
   the last line of a chapter used to sit hard against the bottom of the viewport,
   which is exactly where the pager appears, so the one moment it is meant to show
   was the one moment it had text underneath it. This blank tail is sized to clear
   the pager whole — its 1.5rem offset plus its own two-line height plus air — so
   the bar always lands on paper. */
.verses.reader-flow {
    padding-top: calc(3rem + var(--safe-t));
    padding-bottom: calc(7rem + var(--safe-b));
}

/* Chapter pager in the reader's view: pinned to the foot of the screen and
   centered, rather than sitting right-aligned at the end of the text. It stays
   out of sight until the chapter is nearly done, so nothing hovers over the
   page while there is still reading to do. */
.reader-pager {
    position: fixed;
    left: 0;
    right: 0;
    bottom: calc(1.5rem + var(--safe-b));
    z-index: 1026;
    margin-top: 0 !important;
    opacity: 0;
    pointer-events: none; /* unreachable, not merely invisible, while hidden */
    transition: opacity 0.25s ease;
}

.reader-pager.is-visible {
    opacity: 1;
    pointer-events: auto;
}

/* Centered here, against the right-aligned rule the rest of the site follows.
   .pagination is gone from this page — the pager is a .seq-nav since
   2026-07-31 — but the selector stays for any pagination added inside the
   reader later, and the .seq-nav rule beside it is the live one. */
.reader-pager .pagination,
.reader-pager.seq-nav {
    justify-content: center;
    margin-bottom: 0;
    margin-top: 0;
    /* Wide apart (Daniel, 2026-08-01). With the middle cell dropped the two ends
       had only the strip's default half-rem between them and read as one lump;
       set apart they read as two directions, which is what they are. It scales
       with the viewport rather than sitting at a fixed figure, because this bar
       is centred on the screen rather than in a column — clamped so a phone
       keeps them apart without pushing either off the edge. */
    gap: clamp(1.5rem, 12vw, 7rem);
}

/* THE PINNED PAGER IS NAMED BUT NOT SPREAD, and that distinction is the whole
   reason chapter names could be added to it. .reader-pager is fixed across the
   full viewport, so the space-between of a named strip would fling the two
   chapter names into opposite corners of the screen, floating over Scripture
   with a gulf between them. Grouped and centred, the same three cells read as
   one small bar. Chapter names are short enough that it stays small — which is
   exactly what a commentary heading is not, and why comm.php spreads instead. */
.reader-pager.seq-nav-named .seq-end {
    flex: 0 0 auto;
}
.reader-pager.seq-nav-named .seq-name {
    max-width: 30vw;
}

/* ON A PAGE OF SCRIPTURE THE STRIP SPEAKS THE TEXT'S VOICE, NOT THE INTERFACE'S
   (Daniel, 2026-08-01). This is the running header's counterpart at the foot —
   .reader-ref names the chapter at the head of the column, this names the
   chapters either side of it — so it takes the same treatment: the reading
   serif, the same .06em letter-spacing, uppercase, quiet. Set in the interface
   sans it announced itself as app chrome laid over the page.

   SCOPED TO .seq-nav-scripture, NOT .reader-pager. It was written against the
   pinned class first and that was wrong: only the reader's view pins the strip,
   so study and interlinear kept the sans while carrying the same running header
   above them. All three views are Scripture and all three get this. Pinning is a
   separate question and .reader-pager still answers it alone.

   Everything below is TYPE ONLY. The button box, its border and its background
   stay exactly as they are: pinned, the bar floats over the reading column and
   the wallpaper beyond it, and a plate under the words is what keeps it legible
   on every mood. */
.seq-nav-scripture .seq-name,
.seq-nav-scripture .seq-mid .page-link {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    letter-spacing: .06em;
    text-transform: uppercase;
    font-weight: 600;
    /* The same x-height correction .reader-ref carries, for the same reason:
       Roboto Serif runs larger than the sans at a given size, and this is a
       label rather than a heading. */
    font-size: calc(0.875rem * var(--reader-face-scale, 0.914));
}
/* The current chapter is the one place the reader already is, so it is the
   quietest of the three — the same relationship .reader-ref-sep has to the
   reference it divides. It only appears at all when the running header has been
   switched off; see the call in index.php. */
.seq-nav-scripture .seq-mid .page-link {
    opacity: 0.65;
}
/* "previous chapter" is a direction, not a name: smaller, unletterspaced, and
   in the interface face, so the two lines are told apart at a glance rather
   than read as one run of serif.

   0.6875rem is deliberate, not a round number. The name above it lands at
   12.8px once the x-height correction applies, and the 12px this line started at
   left the two so close that they read as a pair of equals instead of a label
   and its caption. 11px restores the step. */
.seq-nav-scripture .seq-where {
    text-transform: none;
    letter-spacing: 0;
    font-size: 0.6875rem;
    opacity: 0.6;
}

@media (hover: hover) and (pointer: fine) and (min-width: 992px) {
    .nav-peek {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        height: 0.75rem;
        z-index: 1029; /* under the navbar, so the bar takes over once shown */
    }

    /* A line scrolling out of view dissolves rather than being sliced off at the
       edge of the screen. The text itself is masked — nothing is painted over
       the page — so the ivory, its vignette and the page curvature all show
       through untouched, which a band of flat color could not manage: the
       paper's tint varies down the sheet, so any fixed color matches it in one
       place and not another.
       The band is held at the top of the screen while the page moves beneath
       it, so app.js keeps the two stops in step with the scroll; both are 0 at
       rest, which leaves the text unmasked. */
    /* On the text column itself, not its wrapper: the chapter reference is
       outside it and so is never touched by the fade, and the gradient is
       measured against the words rather than the padding above them. */
    /* Four stops: the text gathers itself at the top of the screen and lets go
       again at the bottom, so lines arrive and depart the same way. The defaults
       leave it solid — the last two at 100% put the closing fade past the end of
       the column, which is where it belongs when the chapter ends on screen. */
    body:has(.nav-peek) .flow-text {
        -webkit-mask-image: linear-gradient(to bottom,
            transparent var(--fade-top-start, 0px),
            #000 var(--fade-top-end, 0px),
            #000 var(--fade-bottom-start, 100%),
            transparent var(--fade-bottom-end, 100%));
        mask-image: linear-gradient(to bottom,
            transparent var(--fade-top-start, 0px),
            #000 var(--fade-top-end, 0px),
            #000 var(--fade-bottom-start, 100%),
            transparent var(--fade-bottom-end, 100%));
    }

    /* The chapter reference stays put while the text runs past it. Pinned only
       here: on a narrow screen the column fills the card and a floating label
       would land on top of the words, so there it stays in the flow. Its
       horizontal extent is set from the reading card by app.js. */
    body:has(.nav-peek) .reader-ref-flow {
        position: fixed;
        top: 1rem;
        z-index: 1026; /* over the text, under the bar */
        margin-bottom: 0;
        pointer-events: none;
        transition: top 0.18s ease;
    }

    /* With the bar gone there is nothing to clear, so the text rises toward
       the top edge; the two move together, over the same moment. */
    body:has(.nav-peek) .navbar {
        transform: translateY(-100%);
        transition: transform 0.18s ease;
    }

    body:has(.nav-peek) .verses.reader-flow {
        padding-top: 1rem;
        transition: padding-top 0.18s ease;
    }

    /* The progress line rides with the bar: at the very top edge while it is
       away, pushed down to sit under it when it returns, rather than being
       covered by it. --nav-h is the measured bar height, published by app.js. */
    body:has(.nav-peek) #scroll-progress-bar {
        top: 0;
        transition: top 0.18s ease;
    }

    /* Brought back by the pointer merely heading for the top (app.js sets
       .nav-near well before the edge), by the pointer reaching the strip itself
       — which is what still works with scripting off — by keyboard focus, and
       held while one of its menus is open so a dropdown is never left hanging
       over a bar that has slid away. The Navigator is held open the same way and
       for the same reason: it is a panel hanging from the bar, and the pointer
       has to travel down into it to pick a book, which would otherwise dismiss
       the very bar it depends on. :is(.show, .showing) covers the opening
       animation as well as the settled state, and because Bootstrap keeps .show
       on through its closing animation the bar waits for the panel to finish
       leaving. The text and the progress line are moved by the same conditions,
       so the three travel together. */
    body.nav-near .navbar,
    body:has(.nav-peek:hover) .navbar,
    body:has(.navbar:hover) .navbar,
    body:has(.navbar:focus-within) .navbar,
    body:has(#navigatorOffcanvas.show) .navbar,
    body:has(.dropdown-menu.show) .navbar {
        transform: translateY(0);
    }

    body.nav-near .verses.reader-flow,
    body:has(.nav-peek:hover) .verses.reader-flow,
    body:has(.navbar:hover) .verses.reader-flow,
    body:has(.navbar:focus-within) .verses.reader-flow,
    body:has(#navigatorOffcanvas.show) .verses.reader-flow,
    body:has(.dropdown-menu.show) .verses.reader-flow {
        padding-top: 3rem;
    }

    body.nav-near #scroll-progress-bar,
    body:has(.nav-peek:hover) #scroll-progress-bar,
    body:has(.navbar:hover) #scroll-progress-bar,
    body:has(.navbar:focus-within) #scroll-progress-bar,
    body:has(#navigatorOffcanvas.show) #scroll-progress-bar,
    body:has(.dropdown-menu.show) #scroll-progress-bar {
        top: var(--nav-h, 3.875rem);
    }

    body.nav-near .reader-ref-flow,
    body:has(.nav-peek:hover) .reader-ref-flow,
    body:has(.navbar:hover) .reader-ref-flow,
    body:has(.navbar:focus-within) .reader-ref-flow,
    body:has(#navigatorOffcanvas.show) .reader-ref-flow,
    body:has(.dropdown-menu.show) .reader-ref-flow {
        top: calc(var(--nav-h, 3.875rem) + 0.75rem);
    }
}

/* THE CROSS-REFERENCE MARKER IS GONE — removed 2026-07-31 on Daniel's word,
   "I realized it's not necessary". It was the paled red share icon beside a
   verse that had cross-references; the picker under the selected verse already
   offers the trip, and the mark said the same thing a second time.

   Two rules went with it: `.crossref-marker` itself, and the
   `.verse-row.verse-selected` rule that hid it (and a `.commentary-marker`
   that was only ever a reserved name and never rendered). Nothing else styled
   either one. `data-refs="1"` on the verse row is untouched — that, not the
   mark, is what tells the picker whether the cross-reference button is live. */

/* On a phone the panel cannot sit beside the word that opened it — it is wider
   than the viewport — so it becomes a bottom sheet: full width along the bottom
   edge, within reach of a thumb, and always in the same place. app.js leaves the
   inline offsets unset at this width so this rule owns the geometry (see
   isBottomSheet(), which tracks this breakpoint). */
@media (max-width: 575.98px) {
    #strongs-popup {
        left: 0;
        right: 0;
        bottom: 0;
        top: auto;
        max-width: none;
        max-height: 70vh;
        overflow-y: auto;
        border-bottom-left-radius: 0;
        border-bottom-right-radius: 0;
        /* Clear of the home indicator on phones that have one. Through the
           shared variable rather than env() directly, so it answers the
           devtools preview described at the top of this file. */
        padding-bottom: calc(1rem + var(--safe-b)) !important;
        animation: sheet-rise 0.18s ease-out;
    }
}

@keyframes sheet-rise {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
}

/* Navigator: a wrapping grid of category blocks, each a heading over one or
   more columns of book buttons. */
/* Center the whole block when it fits on one row, but left-align once it wraps:
   fit-content shrinks the container to a single row (which margin-inline auto
   then centers); when it wraps it clamps to full width, so auto margins are a
   no-op and flex-start keeps the rows left-aligned. */
.navigator-grid {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: flex-start;
    gap: 1rem 1.5rem;
    width: fit-content;
    max-width: 100%;
    margin-inline: auto;
}

.navigator-cat-name {
    margin-bottom: 0.4rem;
    padding-bottom: 0.15rem;
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #fff;
    border-bottom: 3px solid currentColor;
}

.navigator-cat-cols {
    display: flex;
    gap: 0.4rem;
}

.navigator-col {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.navigator-book-btn {
    white-space: nowrap;
    text-align: left;
}

/* .navigator-paper WAS HERE AND IS DELETED — 2026-08-15. It broke the 66-button
   grid out of the prose column with a centred-parent trick and five
   per-breakpoint caps, on the argument that a grid is scanned rather than read
   and so does not owe anything to a 45rem reading measure. commentary.php shows
   pills now and Daniel asked for them at the text's own width, so nothing
   applied the class any more and every one of those rules was inert.

   ⚠ THE LESSON IN IT IS WORTH MORE THAN THE CODE, so it is kept here: BOOTSTRAP'S
   `.container` DOES NOT SCALE WITH THE VIEWPORT — it SNAPS to 540 / 720 / 960 /
   1140 / 1320. Any `100vw`-derived width inside a container is therefore correct
   only where the container is fluid or exactly at its max, and wrong everywhere
   between. At a 1100px window the container is 960 and a viewport-derived cap
   overran the card by 92px a side. Cap against the breakpoint, never against the
   window. A second trap went with it: `left: 50%` on a RELATIVELY positioned
   element shifts from its NORMAL position, so combining it with `margin-inline:
   auto` centres the box twice. */

/* The dark Navigator keeps its light type — black on near-black would be
   unreadable — and takes the same gold selector as everything else, carried to
   0.28 because the alpha that reads over white nearly vanishes over the panel's
   near-black. Driven through Bootstrap's variables for the reason given at
   .btn-outline-secondary below. */
#navigatorOffcanvas .btn-outline-light {
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: rgba(201, 162, 39, 0.28);
    --bs-btn-hover-border-color: rgba(201, 162, 39, 0.7);
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: rgba(201, 162, 39, 0.28);
    --bs-btn-active-border-color: rgba(201, 162, 39, 0.7);
}

/* THE CONFIDENCE CHIPS AT NORMAL WEIGHT, in the two places a mark appears
   BESIDE OTHER TEXT rather than as the label of a comment (Daniel, 2026-08-15,
   in two passes — the explanation list first, then the search band).

   Bootstrap's .badge is 700. That weight earns itself on comm.php, where the
   chip labels a comment and has a heading to hold its own against; in a list of
   three being defined, and in a search result where it trails a title and a
   reference, it shouts over the text it is attached to.

   SCOPED BY SURFACE, NOT BY FORKING THE MARKS — commentaryConfidenceMarks()
   still returns one set of classes for all three places that print one, so the
   words and their colors cannot drift apart. comm.php is deliberately not in
   this list; if it ever should be, add it here rather than changing what that
   function returns. */
.conf-mark {
    font-weight: 400;
}

/* ─────────────────────────────────────────────────────────────────────────
   THE COMMENTARY'S BOOK PICKER AS PILLS — 2026-08-15, Daniel's design.
   Eleven category pills; clicking one spreads its books as tiles that arrive in
   sequence. Markup and the reasons it degrades the way it does: navigator.php.

   ⚠ THE CATEGORY COLOUR IS IDENTITY; GOLD IS STILL "HERE". The eleven colours
   below already existed as heading accents, and they say WHICH GROUP. They are
   deliberately kept off the tiles' backgrounds: the tiles are .btn-outline-
   secondary, whose hover is the standing gold wash, and a coloured fill under a
   gold hover would leave the page with two colours competing to mean "this one".
   The colour shows on the pill and on the rule above the open panel. Do not
   tint the tiles. */
.navigator-pills {
    margin-bottom: 0.5rem;
}
.navpill-row {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.5rem 0.55rem;
    margin-bottom: 1.5rem;
}
/* A pill is a real button and reads like one: 44px of height, which is the touch
   floor, and enough horizontal padding that the name is never the whole target.
   The category colour arrives as the underline, the same role it plays on the
   headings this replaces. */
.navpill {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    min-height: 44px;
    padding: 0.4rem 0.95rem;
    font-family: 'Prompt', sans-serif;
    font-size: 0.95rem;
    font-weight: 400;
    color: #2c2926;
    background: #fff;
    border: 1px solid rgba(44, 41, 38, 0.18);
    border-bottom-width: 3px;
    border-bottom-color: var(--cat-color, rgba(44, 41, 38, 0.18));
    border-radius: 2rem;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}
/* The count is the honest part of the pill: "Minor Prophets 12" tells a reader
   what they are opening before they open it, which is the one thing a two-step
   picker owes them over a list that shows everything. */
.navpill-count {
    font-size: 0.8rem;
    font-weight: 300;
    opacity: 0.55;
}
/* GOLD MEANS HERE — the open group takes the app's one selection colour, at the
   .22 that reads on white. Nothing else in this control uses it. */
.navpill[aria-expanded="true"] {
    background: rgba(201, 162, 39, 0.22);
    border-color: rgba(201, 162, 39, 0.7);
    border-bottom-color: var(--cat-color, rgba(201, 162, 39, 0.7));
}
.navpill:hover,
.navpill:focus-visible {
    background: rgba(201, 162, 39, 0.12);
}
/* THE TILT IS GONE — Daniel, 2026-08-15: "remove that tilt, it's too much". It
   was eleven per-category rotations of ±2°, derived from the category id rather
   than random so the page would look the same on every load. The pills sit
   straight now and the eleven colours carry the variety on their own. The
   `.navpill-N` class stays in the markup as the per-category hook; nothing
   styles it today. */

/* The panel is the card's width, not the pill's — they are siblings for that
   reason. The category colour returns here as the rule above the tiles, so the
   open group is tied to the pill that opened it by something other than
   position. */
.navpill-panel {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.4rem;
    padding-top: 1.1rem;
    /* 1px, down from 2 (Daniel, 2026-08-15). At two it read as a heavy bar in the
       category's colour rather than as a rule under the pills — the same
       quietening the landing card went through the same day. */
    border-top: 1px solid var(--cat-color, rgba(44, 41, 38, 0.18));
}
/* A TILE IS A SMALLER PILL (Daniel, 2026-08-15: "style the book button more
   after the pills"). Same rounded shape, same white ground, same quiet border,
   same face — a step down in size and weight, so the row of books reads as the
   children of the pill that opened them rather than as Bootstrap buttons that
   happen to be underneath it.

   ⚠ THE GROUND AND THE BORDER GO THROUGH BOOTSTRAP'S VARIABLES, NOT THROUGH
   `background`/`border-color`. Setting those directly on a .btn wins the paint
   but loses the hover, because Bootstrap's own :hover rule sets the same
   properties from --bs-btn-hover-* and would be overridden — the gold wash that
   means "this one" everywhere in this app comes from .btn-outline-secondary's
   hover variables above, and this must not break it. Nothing here touches a
   --bs-btn-hover-* value.

   (It also cannot be checked by asking the pane: it reports the background of
   ANY element carrying .btn as transparent, even against an inline style. That
   is a known blind spot, and driving the colour through the variables is what
   makes the answer not matter.) */
.navpill-tile {
    --bs-btn-bg: #fff;
    --bs-btn-border-color: rgba(44, 41, 38, 0.18);
    padding: 0.3rem 0.85rem;
    font-family: 'Prompt', sans-serif;
    font-size: 0.9rem;
    font-weight: 300;
    border-radius: 2rem;
    animation: navpill-in 0.28s ease both;
    /* Each tile fades up 25ms after the one before it, which is what reads as
       the group opening rather than the page redrawing. --i is the tile's index,
       set in the markup; the multiplication is here so no PHP times anything. */
    animation-delay: calc(var(--i, 0) * 25ms);
}
/* ⚠ TWO CLASSES, AND IT HAS TO BE. The ink belongs with the three declarations
   above, but .btn-outline-secondary sets --bs-btn-color: #000 and is declared
   LATER in this file — at equal specificity the later rule wins, so the tiles
   came out pure black while the pills were #2c2926. The structural selector
   settles it without an !important. */
.navpill-panel .navpill-tile {
    --bs-btn-color: #2c2926;
}

@keyframes navpill-in {
    from { opacity: 0; transform: translateY(0.4rem); }
    to   { opacity: 1; transform: none; }
}
/* The stagger is the whole idea here, so this is the one place it has to be
   given up completely rather than shortened. The control still works — the pills
   still open, the tiles still appear — it simply stops moving. */
@media (prefers-reduced-motion: reduce) {
    .navpill-tile { animation: none; }
    .navpill { transition: none; }
}

/* Category accent colors (pastels darkened for legibility). Each one is a custom
   property FIRST and a border second: the pills carry the colour on their bottom
   edge and the open panel carries it on its top edge, and one value cannot serve
   two different border sides through . */
.navigator-cat-1 { --cat-color: #9c7777; border-bottom-color: var(--cat-color); } /* Coral */
.navigator-cat-2 { --cat-color: #9c8d77; border-bottom-color: var(--cat-color); } /* Orange */
.navigator-cat-3 { --cat-color: #959c77; border-bottom-color: var(--cat-color); } /* Gold */
.navigator-cat-4 { --cat-color: #7f9c77; border-bottom-color: var(--cat-color); } /* Lime */
.navigator-cat-5 { --cat-color: #779c86; border-bottom-color: var(--cat-color); } /* Mint */
.navigator-cat-6 { --cat-color: #779c9c; border-bottom-color: var(--cat-color); } /* Teal */
.navigator-cat-7 { --cat-color: #77869c; border-bottom-color: var(--cat-color); } /* Sky */
.navigator-cat-8 { --cat-color: #7f779c; border-bottom-color: var(--cat-color); } /* Periwinkle */
.navigator-cat-9 { --cat-color: #95779c; border-bottom-color: var(--cat-color); } /* Lavender */
.navigator-cat-10 { --cat-color: #9c778d; border-bottom-color: var(--cat-color); } /* Pink */
.navigator-cat-11 { --cat-color: #b0894f; border-bottom-color: var(--cat-color); } /* Gold — Revelation */

/* All tooltips are styled as white pills (no more custom popovers in the
   project — everything now uses Bootstrap's tooltip, which auto-calculates
   placement/flip on its own). Placement is left untouched so each tooltip
   still opens in the same direction it always has. */
.tooltip {
    --bs-tooltip-bg: #fff;
    --bs-tooltip-color: #212529;
    --bs-tooltip-opacity: 1;
    --bs-tooltip-border-radius: 50rem;
}

.tooltip .tooltip-inner {
    border: 1px solid rgba(0, 0, 0, 0.175);
}

.tooltip .tooltip-arrow {
    display: none;
}

/* Pagination (reader chapters, search results, Strong's occurrences) always
   sits at the right end of its row. Set here rather than per-instance so any
   pagination added later is aligned without having to remember a utility. */
.pagination {
    justify-content: flex-end;
}

/* Grow to fit the book grid but never past the viewport; the body scrolls if
   the grid is taller than the space below the navbar. */
#navigatorOffcanvas {
    height: auto;
    bottom: auto;
    max-height: calc(100vh - 3.5rem);
    overflow: hidden;
    transition: none; /* appear instantly instead of sliding down */
}

#navigatorOffcanvas .offcanvas-body {
    padding: 3rem 1.5rem 1.5rem;
    overflow-y: auto;
}

/* Landing page (no chapter selected) elements. */

/* The hero's own search field, above the English verse and sharing its left
   edge. The width is capped so it reads as an entry point, not a bar across
   the hero; 88vw is the same cap the verse itself takes on narrow screens.
   The submit button needs a solid page background only while not hovered:
   outline buttons are transparent at rest, and the drawn hills would show
   through it — but on hover Bootstrap's own gray fill must win, so the rule
   steps aside instead of outweighing it. */
/* THE PAGE SAYS WHAT IT IS, AND OFFERS ONE PLAIN WAY IN — 2026-08-15. Above the
   search field in the same left column, so a stranger reads the three in the
   order they need them: what this is, the way in, then the way to look
   something up.

   COLOUR IS var(--hills-near), the same the verses take, and that is the whole
   reason this needs no per-mood rules: the variable is the darkest tone of
   whichever colorway is drawn, so the type follows Dawn, Day, Dusk and Night by
   itself. Anything hard-coded here would have to be checked four times and
   would still break on a fifth mood.

   Positioned at 6% against the search's 17%: the block runs to roughly 13% at
   its tallest, so the two never meet, and the verse at 25% is clear of both. */
/* ⚠ TOP IS IN rem, NOT A PERCENTAGE, because this is the one hero element that
   has to clear the FIXED NAVBAR — 62px of it — and a percentage does not know
   how tall the window is. At 6% it slid under the bar on a short screen and the
   heading was cut in half. 5rem = 80px puts it clear with air. */
/* ⇦ THE HERO IS PLACED IN rem, AT ONE SIZE, FOR EVERY SCREEN — 2026-08-15,
   Daniel's simplification, step one. Every top/left/right/bottom below is a
   rem, every display line is the verse's 2.2rem, every reference line is
   .welcome-ref, and the hero has no media queries left at all.

   The rem values are the percentages as they resolved at 1280x800, so the
   composition lands where it was tuned on a desktop; what changes is that it no
   longer moves with the window. */
.welcome-intro {
    width: 45rem;
    /* Same rule as the verse: the box narrows with the window and the heading
       wraps inside it, at one type size. */
    max-width: 100%;
    /* The block's trailing air, and it is trailing air for the STACKED layout
       only — see the rule under this one. It was tripled to 108px to separate the
       search from the English verse; what it holds apart now is this block and
       the topics column below it, on the widths where the two are stacked. */
    margin-bottom: 6.75rem;
    color: var(--hills-near, #25707e);
}
/* ⚠⚠ AND NOTHING AT ALL WHEN THE COLUMNS SIT SIDE BY SIDE — 2026-08-23, worth
   108px of the hero's height. At lg and up this block is the whole left column
   and nothing follows it, so its bottom margin was extending the column past its
   own content into empty hills. Invisible, because a margin at the foot of a
   column looks exactly like the space a hero is supposed to have — but it is 108
   real pixels, and the day the left column overtook the right (the shelf strip
   was aligned to the verse-of-the-day card, which pushed it down 56px) they came
   straight off the bottom of the screen.

   ⚠ A MARGIN THAT ONLY MAKES SENSE IN ONE OF TWO LAYOUTS BELONGS IN A QUERY. The
   same fault, in the same row, cost another 40px on `.welcome-topics` earlier the
   same day: both were tuned when the Favorite passages card was a third element
   below them, and neither was revisited when it moved into a column. */
@media (min-width: 992px) {
    .welcome-intro { margin-bottom: 0; }
}
/* THE VERSE'S OWN LOOK — family, weight, colour, line-height and balanced wrap
   all taken from .welcome-verse, so the page's two pieces of display type are
   one thing (Daniel, 2026-08-15).

   ⚠ THE SIZE IS CLAMPED AGAINST THE VIEWPORT'S HEIGHT, and that is deliberate:
   what constrains this block is not how wide the window is but how little room
   sits between the navbar and the search field at 17% — 74px on an 800px
   screen. A vh-based clamp shrinks the type exactly when that band shrinks and
   lets it reach the verse's full 2.2rem on a tall screen, which no width-based
   rule can do. */
/* The verse's own style, flat — no clamp, one size everywhere, and one line:
   the hero's display text does not wrap (Daniel, 2026-08-15). text-wrap:balance
   went with the wrapping it was balancing. */
.welcome-intro-title {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-size: 2.2rem;
    font-weight: 700;
    line-height: 1.15;
    margin: 0;
    color: inherit;
}
/* THE REFERENCE'S OWN LOOK (Daniel, 2026-08-15): .welcome-ref's Prompt at 1rem
   with its 0.5rem lead, uppercased as the references are by their markup class,
   and carrying the same long arrow. It reads as the caption under the heading
   exactly as a reference reads under a verse.

   A link rather than a button, and that stays deliberate: a filled control here
   would be a second emphasis competing with the gold search submit, and the
   hero holds exactly one. */
/* CLOSES THE CARD (Daniel, 2026-08-15) — under the passages, with air above it
   and a rule between. Both are the same offer, somewhere to start reading, but
   this one is the reader's own place and the list is everybody's, so it is set
   apart rather than run in with them. */
.welcome-enter {
    display: inline-block;
    font-family: 'Prompt', sans-serif;
    /* A step under the chapters' 0.95rem (Daniel, 2026-08-15): the list is the
       card's offer and this closes it, so it does not compete with them. */
    font-size: 0.85rem;
    /* Regular, down from 500 (Daniel, 2026-08-15). The card now runs 500 for the
       heading, 400 here and 300 for the chapters — three weights, lightest on
       the five links that repeat, so nothing in it competes with the gold search
       button above. */
    font-weight: 400;
    /* THE RULE IS A DIVIDER ACROSS THE CARD, not an underline on this link: the
       negative side margins pull it out to the card's edges, past the 1.25rem
       of padding that was insetting it 20px at each end, and the padding is put
       back inside so the words stay where they were. */
    margin: 0.35rem -1.25rem 0;
    padding: 0.5rem 1.25rem 0;
    border-top: 1px solid rgba(74, 63, 42, 0.18);
    width: auto;
    text-transform: uppercase;
    color: inherit;
    text-decoration: none;
    transition: opacity 0.2s ease;
}
.welcome-enter:hover,
.welcome-enter:focus-visible {
    color: inherit;
    opacity: 0.68;
}

/* On a phone the heading wraps to two or three lines however small the type is,
   because the column is only 88vw. The vh clamp cannot see that, so the width
   case is handled here: a tighter cap and a smaller caption, which together
   keep the block inside the band above the search. The search stays at 17% and
   the verse at 25%, where they were tuned. */
/* The phone rules for this block live with the rest of the hero's media queries
   further down, AFTER .welcome-search and .welcome-verse are defined — put here
   they lost to those base rules on source order at equal specificity, and the
   stack silently did not move. */

/* ─────────────────────────────────────────────────────────────────────────
   THE HERO IN FLOW — adopted 2026-08-15 (Daniel), after a day behind ?grid=1.
   The flag is gone, the neutralisers that unset each element's hand-tuned
   top/left are gone with it, and the coordinates they were neutralising have
   been removed from the elements themselves.

   THE STAGE keeps what the percentages were really buying: a hero that fills
   the window, anchored under the fixed navbar. It also carries the one width
   the search field and the card share — see --welcome-card-w below. */
/* ⚠ ANCHORED TO THE TOP, NEVER CENTRED (Daniel, 2026-08-15: "the top element is
   moving, I want just the opposite. top element should stay").

   justify-content: center looked right on a tall window and was wrong on a
   short one: once the content is taller than the stage, centring distributes
   the overflow to BOTH ends, so the heading rides up underneath the fixed
   navbar and is cut in half. Anchored at the start, the top element stays where
   it is at every height and any overflow falls off the bottom, where a page can
   scroll.

   The padding is the navbar plus a small gap, which is the whole distance from
   the top of the screen — the bar is 62px and fixed, so nothing can sit above
   it. That is as close to the top as this can legally come. */
.welcome-stage {
    /* ⚠ 100vh, NOT 100vh MINUS THE NAVBAR. The bar is fixed, so it takes no
       space in flow and the stage's box already starts at the top of the
       screen — subtracting its height as well left the stage ending 62px short
       and the card floating 86px above the foot of the window instead of the
       48 it is tuned to. The navbar is cleared by the padding-top below, which
       is the only place it should be paid for. */
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    /* 62px of fixed navbar plus the gap under it. 0.75rem → 1.5rem → 3rem over
       three passes, on Daniel's word each time: 110px from the top of the
       screen in total. It does not change with the window's height — the whole
       point of anchoring to the top is that this number holds. */
    padding-top: calc(62px + 3rem);
    /* The card's old `bottom: 3rem`, kept as the stage's floor now that the card
       is pushed down to it rather than pinned there. */
    padding-bottom: 3rem;
}
/* Vertical rhythm as margins, which is the whole reason for doing this: the
   elements hold each other apart instead of being held apart by hand.

   ⚠⚠ THE CARD IS NO LONGER PINNED TO THE FOOT OF THE SCREEN — 2026-08-23, and
   the rule that pinned it has been REMOVED rather than left to do nothing. It
   was `.welcome-stage > .welcome-mostread { margin-top: auto }`, which worked
   only because the card was a DIRECT child of the stage; Daniel moved it under
   the verse of the day, inside the right-hand column, so there is no flex parent
   for an auto margin to push against and the selector no longer matches
   anything. Left in place it would have read as live layout for the next person
   to look.

   Why it moved: pinning the card to the floor made the leftover space a band
   under the row, which is 59px on an ordinary window — while the real emptiness
   was 354px of LEFT COLUMN, unused because the topics column beside it is
   taller. Filling that needed the columns to carry the weight instead of the
   stage. See the shelf-strip block near the foot of this file. */
.welcome-mostread { margin-top: 1.75rem; }
/* ───────────────────────────────────────────────────────────────────────── */

/* IN FLOW inside .welcome-intro (2026-08-15) — no top, no left, no position of
   its own. The heading and the way in sit above it and hold it in place, so the
   field cannot be placed into another element by arithmetic. Its width is the
   one measurement it still owns. */
.welcome-search {
    /* 423px, and it stays there (Daniel, 2026-08-15: "keep the current width of
       search field"). It was 23.75rem until the field was widened to match the
       card; the card has since gone back to sizing itself and is now wider than
       this, so the two are deliberately no longer a pair and there is nothing
       left to share the number with. Written flat for that reason — the custom
       property that carried it between them has been removed. */
    width: 26.4375rem;
    max-width: 100%;
    margin-top: 1.75rem;
}
/* A little larger than the small size the input group is built at — the hero is
   a spacious page and the field was reading as an afterthought on it. Set here
   rather than by dropping input-group-sm, which would jump it a whole step. */
.welcome-search .form-control,
.welcome-search .btn {
    font-size: 0.95rem;
    padding-top: 0.4rem;
    padding-bottom: 0.4rem;
}
.welcome-search .btn:not(:hover) {
    background-color: var(--bs-body-bg);
}
/* THE GOLD SUBMIT BUTTON — adopted 2026-08-15. This landing page had nothing on
   it drawn to be looked at first; the gold chip is that one thing, and it stays
   the only one.

   Black on --biblion-gold measures 8.68:1, which is AAA; white on the same gold
   is 2.42:1 and fails outright, so the black is the only foreground that works
   here rather than a preference. It is #000 rather than the app's ink #2c2926
   (5.98:1) because a filled gold chip this small wants the harder edge, and the
   magnifier glyph inherits it.

   It overrides the :not(:hover) body-bg rule above by coming later with equal
   specificity — that rule exists so the drawn hills do not show through an
   outline button, and a filled one has nothing to show through.

   A `.welcome-search-wide` rule lived here for the hour the button carried the
   words "Find it": at 65px it pushed the Ctrl K chip 23px inside itself, so the
   chip needed its own offset. The glyph returned the button to 33px and the
   chip's plain 2.6rem clears it again, so that rule is deleted rather than left
   behind matching nothing. */
/* EVERY OUTLINE-SECONDARY BUTTON IN THE APP: black type, gold selector
   (Daniel, 2026-08-15 — "for all buttons styled like this throughout the app").
   Around forty of them: Export this list, the article filters, the commentary's
   66 book buttons, and the rest.

   Bootstrap's default drew the LABEL in #6c757d, the same muted grey it uses
   for disabled-looking furniture, and filled solid grey with white type when
   touched. Black says these are content; the gold wash is the same
   rgba(201,162,39,.22) the command palette uses for the line you are on, a
   search hit uses for the word you asked for, and the mood menu uses for the
   mood you are in. One colour for "this one", wherever a reader points.

   ⚠ DRIVEN THROUGH BOOTSTRAP'S OWN VARIABLES, NOT BY SETTING background-color,
   and the reason is worth keeping because it cost an hour on 2026-08-15.

   A first attempt set background-color directly at (0,3,0) — beating
   Bootstrap's own .btn.active, later in the file, with no !important anywhere
   in the matching cascade. It could not be CONFIRMED to paint, and the
   investigation turned up a measuring trap rather than a verdict: in the
   headless Browser pane, getComputedStyle().backgroundColor returns transparent
   for ANY element carrying .btn, even when an inline background-color is set on
   it, while the identical inline style on a plain <a> beside it reads back
   correctly. So the pane cannot answer the question either way for a .btn.

   Overriding the variables is the right shape regardless: it is what Bootstrap
   itself paints from, it needs no specificity fight, it survives an upgrade,
   and the variables DO read back correctly in the pane, so the change is at
   least verifiable at the layer this file controls. Do not "simplify" this
   block back into plain declarations. */
.btn-outline-secondary {
    --bs-btn-color: #000;
    --bs-btn-hover-color: #000;
    --bs-btn-hover-bg: rgba(201, 162, 39, 0.22);
    --bs-btn-hover-border-color: rgba(201, 162, 39, 0.55);
    --bs-btn-active-color: #000;
    --bs-btn-active-bg: rgba(201, 162, 39, 0.22);
    --bs-btn-active-border-color: rgba(201, 162, 39, 0.55);
}

/* THE DESTRUCTIVE CONTROLS ARE BLACK UNTIL YOU REACH THEM — Daniel, 2026-08-15,
   from the "Clear all bookmarks" button on bookmarks.php. Twelve buttons across
   the app wear one of these two classes: clearing bookmarks, favorites, notes
   and the search log, deleting a note, an ad, a message, a tag, a user, and
   closing an account.

   The same convention as .btn-gold and .btn-outline-secondary below — a black
   label at rest, the colour arriving on hover — so the app has one grammar for
   its buttons rather than a red family that behaves differently from the rest.

   ⚠ DRIVEN THROUGH BOOTSTRAP'S VARIABLES, NEVER `background-color`. Setting the
   paint directly wins at rest and loses on hover, because Bootstrap's own :hover
   rule assigns the same properties from --bs-btn-hover-*. This is the standing
   rule for every button on this site; see [[bible-gold-system]].

   ⚠ AND IT COSTS SOMETHING, WHICH IS WORTH KNOWING RATHER THAN FORGETTING: red
   at rest is a warning a reader reads before their hand arrives. Black defers it
   to the hover. What carries the warning instead is the confirm dialog every one
   of these buttons already opens — data-confirm / data-confirm-title, with the
   count and the words "this cannot be undone" written into it. If a destructive
   control is ever added WITHOUT a confirm, it should not use these classes. */
.btn-outline-danger {
    --bs-btn-color: #000;
    --bs-btn-border-color: rgba(0, 0, 0, 0.25);
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: var(--biblion-tomato);
    --bs-btn-hover-border-color: var(--biblion-tomato);
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: var(--biblion-tomato);
    --bs-btn-active-border-color: var(--biblion-tomato);
    --bs-btn-focus-shadow-rgb: 207, 65, 37;
}
/* The solid one is the same idea with the ground filled: near-black rather than
   pure #000, which reads as a control instead of as a hole in the page. */
.btn-danger {
    --bs-btn-bg: #1b1b1b;
    --bs-btn-border-color: #1b1b1b;
    --bs-btn-color: #fff;
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: var(--biblion-tomato);
    --bs-btn-hover-border-color: var(--biblion-tomato);
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: var(--biblion-tomato);
    --bs-btn-active-border-color: var(--biblion-tomato);
    --bs-btn-focus-shadow-rgb: 207, 65, 37;
}

.btn-gold {
    background-color: var(--biblion-gold);
    border-color: var(--biblion-gold);
    color: #000;
    font-weight: 500;
}
.btn-gold:hover,
.btn-gold:focus-visible {
    /* Darkened by hand rather than with a filter: color-mix() is below the
       browser floor (Chrome 108 / FF 121 / Safari 15.4) and a filter would take
       the black text down with the gold. */
    background-color: #b18d18;
    border-color: #b18d18;
    color: #000;
}
/* The hero only. `.welcome-search .btn:not(:hover)` above is (0,3,0) and would
   otherwise paint the body background over the gold at rest — it exists so the
   drawn hills cannot show through an OUTLINE button, and a filled one has
   nothing to show through. */
.welcome-search .btn.btn-gold:not(:hover) {
    background-color: var(--biblion-gold);
}
/* The palette hint — a small keyboard chip riding the right end of the search
   field, just clear of the magnifier: the one ambient pointer to Ctrl/Cmd+K.
   Furniture, not a message: no dismissal state, no animation. The button
   wrapper is invisible; the kbd inside carries the whole look, overriding
   Bootstrap's inverted kbd styling (dark chip, white text — far too heavy
   here) and its monospace with the field's own face. It steps aside the
   moment the field is actually in use — focus or content — so typed text
   never runs under it; visibility rather than display, so it also stops
   catching clicks meant for the text underneath. */
.palette-hint {
    position: absolute;
    top: 50%;
    right: 2.6rem;
    transform: translateY(-50%);
    z-index: 5;
    border: 0;
    padding: 0;
    background: none;
    line-height: 1;
}
.palette-hint kbd,
.note-save-hint kbd {
    display: inline-block;
    font-family: inherit;
    font-size: 0.7rem;
    line-height: 1;
    padding: 0.22rem 0.4rem;
    color: var(--bs-secondary-color);
    background-color: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: 0.25rem;
}
.welcome-search .form-control:focus ~ .palette-hint,
.welcome-search .form-control:not(:placeholder-shown) ~ .palette-hint {
    visibility: hidden;
}

/* The hero's exit: submitting the search above sends the three verses
   travelling to a vertical midline and into it — the page closing like a book
   before opening at the answer. The machinery lives in initWelcomeSearchExit
   (app.js): two fixed half-viewport clippers whose shared inner edge IS the
   midline; each verse is cloned into whichever half its box touches and slid
   far enough to pass wholly under that edge. A verse straddling the middle
   lands a clone in both halves, so its left and right parts converge — the
   split is the same recipe, not a special case. */
.welcome-exit-clip {
    position: fixed;
    top: 0;
    bottom: 0;
    overflow: hidden;
    pointer-events: none;
}
.welcome-exit-left  { left: 0; width: 50%; }
.welcome-exit-right { left: 50%; right: 0; }
/* Two classes, not one, and not for show: the clone keeps its element's own
   classes, and those carry a transition of their own for hover opacity
   (.welcome-ref-link today; .welcome-link until 2026-08-15). transition is ONE
   property — at equal specificity the
   later rule replaces this one wholesale, the travel snaps, and the exit is
   over in a frame. The ancestor wins the cascade without an !important. */
.welcome-exit-clip .welcome-exit-clone {
    position: absolute;
    margin: 0;
    /* ease-in: accelerating INTO the seam reads as being taken up by it;
       easing out would read as hesitation at the threshold. */
    transition: transform 0.45s ease-in;
    will-change: transform;
}
/* A faint hairline in the hills' own tone where the text vanishes, so the
   midline reads as a place rather than a coincidence. */
.welcome-exit-seam {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 50%;
    width: 1px;
    background-color: var(--hills-near, #25707e);
    opacity: 0;
    transition: opacity 0.18s ease;
    pointer-events: none;
}
.welcome-exit-seam.on { opacity: 0.35; }

/* THE VERSE AND ITS REFERENCE ARE BACK — removed and restored the same day,
   2026-08-15. It is in FLOW inside the intro block now rather than placed at a
   percentage: it is the first thing in that block, the search follows it, and
   neither needs to know where the other is. */
.welcome-verse {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-size: 2.2rem;
    line-height: 1.15;
    /* The box breaks the line, not a nowrap or a clamp — the type is one size
       everywhere and the text wraps inside whatever width is left. */
    max-width: 100%;
    /* The darkest tone of whichever colorway is drawn, so it follows all four
       moods without a rule each. */
    color: var(--hills-near, #25707e);
}

.welcome-ref {
    font-size: 1rem;
    margin-top: 0.5rem;
    font-family: 'Prompt', sans-serif;
}
.welcome-ref-link {
    color: inherit;
    text-decoration: none;
    display: inline-block;
    transition: opacity 0.2s ease;
    /* The 44px touch target, bought with padding and given back with an equal
       negative margin so no type moves. */
    padding: 0.8rem 0.25rem;
    margin: -0.8rem -0.25rem;
}
.welcome-ref-link:hover,
.welcome-ref-link:focus-visible {
    opacity: 0.68;
}
.welcome-ref-link:hover .welcome-ref-arrow {
    opacity: 1;
}
.welcome-ref-arrow {
    font-size: 0.72em;
    margin-left: 0.5em;
    vertical-align: 0.05em;
    opacity: 0.75;
}
.welcome-enter:hover .welcome-ref-arrow {
    opacity: 1;
}
}

/* THE HERO'S ONLY MEDIA QUERY, AND IT CHANGES NOTHING BUT THE TOP SPACING
   (Daniel, 2026-08-15: "on small screen do nothing but decrease the top
   margin/padding"). No type size changes, no element hides, no width switches —
   the heading and the verse are 2.2rem here as everywhere, and they wrap
   because their box is narrower, not because a rule made them smaller.

   Three earlier blocks stood here and are gone: the lexicon hidden below
   1150px, a phone-only stack shift with clamped type, and the Shema hidden
   below 992px. The first two went with the elements they hid; the third went
   because the hero is one size at every width now. */
@media (max-width: 767.98px) {
    /* 4rem, not less: the fixed navbar is 58px here, and anything under 3.7rem
       puts the heading behind it. */
    .welcome-intro { top: 4rem; }
}

/* Most read this week — the hero's fourth element, when the week has earned
   it (see the band's note in index.php). It keeps the composition's own
   grammar: the search field's left edge, the references' small uppercase for
   its heading, the reading serif for the chapters — so when it appears it
   reads as part of the artwork, not as a widget parked on it.

   IT IS THE ONE HERO ELEMENT STANDING ON THE HILLS RATHER THAN IN THE SKY,
   and that decides its ink. The other three float high on the pale sky and so
   take --hills-near, the darkest ridge tone. This sits at the foot of the
   viewport, which in every colorway is ridge 7 — the DARKEST fill in the
   whole palette (day #25707e, dusk #3f3663, dawn #3c4a6b, night #0d1620; see
   the COLORWAYS table in css/background.gen.py). Drawn in --hills-near it was
   therefore painted in almost exactly the colour it stood on: invisible in
   day, dusk and dawn alike, and worst on a small screen where the ridges
   climb higher up the viewport. Reported by Daniel, 2026-08-03.

   So the band is written in the LIGHT of its own sky instead — each mood's
   palest tone over its own darkest ridge. Spelled out per colorway rather
   than derived: there is no custom property for "the pale one" (--hills-sky
   is the sky's top stop, which is itself dark at night), and color-mix() is
   below the browser floor. If background.gen.py's palettes ever move, these
   four move with them. */
/* ⇦ A CARD UNDER THE SEARCH FIELD SINCE 2026-08-15 (Daniel), not a band on the
   hills. It sat bottom-left of the viewport in the hills' own light, which is
   why every colour below was measured against a ridge; on paper it needs none
   of that. The four mood tints and the contrast notes that went with them are
   gone with the surface they were tuned for — ink on a light card is legible in
   every colorway by construction.

   Left-aligned, one chapter per row, and in flow beneath the form inside the
   intro block, so it cannot be positioned into anything. */
.welcome-mostread {
    /* Fits its content rather than filling the column: with the chapters set as
       USFM codes the grid needs a fraction of the width it used to, and a
       full-width card was mostly empty. max-width keeps it honest on a narrow
       screen, where fit-content would otherwise ask for more than there is. */
    /* THE CARD SIZES ITSELF, THE FIELD DOES NOT (Daniel, 2026-08-15). fit-content
       resolves to the list's max-content width, which is all five names on ONE
       line — so the card grows past the search field above it rather than
       wrapping the names into a ragged second row. The two are no longer a pair,
       and that is the instruction: the field keeps the width it has.

       `max-width: 100%` is what makes this safe on a phone, where one row of five
       cannot fit: the cap bites, and the list — which is still `flex-wrap: wrap`
       — falls into as many rows as it needs. No breakpoint, which is the standing
       rule for this page. */
    width: fit-content;
    max-width: 100%;
    /* Equal on both sides. It was 0 on the right for an hour, while the card
       was full-width and the last column ended far short of the edge; now the
       card fits its content, so the right pad is holding the last chapter off
       the edge exactly as the left one holds the first.

       The foot is tighter than the head (Daniel, 2026-08-15) because the head
       carries a heading and the foot carries a line that already has the rule's
       own air above it — 1rem there read as a gap the card had no reason for. */
    padding: 1rem 1.25rem 0.6rem;
    /* The app's gold wash — #f3ebcf, the same value the Strong's popup takes —
       carried at 0.92 so a little of the hills reads through it (Daniel,
       2026-08-15). It was fully opaque first, on the argument that a card on a
       drawn wallpaper should cover rather than tint it; at this alpha the tint
       is slight enough that the type is unaffected and the card still sits ON
       the picture rather than floating over it. 0.92 → 0.8 → 0.7 over three
       passes. */
    background: rgba(243, 235, 207, 0.7);
    border-radius: var(--bs-border-radius);
    box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
    text-align: left;
    /* NOT BLACK (Daniel, 2026-08-15: "too contrasting"). #4a3f2a is the gold's
       own hue carried down to an ink — the same family as the card and the
       star, so the three read as one object rather than as type dropped onto a
       tint. Measured on the composited card over a mid ridge: 9.4:1, which is
       AAA at this size, so softening the contrast costs nothing that matters.
       Pure black on the same card measures 14.7:1 and was the harshness. */
    color: #4a3f2a;
}
/* THE STAR IS GONE — 2026-08-15, and the whole short history of it is the point:
   gold at full strength, then washed to the card's own ink at 0.45, then
   removed. Each step was Daniel quietening the same card, and the last one is
   the honest end of that line: a heading that names the list does not need a
   glyph in front of it. Do not put an icon back on this card. */

/* The heading steps back from the chapters, but only as far as the ridge
   behind it allows: at 0.75 — the opacity a label like this would ordinarily
   take — it measured 3.36:1 in day and 3.78:1 in dusk against the near ridge,
   under the 4.5:1 that 12.8px text needs. 0.9 clears it in all four moods and
   still reads as the quieter of the two lines. Measure again if the palettes
   or this size ever move. */
.welcome-mostread-head {
    font-family: 'Prompt', sans-serif;
    font-size: 0.8rem;
    /* 500, down from 700 (Daniel, 2026-08-15). It is uppercase and letterspaced,
       which already sets it apart from the chapters; bold on top of both made a
       heading louder than the five links it heads. */
    font-weight: 500;
    letter-spacing: 0.08em;
    /* Full strength, and that matters more now the weight has come down: the 0.9
       was measured against the hills, where a label at ordinary opacity failed
       contrast. On the card the inherited ink measures 9.4:1. */
    margin-bottom: 0.25rem;
}

/* One quiet line of chapters, a middle dot between them — the separators are
   drawn, not typed, so a screen reader hears a list of chapters and no
   punctuation. */
/* ONE ROW WHEN THERE IS ROOM (Daniel, 2026-08-15). This has been a middot line,
   a one-per-row column, and a fixed three-column grid; with the chapters
   shortened to USFM codes all six fit a single row and the fixed count was
   forcing a second one.

   Wrap rather than `repeat(6, …)`, and that is the whole point: a fixed count
   cannot fold on a narrow screen without a breakpoint, and this page has no
   breakpoints by standing rule. Flex-wrap gives one row wherever the card's
   max-width allows and folds by itself where it does not — one rule, every
   width. */
.welcome-mostread ul {
    display: flex;
    flex-wrap: wrap;
    column-gap: 1.25rem;
    row-gap: 0.15rem;
}

/* The separating middot went with the single row. The grid's own gaps are what
   hold the chapters apart now. */

/* Ordinary links. The hover fade matches the hero's reference links beside
   them, so the two behave alike. Until 2026-08-15 this note warned them off
   .welcome-link, which would have swallowed their first click; there is no
   longer a confirm for anything to be caught by. */
.welcome-mostread-link {
    /* THE READING SERIF (Daniel, 2026-08-15) — these are book names, and a book
       name is Scripture's own vocabulary rather than interface wording. It also
       sets the card apart from the topic pills across the hero, which are Prompt
       precisely because they are subjects to pick rather than books to read.

       (A note arguing the opposite — Prompt, because a chapter in a card is
       navigation — stood here until the same day and was DELETED with this
       edit: it was the earlier half of the decision and contradicted the rule
       it sat on top of.)

       ⚠ ONLY 400, 600 AND 800 OF THIS FACE SHIP. Anything else is either
       resolved to one of them or synthesised, and a synthesised weight is the
       shear the italics carried until they were fixed ([[bible-typography]]).
       300 was tried here and was never real; 800 is. */
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-size: 0.9rem;
    font-weight: 800;
    color: inherit;
    text-decoration: none;
    transition: opacity 0.2s ease;
}

/* Hover is the fade alone — no underline (Daniel, 2026-08-03): the hero's own
   verses fade without one, and the band should speak the same way. */
.welcome-mostread-link:hover,
.welcome-mostread-link:focus-visible {
    color: inherit;
    opacity: 0.68;
}

/* THE BAND'S TWO MEDIA QUERIES ARE GONE — 2026-08-15. One shrank it to three
   chapters below 992px because five wrapped into the Greek block's gloss
   (measured overlapping by 25px at 360x640); the other hid it below 620px of
   height because the hero's own elements filled the viewport. Both were about
   elements that no longer exist — the Shema, the lexicon and the verse were all
   removed the same day — and about a band positioned over the hills, which this
   is not any more. It is a card in flow under the search field: it takes the
   room it needs and pushes the page down, which is what flow is for. */
/* Achievement badges under "Member since" on the account page. Each is a circle
   with a washed-out fill and a saturated ring and icon (--badge-fill / --badge-ring
   set per badge). Earned ones show in color; locked ones keep their color as a
   hint but are desaturated and dimmed. Quiet by design — no animation, no popups. */
.badge-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
}

/* Summary dots in the Account details row: one per badge, filled black when
   earned, grey when not. The whole row is a button that opens the Badges section. */
.badge-dots {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem;
    padding: 0.15rem 0;
    border: 0;
    background: none;
    cursor: pointer;
}
.badge-dot {
    width: 0.7rem;
    height: 0.7rem;
    flex: 0 0 auto;
    border-radius: 50%;
    background: #ced4da; /* grey — locked */
}
/* Earned dots carry their tier's metal color. */
.badge-dot.tier-1 { background: #b0703c; } /* bronze */
.badge-dot.tier-2 { background: #99a1ab; } /* silver */
.badge-dot.tier-3 { background: #cba52a; } /* gold */

/* A badge card is one button with two faces, only ever one of them showing: the
   medal, or what the medal is. Which one is decided entirely by aria-expanded,
   so the state a screen reader hears and the state the eye sees are the same
   fact. The button is styled back down to card body — it should read as the card
   itself, which is what it is. */
.badge-flip {
    /* ⚠⚠ BOTH FACES OCCUPY ONE GRID CELL, and that is what makes every card the
       same size. They used to be swapped with `display: none`, so a card was only
       ever as tall as the face it was showing — medal cards tall, locked cards
       four lines of text — and a card CHANGED HEIGHT under the pointer when it
       turned. Stacked, with the hidden face keeping its space (see the visibility
       rules below), the button is always as tall as the taller face and nothing
       moves.
       ⚠ NO min-height: it was 8.25rem, tuned to a 100px medal, and the medal is
       fluid now — a floor in rems is either invisible or a squeeze depending on
       the viewport. */
    display: grid;
    align-items: center;
    width: 100%;
    border: 0;
    background: none;
    color: inherit;
    cursor: pointer;
    /* ⚠ THE PADDING IS SET HERE, AND `p-3` WAS TAKEN OFF THE BUTTON to let it be:
       Bootstrap's spacing utilities carry !important, so the class would have won
       and no rule here could tighten the card around its medal. */
    padding: 0.7rem;
}
.badge-face-medal,
.badge-face-text {
    grid-area: 1 / 1;
}
/* ⚠ A SQUARE CARD — Daniel, 2026-08-23 — AND NOT AT EVERY WIDTH. With both faces
   in one cell the button's height is whatever the taller face needs, which is a
   different shape at every column width; aspect-ratio makes it one. But the text
   face has to fit inside that square, and two-up on a 390px phone the longest
   locked description needs 151px in a card 151px wide — a square there would clip
   it. Above the breakpoint the cards are wide enough that the same text takes far
   fewer lines, so the square is free. Measured both, before choosing the gate. */
@media (min-width: 576px) {
    .badge-flip { aspect-ratio: 1 / 1; }
}
.badge-flip:focus-visible {
    outline: 2px solid var(--bs-primary, #0d6efd);
    outline-offset: -2px;
    border-radius: inherit;
}
.badge-flip[aria-expanded="false"] .badge-face-text { visibility: hidden; }
.badge-flip[aria-expanded="true"] .badge-face-medal { visibility: hidden; }
/* ⚠ NO PADDING OF ITS OWN — the square provides the air, and this padding was
   what stopped the square being square. `aspect-ratio` is a preferred size, not a
   cap: when the content is taller than the ratio allows, the box GROWS. Medal 166
   + this padding 11 + the button's 22 came to 200 inside a 194px square, so every
   card rendered 196x202 and looked square without being it. Measured. */
.badge-face-medal {
    display: block;
}

/* A DEVOTIONAL YEAR AS TWELVE GRIDS — 2026-08-15. Four published books put ~1,830
   dated lines on one page; a book now has its own page and each month is a block
   of day numbers rather than thirty paragraphs.

   auto-fill with a minmax track, so it is one rule for every width: as many
   columns as fit, never fewer than the cell can be read at. No media query, and
   nothing to keep in step with a breakpoint.

   Deliberately NOT laid out as a calendar — these readings are month-and-day and
   belong to no year, so aligning them to weekdays would be right once and wrong
   every year after. */
.dev-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(2.75rem, 1fr));
    gap: 0.3rem;
    margin: 0;
    padding: 0;
}
.dev-cell {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.2rem;
    min-height: 2.1rem;
    border: 1px solid #e3e3e3;
    border-radius: 0.25rem;
    font-variant-numeric: tabular-nums;
}
/* The whole cell is the target where the day has one reading — a 2.75rem box is
   a comfortable tap, a bare numeral is not. */
.dev-cell > a {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    min-height: 2.1rem;
    text-decoration: none;
    border-radius: inherit;
    white-space: nowrap;
}
.dev-cell > a:hover,
.dev-cell > a:focus-visible { background: #f3ede0; text-decoration: none; }
/* TWO READINGS A DAY ARE TWO CELLS, "1 AM" and "1 PM" (Daniel, 2026-08-15), so
   every cell is exactly one link and the whole cell is the target. Only the
   track has to widen; nothing else about the grid changes. */
.dev-grid-wide { grid-template-columns: repeat(auto-fill, minmax(3.6rem, 1fr)); }
/* Smaller in the wide grid so "14 AM" holds ONE line — two words in a cell is
   the only place this grid can wrap, and a cell that wraps breaks the row it is
   in. nowrap on the link is the guarantee; the smaller size is what makes the
   guarantee comfortable rather than cramped. */
.dev-grid-wide .dev-cell { font-size: 0.85rem; }
.dev-slot { font-size: 0.7rem; color: #777; margin-left: 0.15rem; }
.dev-cell > a:hover .dev-slot,
.dev-cell > a:focus-visible .dev-slot { color: inherit; }
/* Today, marked the same gold the verse rule uses so the page has one accent. */
.dev-cell-today { border-color: #c9a24a; box-shadow: inset 0 0 0 1px #c9a24a; }

/* Daily devotional: the key verse above the reading, in the articles' own
   quotation colours (Daniel, 2026-08-15 — "use same colors as here on articles
   for devotional readings"). It already shared the #c9a24a rule; what it was
   missing was the paper fill behind it and the darker ink, so the same verse
   looked like a citation here and like a quotation there.

   The padding and the radius come with the fill rather than as a separate
   decision: the old 0.15rem/0 padding was measured for a bare left rule, and a
   background needs room on all four sides or it reads as a stripe. Matched to
   .article-verse blockquote so the two are one thing.

   UPRIGHT, NOT ITALIC (Daniel, 2026-08-15: "do not use cursive in
   devotionals"). It was italic from the days when it was a small citation
   under a heading, and the same reason the articles' quotation is upright
   applies here: this is Scripture speaking in its own right, not an aside
   about the reading. A whole quoted paragraph in italic is also simply harder
   to read.

   Its own voice is still kept in size — 1.05rem against the article's 1.15rem
   — because this verse sits above a reading rather than inside one, and the
   reading is what the page is for. Used on devotional.php and twice on
   devotions.php. */
.devotional-verse {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-size: 1.05rem;
    line-height: 1.5;
    color: #2c2926;
    background: #faf7f0;
    border-left: 3px solid #c9a24a;
    border-radius: 0 var(--bs-border-radius) var(--bs-border-radius) 0;
    padding: 1rem 1.25rem;
    margin: 0 0 1rem;
}

/* Where a partial translation has nothing to show. Tyndale never finished the
   Old Testament, so rather than filling the slot with another translation's
   words the page says so — set apart from Scripture by being lighter and
   italic, so it can never be mistaken for the text itself. */
.verse-untranslated {
    font-style: italic;
    color: #8a837c;
}

/* The same thing said once for a whole chapter the translation does not reach. */
.chapter-untranslated {
    font-style: italic;
    color: #8a837c;
    text-align: center;
    margin: 3rem 0;
    line-height: 1.6;
}

.chapter-untranslated .small {
    font-style: normal;
    color: #a49c94;
}

/* The reminder toasts (devotional, reading plan) ride in the upper-left,
   dropped a little below the fixed navbar so they clear the brand, and above
   it in the stack so they are never hidden behind the bar. Translucent, with
   a soft blur so the page shows faintly through them. One shared container
   for both toasts (see includes/navbar.php) so Bootstrap's own stacking rule
   (.toast-container > :not(:last-child)) spaces them when both are showing,
   rather than two fixed-position containers landing on top of each other. */
.reminder-toasts {
    top: 4rem;
    z-index: 1035;
}
.reminder-toasts .toast {
    background-color: rgba(33, 37, 41, 0.82);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    color: #f8f9fa;
}
.reminder-toasts .toast-header {
    background-color: rgba(33, 37, 41, 0.4);
    color: #f8f9fa;
    border-bottom: 0;
}
.reminder-toasts .toast-header .btn-close {
    filter: invert(1) grayscale(100%) brightness(200%);
}

/* Pale tier-tinted card backgrounds with a thicker tinted outline — set through
   Bootstrap's own card variables so they apply cleanly. Gold, silver, bronze, and
   a plain grey for locked. */
/* ⚠ THE CARD NO LONGER FILLS ITS COLUMN. It is capped and centred instead, which
   is the only way to make it smaller while the medal inside keeps its size — the
   column width is the grid's business, not the card's. The cap is the medal plus
   its padding plus the border, so the two move together: change one and check the
   other. */
.badge-card {
    --bs-card-border-width: 1px;
    max-width: 12.25rem;
    margin-inline: auto;
}
.badge-card-t3     { --bs-card-bg: #f6ecc6; --bs-card-border-color: #e3d195; } /* pale gold */
.badge-card-t2     { --bs-card-bg: #eef2f5; --bs-card-border-color: #ccd4dd; } /* pale silver */
.badge-card-t1     { --bs-card-bg: #f8f1e8; --bs-card-border-color: #e2ceb1; } /* pale bronze */
.badge-card-locked { --bs-card-bg: #f5f5f5; --bs-card-border-color: #dcdcdc; } /* pale grey */
/* The badge's name, in the same ExtraBold serif the page headings use: a badge
   is a title, and it should be set like one rather than in the body sans. The
   markup deliberately carries no fw-* utility: those are !important and would
   beat this, leaving the name at 600 with no sign of why. */
.badge-card-name {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-weight: 800;
}
/* A locked badge's name greys out with the rest of its card, so the whole thing
   reads as not-yet-earned at a glance. This is the card's own neutral grey family
   — the outline above, not Bootstrap's blue-tinted muted — but lighter, close to
   that #dcdcdc outline while stopping short of it: the outline used literally would
   sit near 1.3:1 on this background and vanish. This lands about 2.2:1, which the
   extrabold weight carries, and it is still the name of something worth earning. */
.badge-card-locked .badge-card-name { color: #a6a6a6; }
/* THE MEDAL IS A DRAWING NOW — public/img/badges/<tier>/<slug>.svg, one of 45.
   What stood here was a coin built out of CSS: metal gradients, a painted dome
   sheen, 120 teeth of reeding in a repeating conic gradient, and a ring of stars
   each placed by rotate-out-and-counter-rotate. All of it is in the artwork, so
   all of it went — including the per-icon nudges, which existed because a Font
   Awesome glyph does not sit centred in its own box.

   ⚠ THEY ARE <img>, NEVER INLINE SVG. Every file declares the same gradient ids,
   so 45 inlined medals would all resolve url(#metal) to the first one's gradient.

   ⚠ FLUID, AND `height: auto` IS LOAD-BEARING. The width/height attributes on
   the <img> are a presentational hint; without a declared height they would win
   and the medal would render 100px tall inside a wider box. It stays square at
   any size because the art is 256x256 and the attributes agree.

   It fills the card less a small padding — Daniel, 2026-08-23 — with a ceiling
   at 256px, the size the medals were actually drawn at: past that a card would
   only be enlarging a picture. */
.badge-medal {
    display: block;
    /* ⚠ A CEILING IN PIXELS, NOT A PERCENTAGE OF THE CARD. It was 85% of the
       content box, which meant shrinking the card shrank the medal with it —
       and the card was asked to shrink while the medal stayed. 166px is the size
       it had reached by then; the card's own max-width above is derived from it. */
    width: 100%;
    max-width: 166px;
    height: auto;
    margin: 0 auto;
}
/* ⚠ A LOCKED BADGE IS THE BRONZE MEDAL, DESATURATED — there is no fourth set of
   files. Showing the medal at all is the point: the card says what earns it, and
   the picture says what it will look like. `grayscale` alone still reads as a
   real coin, so the opacity is what says "not yet"; brightness lifts it back off
   the card so it stays a shape rather than a smudge. */
.badge-medal.locked {
    filter: grayscale(1) opacity(0.45) brightness(1.08);
}

/* The busy spinner a slow form submit puts in place of its button's icon (see
   initBusySubmit in app.js). It fades in rather than appearing, which is what
   makes the delay before it safe: search times run continuously from a quarter
   of a second to well over one, so whatever delay is chosen there are queries
   that finish just after it, and a spinner that appeared at full strength for
   forty milliseconds would read as a blink of something going wrong. Fading,
   those cases never rise far enough to be seen at all, while a real wait is at
   full opacity almost at once.

   The shorthand carries Bootstrap's own rotation as well, because restating
   animation would otherwise drop it — and it reads the speed from Bootstrap's
   variable, so reduced motion still slows the turn to 1.5s as it does
   everywhere else. */
.busy-spinner {
    animation: spinner-border var(--bs-spinner-animation-speed, 0.75s) linear infinite,
               busy-fade 0.22s ease-out;
}
@keyframes busy-fade { from { opacity: 0; } }

/* ==== Articles ==== */

/* The metadata line under an article's title (and on each list entry): date,
   reading time, difficulty, tags — separated by middots the markup does not
   have to carry, so every page builds the line from bare spans. */
.article-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 0.4rem;
    font-size: 0.9rem;
}
.article-meta > span + span::before {
    content: "\00b7";
    margin-right: 0.4rem;
}

/* Difficulty: three ascending bars filled to the level, the fill colored by
   the level itself — green, orange, red as the material climbs. The bars are
   aria-hidden in the markup — the text label beside them says the same
   thing, and is what a screen reader meets, so the color never carries the
   meaning alone. */
.difficulty {
    display: inline-flex;
    align-items: flex-end;
    gap: 2px;
    margin-right: 0.3rem;
}
.difficulty-bar {
    width: 4px;
    background: #d8d4cf;
    border-radius: 1px;
}
.difficulty-bar:nth-child(1) { height: 6px; }
.difficulty-bar:nth-child(2) { height: 9px; }
.difficulty-bar:nth-child(3) { height: 12px; }
.difficulty-1 .difficulty-bar.filled { background: #198754; }
.difficulty-2 .difficulty-bar.filled { background: #fd7e14; }
.difficulty-3 .difficulty-bar.filled { background: #dc3545; }

/* Popularity (articleslog.php): ten equal bars filled to the rating in one
   accent color — unlike difficulty, a higher popularity is never a warning,
   so there is no color climb. */
.popularity-bars {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    margin-right: 0.3rem;
    vertical-align: middle;
}
.popularity-bar {
    width: 3px;
    height: 10px;
    background: #d8d4cf;
    border-radius: 1px;
}
.popularity-bar.filled {
    background: var(--bs-primary, #0d6efd);
}

/* The {{…}} verse embed: the reason the markup exists, so it carries real
   visual weight — the reading serif at a size up, on a warm paper tint, with
   its reference underneath — a proper quotation, not a small italic citation.
   Upright on purpose: the verse is the primary text speaking, not an aside. */
.article-verse {
    margin: 1.5rem 0;
}
.article-verse blockquote {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-size: 1.15rem;
    line-height: 1.65;
    color: #2c2926;
    background: #faf7f0;
    border-left: 3px solid #c9a24a;
    border-radius: 0 var(--bs-border-radius) var(--bs-border-radius) 0;
    padding: 1rem 1.25rem;
    margin: 0;
}
/* Inline verse numbers of a multi-verse quotation: present but unassertive. */
.article-verse blockquote b {
    font-size: 0.8em;
    font-weight: 600;
    color: #a3873e;
}
.article-verse figcaption {
    font-size: 0.9rem;
    margin-top: 0.35rem;
    text-align: right;
}
.article-verse figcaption::before {
    content: "\2014\00a0"; /* — */
    color: #6c757d;
}

/* The author's own > quotations — someone else's words, so they take the
   quiet devotional-verse look rather than the embed's weight. */
.article-quote {
    border-left: 3px solid #d8d4cf;
    padding: 0.15rem 0 0.15rem 1rem;
    margin: 0 0 1rem;
    color: #555;
}
.article-quote p { margin-bottom: 0.5rem; }
.article-quote p:last-child { margin-bottom: 0; }

/* A displayed tag is a Bootstrap pill in its catalog color (articleTagPill
   sets background and ink inline). Informative, not a control: it is a span,
   and it drops the badge's bold for the normal weight — a label, not a
   button shouting. */
.article-tag {
    font-weight: 400;
}

/* The bookmark beside an article's title — the whole save/remove control, with
   no button chrome around it: the icon IS the button, and its tooltip carries
   the words a label would have. Two glyphs live inside it and one shows at a
   time; :hover and :focus-visible swap them, so the keyboard sees the same
   change the pointer does. */
.article-bookmark {
    padding: 0;
    border: 0;
    background: none;
    line-height: 1;
    /* Lifted clear of the title's line — the row is align-items-start, and this
       carries it up into the space above, level with the facts strip's foot. */
    margin-top: -1rem;
}
.article-bookmark .article-bookmark-over { display: none; }
.article-bookmark:hover .article-bookmark-idle,
.article-bookmark:focus-visible .article-bookmark-idle { display: none; }
.article-bookmark:hover .article-bookmark-over,
.article-bookmark:focus-visible .article-bookmark-over { display: inline-block; }

/* An article's rendered body (article.php and the editor's preview): its
   running text a touch above the interface's 1rem, matching the lead — this
   is reading matter. Set on the container so paragraphs, lists and quotes
   follow together; the embeds, headings and captions keep their own explicit
   sizes. */
.article-body {
    font-size: 1.05rem;
}

/* A map link in a body (@@Jebus;conquest@@): the trailing crosshairs is there
   because a place name inside a sentence would otherwise look exactly like a
   verse link, and the two lead somewhere quite different. It keeps the
   ordinary link colour — a second accent in running prose would be one more
   thing than the page needs — and carries no underline at all, hover
   included: a rule under the name also runs under the joining space and the
   mark, and that read as ugly (Daniel, 2026-07-29). The colour and the
   crosshairs are the affordance; hover still shifts the link colour. The
   icon is sized in em so it holds its proportion against whatever font size
   the reader has chosen. */
.article-map {
    text-decoration: none;
}
.article-map i {
    font-size: 0.85em;
}

/* The reading-facts strip atop the article page, and the editor's own Draft
   marker beside its title (.badge-facts-size): pills a size up from the card
   corners' — these pages speak for themselves, not from a shelf label. */
.article-facts .badge,
.badge-facts-size {
    font-size: 0.85rem;
    padding: 0.35em 0.75em;
}

/* The tick-box rows of a tags menu (articles.php card footers and the article
   editor). They are not .dropdown-item, so they miss Bootstrap's
   white-space: nowrap — and an absolutely positioned menu shrinks to fit its
   containing block, which here is only the narrow "Tags" toggle. The menu
   could therefore grow no wider than the longest WORD, and a two-word tag name
   broke onto a second line (user, 2026-07-26). nowrap makes the whole label
   the menu's minimum width instead, so it widens to the longest tag.

   The flex row replaces .form-check's floated input and negative text indent:
   box, dot and words sit on one line, and nothing hangs into the padding. */
.dropdown-menu[data-tag-menu] .form-check {
    display: flex;
    align-items: center;
    padding-left: 0;
    min-height: 0;
}
.dropdown-menu[data-tag-menu] .form-check-input {
    float: none;
    flex: 0 0 auto;
    margin: 0 0.5rem 0 0;
}
.dropdown-menu[data-tag-menu] .form-check-label {
    white-space: nowrap;
    cursor: pointer;
}

/* A tag's color, worn as a small dot beside its name in the CONTROLS — the
   filter buttons, the classification dropdown, tagadmin's rows. The pills
   above are the display; the dot stays the compact form-control marker.
   Set inline from the catalog (tagadmin.php). */
.tag-dot {
    display: inline-block;
    width: 0.55em;
    height: 0.55em;
    border-radius: 50%;
    margin-right: 0.35em;
    vertical-align: middle;
    /* Optically centered against lowercase text; middle alone sits a hair low. */
    margin-top: -0.15em;
}

/* tagadmin's color picker: one circle wearing the tag's current color;
   clicking it drops a menu of the catalog colors (TAG_COLORS in tagadmin.php)
   three across, however many rows that takes. Picking a swatch repaints the
   circle and writes the form's hidden color field — registered, but like the
   name beside it, nothing is stored until Save. A tag colored before the palette existed keeps its
   color until a swatch is deliberately picked; the new-tag circle starts as
   a dashed hole until the first pick. The current swatch wears a ring of
   its own color, the menu's white showing through as the gap. */
.tag-color-toggle {
    flex: 0 0 auto;
    width: 1.6rem;
    height: 1.6rem;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: var(--swatch);
}
.tag-color-toggle.tag-color-unset {
    background: transparent;
    border: 2px dashed #b0a99e;
}
.tag-color-menu {
    min-width: 0;
    padding: 0.65rem;
}
.tag-color-menu.show {
    display: grid;
    grid-template-columns: repeat(3, auto);
    gap: 0.55rem;
}
.tag-swatch {
    width: 1.35rem;
    height: 1.35rem;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: var(--swatch);
}
.tag-swatch:hover {
    box-shadow: 0 0 0 2px #fff, 0 0 0 3px #b0a99e;
}
.tag-swatch[aria-pressed="true"] {
    box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--swatch);
}
.tag-color-toggle:focus-visible,
.tag-swatch:focus-visible {
    outline: 2px solid #212529;
    outline-offset: 2px;
}

/* An article card that unfolds its gear footer on click (producers only —
   readers' cards never carry the attribute). The cursor is one affordance;
   the chevron at the meta row's left edge is the visible one, and it turns
   over while the panel is open. */
article.card[data-gear] {
    cursor: pointer;
}
.gear-chevron {
    transition: transform 0.2s ease;
    align-self: flex-end;
    /* Sunk toward the card's lower edge, into the body's bottom padding —
       the arrow points at the footer it reveals, so it belongs against it. */
    transform: translateY(0.5rem);
}
article.card.gear-open .gear-chevron {
    transform: translateY(0.5rem) rotate(180deg);
}

/* The card's two-line head on the Articles shelf: the author stands over the
   title as a small uppercase Prompt eyebrow — the face .prose-subheading
   wears — and the title itself steps up into the reading serif. Roboto Serif
   ships in 400/600/800 here, so "bold" is the 800 cut: the same weight every
   other headline on the site carries. */
.article-card-author {
    font-family: 'Prompt', sans-serif;
    font-weight: 600;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.1rem;
}
.article-card-title {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-weight: 800;
    font-size: 1.35rem;
    line-height: 1.25;
}
/* The title is a link but wears the ink of the page, not link blue — the
   whole card already reads as the way in, and a blue headline would make the
   shelf look like a list of URLs. */
.article-card-title a {
    color: inherit;
}

/* The editor's floating Shorthands panel: a reference held open beside the
   writing, so it shades nothing and blocks nothing — only the card itself
   takes the pointer, and the page underneath stays live. Placed by
   initShorthandsPanel() in app.js, which writes left/top as it is dragged.
   Below Bootstrap's modal (1055) but above everything ordinary. */
.shorthands-panel {
    position: fixed;
    z-index: 1050;
    width: min(34rem, calc(100vw - 2rem));
    max-height: min(32rem, calc(100vh - 4rem));
    display: flex;
    box-shadow: 0 0.75rem 2rem rgba(0, 0, 0, 0.25);
}
/* The header is the grip. grab/grabbing rather than move: this is a thing
   being picked up, not a canvas being panned. */
.shorthands-panel .card-header {
    cursor: grab;
    user-select: none;
    -webkit-user-select: none;
}
.shorthands-panel.dragging .card-header {
    cursor: grabbing;
}
/* The body scrolls, not the panel, so the header stays put while the table
   runs under it. width:auto because the body wears .prose for its typography,
   and .prose brings its fixed 45rem column with it — wider than the card, so
   the body bulged past the header and the top read as "narrower". The type
   is wanted here; the column is not. Small type throughout — a reference
   panel, not reading matter. */
.shorthands-panel .card-body {
    overflow-y: auto;
    width: auto;
    font-size: 0.85rem;
}
.shorthands-panel .table {
    font-size: 0.85rem;
    margin-bottom: 0;
}

/* The editor's body field: markup is written in the mono face so the markers
   line up and read as markers, not as prose gone wrong. */
.article-body-input {
    font-family: 'Roboto Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.9rem;
}

/* The editor's reference picker (initRefPicker in app.js): a small menu that
   follows the caret through an unclosed [[…, {{… or @@…. Named things — books,
   places, map plates — come as a plain list; chapter and verse numbers wrap
   into a grid, which is the shape a run of up to 176 small numbers wants. That
   grid is also why the picker binds the whole arrow cluster rather than just
   ↑ ↓: see the keydown handler for what it costs to bind half of it. */
.ref-picker {
    position: absolute;
    z-index: 1050;
    min-width: 9rem;
    max-width: 17rem;
    max-height: 13rem;
    overflow-y: auto;
    background: #fff;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
    padding: 0.25rem;
}
.ref-picker-item {
    display: block;
    width: 100%;
    border: 0;
    background: none;
    text-align: left;
    padding: 0.2rem 0.5rem;
    border-radius: 0.25rem;
    font-size: 0.95rem;
}
.ref-picker-item:hover { background: var(--bs-light); }
.ref-picker-item.active {
    background: var(--bs-primary);
    color: #fff;
}
.ref-picker-nums {
    display: flex;
    flex-wrap: wrap;
    width: 17rem;
}
.ref-picker-nums .ref-picker-item {
    flex: 0 0 auto;
    width: 3rem;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* Maps ---------------------------------------------------------------------
   A map is a plate: it carries its own paper and ink rather than taking the
   reader's chosen mood, the way a folded map bound into a book does. Holding
   the tokens here on .map-plate rather than on :root is what does that — the
   background can change around it and the cartography stays legible. They are
   still custom properties, so giving a mood its own colorway later is a block
   of overrides here and no change to the build script.

   These same seven values are the fallbacks compiled into the SVG by the map
   build script, which is not in this tree — the toolchain lives in Daniel's
   backups (map-maker), so a map still draws correctly opened on its own but
   these seven and its copy of them can only be reconciled when it is back. */
.map-stage,
.map-plate {
    --map-sea:       #D9E4E8;
    --map-land:      #F0DDB0;
    --map-lake:      #D3E0E5;
    --map-ink:       #3B3227;
    --map-ink-soft:  #6E6355;
    --map-accent:    #B0503A;
    --map-water-ink: #5B7480;
}

/* The map page is a viewer, not a document: exactly one screen, no scrolling.
   Held here rather than on .map-stage because the drawing is sized from the
   window, and a scrollbar appearing would narrow the window and resize it. */
body.map-fixed { overflow: hidden; }

/* The stage fills the window beneath the fixed bar and gives the drawing a
   white margin to sit in. --nav-h is the measured bar height app.js publishes;
   the fallback is the height the bar has before it reports one. 100dvh rather
   than 100vh so the mobile address bar sliding away does not leave the map
   short. A fixed height, not a minimum — a minimum lets the SVG's own aspect
   ratio push the stage past the bottom of the window, which is what scrolls.
   The vh line above it is not decoration: dvh is Chrome 108 / Firefox 101 /
   Safari 15.4, and an engine that does not know the unit drops the whole
   declaration rather than the unit — which leaves height:auto, exactly the
   case the paragraph above says must not happen, with body.map-fixed's
   overflow:hidden then clipping whatever runs past the fold. */
.map-stage {
    background: #fff;
    height: 100vh;
    height: 100dvh;
    overflow: hidden;
    /* The top needs no --safe-t of its own: --nav-h is measured from the real
       bar by app.js, and the bar grew by the notch, so the clearance follows.
       The other three are added here because the body cannot carry them on this
       page (see body.map-fixed above). */
    padding: calc(var(--nav-h, 3.875rem) + 1.25rem)
             calc(1.5rem + var(--safe-r))
             calc(1.25rem + var(--safe-b))
             calc(1.5rem + var(--safe-l));
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
}
.map-stage-head {
    flex: 0 0 auto;
    display: flex;
    /* flex-start, not baseline: the title is 1.5rem over a second line, and
       baseline-aligning a small button to it drops the button half way down
       the block instead of holding the top right corner. */
    align-items: flex-start;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.5rem 1.5rem;
}

/* The plate takes whatever height the head and caption leave. min-height:0 is
   what allows a flex child to be shorter than its content — without it the SVG
   refuses to shrink and pushes the caption off the bottom of the window. */
.map-plate {
    position: relative;   /* the controls are positioned against this */
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    margin: 0;
    /* The same sea the SVG below paints, and the reason is the switch from one
       plate to another. This figure gets its size from flex, so it is laid out
       the instant the new document is, but the drawing inside it is not: the
       browser spends around half a second rasterising the plate's vector paths
       (measured on roman-empire — and it is paint, not parsing; the whole 690 KB
       document parses in 5 ms). Left transparent, the white .map-stage showed
       through a plate-sized rectangle for that whole interval, so changing map
       flashed a white sheet where the old one had been. Painting the water
       colour here means the gap is already the colour the drawing is about to
       be. Nothing about the settled page changes — the SVG covers this figure
       exactly, and carries the same colour as its own backstop below. */
    background: var(--map-sea);
}
/* preserveAspectRatio on the SVG letterboxes the drawing inside whatever box
   it is given, so this scales the map to fit without ever cropping it.
   flex-basis 0 rather than auto is the load-bearing part: with auto, the flex
   algorithm starts from the SVG's intrinsic height (width ÷ its aspect ratio)
   and only shrinks from there, which on a wide window is taller than the
   space available. Starting from zero and growing into the space left over
   makes the window decide the size instead of the drawing. */
.map-plate svg {
    flex: 1 1 0;
    min-height: 0;
    width: 100%;
    height: 100%;
    display: block;
    /* Last-resort backstop. The drawing paints its water well past the frame,
       so on any ordinary window the bands are real sea; this only shows on a
       viewport so wide or so tall that even the bleed runs out, and it keeps
       that case looking like more ocean rather than a white gap. */
    background: var(--map-sea);
}
/* A settlement the people & places layer can answer for. Marked by app.js, so a
   plate with no linked places gains nothing at all.

   ⚠ THE RESTING PLATE IS UNCHANGED, and that is the point. The plates are drawn
   work and their labels are placed by hand; sprinkling a permanent badge over
   thirty-odd of them would spoil the picture to advertise a feature. The signal
   arrives on HOVER instead — the pointer, the dot's violet halo and the label
   taking the same violet — which is the ordinary web affordance for "this does
   something", in the colour this app already uses for a reference work
   speaking ([[bible-gold-system]]). A reader who clicks one learns the whole
   plate is live.

   ⚠ NOT GOLD. Gold means HERE, the thing selected, and the ring the click draws
   is what says that. This says "there is more behind this", which is a
   different sentence. */
.map-place--linked { cursor: pointer; }
.map-place--linked .map-dot {
    transition: stroke 0.12s ease, stroke-width 0.12s ease;
}
.map-place--linked:hover .map-dot {
    stroke: #7a5ea8;
    stroke-width: 3;
    paint-order: stroke;   /* the halo sits behind the dot, never over it */
}
.map-place--linked:hover .map-label { fill: #6a4f98; }
/* A finger has no hover, so the affordance cannot be the whole story on a
   phone — there the tap is the discovery, and it works with no marking at all.
   The transition is dropped rather than the styling: an effect that only ever
   fires on the way OUT of a tap reads as a flicker. */
@media (hover: none) {
    .map-place--linked .map-dot { transition: none; }
}

/* ---- The card a label opens (initMapFinder → showCard) ---------------------
   What the Bible does at the place the reader just picked, under the plate that
   says where it is.

   ⚠ IT IS VIOLET, and that is the app's own vocabulary rather than a new
   colour: this is a REFERENCE WORK QUOTED — the same act as the lexicon article
   behind a Strong's number and the people & places band in search, which is
   where a reader has already met this hue ([[bible-gold-system]]). The plate
   above it is ours and wears none of it.

   ⚠ flex: 0 0 auto AND A CAP. This page is exactly one screen and never
   scrolls, so every pixel this takes comes off the drawing. Three short lines is
   the whole budget; anything longer belongs on the place's own page, which is
   what the card links to. */
.map-card {
    flex: 0 0 auto;
    /* ⚠ A BACKSTOP, NOT A TARGET. Measured at 375px the real cards run 108–130px
       — the tall ones being those with both an event count and an excavation,
       Jericho and Ai — so a cap at 8.5rem left six pixels of headroom and would
       have started clipping the first time a name ran long. 9.5rem keeps every
       card whole and still stops a pathological one eating the plate. */
    max-height: 9.5rem;
    overflow-y: auto;
    border-left: 3px solid #7a5ea8;
    background: rgba(122, 94, 168, 0.05);
    border-radius: 0 0.25rem 0.25rem 0;
    padding: 0.5rem 0.75rem;
}
.map-card-name {
    font-weight: 600;
    margin: 0 0 0.1rem;
}
/* Same measured reason as the search band: Bootstrap's #0d6efd is 4.63:1 on
   white and the violet wash drops it to 4.22:1, which fails AA. #0a58ca is
   Bootstrap's own hover blue and clears at 5.9:1 on this exact ground. */
.map-card a { color: #0a58ca; }
.map-card-also { font-weight: 400; }
.map-card-line {
    font-size: 0.95rem;   /* the band-answer size — see the note by .search-who-line */
    margin: 0;
}
.map-card-dig { color: var(--bs-secondary-color, #6c757d); }
/* ⚠ MEASURED, NOT GUESSED: with the book list in, the card ran to 136px at
   375px wide, hit its cap and scrolled inside itself — a reader on a phone
   scrolling a small box to finish one sentence, while the plate above lost a
   quarter of its height. The books go; the type, the verse count and the spade
   stay, and they are what the card is for. */
@media (max-width: 575.98px) {
    .map-card-books { display: none; }
}
/* Smaller than the answer above it, by the same rule as the search bands' own
   credit: a licence condition discharged in the reader's sight, which must be
   present and legible without out-shouting the thing it acknowledges. */
.map-card-credit {
    font-size: 0.8rem;
    color: var(--bs-secondary-color, #6c757d);
    margin: 0.3rem 0 0;
}

.map-plate-title {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-weight: 800;
    font-size: 1.5rem;
    line-height: 1.2;
    margin: 0 0 0.15rem;
    color: var(--map-ink);
}
/* The passage and its date are apparatus rather than cartography, so they are
   set in the interface face while the map's own title stays in the reading
   serif — the same division the rest of the site makes. No opacity on the
   row: the date is meant to read as black, and a dimmed black is grey. */
.map-plate-sub {
    font-family: 'Prompt', sans-serif;
    font-size: 0.9rem;
    letter-spacing: 0.01em;
    margin: 0;
}
.map-plate-sub a { color: var(--bs-primary, #0d6efd); }
.map-plate-date { color: #000; }
/* Short windows — a laptop in landscape, a phone turned sideways. The margin
   is the first thing to give, so the drawing keeps as much height as it can.
   The notes used to be hidden here as well; they are behind a button now, so
   there is nothing left on the plate for a short window to reclaim. */
@media (max-height: 620px) {
    .map-stage {
        padding: calc(var(--nav-h, 3.875rem) + 0.6rem) 0.9rem 0.6rem;
        gap: 0.5rem;
    }
}

/* The two controls at the top right, opposite the title: the notes button and
   the switcher. Held in one flex row so they stay on the same line and wrap
   together rather than one of them dropping below the other. */
.map-controls {
    flex: 0 0 auto;
    display: flex;
    align-items: flex-start;
    gap: 0.4rem;
}
.map-notes-btn {
    font-family: 'Prompt', sans-serif;
    font-size: 0.85rem;
    color: var(--map-ink);
}

/* The notes panel. Narrower than the Navigator's — this is a paragraph to
   read, not a list to scan, and a long measure is harder to read not easier.
   The interface face, because notes are apparatus and not cartography: the
   same division the plate's own title and subtitle already make. */
.map-notes {
    --bs-offcanvas-width: 26rem;
    font-family: 'Prompt', sans-serif;
}
.map-notes .offcanvas-title {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-weight: 800;
    font-size: 1.15rem;
    margin: 0;
}
.map-notes .offcanvas-body {
    font-size: 0.9rem;
    line-height: 1.6;
}
.map-notes-sub {
    font-size: 0.85rem;
    letter-spacing: 0.01em;
    opacity: 0.7;
    margin-bottom: 0.9rem;
}
.map-notes-more {
    margin-top: 1.1rem;
    font-size: 0.85rem;
}

/* The map switcher. Sits at the top right of the stage, opposite the title. */
.map-switch { flex: 0 0 auto; }
.map-switch .dropdown-toggle {
    font-family: 'Prompt', sans-serif;
    font-size: 0.85rem;
    color: var(--map-ink);
}
.map-switch .dropdown-menu { font-family: 'Prompt', sans-serif; font-size: 0.85rem; }

/* ---- The finder ---------------------------------------------------------
   Names a place on the plate in front of you. The menu is sized to the list
   rather than the button, and its own body scrolls, so a plate with 56 names
   cannot put a scrollbar on a page whose whole rule is one screen. */
.map-find-btn {
    font-family: 'Prompt', sans-serif;
    font-size: 0.85rem;
    color: var(--map-ink);
}
.map-find-menu {
    width: 17rem;
    padding: 0.5rem;
    font-family: 'Prompt', sans-serif;
}
.map-find-input { font-size: 0.85rem; }
.map-find-results {
    margin: 0.4rem 0 0;
    max-height: 15rem;
    overflow-y: auto;
}
.map-find-results:empty { display: none; }
.map-find-results li > button {
    display: block;
    width: 100%;
    text-align: left;
    border: 0;
    background: none;
    padding: 0.3rem 0.4rem;
    border-radius: 0.25rem;
    font-size: 0.85rem;
    color: var(--map-ink);
}
.map-find-results li > button:hover,
.map-find-results li > button:focus,
.map-find-results li > button.is-active {
    background: rgba(0, 0, 0, 0.06);
    outline: none;
}
/* The era and the kind are apparatus: they tell a reader which Jerusalem this
   line means without competing with the name itself. */
.map-find-kind {
    float: right;
    font-size: 0.7rem;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    opacity: 0.5;
    padding-top: 0.15rem;
}
.map-find-none {
    font-size: 0.85rem;
    opacity: 0.7;
    padding: 0.4rem;
}

/* ---- The spotlight ------------------------------------------------------
   Answering "where is it" by darkening everything else for a moment. The dim
   PLAYS AND LEAVES: a reader should never have to find the control that undoes
   what a search did to their map. What stays behind is the ring, which is the
   answer, and a tap anywhere clears that too.

   All of it is injected into the plate's own SVG, so it sits in map units and
   needs no agreement with the page about where the drawing ended up. */
.map-spot-dim {
    fill: #1b1712;
    opacity: 0;
    pointer-events: none;
}
.map-spot.is-playing .map-spot-dim {
    animation: map-spot-dim 2.1s ease-out forwards;
}
@keyframes map-spot-dim {
    0%   { opacity: 0; }
    12%  { opacity: 0.62; }
    62%  { opacity: 0.62; }
    100% { opacity: 0; }
}
.map-spot-ring {
    fill: none;
    stroke: var(--map-accent, #B0503A);
    stroke-width: 2.5;
    pointer-events: none;
}
/* The ping: one ring expanding out of the mark and fading, so the eye is led to
   the spot rather than left to notice a static circle. transform-box keeps the
   origin on the circle itself — without it the scale runs from the SVG's origin
   and the ring flies off the plate. */
.map-spot-ping {
    fill: none;
    stroke: var(--map-accent, #B0503A);
    stroke-width: 2;
    transform-box: fill-box;
    transform-origin: center;
    pointer-events: none;
}
.map-spot.is-playing .map-spot-ping {
    animation: map-spot-ping 1.6s ease-out 2;
}
@keyframes map-spot-ping {
    0%   { transform: scale(0.4); opacity: 0.9; }
    100% { transform: scale(2.2); opacity: 0; }
}
/* A reader who has asked not to be moved gets the answer without the theatre:
   the dim never plays, and the ring is simply there. */
@media (prefers-reduced-motion: reduce) {
    .map-spot.is-playing .map-spot-dim,
    .map-spot.is-playing .map-spot-ping { animation: none; }
    .map-spot-ping { display: none; }
}

/* Everything else this stylesheet moves, answered in one place rather than
   beside each rule — a cross-cutting promise is easier to keep honest when it
   can be read in one screen.
   Bootstrap does NOT cover these for us. Version 4 shipped a blanket
   *,*::before,*::after reset; version 5 dropped it in favour of a per-component
   mixin, so its own dropdowns and modals are handled and nothing of ours is.
   Only real movement is listed. The background-color fades on .verse-row and
   .collapse-heading stay: a color change in place is not motion, and removing
   it would cost the feedback without answering the request.
   The landing hero's exit needs no entry — welcomeHeroExit() in app.js tests
   the same query and declines to play at all, so no clone is ever built. */
@media (prefers-reduced-motion: reduce) {
    /* The reader's bar and the three things that travel with it. Reduced
       motion means it arrives and leaves instantly, NOT that it stops
       hiding — the bar retreating is the feature, the glide is the flourish. */
    body:has(.nav-peek) .navbar,
    body:has(.nav-peek) .verses.reader-flow,
    body:has(.nav-peek) #scroll-progress-bar,
    body:has(.nav-peek) .reader-ref-flow {
        transition: none;
    }

    /* The bottom sheets stop sliding up from the bottom edge and are simply
       there. Same reasoning: the panel still appears, it just does not travel. */
    #strongs-popup,
    #refs-picker { animation: none; }

    .reader-pager,
    .collapse-caret,
    .fav-star,
    .gear-chevron {
        transition: none;
    }
}

/* ---- The small-screen viewer -------------------------------------------
   A 16:9 plate letterboxed into a portrait phone is about a fifth of the size
   it was drawn at, and none of it legible. On narrow screens app.js takes the
   drawing over: it fills the frame at the scale the plate was meant to be read
   at, cropped left and right, and the reader drags and pinches to move about.
   Everything here hangs off .map-live, which the script adds only while it is
   actually driving; a phone with no scripting keeps the letterbox, which is
   whole and readable-ish rather than a cropped map nobody can move. */
.map-plate.map-live svg {
    /* The browser's own pan and pinch must be off the table, or the first
       two-finger gesture zooms the page and the viewer never sees it. This is
       safe to take away here only because the page does not scroll at all. */
    touch-action: none;
    cursor: grab;
    user-select: none;
    -webkit-user-select: none;
}
.map-plate.map-live.map-dragging svg { cursor: grabbing; }

/* The one piece of furniture the viewer adds: back to the whole plate, and out
   to fill the screen again. Bottom right, over the drawing's own margin, small
   enough to sit on a map without being part of it. */
.map-fit-btn {
    position: absolute;
    right: 0.5rem;
    bottom: 0.5rem;
    z-index: 2;
    width: 2.25rem;
    height: 2.25rem;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    color: var(--map-ink);
    background: rgba(255, 255, 255, 0.88);
    /* The plate is busy underneath; a flat white square would read as a hole
       punched in the map, and a shadow keeps it floating above instead. */
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
}


/* ---- The command palette (Ctrl/Cmd+K) -----------------------------------
   A door onto pages that already exist, opened from the keyboard. It is not a
   Bootstrap modal on purpose — see includes/palette.php — so the scrim, the
   stacking and the position are all its own, and the body is never scroll-
   locked underneath it. Nothing here runs on a page where it is not opened:
   the element is `hidden` until app.js says otherwise.

   z-index sits above the fixed-top navbar (1030) and above Bootstrap's own
   modal backdrop (1050), because the palette may be opened over an open
   offcanvas and must never arrive underneath one. */
.palette {
    position: fixed;
    inset: 0;
    z-index: 1080;
    display: flex;
    justify-content: center;
    /* Not centered vertically. A panel that grows downward from a fixed line
       keeps the field still while the list under it lengthens and shortens,
       where a centered one would slide the field up and down under the
       reader's hands as they type. */
    align-items: flex-start;
    /* Fixed and inset:0, so it covers the notch too and takes its own insets. */
    padding: calc(12vh + var(--safe-t)) calc(1rem + var(--safe-r))
             calc(1rem + var(--safe-b)) calc(1rem + var(--safe-l));
}
.palette[hidden] { display: none; }

.palette-scrim {
    position: absolute;
    inset: 0;
    background: rgba(20, 18, 15, 0.45);
}

.palette-panel {
    position: relative;
    width: 100%;
    max-width: 34rem;
    background: #faf7f0;
    color: #2c2926;
    border-radius: 0.5rem;
    /* Deep and soft rather than a border: the panel is meant to read as a card
       lifted off the page, which is the same language as the plate's own
       furniture on the map page. */
    box-shadow: 0 1.5rem 3rem rgba(0, 0, 0, 0.35);
    font-family: 'Prompt', sans-serif;
    overflow: hidden;
}

/* ---- The field ---- */
.palette-field {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    padding: 0.85rem 1rem;
    border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}
/* Black, inheriting the panel's own text — and it was briefly gold on
   2026-08-15 by my misreading of "use that gold shade on the command palette",
   which meant the SELECTOR, not this glyph. Daniel: "actually i liked it
   black." Leave it alone. */
.palette-field-icon {
    font-size: 0.95rem;
    opacity: 0.45;
}
.palette-input {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    background: none;
    padding: 0;
    font-size: 1.05rem;
    font-weight: 300;
    color: inherit;
}
.palette-input:focus { outline: none; }
.palette-input::placeholder { color: inherit; opacity: 0.4; }

/* ---- The results ---- */
.palette-results {
    list-style: none;
    margin: 0;
    padding: 0.35rem;
    max-height: min(24rem, 50vh);
    overflow-y: auto;
}
.palette-results:empty { display: none; }

.palette-hit {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    padding: 0.45rem 0.6rem;
    border-radius: 0.3rem;
    cursor: pointer;
}
/* One highlight, driven from the script — there is deliberately no :hover rule
   to compete with it. The pointer moves the highlight instead (see the
   mousemove handler), so the keyboard and the mouse can never disagree about
   which line Enter would open. */
/* THE HIGHLIGHT WAS ALREADY GOLD — but not THE gold (corrected 2026-08-15,
   Daniel asked whether the app's shade would look better here). It was
   rgba(201, 162, 74, .22): a hand-written third value whose blue channel is 74
   where --biblion-gold's is 39, so it sat very slightly green of everything
   else that calls itself gold in this app.

   Only the hue is corrected. The 0.22 stays because it was tuned against this
   panel's own background, and --biblion-gold-tint's 0.16 is a wash meant for
   the verse band, not a selection highlight. Written out as rgba rather than
   derived from the variable because alpha-adjusting a custom property needs
   color-mix(), which is below the browser floor. If --biblion-gold ever moves,
   this line moves with it. */
.palette-hit.is-active {
    background: rgba(201, 162, 39, 0.22);
}

/* What kind of thing the line is — Read, Map, Go, Search. Apparatus: it tells a
   reader why John and the Gospels plate are both in the list without competing
   with either name. Fixed width so the labels beside them start on one line. */
.palette-kind {
    flex: 0 0 3.4rem;
    font-size: 0.65rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    opacity: 0.45;
    padding-top: 0.15rem;
}
.palette-label {
    flex: 0 1 auto;
    font-size: 0.95rem;
    font-weight: 300;
}
/* A plate's subtitle, trailing its title. Truncated rather than wrapped: the
   list reads as one line per destination, and a two-line row among twelve
   single ones is read as a heading. */
.palette-note {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 0.78rem;
    opacity: 0.5;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ---- The footer ---- */
/* Where the key grammar is written down for the reader, and the only place it
   is. Hidden on a short window, where the results are worth more than the
   lesson. */
.palette-foot {
    display: flex;
    flex-wrap: wrap;
    gap: 0.9rem;
    padding: 0.5rem 1rem;
    border-top: 1px solid rgba(0, 0, 0, 0.08);
    font-size: 0.7rem;
    opacity: 0.55;
}
.palette-foot kbd {
    font-family: inherit;
    font-size: 0.7rem;
    padding: 0.05rem 0.3rem;
    margin-right: 0.2rem;
    color: inherit;
    background: rgba(0, 0, 0, 0.07);
    border-radius: 0.2rem;
}
@media (max-height: 560px) {
    .palette { padding-top: 6vh; }
    .palette-foot { display: none; }
}

/* ---- The chapter player --------------------------------------------------
   Raised by "Listen to this chapter" in the Options menu, and absent until
   then. A bar along the foot of the viewport rather than anything in the
   reading column: a media control is chrome, not text, and the one place it can
   sit without pushing the chapter around is outside the flow entirely.

   Dark, like the navbar, for the same reason — it reads as the app's furniture
   rather than as part of the page being read. */
.audio-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1030;
    background: #212529;
    color: #dee2e6;
    box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.25);
}

/* Stated even though `hidden` normally implies it: the bar is a flex container
   by nature and any rule that sets display on it would silently defeat the
   attribute. */
.audio-bar[hidden] {
    display: none;
}

.audio-bar-inner {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    max-width: 900px;
    margin: 0 auto;
    padding: 0.5rem 1rem;
}

.audio-btn {
    padding: 0.25rem 0.5rem;
    color: inherit;
    line-height: 1;
    background: none;
    border: 0;
}

.audio-btn:hover,
.audio-btn:focus-visible {
    color: #fff;
}

/* Tabular figures, and a floor wide enough for the longest reading this can
   show. Without both, the text either side of the scrubber shifts by a pixel or
   two every second and the whole bar looks unsteady. */
.audio-time {
    min-width: 3.2em;
    font-size: 0.8rem;
    font-variant-numeric: tabular-nums;
    text-align: center;
    opacity: 0.85;
}

/* A real range input, so dragging, focus and the arrow keys all work without
   any code of ours. accent-color paints the filled track and the thumb in the
   brand gold and is supported across the whole compatibility floor. */
.audio-seek {
    flex: 1 1 auto;
    min-width: 0;
    accent-color: var(--biblion-gold);
}

/* THE VERSE BEING SPOKEN. Deliberately not the selection color: a reader can
   have a verse selected while another is being read, and the two marks have to
   be tellable apart. Gold rather than blue ties it to the brand mark and to the
   scrubber, and the alpha is low enough to sit under the text rather than
   compete with it.

   No scrolling comes with this (Daniel, 2026-08-02): the page never moves on
   its own, so a reader can read ahead or back while it plays. */
.verse-row.verse-speaking {
    background-color: var(--biblion-gold-tint);
    border-radius: var(--bs-border-radius, .375rem);
}

/* The reader's view is inline text, so the mark follows the words across line
   breaks instead of drawing a box that fragments line by line -- the same
   reasoning as .verse-flow.verse-selected above. */
.verse-flow.verse-speaking {
    background-color: var(--biblion-gold-tint);
    box-shadow: none;
    border-radius: var(--bs-border-radius, .375rem);
}

/* Room for the bar, so the last verse of a chapter is not sitting under it —
   and the room belongs to the PAPER, not to the body.

   Padding on the body sits OUTSIDE the sheet, so it pushed the sheet's bottom
   edge up and left a strip of the mood background showing between the paper and
   the bar (reported 2026-08-03). Giving the space to the sheet instead clears
   the text exactly as before AND lets the paper run on underneath the bar, so
   there is nothing between them to see. The fore-edges already did this: they
   are fixed at bottom: 0 and pass behind the bar, which is why only the paper
   fell short.

   !important because Bootstrap's .p-4 carries it; specificity alone would not
   win. 5rem = p-4's own 1.5rem plus the bar's 3.5rem. */
body.audio-open .container > .bg-white {
    padding-bottom: 5rem !important;
}

/* The bar's failure state. The controls go rather than being disabled: a greyed
   scrubber invites a reader to keep trying it, and there is nothing behind it to
   try. */
/* Underlined on purpose, unlike the app's other links. It only ever appears
   when something has already failed, and a reader who has just been told the
   audio will not play needs to see that there is somewhere to go about it. */
.audio-msg {
    font-size: 0.8rem;
    color: #f1aeb5;
    text-decoration: underline;
}

.audio-msg:hover,
.audio-msg:focus-visible {
    color: #fff;
}

/* EVERYTHING BUT THE MESSAGE AND THE WAY OUT. The controls go rather than being
   disabled: a greyed scrubber invites a reader to keep trying it, and there is
   nothing behind it to try. Widened 2026-08-02 from just the play/seek/times to
   every button but Close — stepping verses and chapters was still on offer over
   audio that could not load, which is what the failure looked like in practice. */
.audio-bar.audio-failed .audio-btn:not(.audio-close),
.audio-bar.audio-failed .audio-seek,
.audio-bar.audio-failed .audio-time {
    display: none;
}

/* THE FLOATING PILLS HAVE TO GET OUT OF THE BAR'S WAY. Both sit at
   bottom: 1.25rem with z-index 1030 — the corner the player now occupies — so
   without this the bar lands straight across them. Same treatment the pinned
   chapter pager already gets above, for the same reason.

   The combined case is stated explicitly because a transform does not
   accumulate across rules: with both classes on the body the later rule simply
   wins, so "lifted for the pager" and "lifted for the bar" would come out as
   one lift and the pill would still be under the other thing. */
body.audio-open .about-contents-btn,
body.audio-open .return-trail-btn {
    transform: translateY(-3.5rem);
}

body.audio-open.reader-pager-out .about-contents-btn {
    transform: translateY(-6.75rem);
}

/* THE AUTO-ADVANCE SWITCH, when it is on. Gold, matching the scrubber and the
   spoken-verse mark rather than the picker's favorite star — here it means
   "this is running", which is what the colour means everywhere on this bar.

   The hover is repeated inside the state selector on purpose. A hover rule that
   outranks a state rule holds the control at its off colour for as long as the
   pointer rests on it, so a reader sees nothing happen when they click; it cost
   a real bug report on the picker's star. A state must outrank a hover. */
.audio-btn.audio-auto[aria-pressed="true"],
.audio-btn.audio-auto[aria-pressed="true"]:hover {
    color: var(--biblion-gold);
}

/* Reading speed: the one control in this row that is a word rather than a
   glyph, because a speed is a number and no icon says "1.25" without a legend.

   TABULAR FIGURES AND A FIXED WIDTH for the same reason .audio-time has them —
   the label changes under the reader's own finger (1× → 1.25× → 1.5×), and
   without a floor the whole right-hand end of the bar would jump sideways as
   they tapped. 2.75rem holds the widest member of the ladder.

   Gold whenever it is not 1×, matching how auto-advance marks itself as on:
   the bar says "something here is not the default" in one colour throughout. */
.audio-btn.audio-rate {
    font-size: 0.8rem;
    font-variant-numeric: tabular-nums;
    min-width: 2.75rem;
    text-align: center;
}

.audio-btn.audio-rate.audio-rate-set,
.audio-btn.audio-rate.audio-rate-set:hover {
    color: var(--biblion-gold);
}

/* A disabled step button still reads as part of the row, just unavailable —
   at the head of a chapter there is no previous verse to go to. */
.audio-btn:disabled {
    opacity: 0.35;
    cursor: default;
}

/* NARROW SCREENS PUT THE SCRUBBER ON ITS OWN LINE. Seven controls, two clocks
   and a slider do not share 375px usefully: the slider is the one that has to
   be wide to be draggable at all, and the buttons are the ones that must not
   shrink below a fingertip. So the slider takes the full width above the row
   and everything else keeps its natural size underneath. */
@media (max-width: 575.98px) {
    .audio-bar-inner {
        flex-wrap: wrap;
        row-gap: 0.35rem;
        gap: 0.4rem;
        padding-left: 0.6rem;
        padding-right: 0.6rem;
    }
    .audio-seek {
        order: -1;
        flex: 1 1 100%;
    }
    .audio-time {
        min-width: 2.8em;
        font-size: 0.75rem;
    }
}

/* WHERE THE AUDIO HAS GOT TO, when it is no longer this page. The player moves
   between chapters without navigating — a page load would end playback — so the
   reader can be listening to Acts 3 while looking at Acts 2, and this both says
   so and is the way to catch the page up. Hidden while the two agree, which is
   most of the time. */
.audio-elsewhere {
    padding: 0.15rem 0.6rem;
    color: #212529;
    font-size: 0.75rem;
    white-space: nowrap;
    text-decoration: none;
    background: var(--biblion-gold);
    border-radius: 50rem;
}

.audio-elsewhere:hover,
.audio-elsewhere:focus-visible {
    color: #212529;
    filter: brightness(1.08);
}

.audio-elsewhere[hidden] {
    display: none;
}

/* On a phone the bar already wraps the scrubber onto its own line; this rides
   with the buttons and must be allowed to shrink rather than push them out. */
@media (max-width: 575.98px) {
    .audio-elsewhere {
        overflow: hidden;
        max-width: 8rem;
        text-overflow: ellipsis;
    }
}

/* WAITING FOR A GESTURE. The reader followed the audio to another chapter, the
   position came across, and the browser refused to start the sound because a
   fresh document has no user activation. The button says so by going gold —
   the same gold that means "this is running" everywhere else on the bar — and
   the next click or keypress anywhere resumes it. */
.audio-bar.audio-waiting .audio-play {
    color: var(--biblion-gold);
}

/* THE INTRODUCTION PLAYER — comm.php's intro pages. Inline in the prose column,
   not a bar over the viewport: an introduction is a page you sit and read.
   The reader's .audio-btn is styled for the dark bottom bar (hover goes white),
   so these controls carry their own colors for a light page. Flat, bordered,
   quiet — the reading surface is not the place for chrome. */
.intro-listen-btn {
    padding: 0 0.4rem;
    margin-left: 0.35rem;
    font-size: 0.55em;          /* sized to the h1 it sits in, not the page */
    color: var(--biblion-gold);
    vertical-align: 0.35em;
    background: none;
    border: 0;
    line-height: 1;
}

.intro-listen-btn:hover,
.intro-listen-btn:focus-visible {
    color: var(--bs-body-color);
}

/* ⚠⚠ THE SIZE ABOVE IS 0.55em OF AN h1, AND /rituals HAS NO h1 TO TAKE IT FROM.
   That page's heading is white type over the banner artwork, where a gold icon
   has nothing to sit on, so its headphones ride the standfirst paragraph
   instead — and 0.55em of 16px body text rendered 9px against the 18px the same
   button takes on /stories and the commentary introductions. Measured, not
   guessed: 18.48px is what 0.55em of the 33.6px .prose-title resolves to, so it
   is stated absolutely here and the three agree wherever the type scale moves.
   `middle` because this one sits in running text rather than lifted into a
   heading. */
.intro-listen-btn.rit-listen {
    font-size: 1.155rem;
    vertical-align: middle;
}

.intro-audio {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    max-width: 26rem;
    padding: 0.5rem 0.75rem;
    margin-bottom: 1.25rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 0.375rem;
}

.intro-audio .audio-btn {
    color: inherit;
}

.intro-audio .audio-btn:hover,
.intro-audio .audio-btn:focus-visible {
    color: var(--biblion-gold);
}

.intro-audio-seek {
    flex: 1;
}

.intro-audio-time {
    font-size: 0.8rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    opacity: 0.85;
}

.intro-audio-rate {
    min-width: 2.75rem;
    font-size: 0.8rem;
    font-variant-numeric: tabular-nums;
}

.intro-audio-rate.is-on {
    color: var(--biblion-gold);
}


/* THE NAME COMPLETION LIST under a search field (initSuggest in js/app.js).
   Absolutely positioned so it covers the page rather than pushing it: a list
   that reflowed the layout under a typing reader would move the field itself.
   The wrapper it hangs from is Bootstrap's .input-group, which is already
   position: relative — nothing new is introduced to hold it. */
.suggest-list {
    position: absolute;
    z-index: 1050;          /* over the field's own group, under a modal */
    top: 100%;
    left: 0;
    right: 0;
    margin: 0.15rem 0 0;
    padding: 0.2rem 0;
    max-height: 19rem;
    overflow-y: auto;
    background: #fff;
    /* ⚠⚠ A PANEL THAT DECLARES A BACKGROUND MUST DECLARE A FOREGROUND. This
       list is white, but it hangs inside .welcome-intro, which paints its text
       var(--hills-near) — the wallpaper's near ridge. So the names took the
       WALLPAPER's colour on a white panel, and in the night colorway that is
       #cdd9e6, pale moonlit blue: about 1.4:1 against #fff, effectively
       invisible. Day, dusk and dawn only escaped because their ridges happen to
       be dark (#25707e, #3f3663, #3c4a6b) — legible by luck, not by design.
       The counts and the icons were always fine, because they alone set a
       colour of their own; that difference is what the screenshot showed.
       .palette-panel already pairs its background with a colour, and this is
       the same rule ([[bible-gold-system]]: a transparent control inherits its
       panel's tint, and the reader pays for it in contrast). */
    color: var(--bs-body-color, #212529);
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: var(--bs-border-radius, 0.375rem);
    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.12);
}

.suggest-item {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    /* 44px of target, declared rather than arrived at by padding — the floor
       this project has already measured wrong once by copying someone else's
       padding ([[bible-ux-audit]]). */
    min-height: 44px;
    padding: 0.45rem 0.8rem;
    cursor: pointer;
}

/* ⚠ HOVER AND KEYBOARD MUST LOOK THE SAME. Two ways of pointing at the same row
   that draw differently read as two different states. */
.suggest-item:hover,
.suggest-item.is-active {
    background-color: var(--biblion-gold-tint, rgba(201, 162, 39, 0.16));
}

.suggest-item i {
    flex: none;
    width: 1rem;
    text-align: center;
    color: var(--bs-secondary-color, #6c757d);
    font-size: 0.85em;
}

.suggest-name  { flex: 1 1 auto; }

/* The verse count is the only ranking a reader gets, so it rides the row rather
   than hiding behind a hover — the same call .pp-chip-n makes. */
.suggest-count {
    flex: none;
    font-size: 0.8rem;
    color: var(--bs-secondary-color, #6c757d);
    font-variant-numeric: tabular-nums;
}

/* THE PLACE PHOTOGRAPH — one frame for all of them, because the archive is not
   uniform and the presentation must be. The images run 765x1024 to 1024x704 and
   1898 to 1960; measured, three of the first nineteen are colour film among
   sixteen neutral monochrome scans.

   ⚠⚠ NO FILTER. NOT GRAYSCALE, NOT SEPIA. THE PHOTOGRAPH IS SHOWN AS IT IS.
   A grayscale filter shipped here briefly, to make the three colour ones match
   the sixteen — Daniel removed it the same day: *"let the colored ones keep the
   color"*, and he is right. These are historical documents, and normalising one
   to match its neighbours is a smaller version of the same act as toning it to
   match a colour scheme. The frame is what unifies them — one aspect, one crop,
   one caption — and that is enough. Sepia was never on the table: it would add
   a patina that was never on the negative.

   ⚠ SO THE UNIFYING IS ALL STRUCTURAL. If a future set looks ragged, fix the
   crop or the caption, not the pixels. */
.place-photo {
    margin: 1.25rem 0 1.5rem;
}

.place-photo img {
    display: block;
    width: 100%;
    /* One aspect for every place, and `cover` centres what does not fit rather
       than letterboxing the odd frame.

       ⚠⚠ MEASURED ACROSS ALL 78, 2026-08-20 — the claim that stood here before
       was written from a glance and was wrong three ways. It said Bethlehem was
       "the portrait outlier at 0.75 against everything else's 1.21-1.45". In
       fact FOUR of the original nineteen were already portrait, Bethlehem was
       not the hardest crop even then, and the landscape spread is 1.20-1.97.

       Six of the 78 are portrait, and the fraction of the frame that survives
       the crop is: Rabbah 0.64 → 43%, Jezreel 0.69 → 46%, Galilee 0.71 → 47%,
       Bethlehem 0.75 → 50%, Bethel and Tekoa 0.77 → 51%. Two are WIDER than 3:2
       and lose their sides instead: Mount Sinai 1.85 → 81% of the width, the
       Red Sea 1.97 → 76%.

       Daniel looked at Rabbah and Tekoa cropped and kept them (2026-08-20), so
       these numbers are the accepted state, not a defect list. They are here so
       the next person to widen this set knows what the frame costs — and
       because a comment nobody measured is how the wrong figure survived two
       rounds ([[bible-ux-audit]]). Re-measure before editing this. */
    aspect-ratio: 3 / 2;
    object-fit: cover;
    border-radius: var(--bs-border-radius, 0.375rem);
    background: rgba(0, 0, 0, 0.04);   /* what shows while it loads */
}

/* The caption is the credit, so it takes the violet: a reference work speaking,
   as ink rather than as a wash over the picture ([[bible-gold-system]]). The
   photograph itself is left alone — the colour language belongs on the chrome. */
.place-photo figcaption {
    margin-top: 0.5rem;
    padding-left: 0.7rem;
    border-left: 3px solid #7a5ea8;
    font-size: 0.85rem;
    line-height: 1.45;
}

.place-photo-title {
    display: block;
    color: var(--bs-body-color, #212529);
}

.place-photo-credit {
    display: block;
    color: var(--bs-secondary-color, #6c757d);
}

/* ⚠ The link inside the caption is on the page's white, not on a violet ground,
   so the default blue clears AA here — unlike inside .search-who, where the 5%
   wash drops it to 4.22:1 and it had to be darkened. */
.place-photo-credit a { color: inherit; text-decoration: underline; }
.place-photo-credit a:hover { color: #212529; }

/* VISIONS & PROPHECIES — the chapter atlases. The shelf itself (bands, contents,
   open-all) is the shared .shelf-band machinery /rituals uses; everything here is
   what a chapter atlas needs on top of it.

   A card leads with its PICTURE and not with a crop of one: unlike the ritual
   cards, which needed a 16:7 thumbnail cut because their banners keep a third of
   empty charcoal for a title, these paintings are edge-to-edge scenes at their
   own aspect. The whole plate is the thumbnail. */
.plate-index {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: 1.25rem;
    margin: 1.25rem 0 2.75rem;
}
.plate-card {
    display: flex;
    flex-direction: column;
    height: 100%;
    background: #fff;
    border: 2px solid var(--bs-border-color, #dee2e6);
    border-radius: 0.75rem;
    overflow: hidden;
    text-decoration: none;
    color: inherit;
}
.plate-card:hover, .plate-card:focus-visible {
    border-color: var(--biblion-gold, #c9a227);
    outline: none;
}
/* ⚠ EVERY CARD'S PICTURE IS THE SAME HEIGHT, and it has to be forced: the plates
   are not one shape. The first drop was mostly 2:1 with 1.5:1, 2.08:1 and 3:1
   among it, and left at their own aspects the cards came out ragged — a tall
   Revelation 5 beside a short Revelation 6 in the same row, with the text
   starting at a different height in each.

   `aspect-ratio` here overrides the width/height attributes (which are only a
   presentational hint), and `object-fit: cover` fills the box by cropping rather
   than by squashing — a squashed painting is worse than a cropped one. 2:1 is
   the shape most of them already are, so most are shown whole. The chapter page
   shows the full plate, uncropped: that is where the artwork is the point. */
.plate-thumb {
    display: block;
    width: 100%;
    aspect-ratio: 2 / 1;
    height: auto;
    object-fit: cover;
    border-bottom: 1px solid var(--bs-border-color, #dee2e6);
}
.plate-card-text {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0.7rem 0.85rem 0.85rem;
}
.plate-card-title {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-size: 1.15rem;
    margin: 0;
    scroll-margin-top: 6rem;
}
.plate-card-sub { font-size: 0.9rem; line-height: 1.35; }
.plate-card-meta {
    font-size: 0.9rem;
    color: var(--bs-secondary-color, #6c757d);
}

/* One chapter. */
.plate-subtitle {
    margin: -0.25rem 0 1rem;
    font-size: 0.9rem;
    color: var(--bs-secondary-color, #6c757d);
}
.plate-figure { margin: 0 0 1.25rem; }
.plate-figure img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: 0.5rem;
}
/* The scenes, numbered, because the ORDER is the whole argument for drawing a
   chapter this way — a list of topics would not need numbering and would not
   deserve it. */
.pro-scenes { margin: 0 0 1.5rem; }
.pro-scene {
    display: grid;
    grid-template-columns: 1.7rem 1fr auto;
    align-items: baseline;
    gap: 0.3rem 0.7rem;
    padding: 0.6rem 0;
    border-top: 1px solid var(--bs-border-color, #dee2e6);
}
/* The last scene draws no closing rule, for the same reason the last band does
   not: the credit below opens with one. */
.pro-scene-n {
    align-self: center;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    background: var(--bs-tertiary-bg, #f8f9fa);
    border: 1px solid var(--bs-border-color, #dee2e6);
    color: var(--bs-secondary-color, #6c757d);
    font-size: 0.9rem;
    font-variant-numeric: tabular-nums;
    line-height: 1.4rem;
    text-align: center;
}
.pro-scene-text {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-size: 0.9rem;
    font-weight: 700;
    line-height: 1.35;
}
/* ⚠ THE TWO PARTS OF A LABEL STAY TWO PARTS. They are the phrases as written —
   "Four winds and sea" / "four beasts arise" — and joining them makes nonsense
   of some and a sentence of others, with no rule that gets both right. */
.pro-scene-line { display: block; }
.pro-scene-ref { font-size: 0.9rem; white-space: nowrap; }
.plate-read { font-size: 0.9rem; margin-bottom: 1.5rem; }
@media (max-width: 575.98px) {
    .pro-scene { grid-template-columns: 1.7rem 1fr; }
    .pro-scene-ref { grid-column: 2; }
}
/* ⚠⚠ THE SCENE'S VERSE IS IN THE GOLDEN BAND NOW — Daniel, 2026-08-23: "apply
   the gold styling and font throughout as in our style rules for verse
   quotations". It wore a style of its own until then: the reading serif at the
   page's label size, in the muted secondary ink, with no ground under it. That
   read as a caption about the scene rather than as Scripture speaking, which is
   the exact drift the devotional's key verse and the topic pages were both
   corrected for ([[bible-gold-system]]).

   The element carries BOTH classes — `pro-scene-quote devotional-verse` — so the
   band itself (paper fill, gold left rule, the darker ink) comes from the site's
   one quotation style and cannot drift from it, and only what a scene row needs
   on top is written here. This rule sits after `.devotional-verse` in the file,
   which is what lets these win; move either and the size goes back to 1.05rem.

   ⚠ SMALLER AND TIGHTER THAN A KEY VERSE, ON PURPOSE. A devotional prints one
   band above a reading; a chapter atlas prints up to eight in a numbered list, so
   they take the row's own 0.9rem and about half the padding. The COLOUR is not
   reduced with them: #2c2926 comes from the band and stays, because a quotation
   that has been given a ground and then greyed out is back to being a caption. */
.pro-scene-quote {
    grid-column: 2 / -1;
    font-size: 0.9rem;
    line-height: 1.5;
    padding: 0.5rem 0.7rem;
    margin: 0.15rem 0 0;
}
/* ⚠ THE QUOTED VERSE NAMES ITSELF. The row's reference is the scene's whole
   range and the quotation is one verse inside it, chosen for fitting the scene's
   label — so without this a reader would reasonably take the quote for the first
   verse of the range, which it usually is not. Since 2026-08-23 it also names the
   translation, because the text follows the reader's own.

   ⚠ NO FAMILY OF ITS OWN — it inherits the band's reading serif (Daniel,
   2026-08-23: "use same font for v2 · WEB as well"). It was set in Prompt back
   when the quote was loose grey text and the sans was what set the tag apart from
   it. Inside the golden band that distinction is drawn by the ground, and a sans
   tail on a serif quotation just looked like two fragments. It matches the house
   pattern besides: on /stories and /topics the reference and version sit inside
   the band in the band's own face. */
.pro-scene-quote-ref {
    white-space: nowrap;
}
/* Where a plate is, when its name does not say — "The woman in the ephah" needs
   "Zechariah 5:5–11" beside it, "Daniel 7" does not. */
.plate-passage {
    font-size: 0.9rem;
    color: var(--bs-secondary-color, #6c757d);
}
.plate-lead-passage {
    display: block;
    font-weight: 700;
    color: var(--bs-body-color, #212529);
}

/* THE COMMENTARY ON A STEP OR A SCENE, at the foot of it. This site has a
   written comment on every verse in the canon and a chart step is exactly where
   a reader wants one, so the sections are named by their own headings rather
   than hidden behind "commentary".

   ⚠ ON THE RITUAL PAGE IT SITS OUTSIDE THE CARD, never in it: the card is itself
   an <a> and a link cannot nest inside a link. The wrapper is what carries the
   return anchor. */
/* ⚠ A REAL BOX, NOT `display: contents`. The wrapper exists to be scrolled to
   when a reader returns from the commentary, and an element with `display:
   contents` generates no box — the browser cannot scroll to it, and the fragment
   silently did nothing. It also moved the connector's last-child test up a level;
   see the rule above. */
.rit-step {
    display: block;
    position: relative;
    margin: 0 0 2.25rem;
}
.rit-card-comm,
.pro-scene-comm {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.25rem 0.6rem;
    font-size: 0.9rem;
}
/* ⚠ TIGHT TO THE CARD ABOVE IT — Daniel, 2026-08-23. It sat in the middle of the
   gap between one step and the next, which made it read as a heading for what
   followed rather than as a footnote to what it belongs to. Proximity is the only
   thing saying which card it is about. */
.rit-card-comm {
    margin: 0.45rem 0 0;
    padding: 0 0.25rem;
}
.pro-scene-comm {
    grid-column: 2 / -1;
    margin-top: 0.15rem;
}
.rit-card-comm-label,
.pro-scene-comm-label {
    color: var(--bs-secondary-color, #6c757d);
}
/* ⚠ THEY STAY LINK BLUE. Violet was tried first, on the reasoning that violet
   means "a reference work is speaking" — but the refs page settled this exact
   question already: its often-linked band is violet while the references INSIDE
   it stay blue, "because every reference on this page is blue and goes
   somewhere; making these five the exception would say they are a different kind
   of thing". These are links that go somewhere, and the muted label beside them
   already says what they are. The reading face is the one borrowed thing: the
   headings are the commentary's own words. */
.rit-card-comm a,
.pro-scene-comm a {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
}
/* A scene or a step returned to from the commentary lands clear of the fixed
   navbar rather than under it — the same 6rem the verse rows and the ritual
   index titles use. */
.pro-scene,
.rit-step,
.rit-card { scroll-margin-top: 6rem; }

/* GREAT BIBLE STORIES — the third illustrated shelf. Almost all of it is the
   shared machinery: .shelf-band for the two bands, .plate-card for the cards,
   .plate-figure for the painting. What is here is the little a story needs that
   a chapter atlas does not.

   ⚠ THE THUMBNAILS KEEP THEIR OWN SHAPE. .plate-thumb forces 2:1 because the
   prophecy plates arrive at five different aspects and had to be made to agree
   with each other; all twelve paintings here are 3:2, so agreeing costs nothing
   and cropping would throw away a quarter of every picture — including the
   rainbow at the top of the ark and the water walls beside the Red Sea, which
   are the parts that say which story it is. */
.stories-index .plate-thumb { aspect-ratio: 3 / 2; }

/* ⚠⚠ THE SIX QUOTED VERSES CARRY NO QUOTATION STYLE OF THEIR OWN. The band is
   `.devotional-verse` — the site's single Scripture style, paper fill, gold left
   rule, reading serif, reference inside it and the version after a middle dot —
   and there is deliberately no rule for it here, exactly as `.topic-passage`
   deliberately has none. A story-page quotation style is how the same verse
   comes to look like Scripture on one page and a citation on another, which is
   the drift the devotional's key verse and the topic pages were both corrected
   for ([[bible-gold-system]]). What is below is only the SPACING around them.

   A first pass here did invent one — a bare gold left rule and no fill — and it
   was wrong twice over: it read as a citation, and it used `--biblion-gold`
   `#c9a227`, which is the "this is the one" gold and not the `#c9a24a` rule the
   quotation band draws. */
.story-verses { margin: 0 0 1.5rem; }
/* Tighter than a devotional's single key verse, which stands alone above a
   reading; six in a row want to read as one passage told in six steps rather
   than six separate pronouncements. */
.story-verse .devotional-verse { margin-bottom: 0.6rem; }
.story-verse:last-child .devotional-verse { margin-bottom: 0; }

/* THE VERSE BEING READ ALOUD (initStoryAudio in js/app.js). Gold at the
   reference alpha, because this is the app's one meaning for that colour —
   "this one", the thing you are being pointed at. The strip has no seek bar on
   purpose, so these six washes ARE the progress indicator.

   ⚠ THE PADDING IS HORIZONTAL ONLY, AND PULLED BACK BY AN EQUAL NEGATIVE
   MARGIN. The wash needs room at the sides or it hugs the text, but any
   VERTICAL padding would change the spacing of all six the moment the feature
   shipped — and a layout that moves when a colour appears is the fault that
   costs a redraw to find, not a colour that is slightly tight. Nothing here
   shifts by a pixel; only the background changes. */
.story-verse {
    padding: 0 0.6rem;
    margin: 0 -0.6rem;
    border-radius: 0.375rem;
    transition: background-color 200ms ease;
}

/* ⚠ NO WRAPPER WASH HERE EITHER — the quotation inside carries the mark, same
   as every shelf; see the block below. The wrapper keeps its padding/negative-
   margin pair only so the lit band's geometry cannot shift. */

/* THE SAME MARK ON THE OTHER THREE SHELVES — a ritual step, a prophecy scene, a
   topic passage. One player lights all four.

   ⚠⚠ THE MARK IS ON THE QUOTATION'S OWN BOX, NOT ON THE WRAPPER — and it took
   three tries to land here, each checked in Daniel's Chrome, so do not walk it
   back through the same three. (1) A wash on the wrapper alone: the quotation's
   opaque #faf7f0 painted OVER it, so everything lit except the verse being read.
   (2) Wash on the wrapper plus the faithful composite on the quotation: right
   arithmetic, but the quotation is an INSET box — padding, radius, gold rule —
   so the wrapper's full-bleed wash read as a second band stacked under a first,
   two steps of colour where one thing was meant. Identical values measured; the
   GEOMETRY was the difference. (3) So the wrapper stays paper and the mark goes
   on the silhouettes the eye already knows: the quotation's band deepens, a
   ritual card's white fills, the scene badge warms. Nothing changes shape, and
   the commentary sentence below a quotation stays on paper — it is not being
   read.

   #f3ebcf is the reference wash composited over white, already named in this
   file (the Strong's popup, `.rit-outcome`'s family). A second translucent
   layer would double-tint against every sibling; the composite cannot.

   ⚠ `.rit-outcome` carries #f6f0dc — gold already, for "this is the outcome" —
   and also `.rit-card`, so the reading shade wins only while the voice is on
   it. One colour means HERE, and here outranks the outcome for that long. */
.is-reading .devotional-verse,
.is-reading .rit-card {
    background: #f3ebcf;
    transition: background-color 200ms ease;
}

.is-reading .pro-scene-n {
    background: #eee6cc;      /* the badge's own grey, warmed to match */
    transition: background-color 200ms ease;
}

@media (prefers-reduced-motion: reduce) {
    .is-reading .devotional-verse,
    .is-reading .rit-card,
    .is-reading .pro-scene-n { transition: none; }
}

@media (prefers-reduced-motion: reduce) {
    .story-verse { transition: none; }
}
/* ⚠ THE COMMENTARY LIST IS A LIST, not the wrapped row of chips the ritual and
   prophecy pages use. Those name one comment or two beside a single step; a
   story covers a whole passage and pulls in as many as six, each with its own
   verse range — and a range is unreadable in a wrapped row where the eye cannot
   tell which reference belongs to which heading.

   Each row is ONE line, the range pushed to the right edge, wrapping to a second
   only when a heading is long enough to need it. Given a line of its own always,
   as it was first written, four comments on Genesis 8–9 took eight rows to say
   what fits in four. */
.story-comm-label {
    margin: 1.25rem 0 0.35rem;
    font-size: 0.9rem;
    color: var(--bs-secondary-color, #6c757d);
}
.story-comm { margin: 0 0 1.5rem; font-size: 0.9rem; }
.story-comm li {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: baseline;
    gap: 0 0.75rem;
    padding: 0.35rem 0;
    border-top: 1px solid var(--bs-border-color, #dee2e6);
}
.story-comm a {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
}
.story-comm-ref {
    color: var(--bs-secondary-color, #6c757d);
    white-space: nowrap;
}
/* Returned to from the reader or the commentary, a verse or the passage block
   lands clear of the fixed navbar — the same 6rem every other return target
   uses. */
.story-verse,
.story-passage { scroll-margin-top: 6rem; }

/* ──────────────────────────────────────────────────────────────────────────
   THE HERO'S SHELF STRIP — one story, one chart, one plate, under the search
   field in the hero's left column (Daniel, 2026-08-23).

   ⚠⚠ IT WENT IN THE WRONG BOX FIRST, and the mistake is worth keeping because it
   was invisible rather than broken. The strip was built as a sibling of the
   stage, filling the gap between the row and the Favorite passages card — which
   on Daniel's own window is 59px, so he saw nothing at all. The empty hills he
   was pointing at are not below the row: the row is 467px tall because the
   TOPICS column is, while the verse and the field beside it come to 280px, so
   the emptiness is 354px of LEFT COLUMN. Measure which box the empty space
   belongs to before filling it — a gap in a two-column row usually belongs to
   the shorter column, not to the row.

   ⚠ AND THE FAVORITE PASSAGES CARD MOVED TO MAKE IT FREE. Under the verse of the
   day, the right column becomes the tall one and the left carries the strip
   inside its shadow, so the strip costs the hero NO extra height. Add anything
   to either column and measure both, because the taller one sets the row.

   ⚠ IT STILL STANDS DOWN WHERE THERE IS NO ROOM. Below 992px the two columns
   stack and every element of the hero queues into one, where nothing has slack;
   the height floor is the hero's own rather than the strip's. `display: none`
   rather than visibility, so it takes no space at all when it stands down.

   ⚠ NOTHING HERE MOVES. It is not a carousel: hero motion was settled in the
   negative on 2026-07-30 ([[bible-wallpaper-motion]]). The cards change on every
   page load instead, which is what "different cards each time" actually asked
   for. Do not add autoplay, arrows or dots to this.

   ⚠ THE WHOLE LAYOUT IS ON THE BASE RULE AND ONLY `display` IS IN THE QUERY.
   Written the other way round first — columns and gap inside the media block —
   and the fault showed the moment the strip was forced visible outside it for a
   look: the cards fell into ONE column, because the only thing the base rule
   knew was that it was a grid. Layout that lives inside a gate is layout that
   disappears the day the gate moves. */
.welcome-shelves {
    display: none;
    grid-template-columns: repeat(3, 1fr);
    /* .plate-index's gap, so three cards here are spaced as three cards on the
       shelf they come from. */
    gap: 1.25rem;
    /* ⚠ 3rem, AND IT IS FREE (Daniel, 2026-08-23: "put some more space between
       search and those cards"). It was 1.75rem, the search field's own top
       margin. The reason a bigger number costs nothing here is the two-column
       arithmetic: the row takes its height from the TALLER column, and with the
       Favorite passages card moved across, that is the right one. The left column
       has slack to spend until it catches up — so this margin is free until it
       is not, and the day the left column overtakes the right, the whole hero
       grows by whatever is added. Measure both columns before enlarging it
       again.

       1.75rem → 3rem → 4.5rem → 8rem over four passes, on Daniel's word each
       time. The last one is not a taste judgement but a MEASUREMENT: he asked for
       the strip to start level with the verse-of-the-day card across the hero.
       At 2294x791 the strip's top was 354 and the card's 410, so the margin went
       from 72px to 128px — 8rem exactly.

       ⚠⚠ AND IT LINES UP AT ONE SHAPE OF WINDOW, NOT AT ALL OF THEM. The card's
       top is set by how many rows the topic pills wrap into, which changes with
       width; the strip's is set by this number, which does not. Four rows of
       pills is what 8rem was measured against. Nothing in normal flow can make an
       element in one column track an element in another — that needs absolute
       placement or script, and this page is deliberately built without either
       (see the note on .welcome-stage: every collision it used to have came from
       elements placed by arithmetic instead of held apart by margins). So the two
       drift apart as the window narrows, and that is the accepted cost of the
       alignment rather than an oversight.

       ⚠ IT ALSO SPENDS THE COLUMN'S SLACK AND THEN SOME. The left column is now
       618px against the right's 562, so the LEFT one sets the row and every
       further rem here is a rem added to the whole hero. Measured after the
       change: hero 776px, still one screen at 791. */
    margin-top: 8rem;
}

/* ⚠⚠ THERE IS NO HEIGHT GATE ANY MORE, AND THAT WAS DELIBERATELY GIVEN UP
   (Daniel, 2026-08-23: "they are not showing now on mid size screen"). It was
   `min-height: 770px`, the measured point below which the hero stops fitting one
   screen — which sounded right and in practice meant an ordinary browser window
   on an ordinary monitor, where the chrome takes 100px off the top, showed
   nothing at all. A feature that hides itself on the commonest window is not a
   feature.

   What is traded for it: on a window shorter than about 770px the hero runs past
   100vh and the page scrolls a little. That is a smaller cost than it looks —
   the stage is `min-height`, not `height`, so it was always allowed to grow, and
   the Favorite passages card is inside a column now rather than pinned to the
   floor of the screen, so nothing is left dangling by the overflow. */
@media (min-width: 992px) {
    .welcome-shelves { display: grid; }
}

/* A card is the same object as the one at the foot of the hero — the gold wash at
   0.7 so a little of the hills reads through, and #4a3f2a rather than black for
   the ink, which measured 9.4:1 there. Reusing the values rather than the class:
   the two cards are independent and a shared class would tie every future change
   of one to the other, which is the reason .welcome-votd-head does not share with
   .welcome-mostread-head either. */
/* ⚠ THE PICTURE SITS ON TOP, not beside the text, because the column decides it.
   Side by side is the better shape and it is what this was built as — but the
   strip lives in half the hero now, so a card is about 205px wide at 1440 and
   149px at the lg breakpoint. Take 144px of that for a thumbnail and the title
   has 45px to live in. Stacked, the picture gets the card's full width and the
   title gets it back. */
/* ⚠ NO PADDING ON THE CARD — the picture runs to its edges (Daniel, 2026-08-23:
   "show the picture in card without borders"). It had 0.5rem all round, which
   drew a pale frame of card ground on all four sides of every painting. The
   padding belongs to the TEXT instead; the card's own `overflow: hidden` and
   radius round the picture's top corners, so the image needs no radius of its
   own and must not be given one — a radius on the image would show the ground
   through the corner it cuts. */
/* ⚠ THE CARD'S METRICS ARE .plate-card's, TO THE PIXEL (Daniel, 2026-08-23): a
   2px ring, a 0.75rem radius, and 0.7/0.85/0.85rem of padding on the words. They
   are the same object seen in two places — a picture from a shelf, on a card,
   that opens that shelf — and a hero card whose selector was 1px while the
   shelf's was 2px read as a near-miss rather than as a family.

   ⚠ THE RING IS TRANSPARENT AT REST HERE, WHICH .plate-card's IS NOT, and that
   is the one thing deliberately not copied. On /stories a card is white on white
   and needs a grey edge to be a card at all; this one is a warm panel on a drawn
   wallpaper, where a grey frame would draw a box around a painting that already
   has an edge. Same WIDTH, so the gold lands with the same weight in both
   places — which is what "same width as on great bible stories page" asked for —
   and declared at rest so the card does not change size when it takes a colour. */
.welcome-shelf {
    display: flex;
    flex-direction: column;
    background: rgba(243, 235, 207, 0.7);
    border: 2px solid transparent;
    border-radius: 0.75rem;
    box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
    color: #4a3f2a;
    text-decoration: none;
    overflow: hidden;
}
/* ⚠ A BORDER, NOT A GOLD FILL. The hero is allowed exactly one saturated mark and
   the verse-of-the-day button is it ([[bible-gold-system]]); a row of three cards
   lighting up gold would take that away from it. The border is transparent at
   rest — declared above so the card does not change size when it takes a colour. */
.welcome-shelf:hover,
.welcome-shelf:focus-visible {
    border-color: var(--biblion-gold, #c9a227);
    color: #4a3f2a;
    outline: none;
}
/* ⚠ `height: auto` IS LOAD-BEARING. The <img> carries width and height
   attributes so the box is reserved before the file arrives; without this the
   attribute wins over `aspect-ratio` and the picture draws 162px tall whatever
   the card's width. Same pairing .plate-thumb uses on the shelves themselves. */
.welcome-shelf-thumb {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
    object-fit: cover;
}
/* .plate-card-text's own padding and gap, for the reason above. */
.welcome-shelf-text {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-width: 0;
    padding: 0.7rem 0.85rem 0.85rem;
}
/* The shelf's name, in the small uppercase every label on this page wears. */
.welcome-shelf-head {
    font-family: 'Prompt', sans-serif;
    font-size: 0.8rem;
    font-weight: 500;
    letter-spacing: 0.08em;
}
/* ⚠ THE SAME FACE, SIZE AND WEIGHT AS .welcome-mostread-link (Daniel,
   2026-08-23) — Roboto Serif 800 at 0.9rem. Both are the name of somewhere to go
   on a card in the hero, and they now say so in one voice; it was 1rem/400,
   which read as a heading beside the chapters rather than as their sibling.

   ⚠ 800 IS NOT A CHOICE, IT IS THE CUT THAT EXISTS. Only 400, 600 and 800 of
   this face ship — anything else is resolved to one of them or synthesised, and
   a synthesised weight is the shear the italics carried until they were fixed
   ([[bible_typography]]). Copied rather than shared through a class, for the
   reason the two cards' headings are also copied: they are independent objects
   and a shared class would tie every future change of one to the other.

   Clamped to two lines: "The basket and the choice, and the covenant restated"
   is a real chart title and would otherwise set the three cards to different
   heights. */
.welcome-shelf-title {
    font-family: 'Roboto Serif', 'Reading Fallback', Georgia, serif;
    font-size: 0.9rem;
    font-weight: 800;
    line-height: 1.3;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.welcome-shelf-ref {
    font-family: 'Prompt', sans-serif;
    font-size: 0.8rem;
    /* The passage steps back from the title without changing size — weight and a
       little opacity, not a size step ([[bible_typography]]). 0.75 measures 7.0:1
       on this card, well clear at this size. */
    opacity: 0.75;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ⚠⚠ BELOW lg THE CARD TURNS ON ITS SIDE (Daniel, 2026-08-23: "now these could
   be shown on smaller screen right?"). Below 992px the two columns stack into
   one, so the strip has the full width and no longer has to fit three pictures
   across half a column — but stacked cards at full width made each picture 364px
   wide and the strip 846px tall, three billboards a phone reader has to scroll
   past. One per row with the picture at the left instead, which is the shape
   this card was first built as.

   ⚠ AND IT HAS TO SIT HERE, AFTER THE CARD RULES, not up beside the other media
   query. Written there first and none of it applied: `flex-direction: row` and
   `width: 8rem` lose to the base rules' `column` and `100%` on the later-wins
   rule, since they are the same specificity. A media query is not more specific
   than the rule it means to override — only later.

   No height gate down here. The hero is already a scrolling column at this width,
   so there is no "no room" state left to protect against, and hiding the strip
   would only mean a phone reader never meets the shelves at all. */
@media (max-width: 991.98px) {
    .welcome-shelves {
        display: grid;
        grid-template-columns: 1fr;
        /* ⚠ EQUAL ROWS, because the cards do not carry equal words. A chapter
           atlas is titled "Daniel 9" with no passage line under it (the title
           already IS the passage), while "Clean and unclean: land animals" takes
           two lines and a reference — measured 56px against 94px, which reads as
           three different components rather than three of the same. `1fr` on
           auto rows makes every row the height of the tallest. */
        grid-auto-rows: 1fr;
        gap: 0.6rem;
        margin-top: 2rem;
    }
    .welcome-shelf { flex-direction: row; align-items: stretch; }
    /* ⚠ A FIXED WIDTH AND THE ROW'S OWN HEIGHT — not the 16:9 box the stacked
       card uses. At 8rem wide with the ratio still on, the picture was 128x72 in
       a card 96 tall: too big beside the words (Daniel) and short of the card's
       own edges, so it read as a stamp rather than as part of the card. 6rem
       wide, `aspect-ratio: auto` to switch the ratio off, and `height: 100%` so
       it runs the full height of whatever the text needs. `object-fit: cover` is
       inherited from the base rule and is what keeps it from stretching. */
    .welcome-shelf-thumb {
        flex: 0 0 auto;
        width: 6rem;
        height: 100%;
        aspect-ratio: auto;
    }
    .welcome-shelf-text { padding: 0.5rem 0.75rem; }
}

/* ─────────────────────────────────────────────────────────────────────────
   THE TOPIC PAINTINGS — 2026-08-23, one per topic, on both surfaces.

   ⚠ THE CARDS ARE .plate-card AND .plate-index, THE SHELVES' OWN. Four indexes
   on this site now put a picture on a card — stories, covenant law, prophetic
   visions and this — and they are one component, not four that resemble each
   other. What is below is only what a topic card needs on top of it. */

/* ⚠ THREE ACROSS, NOT auto-fill. .plate-index fills to `minmax(15rem, 1fr)`,
   which in a 720px prose column gives two columns of 340px and a ragged last
   row of one. Three fixed gives about 225px a card, which is the size the
   pictures were measured against — at the 120px the drop recommended for a side
   thumbnail they read as decoration and nothing else. The margins go, because
   the band's own padding is the air here. */
.topic-cards {
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem;
    margin: 0;
}
/* ⚠ 3:2, AND THE CROP POSITION COMES FROM THE DROP. .plate-thumb forces 2:1
   because the prophecy plates arrive at five shapes; all 32 of these are 3:2, so
   they are shown whole. `object-position` is set inline per card from the
   manifest's own `objectPosition` — the artist composed each scene knowing where
   it would be cut, and a page-wide 50% 50% would throw that away. It is inline
   because it is data, not style: 32 rules in a stylesheet naming individual
   topics is a stylesheet that has to be edited every time a painting is
   redrawn. */
.topic-cards .plate-thumb { aspect-ratio: 3 / 2; }
/* On a band's wash rather than on white, so the card's own ground has to be
   opaque or the wash tints the picture's surround and each band's cards look
   like a different set. */
.topic-cards .plate-card { background: #fff; }
.topic-cards .plate-card-title { font-size: 1.05rem; }

/* ⚠⚠ ON A PHONE THE CARD TURNS ON ITS SIDE, and the reasoning is the opposite of
   the desktop one rather than an extension of it. Both layouts were built and
   measured in a 390px window:

       two across   picture 124x82    page 4,530px
       side by side picture 112x121   page 4,729px

   ⚠ THE ROW IS NOT THE SHORTER ONE. It was chosen expecting it to be, on an
   estimate of about 3,400px that was simply wrong — the blurb wraps to three
   lines in the 155px of text left beside the picture, so a row is 107–125px
   rather than the ~90 guessed. The measurement is kept here because the estimate
   was off by 40% and the next person to reach for "obviously shorter" should see
   that.

   It is still the better of the two: the picture is decoration at either size —
   124px is exactly what was rejected for the desktop grid — and side by side
   shows about a quarter more of it, in a page 4% taller, with the word and its
   sentence reading as one line the way they did before this page had pictures at
   all. The painting does its real work on the topic's own page, which is the full
   width of the column even here. */
@media (max-width: 767.98px) {
    .topic-cards { grid-template-columns: 1fr; gap: 0.6rem; }
    .topic-cards .plate-card { flex-direction: row; align-items: stretch; }
    .topic-cards .plate-thumb {
        flex: 0 0 auto;
        width: 7rem;
        height: 100%;
        aspect-ratio: auto;
        border-bottom: 0;
        border-right: 1px solid var(--bs-border-color, #dee2e6);
    }
    .topic-cards .plate-card-title { font-size: 1rem; }
    .topic-cards .plate-card-sub { font-size: 0.85rem; }
}

/* One topic's own painting, above its passages. */
.topic-art { margin: 0 0 1.75rem; }
.topic-art img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: 0.5rem;
}
/* The passage the PICTURE is of — not one of the sixteen quoted below it, which
   is exactly why it is captioned. Set as interface rather than as Scripture: it
   is a label on an illustration, not a quotation. */
.topic-art figcaption {
    margin-top: 0.4rem;
    font-family: 'Prompt', sans-serif;
    font-size: 0.8rem;
    color: var(--bs-secondary-color, #6c757d);
}

/* ─────────────────────────────────────────────────────────────────────────
   THE SKIP LINK AND THE MAIN LANDMARK — 2026-08-23, from a live accessibility
   audit that found the site had neither on any of its 54 pages.

   ⚠ HIDDEN BY POSITION, NOT BY `display: none` OR `visibility: hidden`. Either
   of those removes the link from the focus order, which is the one thing it
   exists to be in — it would then be invisible AND unreachable, which is worse
   than not having it. It is parked above the top of the screen and comes back
   the instant it takes focus.

   ⚠ ABOVE THE NAVBAR IN Z-ORDER. The bar is `fixed-top` at Bootstrap's z-index
   1030, so anything lower appears behind it and a keyboard reader tabs to a
   control they cannot see. */
.skip-link {
    position: absolute;
    top: -3rem;
    left: 0.5rem;
    z-index: 1040;
    padding: 0.5rem 0.9rem;
    background: var(--biblion-gold, #c9a227);
    /* Black on this gold measures 8.68:1 — the same pairing .btn-gold uses, and
       for the same measured reason: white on it is 2.42:1 and fails outright
       ([[bible-gold-system]]). */
    color: #000;
    font-family: 'Prompt', sans-serif;
    font-size: 0.9rem;
    border-radius: 0 0 var(--bs-border-radius) var(--bs-border-radius);
    text-decoration: none;
    transition: top 0.15s ease;
}
.skip-link:focus {
    top: 0;
    color: #000;
    outline: 2px solid #000;
    outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
    .skip-link { transition: none; }
}
/* The landmark the link lands on. `scroll-margin-top` is the same 6rem every
   other jump target on this site uses, so arriving here clears the fixed bar
   rather than parking the first line underneath it.

   No focus ring on the container itself: it takes focus programmatically, the
   reader has just pressed the link that says where they are going, and a box
   drawn around the entire page reads as an error rather than as a position. */
#main { scroll-margin-top: 6rem; }
#main:focus { outline: none; }

/* ⚠ THE TOPIC CARDS WERE RAGGED ON A PHONE — 89px to 144px, and because the
   thumbnail is `height: 100%` the PAINTINGS varied with them, 85px to 140px, so
   the same 32 pictures were drawn at a dozen different sizes down one column.
   The desktop grid never had this: three to a row, every row stretched, images a
   uniform 139px. The single-column layout gives every card its own row, so the
   rows have to be told to match. Same fix, and the same reason, as the hero
   strip's own narrow layout. */
@media (max-width: 767.98px) {
    .topic-cards { grid-auto-rows: 1fr; }
}
