/* ------------------------------------------------------------------ *
   The Book — chronological reader
   Light is a leaf of vellum on a table; dark is the same room unlit.
   Both are defined as complete palettes so neither depends on the other.
 * ------------------------------------------------------------------ */

/* A control filled with the accent takes the *ground* colour for its type,
   never a literal white. The accent is a deep purple in light and a pale
   pink in dark, so white on it measures 10.5:1 in one theme and 2.3:1 in
   the other -- and 1.7:1 on the hover accent, which is a button whose label
   has effectively gone. It was written correctly once, on the player's play
   button, with a comment saying why, and five other rules went on using
   #fff: the skip link, the contents toggle, an open verse menu, a pressed
   chip, and the primary chip that carries "Open in Google Maps".
   tests/python/test_palette.py now reads the literals out of this file and
   measures them, which is the check that should have caught it. */

/* Two accents, and the difference between them is the whole system.

   RUBRIC is the red a scribe changed pens for. In a manuscript it marks
   the structure of the text and nothing else — the chapter number, the
   verse number, the heading, the initial — which is where the word
   rubric comes from, and it does exactly that job here.

   TYRIAN is the site's own machinery: a link, a focus ring, the control
   you just pressed. It is the argaman of Exodus 26 and the trade of
   Lydia in Acts 16, the dye of the coast these texts were written on.

   So a red mark on this site is the book speaking and a purple one is
   the reader speaking, and neither is ever used for the other's work.
   The rule is easy to state and was quietly broken in three places — a
   thread card ruled in rubric, a callout ruled in rubric, the player's
   own progress bar drawn in rubric — none of which is the structure of
   a text. They are the site's furniture, and they are purple now.

   GILT is the third material and the one held back: gold leaf, which a
   scribe put on neither the text nor the rubric but on the border of the
   leaf. It rules the editorial callouts — the volume's own asides, which
   are neither scripture nor a control — and it does nothing else. It sat
   in this file defined and used nowhere at all.

   Everything else follows from materials: unbleached parchment for the
   ground, a brighter leaf raised off it, and iron-gall ink — which
   oxidises violet-black rather than grey — for type. Dark inverts the
   two, which is what a photographic negative of a manuscript looks
   like: the leaf goes to the colour of the ink and the ink to the
   colour of the leaf.

   No font, image or texture is fetched. The site ships no dependencies
   and its single-file build has to open from file:// with the network
   off, so the grain of the paper is a gradient and the type is whatever
   the device already has. */

:root {
  --paper:      #eee7d9;
  --paper-2:    #e4dbc9;
  --raised:     #fcf9f2;
  --ink:        #1d1822;
  --ink-soft:   #554d5c;
  /* Measured rather than eyeballed. This was #837a89, which is 3.34:1 on
     the page and 2.99:1 on the row a table highlights under the pointer --
     under WCAG AA for body text at every size this palette uses it at, and
     under the 3:1 floor for anything at all on that last ground. It carries
     .muted, .tiny, the crumbs, the table heads, every count and every date,
     which is to say most of the small type on the site. */
  --ink-faint:  #655e6a;
  --rule:       #d4cab8;
  --rule-soft:  #e3dac9;
  --accent:     #632a55;
  --accent-2:   #83406f;
  --accent-bg:  #efe3ec;
  --rubric:     #9b2f21;
  --rubric-2:   #b8503f;
  --gilt:       #906b23;
  --good:       #2f6b5f;
  --good-bg:    #dfeae6;
  --warn:       #8a5a12;
  --warn-bg:    #f2e7cf;
  --fix:        #33587e;
  --fix-bg:     #e0e7f0;
  --mark:       #f4dd9c;

  /* The gutter of a bound leaf, and the grain across it. Both are drawn
     rather than photographed, and both are nearly invisible on purpose:
     texture a reader notices is texture in the way. */
  --spine:      rgba(74, 55, 30, .17);
  --grain:      rgba(74, 55, 30, .022);
  --wash-1:     rgba(255, 253, 246, .75);
  --wash-2:     rgba(99, 42, 85, .045);

  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua",
           Georgia, "Times New Roman", serif;
  --sans:  ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
           "Helvetica Neue", Arial, sans-serif;
  --mono:  ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --shadow: 0 1px 2px rgba(58, 44, 24, .07), 0 10px 30px rgba(58, 44, 24, .09);
  --radius: 10px;
  --measure: 34rem;
  /* The leaf the reader is set on, and the width every control above it
     lines up with. One number, so they cannot drift apart. */
  --leaf: calc(var(--measure) + 5rem);
  --reader-size: 1.075rem;

  /* One vertical rhythm, so the page has a beat rather than a hundred
     hand-picked margins. Named for what they separate rather than for how
     big they are: the sizes may change, the relationships may not. */
  --gap-hair:  .3rem;    /* parts of one thing */
  --gap-tight: .6rem;    /* a label and its value */
  --gap:       1.1rem;   /* one block from the next */
  --gap-wide:  1.9rem;   /* one group of blocks from the next */
  --gap-part:  3rem;     /* one part of the page from the next */

  /* Native widgets -- select dropdowns, scrollbars -- are drawn by the
     browser itself, and follow this rather than the palette above. */
  color-scheme: light;
}

/* The unlit room. Kept as one block and applied twice, because the theme
   can be chosen as well as inherited and neither route may be the poor
   relation of the other. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper:     #14121a;
    --paper-2:   #1c1925;
    --raised:    #211d2b;
    --ink:       #ece3d3;
    --ink-soft:  #aea5b1;
    --ink-faint: #8b8393;
    --rule:      #383140;
    --rule-soft: #2b2535;
    --accent:    #d29ac1;
    --accent-2:  #e7b8d5;
    --accent-bg: #2d2231;
    --rubric:    #e2836b;
    --rubric-2:  #f0a893;
    --gilt:      #d9b46b;
    --good:      #78c0b0;
    --good-bg:   #162623;
    --warn:      #d9b269;
    --warn-bg:   #262015;
    --fix:       #8ab3d8;
    --fix-bg:    #182231;
    --mark:      #5a4520;
    --spine:     rgba(0, 0, 0, .55);
    --grain:     rgba(255, 250, 235, .014);
    --wash-1:    rgba(60, 52, 78, .40);
    --wash-2:    rgba(210, 154, 193, .05);
    --shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 12px 34px rgba(0, 0, 0, .40);
    color-scheme: dark;
  }
}

:root[data-theme="light"] { color-scheme: light; }

:root[data-theme="dark"] {
  --paper:     #14121a;
  --paper-2:   #1c1925;
  --raised:    #211d2b;
  --ink:       #ece3d3;
  --ink-soft:  #aea5b1;
  --ink-faint: #8b8393;
  --rule:      #383140;
  --rule-soft: #2b2535;
  --accent:    #d29ac1;
  --accent-2:  #e7b8d5;
  --accent-bg: #2d2231;
  --rubric:    #e2836b;
  --rubric-2:  #f0a893;
  --gilt:      #d9b46b;
  --good:      #78c0b0;
  --good-bg:   #162623;
  --warn:      #d9b269;
  --warn-bg:   #262015;
  --fix:       #8ab3d8;
  --fix-bg:    #182231;
  --mark:      #5a4520;
  --spine:     rgba(0, 0, 0, .55);
  --grain:     rgba(255, 250, 235, .014);
  --wash-1:    rgba(60, 52, 78, .40);
  --wash-2:    rgba(210, 154, 193, .05);
  --shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 12px 34px rgba(0, 0, 0, .40);
  color-scheme: dark;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.62;
  -webkit-text-size-adjust: 100%;
}

/* The ground the leaves sit on: two soft washes and the laid lines of a
   sheet of paper, drawn with gradients because nothing here may be
   fetched. Fixed and behind everything, so it does not scroll with the
   text and does not repaint as the page grows. Dropped for a reader who
   has asked for less: it is decoration and nothing depends on it. */
body::before {
  content: "";
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    repeating-linear-gradient(0deg, var(--grain) 0 1px, transparent 1px 3px),
    radial-gradient(120% 80% at 8% -15%, var(--wash-1), transparent 62%),
    radial-gradient(90% 65% at 100% 0%, var(--wash-2), transparent 58%),
    var(--paper);
}
@media (prefers-reduced-transparency: reduce) {
  body::before { background: var(--paper); }
}

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--accent-2); }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: var(--paper); padding: .6rem 1rem;
}
.skip:focus { left: .5rem; top: .5rem; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }

/* ---------- top bar ---------- */

.topbar {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; gap: 1rem;
  padding: .55rem clamp(.75rem, 3vw, 1.6rem);
  background: color-mix(in srgb, var(--paper) 86%, transparent);
  backdrop-filter: blur(12px) saturate(1.1);
  border-bottom: 1px solid var(--rule);
  flex-wrap: wrap;
}
/* The head rule a printer sets under a running head: a hairline of the
   book's own red, not another grey line. */
.topbar::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: linear-gradient(90deg,
    var(--rubric) 0, color-mix(in srgb, var(--rubric) 30%, transparent) 38%,
    transparent 72%);
  opacity: .55; pointer-events: none;
}

/* The wordmark. The subtitle is the whole argument of the project in five
   words, so it belongs in the mark rather than buried on the home page. */
/* Soft live-stream CTA. Lives inside .topbar so phone tuck and the
   layout test that requires .topbar at y=0 both keep working. Soft chips,
   no tip links, dismissible for the session. */
.live-now {
  flex: 1 0 100%;
  display: flex; flex-wrap: wrap; align-items: center;
  gap: .35rem .65rem;
  margin: 0 0 .1rem;
  padding: .32rem .6rem;
  border-radius: 8px;
  background: color-mix(in srgb, var(--accent) 9%, var(--raised));
  border: 1px solid color-mix(in srgb, var(--accent) 20%, var(--rule));
  color: var(--ink);
  font-size: .84rem; line-height: 1.35;
}
.live-now[hidden] { display: none !important; }
.live-now-dot {
  width: .55rem; height: .55rem; border-radius: 50%;
  background: var(--rubric); flex: 0 0 auto;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--rubric) 22%, transparent);
  animation: live-pulse 1.6s ease-in-out infinite;
}
@keyframes live-pulse {
  50% { opacity: .45; box-shadow: 0 0 0 5px color-mix(in srgb, var(--rubric) 10%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  .live-now-dot { animation: none; }
}
.live-now-text { flex: 1 1 12rem; min-width: 0; }
.live-now-text strong { color: var(--rubric); font-weight: 700; }
.live-now-links {
  display: inline-flex; flex-wrap: wrap; align-items: center;
  gap: .3rem .45rem;
}
.live-now-links a {
  font-weight: 600; text-decoration: none; font-size: .8rem;
  padding: .18rem .58rem; border-radius: 999px;
  background: var(--accent-bg); color: var(--accent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 22%, transparent);
}
.live-now-links a:hover {
  background: var(--accent); color: var(--paper);
  box-shadow: none;
}
.live-now-stay {
  color: var(--ink-faint); font-size: .78rem;
  white-space: nowrap;
}
.live-now-dismiss {
  margin-left: auto;
  font-size: .75rem !important;
  padding: .2rem .55rem !important;
}

.brand {
  display: inline-flex; align-items: center; gap: .5rem;
  color: var(--ink); text-decoration: none; white-space: nowrap;
}
.brand-mark { color: var(--rubric); display: flex; align-items: center; }
.brand:hover .brand-mark { color: var(--rubric-2); }
.brand-text { display: flex; flex-direction: column; line-height: 1.05; }
.brand-name {
  font-family: var(--serif); font-size: 1.16rem; font-weight: 600;
  letter-spacing: .005em;
}
.brand-sub {
  font-size: .62rem; color: var(--ink-faint);
  letter-spacing: .07em; text-transform: uppercase;
}
.brand:hover .brand-name { color: var(--accent); }

/* Visible only to screen readers. */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

.nav { display: flex; gap: .15rem; flex: 1; flex-wrap: wrap; }
.nav a {
  padding: .34rem .68rem; border-radius: 999px;
  color: var(--ink-soft); text-decoration: none;
  font-size: .875rem; white-space: nowrap;
}
.nav a:hover { background: var(--paper-2); color: var(--ink); }
.nav a[aria-current="page"] {
  background: var(--accent-bg); color: var(--accent); font-weight: 600;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 22%, transparent);
}

.tools { display: flex; gap: .3rem; align-items: center; }

button.ghost {
  font: inherit; font-size: .84rem;
  background: transparent; color: var(--ink-soft);
  border: 1px solid var(--rule); border-radius: 999px;
  padding: .3rem .7rem; cursor: pointer;
}
button.ghost:hover { background: var(--paper-2); color: var(--ink); border-color: var(--ink-faint); }

/* ---------- layout ---------- */

main { padding: clamp(1.2rem, 4vw, 2.6rem) clamp(.9rem, 4vw, 2rem) 4rem; }
.wrap { max-width: 60rem; margin: 0 auto; }
.wrap-wide { max-width: 74rem; margin: 0 auto; }

h1, h2, h3, h4 {
  font-family: var(--serif); line-height: 1.18; font-weight: 600;
  text-wrap: balance;
}
h1 {
  font-size: clamp(1.9rem, 5.2vw, 3rem); margin: 0 0 var(--gap-tight);
  letter-spacing: -.018em;
}
h2 {
  font-size: 1.5rem; margin: var(--gap-part) 0 var(--gap-tight);
  letter-spacing: -.008em;
}
h3 { font-size: 1.12rem; margin: var(--gap-wide) 0 var(--gap-hair); }

/* Everything the volume says in its own voice is set in the volume's own
   type. The scripture always was; the prose around it -- the lead
   paragraphs, the notes on a work, the reasoning under a thread, the
   footer -- was in the interface's sans, which meant the codex stopped at
   the edge of the leaf and the rest of the site read like a form. The
   division now is the one a printed book uses: text face for anything
   written to be read, sans for the apparatus that operates it. Labels,
   buttons, table heads, counts and dates stay sans, because they are not
   prose and are read by glance rather than by line. */
.lede,
.era-intro,
.thread-card p,
.stop-why,
.stance .why,
.note-block,
.callout,
.finding p,
.lex-text,
.foot p,
.empty {
  font-family: var(--serif);
}

.lede {
  font-size: 1.16rem; line-height: 1.6; color: var(--ink-soft);
  max-width: 42rem; text-wrap: pretty;
}
.muted { color: var(--ink-faint); font-size: .9rem; }
.tiny  { font-size: .78rem; color: var(--ink-faint); }

.rule {
  border: 0; border-top: 1px solid var(--rule); margin: var(--gap-wide) 0;
}
/* A rule and the heading under it are one gesture: the heading does not
   also need its own three rems of air above it. */
.rule + h2, .rule + .group-head h2 { margin-top: var(--gap-wide); }

/* The canon table's own footnotes: why a book is absent, or which part of
   one is here. Closed by default -- the table is a table first. */
.why { margin-top: .3rem; max-width: 34rem; }
.why > summary { cursor: pointer; color: var(--accent); }
.why p { margin: .35rem 0 0; }

/* ---------- home ---------- */

/* A title page. The rule above the title is the one a printer sets there,
   and the mark on it is the same five rectangles as the tab icon. */
.hero { padding: 1.6rem 0 .5rem; position: relative; }
.hero::before {
  content: "";
  display: block; width: 3.2rem; height: 2px; margin: 0 0 1.3rem;
  background: var(--rubric); opacity: .8;
}
.hero h1 { max-width: 18ch; }
.hero .lede { margin-top: 1rem; }
/* The apparatus under the argument: what is checked and what is admitted.
   Set smaller and softer than the lede, because a reader who wants it is
   already reading down, and one who does not should meet the eras sooner. */
.hero-for {
  margin-top: .9rem; max-width: 42rem;
  font-size: .95rem; line-height: 1.6; color: var(--ink-faint);
  text-wrap: pretty;
}

/* Hidden by default and shown only at phone width, where the paragraphs are
   clamped. Declared before the media query so the query wins: the other way
   round this rule overrides it and the button is never shown at all, which
   leaves the clamped text with no way to open. */
.hero-more {
  display: none; margin-top: .35rem;
  background: none; border: 0; padding: 0;
  font: inherit; font-size: .78rem; font-family: var(--mono);
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-faint); cursor: pointer;
}
.hero-more:hover { color: var(--accent); }

/* The door the front page used to bury in a paragraph. One line: a kicker,
   the first work, and the sentence that sells the edition. Not a card and
   not a verse -- those would push the jump box off a phone. */
.start-here {
  display: flex; align-items: baseline; flex-wrap: wrap;
  gap: .15rem .7rem;
  margin: .85rem 0 0;
  padding: .65rem 0 0;
  border-top: 1px solid var(--rule);
  max-width: 46rem;
}
.start-here-kicker {
  font-family: var(--mono); font-size: .7rem; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase; color: var(--rubric);
}
.start-here-title {
  font-family: var(--serif); font-size: 1.12rem; font-weight: 600;
  color: inherit; text-decoration: none;
  text-underline-offset: 3px;
}
.start-here-title:hover { color: var(--accent); text-decoration: underline; }
.start-here-why {
  flex: 1 1 12rem;
  font-size: .88rem; color: var(--ink-faint); font-style: italic;
}

@media (max-width: 620px) {
  /* The why is already the last line of the lede. Repeating it here on a
     320px screen wrapped the door onto three lines and pushed the jump
     box off an iPhone SE. Kicker and title are the door; the sentence
     stays in the paragraph above. */
  .start-here { margin-top: .45rem; padding-top: .4rem; gap: .15rem .45rem; }
  .start-here-why { display: none; }
  .start-here-title { font-size: 1.02rem; }
}

/* On a phone the hero is the whole first screen, and it was all prose: a
   72px headline over a 356px paragraph, with the box you type a reference
   into five screens below. The argument keeps its full voice on a wide
   screen, where there is room for it beside everything else; here it is set
   at reading size rather than display size so that what a visitor can
   actually do is on the screen with it.

   The second paragraph goes behind a line at this width. It is written for
   a reader who already knows the field and is deciding on the apparatus --
   a real reader, and not the one who needs the first screen. */
@media (max-width: 620px) {
  .hero { padding-top: 1rem; }
  .hero h1 { font-size: 1.55rem; max-width: 22ch; }
  /* Both paragraphs fold on a phone, and the first one is the reason this
     is here: six lines of dense prose at 264px, above everything a visitor
     can act on. Three lines say what this is; the rest is there for whoever
     wants it, one tap away and fully in the page for a crawler either way.
     "It opens with a war poem, not with Genesis" is the sentence that sells
     this edition and it was the sixth line down, unread. */
  .hero .lede {
    font-size: 1rem; line-height: 1.5; margin-top: .7rem;
    max-height: 4.5em; overflow: hidden;
  }
  .hero .lede.is-open { max-height: none; }
  .hero-for {
    margin-top: .6rem; font-size: .88rem; line-height: 1.5;
    max-height: 3em; overflow: hidden;
  }
  .hero-for.is-open { max-height: none; }
  .hero-more { display: inline-block; }
}


/* ---------- the way in, for a reader who knows where they are going ----------

   Two controls over the eras: a reference box and a canon filter. They sit
   between the hero and the timeline, so they read as the way into the list
   rather than as a toolbar bolted to the top of the page. Nothing is boxed
   or raised as a unit -- the controls carry their own borders from the base
   input rules, and the group is separated by space alone, the way the
   colophon and the eras already are. */
.home-find { margin: var(--gap-wide) 0 0; }

.home-jump { display: flex; gap: var(--gap-tight); align-items: center; }
/* The box is the control here and the button is its confirmation, so the box
   takes the room and the button takes only what its word needs. */
.home-jump-input { flex: 1 1 auto; min-width: 0; }
.home-jump-go { flex: 0 0 auto; padding-block: .42rem; }

/* "That could be: Isaiah · Second Isaiah · Third Isaiah" -- an answer, so it
   is given the space of one, not tucked under as a caption. Empty it takes
   no room at all, rather than reserving a line that usually says nothing. */
.home-jump-says {
  margin-top: var(--gap-tight); font-size: .88rem; color: var(--ink-soft);
}
.home-jump-says:empty { display: none; }
.home-jump-says a { margin: 0 .1rem; }

.home-filter {
  display: flex; align-items: center; gap: var(--gap-tight);
  margin-top: var(--gap);
}
.home-filter-label {
  flex: 0 0 auto; font-family: var(--mono);
  font-size: .72rem; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-faint);
}
.home-filter-select { flex: 1 1 auto; min-width: 0; max-width: 26rem; }
/* Before its canons are fetched the select still opens -- on "the whole
   library", which is true and is the state the page is in. No spinner and no
   disabled control: the fill lands on the first reach for it, and a reader
   who beats it sees the one option that was always correct. */
.home-filter.is-waiting .home-filter-select { color: var(--ink-soft); }

.home-filter-says {
  margin: var(--gap-tight) 0 0; font-size: .88rem; color: var(--ink-faint);
  font-family: var(--serif);
}
.home-filter-says:empty { display: none; }

/* Filtered, the timeline is a narrowed list rather than a shortened one, so
   the works that remain are what carries the eye and the empty eras are gone
   rather than sitting there empty. */
.timeline.is-filtered .era { margin-bottom: .35rem; }

@media (max-width: 620px) {
  /* The label above the select rather than beside it: at this width "Show"
     and a 26rem select in one row leaves the select too narrow to read a
     canon's name in. */
  .home-filter { flex-wrap: wrap; gap: var(--gap-hair) var(--gap-tight); }
  .home-filter-select { flex-basis: 100%; max-width: none; }
}

/* The numbers are a colophon rather than a dashboard: a quiet line of
   figures under a rule, in the book's own type, with nothing boxed. They
   used to be five display-size columns that made the front page look like
   a stats page. The counts are still there and still checked; they just
   no longer shout. */
.stats {
  display: flex; flex-wrap: wrap; gap: .2rem 1.5rem;
  margin: 1.6rem 0 .4rem;
  border-top: 1px solid var(--rule);
  padding-top: .7rem;
  align-items: baseline;
}
.stat { padding: 0; display: flex; align-items: baseline; gap: .35rem; }
.stat b {
  font-family: var(--serif); font-size: 1.02rem;
  font-weight: 600; letter-spacing: -.01em; line-height: 1.2;
  font-variant-numeric: tabular-nums lining-nums;
}
.stat span {
  font-size: .78rem; color: var(--ink-faint);
}

/* The volume's own aside: not scripture, not a control. Gold leaf, which
   is the one material here that was neither. */
/* The day's passage: a real verse, picked by what the reader has been
   searching for. Set as a quotation rather than as a card of furniture,
   because it is scripture and the page has a way of setting scripture. */
.today {
  margin: var(--gap-wide) 0 0;
  padding: var(--gap) 0 0;
  border-top: 1px solid var(--rule);
  max-width: 42rem;
}
.today-eyebrow {
  margin: 0 0 var(--gap-tight); font-family: var(--sans);
  font-size: .72rem; font-weight: 600; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ink-faint);
}
.today-text {
  font-family: var(--serif); font-size: 1.22rem; line-height: 1.55;
  margin: 0; padding-left: var(--gap); border-left: 2px solid var(--gilt);
  text-wrap: pretty;
}
.today-foot {
  display: flex; align-items: baseline; gap: var(--gap); flex-wrap: wrap;
  margin: var(--gap-tight) 0 0; padding-left: calc(var(--gap) + 2px);
}
.today-ref { font-family: var(--sans); font-size: .82rem; }
.today-forget {
  font: inherit; font-family: var(--sans); font-size: .74rem;
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--ink-faint); text-decoration: underline;
  text-underline-offset: 2px; margin-left: auto;
}
.today-forget:hover { color: var(--ink); }

.callout {
  border-left: 2px solid var(--gilt);
  background: var(--paper-2);
  padding: var(--gap) calc(var(--gap) + .1rem);
  border-radius: 0 var(--radius) var(--radius) 0;
  margin: var(--gap-wide) 0; font-size: 1rem; line-height: 1.6;
  max-width: 46rem;
}
.callout p { margin: .35rem 0; }
.callout p:first-child { margin-top: 0; }
.callout p:last-child { margin-bottom: 0; }
.callout summary {
  cursor: pointer; font-family: var(--serif); font-weight: 600;
  color: var(--ink); list-style: none;
}
.callout summary::-webkit-details-marker { display: none; }
.callout summary::after {
  content: " +"; color: var(--ink-faint); font-weight: 400;
}
.callout[open] summary::after { content: " –"; }
.callout[open] summary { margin-bottom: .45rem; }

/* ---------- timeline ---------- */

.timeline { position: relative; margin: 2rem 0 0; padding-left: 1.6rem; }
.timeline::before {
  content: ""; position: absolute; left: .34rem; top: .6rem; bottom: .6rem;
  width: 2px; background: linear-gradient(var(--rule), var(--rule-soft));
}

.era { position: relative; margin: 0 0 .5rem; }
.era::before {
  content: ""; position: absolute; left: -1.6rem; top: 1.15rem;
  width: .72rem; height: .72rem; border-radius: 50%;
  background: var(--paper); border: 2px solid var(--accent);
}
.era[data-collection="1"]::before { border-color: var(--ink-faint); }

.era-head {
  width: 100%; text-align: left; cursor: pointer;
  background: transparent; border: 0; border-radius: var(--radius);
  padding: .75rem .8rem; font: inherit; color: inherit;
  display: flex; align-items: baseline; gap: .7rem; flex-wrap: wrap;
}
.era-head:hover { background: var(--paper-2); }
/* A section numeral is the structure of the volume, which is rubric's one
   job. Declared once: this was two blocks, the second overriding the accent
   the first had just set. */
.era-num {
  font-family: var(--mono); font-size: .72rem;
  color: var(--rubric);
  border: 1px solid color-mix(in srgb, var(--rubric) 35%, transparent);
  border-radius: 4px; padding: .05rem .3rem; letter-spacing: .04em;
}
.era[data-collection="1"] .era-num { color: var(--ink-faint); border-color: var(--rule); }
.era-name { font-family: var(--serif); font-size: 1.14rem; font-weight: 600; }
.era-date { color: var(--ink-faint); font-size: .84rem; font-variant-numeric: tabular-nums; }
.era-count { margin-left: auto; color: var(--ink-faint); font-size: .78rem; }

.era-body { padding: 0 .8rem 1.1rem 1.5rem; display: none; }
.era[open] .era-body, .era.open .era-body { display: block; }
.era-intro { font-size: .93rem; color: var(--ink-soft); max-width: 46rem; margin: 0 0 1rem; }

.works { display: grid; gap: .4rem; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); }
.work {
  display: block; text-decoration: none; color: inherit;
  background: var(--raised); border: 1px solid var(--rule-soft);
  border-radius: 8px; padding: .55rem .7rem;
  transition: border-color .15s, background-color .15s, transform .15s;
}
.work:hover {
  border-color: var(--accent); background: var(--accent-bg);
  transform: translateY(-1px);
}
@media (prefers-reduced-motion: reduce) {
  .work { transition: none; }
  .work:hover { transform: none; }
}
.work-title { font-family: var(--serif); font-size: .97rem; display: block; }
.work-meta { font-size: .74rem; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.work.note-only { background: transparent; border-style: dashed; }
.work.note-only .work-meta { font-style: italic; }

/* ---------- contents ---------- */

.toolbar {
  display: flex; gap: .5rem; align-items: center; flex-wrap: wrap;
  margin: 1.4rem 0;
}
.seg { display: inline-flex; border: 1px solid var(--rule); border-radius: 999px; overflow: hidden; }
.seg button {
  font: inherit; font-size: .82rem; padding: .34rem .85rem;
  background: transparent; color: var(--ink-soft); border: 0; cursor: pointer;
}
.seg button[aria-pressed="true"] { background: var(--accent); color: var(--paper); }

input[type="search"], input[type="text"], select {
  font: inherit; font-size: .92rem; color: var(--ink);
  background: var(--raised); border: 1px solid var(--rule);
  border-radius: 8px; padding: .45rem .7rem;
}
input[type="search"] { width: 100%; }

table.grid { width: 100%; border-collapse: collapse; font-size: .9rem; }
table.grid th, table.grid td {
  text-align: left; padding: .5rem .7rem;
  border-bottom: 1px solid var(--rule-soft);
  vertical-align: baseline;
}
/* A column of figures is read down, so it is aligned down: right, lining,
   and one width per digit. Left-ragged numbers in a table are a column you
   have to read rather than one you can compare. */
table.grid th.num, table.grid td.num {
  text-align: right;
  font-variant-numeric: tabular-nums lining-nums;
  white-space: nowrap;
  /* Shrink to their content and give the slack to the column carrying the
     names, rather than each column taking a fifth of a sixty-rem table and
     leaving a hand's width of nothing between a work and its date. */
  width: 1%;
}
table.grid th:first-child, table.grid td:first-child { width: 1%; white-space: nowrap; }
/* The work is the thing being listed, so it is the thing set in the book's
   own type -- everything beside it is a fact about it and stays sans. */
table.grid td a { font-family: var(--serif); font-size: 1.02em; }
/* The head is not sticky, and cannot be. It was, at top: 3.2rem, to clear
   the bar -- but a wide table is wrapped in .scroller so a phone can push it
   sideways, and a box that scrolls in one axis is a scrollport in both. A
   sticky offset is measured from the scrollport rather than the viewport,
   and sticky pushes an element *down* to its offset even at scroll zero, so
   the head was landing 51px into the table and sitting on top of the first
   row -- The Song of the Sea on the contents, Genesis on the canons: in
   each case the first line of the thing being listed. Nothing in CSS gets
   both, because the wrapper that makes the table scrollable is the same box
   that redefines what the head is sticky to. The row is worth more than the
   head, so the head goes. */
table.grid thead th {
  border-bottom: 1px solid var(--rule); font-size: .78rem;
  text-transform: uppercase; letter-spacing: .05em; color: var(--ink-faint);
  font-weight: 600;
}
table.grid tbody tr:hover { background: var(--paper-2); }
/* The last row's rule is the table's own bottom edge twice over. */
table.grid tbody tr:last-child td { border-bottom: 0; }
.scroller { overflow-x: auto; border-radius: var(--radius); }
/* Focusable so a keyboard can scroll it, which means it also has to show
   that it has the focus. */
.scroller:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.yes { color: var(--good); font-weight: 700; }
.no  { color: var(--ink-faint); }
.part { color: var(--warn); font-weight: 700; }

/* ---------- threads ---------- */

.thread-cards {
  display: grid; gap: .8rem; margin: 1.8rem 0;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
}
/* A card is a way in, which makes it the reader's machinery rather than
   the book's -- so the rule down its edge is purple. It was red, which
   said "this is the structure of a text" about a link. */
.thread-card {
  display: block; text-decoration: none; color: inherit;
  background: var(--raised); border: 1px solid var(--rule-soft);
  border-left: 2px solid var(--accent);
  border-radius: var(--radius); padding: var(--gap) calc(var(--gap) + .1rem);
  box-shadow: var(--shadow);
  transition: border-color .15s, background-color .15s;
}
.thread-card:hover { border-color: var(--accent); background: var(--accent-bg); }
@media (prefers-reduced-motion: reduce) { .thread-card { transition: none; } }
.thread-card h2 {
  margin: 0 0 var(--gap-tight); font-size: 1.2rem; line-height: 1.22;
  text-wrap: balance;
}
.thread-card p {
  margin: 0 0 var(--gap); font-size: .97rem; line-height: 1.55;
  color: var(--ink-soft);
}
.thread-meta {
  font-size: .72rem; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-faint);
}

/* The spine down the left is the passage of time; each stop hangs off it. */
.thread { list-style: none; margin: 2rem 0 0; padding: 0; position: relative; }
.thread::before {
  content: ""; position: absolute; left: 3.1rem; top: .5rem; bottom: 2rem;
  width: 2px; background: linear-gradient(var(--accent), var(--rule-soft));
}

.stop { display: flex; gap: 1.1rem; margin: 0 0 1.7rem; position: relative; }

.stop-when {
  flex: 0 0 5.4rem; text-align: right; padding-top: .15rem;
  display: flex; flex-direction: column; align-items: flex-end; gap: .1rem;
}
.stop-era {
  font-family: var(--mono); font-size: .74rem; color: var(--accent);
  border: 1px solid var(--accent); border-radius: 4px;
  padding: .05rem .34rem; background: var(--paper);
  position: relative; z-index: 1;
}
.stop-date {
  font-size: .68rem; color: var(--ink-faint);
  font-variant-numeric: tabular-nums; max-width: 5.2rem;
}

.stop-card { flex: 1; min-width: 0; padding-left: .9rem; }
.stop-ref {
  display: inline-block; font-size: .78rem; font-weight: 600;
  text-decoration: none; margin-bottom: .5rem;
  text-transform: uppercase; letter-spacing: .04em;
}
.stop-text {
  font-family: var(--serif); font-size: 1.03rem; line-height: 1.6;
  margin: 0 0 .6rem; padding: 0; color: var(--ink);
}
.stop-vnum {
  font-family: var(--sans); font-size: .62em; font-weight: 600;
  color: var(--accent); vertical-align: .4em; margin-right: .3em;
}
.stop-why { margin: 0; font-size: .875rem; color: var(--ink-soft); }
.stop-aside {
  margin: .6rem 0 0; font-size: .78rem; color: var(--warn);
  background: var(--warn-bg); border-radius: 6px; padding: .5rem .65rem;
}

@media (max-width: 620px) {
  .thread::before { left: 2.2rem; }
  .stop { gap: .7rem; }
  .stop-when { flex-basis: 3.6rem; }
  .stop-date { font-size: .62rem; max-width: 3.6rem; }
  .stop-card { padding-left: .5rem; }
}

/* ---------- reader ---------- */

/* Everything on a reading page is the same column: the leaf sets the
   width and the head, the controls and the pager line up with it, so the
   page reads as one object rather than as a wide header with a page
   floating somewhere under it. */
.reader-head, .pager, .witnesses {
  max-width: var(--leaf); margin-left: auto; margin-right: auto;
}
.reader-head {
  border-bottom: 1px solid var(--rule);
  padding-bottom: var(--gap); margin-bottom: var(--gap);
}
.reader-head h1 { margin-bottom: var(--gap-hair); }
.crumbs { font-size: .8rem; color: var(--ink-faint); margin-bottom: .35rem; }
.crumbs a { color: var(--ink-faint); }

/* The way into the other chapters, and it used to take four rows of the
   screen before a word of scripture -- fifty numbers on Genesis, a hundred
   and fifty on Psalms, above the thing you came to read. It is two rows
   now and scrolls past that, and the reader puts the chapter you are in
   into view, which the old box did not: opening Psalm 119 showed you the
   first sixty numbers and left you to find it.

   Kept as a wall of numbers rather than turned into a menu, because it is
   also how you see how long a book is. */
.chapter-strip {
  /* Positioned so the links inside measure their offsetTop against this box
     rather than against the page: the reader scrolls the current chapter
     into view by that number, and against the page it is the distance from
     the top of the document, which puts the strip at its end every time. */
  position: relative;
  display: flex; gap: .3rem; flex-wrap: wrap;
  margin: var(--gap-tight) 0 0;
  max-height: 4.6rem; overflow-y: auto;
  padding: .15rem .15rem var(--gap-hair);
  scrollbar-width: thin;
  overscroll-behavior: contain;
  /* The half-row showing under the second is the whole point -- it says
     there are more -- but cut flat it reads as a clipping mistake. Faded,
     it reads as a page continuing. Purely decorative: a browser without
     mask-image gets the flat cut and loses nothing it needs. */
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 1.1rem), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 1.1rem), transparent);
}
/* Not while it is being used: a keyboard tabbing through the numbers would
   be tabbing into the faded part and appear to lose the focus ring. */
.chapter-strip:focus-within {
  -webkit-mask-image: none; mask-image: none;
}

/* On a phone the picker sits at the top, where a reader can reach it without
   reading to the end of the chapter first -- and folded, so it costs a line
   rather than the inch that would push the scripture off the screen. Both
   things the reader wants are then true at once: the way to another chapter
   is at hand, and the page opens on the Bible. */
/* Shut, this is one short line and it is measured: the budget check in
   layout.test.js allows 420px before the scripture starts, and every rule
   here is spending against it. The summary carries the fold's whole closed
   height, so it is set tight and the box adds no margin of its own beyond
   the hair it needs to not touch the title above. */
.chapter-fold { margin: var(--gap-hair) 0 0; }
.chapter-fold > summary {
  cursor: pointer; list-style: none;
  font-family: var(--mono); font-size: .7rem; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-faint);
  padding: .1rem 0; line-height: 1.3;
}
.chapter-fold > summary::-webkit-details-marker { display: none; }
.chapter-fold > summary::after {
  content: " ▾"; font-family: var(--sans);
}
.chapter-fold[open] > summary::after { content: " ▴"; }
.chapter-fold > summary:hover { color: var(--accent); }
.chapter-fold .chapter-strip { max-height: 7.5rem; }
.chapter-strip a {
  min-width: 2rem; text-align: center; padding: .18rem .4rem;
  border: 1px solid var(--rule-soft); border-radius: 6px;
  font-size: .8rem; line-height: 1.35; text-decoration: none;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums lining-nums;
}
.chapter-strip a:hover { border-color: var(--accent); color: var(--accent); }
.chapter-strip a[aria-current="true"] {
  background: var(--accent); color: var(--paper); border-color: var(--accent);
  font-weight: 600;
}

/* The text is set on a leaf rather than on the page's own ground: a
   brighter surface, a hairline, and a gutter shadow down the bound edge.
   That is the mark of this volume drawn at full size -- a spine and the
   leaves hanging off it -- and it is what tells the eye where the book
   stops and the apparatus around it starts.

   The measure is unchanged. The leaf is the measure plus its margins,
   which is why every control above it is sized from the same token. */
.reader {
  position: relative;
  /* The leaf is read and selected from, not clicked. Declared here rather
     than three hundred lines further down, where it was a second .reader
     block on its own. */
  cursor: text;
  font-family: var(--serif);
  font-size: var(--reader-size);
  line-height: 1.75;
  max-width: var(--leaf);
  margin: 0 auto;
  padding: clamp(1.3rem, 4vw, 2.5rem) clamp(1rem, 4vw, 2.5rem);
  background: var(--raised);
  border: 1px solid var(--rule-soft);
  border-radius: 2px var(--radius) var(--radius) 2px;
  /* Two leaves under this one, drawn as offset hairlines rather than as
     an image, so the chapter reads as a page in a book with more of the
     book behind it. */
  box-shadow:
    3px 3px 0 -1px var(--raised), 3px 3px 0 0 var(--rule-soft),
    6px 6px 0 -1px var(--raised), 6px 6px 0 0 var(--rule-soft),
    var(--shadow);
  hanging-punctuation: first;
  text-wrap: pretty;
  margin-top: .9rem;
}
.reader::before {
  content: "";
  position: absolute; left: 0; top: 0; bottom: 0; width: 9px;
  border-radius: 2px 0 0 2px;
  background: linear-gradient(90deg, var(--spine), transparent);
  pointer-events: none;
}
/* Set out this way the verse is a line, and a line's number belongs in
   the margin beside it rather than in front of it. */
.reader.verse-per-line .v {
  display: block; margin: 0 0 .5rem;
  padding-left: 2.1rem; text-indent: -2.1rem;
}
.reader.verse-per-line .vnum {
  display: inline-block; min-width: 1.7rem; text-align: right;
  margin-right: .4rem; vertical-align: baseline; font-size: .74em;
}
.reader .v { transition: background-color .25s; }
.reader .v.target { background: var(--mark); border-radius: 3px; }
/* The same mark on the plain pages, where the browser rather than the reader
   decides which verse was asked for. "Copy link to this verse" now hands out
   /read/amos/5/#v13 -- a real page with a real anchor -- so the verse that
   address names has to be visible on arrival, and clear of the sticky bar
   that would otherwise be sitting on top of it. */
.reader .v:target {
  background: var(--mark); border-radius: 3px; scroll-margin-top: 5.5rem;
}
.reader p { margin: 0 0 1.05rem; }

/* The verse number is a button now, but must still read as a superscript
   numeral inside flowing text rather than as a control. */
.vnum {
  font-family: var(--sans); font-size: .66em; font-weight: 700;
  color: var(--rubric); vertical-align: .38em;
  margin-right: .24em; font-variant-numeric: tabular-nums lining-nums;
  background: none; border: 0; padding: 0 .1em; cursor: pointer;
  line-height: 1;
}
.vnum:hover { color: var(--rubric-2); text-decoration: underline; }
/* Opened, it is a control rather than a numeral, so it changes colour
   as well as ground: purple is the site talking. */
.vnum[aria-expanded="true"] {
  background: var(--accent); color: var(--paper); border-radius: 3px;
}

/* A saved verse keeps a quiet marker in the margin of the text. */
.v.is-saved {
  background: linear-gradient(transparent 60%, var(--mark) 60%);
  border-radius: 2px;
}

.vmenu {
  position: absolute; z-index: 50;
  margin-top: .3rem;
  display: flex; flex-direction: column;
  background: var(--raised); border: 1px solid var(--rule);
  border-radius: var(--radius); box-shadow: var(--shadow);
  min-width: 15rem; padding: .25rem;
  font-family: var(--sans);
}
.vmenu button {
  font: inherit; font-size: .85rem; text-align: left;
  background: transparent; border: 0; border-radius: 6px;
  padding: .5rem .6rem; cursor: pointer; color: var(--ink);
}
.vmenu button:hover { background: var(--paper-2); }

.v { position: relative; }

/* ---------- saved ---------- */

.saved-row {
  border-bottom: 1px solid var(--rule-soft);
  padding: 1rem 0;
  display: flex; flex-direction: column; align-items: flex-start; gap: .5rem;
}
.saved-text {
  font-family: var(--serif); font-size: 1rem; line-height: 1.62;
  margin: 0; padding-left: .9rem;
  border-left: 3px solid var(--accent-bg);
  color: var(--ink);
}
.saved-note {
  font: inherit; font-size: .87rem; width: 100%;
  background: var(--paper-2); color: var(--ink);
  border: 1px solid var(--rule-soft); border-radius: 8px;
  padding: .45rem .6rem; resize: vertical;
}
.saved-note::placeholder { color: var(--ink-faint); }

.pager {
  display: flex; justify-content: space-between; gap: 1rem;
  margin: 2.6rem auto 0; max-width: var(--measure); align-items: center;
}
.pager a {
  text-decoration: none; font-size: .88rem; padding: .45rem .8rem;
  border: 1px solid var(--rule); border-radius: 8px; color: var(--ink-soft);
  max-width: 45%;
}
.pager a:hover { border-color: var(--accent); color: var(--accent); }
.pager .spacer { flex: 1; }

/* The editor's note on the work: prose about the book, so it is set in the
   book's type. It was in the interface's sans, which put a sentence of
   commentary in the same voice as the buttons under it. */
.note-block {
  font-family: var(--serif); font-size: 1rem; line-height: 1.6;
  color: var(--ink-soft);
  background: transparent; border-left: 2px solid var(--rule);
  border-radius: 0;
  padding: .2rem 0 .2rem var(--gap); margin: 0 auto var(--gap-wide);
  max-width: var(--leaf);
}

.reader-controls {
  display: flex; gap: .4rem; align-items: center; justify-content: flex-end;
  flex-wrap: wrap; margin: .9rem auto 0; max-width: var(--leaf);
}
/* The chapter's own heading, in the red a scribe would have changed pens
   for, with the hairline that separates it from the leaf above. */
.chapter-title {
  margin: 0 0 1.2rem;
  color: var(--rubric);
  letter-spacing: .16em; text-transform: uppercase; font-size: .72rem;
  font-family: var(--sans); font-weight: 700;
  padding-bottom: .5rem; border-bottom: 1px solid var(--rule-soft);
}
/* A note that is a correction rather than a remark. One work here is printed
   under a title that names a different and far more famous text, and the
   paragraph saying so has to read as a warning rather than as more
   commentary. The warn pair is the one the accuracy report already uses and
   is held to contrast by tests/python/test_palette.py. */
.note-block.is-caution {
  border-left-color: var(--warn); background: var(--warn-bg);
  color: var(--warn); padding-right: var(--gap); border-radius: var(--radius);
}

.note-block p { margin: .4rem 0; }
.note-block p:first-child { margin-top: 0; }
.note-block p:last-child { margin-bottom: 0; }

/* ---------- who wrote this, and when ----------
   Two columns, deliberately identical in weight. Neither tradition is
   styled as the default reading; the reader compares and decides. */

.positions { margin: 0 auto 1.6rem; max-width: var(--leaf); }

.positions-head {
  width: 100%; text-align: left; cursor: pointer; font: inherit; color: inherit;
  background: transparent; border: 1px solid var(--rule); border-radius: var(--radius);
  padding: .6rem .85rem; display: flex; align-items: center; gap: .6rem;
  font-size: .9rem;
}
.positions-head:hover { background: var(--paper-2); }
.positions-head .caret { color: var(--ink-faint); font-size: .75rem; margin-left: auto; }

.gap-tag {
  font-size: .68rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; padding: .1rem .45rem; border-radius: 4px;
}
.gap-tag.none   { background: var(--good-bg); color: var(--good); }
.gap-tag.narrow { background: var(--fix-bg);  color: var(--fix); }
.gap-tag.wide   { background: var(--warn-bg); color: var(--warn); }

.positions-body {
  display: none; flex-direction: column; gap: .7rem; margin-top: .6rem;
}
.positions.open .positions-body { display: flex; }

/* The two views sit side by side where there is room and stack where there
   is not; the date card above them always runs the full width, because the
   whole point of it is the distance between the two. */
.stances {
  display: grid; gap: .7rem;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

/* ---------- the date card ----------
   Two spans on one scale. Reading it should take about a second: if the bars
   overlap the positions agree about the date, and if they do not, the gap on
   the page is the gap in the scholarship. */
.datecard {
  background: var(--raised); border: 1px solid var(--rule-soft);
  border-radius: var(--radius); padding: .8rem .9rem;
}

.datescale { display: flex; flex-direction: column; gap: .45rem; }

.daterow { display: grid; align-items: center; gap: .5rem;
           grid-template-columns: 5.5rem 1fr 8.5rem; }

.datelabel {
  font-family: var(--sans); font-size: .68rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em; color: var(--ink-faint);
}

.datetrack {
  position: relative; height: 1.15rem; border-radius: 3px;
  background: var(--paper-2); overflow: hidden;
}

.datebar {
  position: absolute; top: 0; bottom: 0; border-radius: 3px; min-width: 3px;
}
.daterow.trad .datebar { background: var(--warn); }
.daterow.crit .datebar { background: var(--fix); }

/* A span read from a century or a named period is a looser claim than one
   read from a year, and is drawn as one rather than pretending to an
   exactness the position never had. */
.datebar.century,
.datebar.period,
.datebar.decade { opacity: .62; }
.datebar.period {
  background-image: repeating-linear-gradient(
    45deg, transparent 0 4px, rgba(255, 255, 255, .35) 4px 8px);
}

.dateunknown {
  position: absolute; inset: 0; display: flex; align-items: center;
  padding-left: .4rem; font-size: .7rem; color: var(--ink-faint);
  font-style: italic;
}

.datespan {
  font-family: var(--sans); font-size: .74rem; color: var(--ink-soft);
  text-align: right; font-variant-numeric: tabular-nums;
}

.dateverdict {
  margin: .6rem 0 0; font-size: .84rem; color: var(--ink-soft);
}

.datecite {
  margin: .45rem 0 0; padding-top: .45rem;
  border-top: 1px solid var(--rule-soft);
  font-size: .74rem; color: var(--ink-faint);
}
.datecite a { color: inherit; }

/* On a phone the three-column row has nowhere to go, so the figure moves
   under the bar rather than squeezing it to nothing. */
@media (max-width: 30rem) {
  .daterow { grid-template-columns: 5rem 1fr; row-gap: .15rem; }
  .datespan { grid-column: 2; text-align: left; }
}

.stance {
  background: var(--raised); border: 1px solid var(--rule-soft);
  border-radius: var(--radius); padding: .8rem .9rem;
}
.positions-h { margin: 0; font-size: inherit; font-weight: inherit; }

.stance h3 {
  margin: 0 0 .3rem; font-family: var(--sans); font-size: .7rem;
  text-transform: uppercase; letter-spacing: .06em; color: var(--ink-faint);
  font-weight: 700;
}
.stance .claim { font-family: var(--serif); font-size: 1rem; margin: 0 0 .4rem; }
.stance .why { font-size: .84rem; color: var(--ink-soft); margin: 0 0 .5rem; }

/* The citation is the point of the panel: it is what makes an editorial
   claim checkable rather than merely asserted. */
.stance .cite {
  font-size: .76rem; color: var(--ink-faint); margin: 0;
  padding-top: .45rem; border-top: 1px solid var(--rule-soft);
  font-style: italic;
}

.positions-foot {
  font-size: .78rem; color: var(--ink-faint); margin: 0;
}

/* ---------- lexicon panel ----------
   A sheet rather than a tooltip: entries run long and carry links, which a
   hover bubble cannot hold. Side panel on desktop, bottom sheet on a phone,
   where thumbs reach the bottom of the screen and not the right edge. */

.lex {
  position: fixed; z-index: 60;
  background: var(--raised);
  border: 1px solid var(--rule);
  box-shadow: var(--shadow);
  display: flex; flex-direction: column;
  right: 1rem; bottom: 1rem; top: auto;
  width: min(24rem, calc(100vw - 2rem));
  max-height: min(28rem, 70vh);
  border-radius: var(--radius);
  animation: lex-in .16s ease-out;
}

@keyframes lex-in { from { opacity: 0; transform: translateY(.4rem); } }
@media (prefers-reduced-motion: reduce) { .lex { animation: none; } }

.lex-head {
  display: flex; align-items: baseline; gap: .6rem;
  padding: .7rem .9rem .5rem;
  border-bottom: 1px solid var(--rule-soft);
}
.lex-head h2 {
  font-family: var(--serif); font-size: 1.06rem; flex: 1; margin: 0;
}
.lex-close {
  background: transparent; border: 0; cursor: pointer;
  color: var(--ink-faint); font-size: 1rem; padding: .1rem .3rem;
  line-height: 1;
}
.lex-close:hover { color: var(--ink); }

.lex-body { overflow-y: auto; padding: .8rem .9rem 1rem; }
.lex-text { margin: 0 0 .7rem; font-size: .92rem; line-height: 1.6; }
.lex-none { margin: 0; font-size: .9rem; color: var(--ink-soft); }

.lex-flag {
  margin: 0 0 .7rem; font-size: .8rem;
  padding: .5rem .65rem; border-radius: 6px;
  background: var(--warn-bg); color: var(--warn);
  border-left: 3px solid var(--warn);
}
.lex-flag.measure { background: var(--fix-bg); color: var(--fix); border-color: var(--fix); }

.lex-cite {
  margin: 0 0 .8rem; font-size: .76rem; font-style: italic;
  color: var(--ink-faint);
  padding-top: .5rem; border-top: 1px solid var(--rule-soft);
}

@media (max-width: 620px) {
  .lex {
    left: 0; right: 0; bottom: 0; width: auto;
    border-radius: var(--radius) var(--radius) 0 0;
    border-left: 0; border-right: 0; border-bottom: 0;
    max-height: 62vh;
  }
}

/* A quiet hint that the text responds to a tap at all. */

.apparatus {
  font-family: var(--sans); font-size: .8rem; color: var(--ink-faint);
  border-top: 1px solid var(--rule-soft); padding-top: .8rem;
  margin: 2.4rem auto 0; max-width: var(--measure);
}
.apparatus code { font-family: var(--mono); font-size: .95em; background: var(--paper-2); padding: 0 .25em; border-radius: 3px; }

/* ---------- search ---------- */

/* The query and the book it is asked of, on one line, wrapping to two on a
   phone rather than squeezing the field down to nothing. */
.search-bar { display: flex; gap: var(--gap-tight); flex-wrap: wrap; }
.search-bar input[type="search"] { flex: 1 1 16rem; width: auto; }
.search-bar select { flex: 0 1 15rem; max-width: 100%; }

/* A reference is an answer, not a result, so it is set apart from the list
   of verses below rather than being the first row of it. */
.jump-box {
  margin: var(--gap-wide) 0 0; padding: var(--gap) calc(var(--gap) + .1rem);
  background: var(--raised); border: 1px solid var(--rule-soft);
  border-left: 2px solid var(--accent); border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.jump-head {
  margin: 0 0 var(--gap-tight); font-family: var(--sans);
  font-size: .72rem; font-weight: 600; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ink-faint);
}
.jump-link {
  display: flex; align-items: baseline; gap: var(--gap-tight);
  flex-wrap: wrap; padding: .35rem 0; text-decoration: none; color: inherit;
  border-top: 1px solid var(--rule-soft);
}
.jump-link:first-of-type { border-top: 0; }
/* The one-line form of the same correction, under a reference that resolved
   to a work whose name is doing double duty. The heading above it says "that
   is a place in the volume", which for this one work is a confident answer to
   a question the reader did not ask. */
.jump-caution {
  margin: .4rem 0 0; font-family: var(--sans); font-size: .78rem;
  line-height: 1.45; color: var(--warn);
}
.jump-where {
  font-family: var(--serif); font-size: 1.06rem; color: var(--accent);
  text-decoration: underline; text-underline-offset: 2px;
}
.jump-link:hover .jump-where { color: var(--accent-2); }
.jump-era {
  font-family: var(--sans); font-size: .74rem; color: var(--ink-faint);
  margin-left: auto;
}

.results { margin-top: 1.6rem; }
.result {
  border-bottom: 1px solid var(--rule-soft); padding: .85rem 0;
}
.result-ref {
  display: flex; gap: .5rem; align-items: baseline; flex-wrap: wrap;
  font-size: .8rem; margin-bottom: .2rem;
}
.result-ref a { font-weight: 600; text-decoration: none; }
.result-era { color: var(--ink-faint); }
/* Beside the reference on a result from a work no canon receives. Quiet on
   purpose: it is a note about where the verse comes from, not a warning
   about it, and the works it lands on are half of what this volume is for. */
/* The sentence that says what the mark is measured against, under the search
   controls: "Mark verses not in [any canon]". A sentence rather than a
   labelled box, because the rule is short enough to read as one. */
.marking {
  margin: -.6rem 0 0; font-size: .82rem; color: var(--ink-soft);
}
.marking select { font-size: .82rem; padding: .15rem .45rem; width: auto; }
/* What a tradition's answer says under its name: which canon, and what the
   canon key alone does not say -- that the apocrypha are read and not
   doctrinal, that this church's New Testament is five books shorter. Set as
   prose rather than as another label, because it is a sentence. */
.jump-said {
  margin: .25rem 0 .55rem; font-family: var(--sans); font-size: .82rem;
  line-height: 1.5; color: var(--ink-soft);
}
/* A tradition with no column of its own. Not a link, because there is
   nowhere honest to send it, and marked so that it does not read as one
   that failed to load. */
.jump-link.is-unplaced { cursor: default; }
.jump-link.is-unplaced .jump-where {
  color: var(--ink); text-decoration: none;
}

/* A date carried down from the era a work is filed under, rather than read
   off a dated position of its own. The timeline draws these fainter for the
   same reason: it is a looser claim, and printing it in the same ink as a
   cited one says the volume knows something it does not. */
.grid td.is-era { opacity: .72; font-style: italic; }

.result-outside {
  font-family: var(--sans); font-size: .72rem; color: var(--ink-faint);
  border: 1px solid var(--rule-soft); border-radius: 999px;
  padding: 0 .45rem; white-space: nowrap;
}
.result-text { font-family: var(--serif); font-size: 1rem; line-height: 1.62; }
mark { background: var(--mark); color: inherit; padding: 0 .1em; border-radius: 2px; }

.progress { height: 2px; background: var(--rule-soft); border-radius: 2px; overflow: hidden; margin: 1rem 0; }
.progress i { display: block; height: 100%; background: var(--accent); width: 0; transition: width .2s; }

/* ---------- accuracy ---------- */

.finding {
  border: 1px solid var(--rule-soft); border-radius: var(--radius);
  padding: .85rem 1rem; margin: .6rem 0; background: var(--raised);
}
.finding h4 { margin: 0 0 .3rem; font-size: .98rem; }
.finding p { margin: 0; font-size: .9rem; color: var(--ink-soft); }
.badge {
  display: inline-block; font-size: .68rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em;
  padding: .12rem .45rem; border-radius: 4px; margin-right: .5rem;
  vertical-align: .1em;
}
.badge.pass      { background: var(--good-bg); color: var(--good); }
.badge.fixed     { background: var(--fix-bg);  color: var(--fix); }
.badge.corrected { background: var(--warn-bg); color: var(--warn); }
.badge.gap       { background: var(--warn-bg); color: var(--warn); }
.badge.note      { background: var(--paper-2); color: var(--ink-faint); }

.group-head { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; }
.group-head h2 { margin-bottom: .2rem; }

/* ---------- misc ---------- */

.loading { color: var(--ink-faint); font-size: .9rem; padding: 2rem 0; }
.empty { color: var(--ink-faint); padding: 2rem 0; font-size: .95rem; }

.chips { display: flex; gap: .35rem; flex-wrap: wrap; margin: .8rem 0; }
.chip {
  font-size: .78rem; padding: .2rem .6rem; border-radius: 999px;
  border: 1px solid var(--rule); color: var(--ink-soft);
  background: var(--raised); cursor: pointer; font-family: inherit;
  text-decoration: none;
}
.chip:hover { border-color: var(--accent); color: var(--accent); }
.chip[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: var(--paper); }

.foot {
  border-top: 1px solid var(--rule); padding: 1.6rem clamp(.9rem, 4vw, 2rem) 3rem;
  font-size: .84rem; color: var(--ink-soft);
}
.foot p { max-width: 52rem; margin: .4rem 0; }

.keys { margin: 1.2rem 0 0; max-width: 52rem; }
.keys summary {
  cursor: pointer; font-size: .84rem; color: var(--ink-soft);
  padding: .3rem 0;
}
.keys summary:hover { color: var(--ink); }
.keys dl {
  display: grid; grid-template-columns: auto 1fr; gap: .4rem .9rem;
  margin: .7rem 0; font-size: .84rem; align-items: baseline;
}
.keys dt { margin: 0; }
.keys dd { margin: 0; color: var(--ink-soft); }
.keys kbd {
  font-family: var(--mono); font-size: .78rem;
  background: var(--paper-2); border: 1px solid var(--rule);
  border-radius: 4px; padding: .1rem .35rem; color: var(--ink);
}

@media (max-width: 620px) {
  /* The nav has to claim lines of its own. `width: 100%` cannot make it: the
     base rule's `flex: 1` sets a basis of 0%, which beats width on a flex
     item, so the nav collapsed into whatever was left beside the brand --
     ninety pixels on a phone -- and crushed all seven links into a sliver at
     the right edge. The basis is the thing that has to change.

     Given its own width it wraps to two rows and every link is visible. A
     link behind a sideways swipe is a feature most people never find out
     about, which is too high a price for one row; the height it costs is
     given back below by tucking the whole bar away as you read. */
  .topbar { gap: .5rem .6rem; padding: .45rem .7rem; }
  .nav {
    flex: 1 0 100%; order: 3;
    gap: .12rem .15rem;
  }
  .nav a { padding: .3rem .58rem; font-size: .84rem; flex: 0 0 auto; }

  /* Reading is what the screen is for. The bar leaves as you go down the
     page and comes back the moment you turn back up, so the two rows of
     links cost nothing while you are actually reading. */
  .topbar { transition: transform .2s ease; }
  .topbar.tucked { transform: translateY(-100%); }

  /* The live banner is a soft desktop CTA. On a phone it costs a row the
     fold and scripture-budget checks cannot spare, and the bar already
     tucks away while reading -- so the strip stays off small screens. */
  .live-now { display: none !important; }


  /* Keep the name, drop only the strapline. A lone icon is not a wordmark. */
  .brand-sub { display: none; }
  .brand-name { font-size: 1.02rem; }
  .tools { margin-left: auto; }
  table.grid thead th { top: 5.6rem; }
}

@media print {
  .topbar, .foot, .pager, .chapter-strip, .toolbar { display: none; }
  body { background: #fff; color: #000; }
  .reader { max-width: none; }
}

/* The threads block on the home page: the argument, before the filing. */
.threads-hero { margin: 2.2rem 0 .5rem; }
.threads-hero h2 { margin: 0 0 .3rem; font-size: 1.34rem; }
.threads-hero .thread-cards { margin-top: 1rem; }
.threads-hero .thread-card h3 {
  margin: 0 0 .45rem; font-family: var(--serif); font-size: 1.1rem;
  font-weight: 600; line-height: 1.25; text-wrap: balance;
}

/* Chapter jump, for books too long to scan as a grid. */
.chapter-jump {
  display: flex; gap: var(--gap-hair); align-items: center;
  margin: var(--gap-tight) 0 0;
}
.chapter-jump input {
  width: 6.5rem; font: inherit; font-size: .85rem;
  background: var(--raised); color: var(--ink);
  border: 1px solid var(--rule); border-radius: 8px; padding: .32rem .55rem;
}
.chapter-jump button { cursor: pointer; }

/* Content arriving should settle rather than snap. */
main > * { animation: rise .18s ease-out; }
@keyframes rise { from { opacity: 0; transform: translateY(.25rem); } }
@media (prefers-reduced-motion: reduce) {
  main > * { animation: none; }
  .topbar { transition: none; }
}

.loading::after {
  content: ""; display: inline-block; width: .5rem; height: .5rem;
  margin-left: .4rem; border-radius: 50%;
  background: var(--accent); animation: pulse 1s ease-in-out infinite;
}
@keyframes pulse { 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) { .loading::after { animation: none; } }

/* ---------- place, in the definition panel ----------
   Colour carries how firm the identification is, so a region never looks
   like a pin and a guess never looks like a survey point. */

.place {
  background: var(--paper-2); border-radius: var(--radius);
  padding: .75rem .85rem; margin: 0 0 .9rem;
  border-left: 3px solid var(--good);
}
.place-approximate { border-left-color: var(--warn); }
.place-region      { border-left-color: var(--fix); }
.place-within      { border-left-color: var(--ink-faint); }

.place-kind {
  display: flex; align-items: center; gap: .4rem;
  font-size: .7rem; text-transform: uppercase; letter-spacing: .06em;
  font-weight: 700; color: var(--good); margin-bottom: .4rem;
}
.place-approximate .place-kind { color: var(--warn); }
.place-region .place-kind      { color: var(--fix); }
.place-within .place-kind      { color: var(--ink-faint); }

.place-dot {
  width: .5rem; height: .5rem; border-radius: 50%;
  background: currentColor; flex: 0 0 auto;
}
.place-region .place-dot { border-radius: 2px; width: .62rem; height: .42rem; }
.place-approximate .place-dot { opacity: .55; }

.place-modern { margin: 0 0 .3rem; font-size: .9rem; font-weight: 600; }
.place-note   { margin: 0 0 .4rem; font-size: .82rem; color: var(--ink-soft); }
.place-coords {
  margin: 0 0 .6rem; font-family: var(--mono); font-size: .74rem;
  color: var(--ink-faint); font-variant-numeric: tabular-nums;
}
.place-links { display: flex; gap: .35rem; flex-wrap: wrap; }
.place-links .chip { text-decoration: none; }
.chip.primary {
  background: var(--accent); border-color: var(--accent); color: var(--paper);
}
.chip.primary:hover { background: var(--accent-2); border-color: var(--accent-2); color: var(--paper); }
.place-cite {
  margin: .6rem 0 0; font-size: .72rem; font-style: italic;
  color: var(--ink-faint);
}

/* ---------- surviving manuscripts ----------
   Links out, never reproductions: these objects are all rights reserved.
   Closed by default so it never competes with the text itself. */

.witnesses {
  margin: 0 auto 1.6rem; max-width: var(--leaf);
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--raised);
}
.witnesses summary {
  cursor: pointer; padding: .6rem .85rem; font-size: .88rem;
  color: var(--ink-soft); list-style-position: inside;
}
.witnesses summary:hover { color: var(--ink); }
.witnesses[open] summary {
  border-bottom: 1px solid var(--rule-soft); color: var(--ink);
}

.witness { padding: .85rem; border-bottom: 1px solid var(--rule-soft); }
.witness:last-child { border-bottom: 0; }
.witness h3 {
  margin: 0 0 .25rem; font-size: 1rem;
}
.witness h3 a { text-decoration: none; }
.witness h3 a::after { content: " ↗"; font-size: .7em; opacity: .6; }
.witness-when {
  margin: 0 0 .45rem; font-size: .74rem; color: var(--ink-faint);
  text-transform: uppercase; letter-spacing: .04em;
}
.witness-holds { margin: 0 0 .4rem; font-size: .88rem; }
.witness-why   { margin: 0 0 .5rem; font-size: .85rem; color: var(--ink-soft); }
.witness-rights {
  margin: 0; font-size: .72rem; font-style: italic; color: var(--ink-faint);
}

/* ---------- read aloud ----------
   The passage being spoken is marked twice: the whole verse takes a quiet
   ground so you can find your place after looking away, and the individual
   word takes the highlighter, so following along is possible at speed.
   Word marking uses the Custom Highlight API — where it is missing the
   verse marking alone still carries the position. */

.chapter-title.is-speaking,
.reader .v.is-speaking,
.reader p.is-speaking {
  background: var(--accent-bg);
  border-radius: 3px;
  box-shadow: 0 0 0 .18em var(--accent-bg);
}

::highlight(book-speaking) {
  background: var(--mark);
  color: var(--ink);
}

/* Said out loud where the button is, not only to the live region. */
.listen-note {
  max-width: var(--leaf);
  margin: .5rem auto 0;
  font-family: var(--sans); font-size: .82rem; line-height: 1.5;
  color: var(--warn); background: var(--warn-bg);
  border-radius: var(--radius); padding: .55rem .75rem;
}

.chip[disabled] { opacity: .55; cursor: default; }
.chip[disabled]:hover { border-color: var(--rule); color: var(--ink-soft); }

/* ---------- the player ----------

   A single bar: what a listener touches while listening, at the size they
   have to touch it, and nothing else. The four settings that are chosen once
   live in a drawer behind the gear. What this replaced put five selects and
   six buttons on the face of it, which on a phone is two crowded rows with
   the labels clipped mid-word.

   The ground is a blur over the reading rather than a flat panel, so the
   page stays visible under it and the bar reads as floating above the text
   rather than as a second page stuck to the bottom. Backdrop-filter is
   progressive: where it is not supported the same colour is opaque, which is
   the old appearance and is fine. */
.player {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  isolation: isolate;
  background: var(--raised);
  border-top: 1px solid var(--rule);
  box-shadow: var(--shadow);
  font-family: var(--sans);
  padding: .45rem max(.7rem, env(safe-area-inset-left))
           calc(.45rem + env(safe-area-inset-bottom))
           max(.7rem, env(safe-area-inset-right));
  display: flex; flex-direction: column; gap: .1rem;
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .player {
    background: color-mix(in srgb, var(--raised) 82%, transparent);
    -webkit-backdrop-filter: saturate(1.6) blur(18px);
    backdrop-filter: saturate(1.6) blur(18px);
  }
}
.player[hidden] { display: none; }

.player-line { display: flex; align-items: center; gap: .5rem; }

.player-btn {
  font: inherit; line-height: 1;
  background: transparent; color: var(--ink-soft);
  border: 1px solid transparent; border-radius: 999px;
  width: 2.5rem; height: 2.5rem; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  flex: none;
  transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}
.player-btn .i { width: 1.25rem; height: 1.25rem; display: block; }
.player-btn:hover { background: var(--accent-bg); color: var(--accent); }
.player-btn:active { transform: scale(.94); }
@media (prefers-reduced-motion: reduce) {
  .player-btn { transition: none; }
  .player-btn:active { transform: none; }
}

/* The one control that is pressed constantly, so it is the one that is
   filled and the largest thing on the bar. The accent is a deep purple in
   light and a pale one in dark, so the glyph takes the ground colour rather
   than a fixed white and stays legible in both. */
.player-play {
  background: var(--accent); border-color: var(--accent); color: var(--paper);
  width: 2.9rem; height: 2.9rem;
  box-shadow: 0 2px 10px color-mix(in srgb, var(--accent) 35%, transparent);
}
.player-play .i { width: 1.4rem; height: 1.4rem; }
.player-play:hover {
  background: var(--accent-2); border-color: var(--accent-2); color: var(--paper);
}

.player-transport { display: flex; align-items: center; gap: .1rem; margin-left: auto; }

.player-cont[aria-pressed="true"],
.player-more[aria-expanded="true"] {
  background: var(--accent-bg); color: var(--accent);
}

.player-pos {
  display: flex; flex-direction: column; min-width: 0; line-height: 1.3;
  flex: 1;
}
.player-where {
  font-size: .85rem; font-weight: 600; letter-spacing: -.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.player-unit {
  font-size: .74rem; color: var(--ink-faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-variant-numeric: tabular-nums;
}

/* ---------- scrubbing ----------

   The position used to be a 2px hairline with nothing to grab. It is a real
   range now: drag, arrow keys and a screen reader all land on the same
   jump() the skip buttons use. Styled rather than native because the native
   control is a different shape and weight in every browser, and this one has
   to sit under a 2.9rem button without looking like a form field. */
.player-scrub { padding: 0 .15rem .1rem; }

/* Folded: the bar keeps playing and stops covering the reading.

   What survives is what a listener glances at -- play/pause, where they are,
   and the way back up. The progress hairline stays too, because it is one
   pixel and it is the only thing that shows the chapter moving. Everything
   else is a control that is set and left, so it goes with the fold.

   Only the pieces are hidden; nothing is unmounted, so unfolding does not
   rebuild the bar or lose the drawer's state. The page reads its height off
   --player-h either way, so the inch comes back on its own. */
.player.is-folded .player-scrub,
.player.is-folded .player-drawer,
.player.is-folded .player-cont,
.player.is-folded .player-more,
.player.is-folded .player-close,
.player.is-folded .player-transport .player-btn:not(.player-fold) {
  display: none;
}
.player.is-folded { padding-top: .2rem; padding-bottom: calc(.2rem + env(safe-area-inset-bottom)); }
.player.is-folded .player-line { gap: .4rem; }
.player.is-folded .player-pos { font-size: .8rem; }

.player-seek {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 1.1rem; margin: 0; padding: 0;
  background: transparent; cursor: pointer; display: block;
}
.player-seek::-webkit-slider-runnable-track {
  height: 4px; border-radius: 999px;
  background: linear-gradient(to right,
    var(--accent) calc(var(--seek, 0) * 1%),
    var(--rule-soft) calc(var(--seek, 0) * 1%));
}
.player-seek::-moz-range-track {
  height: 4px; border-radius: 999px; background: var(--rule-soft);
}
.player-seek::-moz-range-progress {
  height: 4px; border-radius: 999px; background: var(--accent);
}
.player-seek::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 13px; height: 13px; border-radius: 50%; margin-top: -4.5px;
  background: var(--accent); border: 2px solid var(--raised);
  box-shadow: 0 1px 4px rgb(0 0 0 / .3);
  transition: transform .15s ease;
}
.player-seek::-moz-range-thumb {
  width: 13px; height: 13px; border-radius: 50%; border: 2px solid var(--raised);
  background: var(--accent); box-shadow: 0 1px 4px rgb(0 0 0 / .3);
}
.player-seek:hover::-webkit-slider-thumb,
.player-seek:focus-visible::-webkit-slider-thumb { transform: scale(1.25); }
.player-seek:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
  .player-seek::-webkit-slider-thumb { transition: none; }
}

/* ---------- the settings drawer ----------

   Opens upward into the page rather than over the reading: the body padding
   is measured from the whole player, so the verse being read stays visible
   with the drawer up. */
.player-drawer {
  border-top: 1px solid var(--rule-soft);
  margin-top: .35rem; padding-top: .5rem;
  display: flex; flex-direction: column; gap: .5rem;
}
.player-drawer[hidden] { display: none; }

.player-opts {
  display: grid; gap: .45rem .9rem;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}
.player-set { display: flex; align-items: center; gap: .5rem; min-width: 0; }
.player-set-label {
  font-size: .7rem; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-faint);
  flex: none; width: 3.4rem;
}
.player-set-control { display: flex; align-items: center; gap: .3rem; flex: 1; min-width: 0; }
.player-set-control select { flex: 1; min-width: 0; }

/* The hairline along the top edge is now the drawn half of the scrubber
   below it, so it is kept but not painted: the seek control carries the
   position visibly, and two progress lines saying the same thing is one
   more than the bar needs. updatePlayer() still writes to it, and it stays
   the fallback where a range input cannot be styled. */
.player-bar { position: absolute; top: 0; left: 0; right: 0; height: 0; }
.player-bar i { display: block; height: 100%; width: 0; background: var(--accent); }

/* Full width of its cell rather than capped: the cap is what clipped
   "Recorded reading" to "Recorded readir" on a phone. */
.player-opts select {
  font: inherit; font-size: .8rem; color: var(--ink);
  background: var(--paper-2); border: 1px solid var(--rule-soft);
  border-radius: 9px; padding: .34rem .5rem;
  max-width: 100%; text-overflow: ellipsis;
}
.player-opts select:hover { border-color: var(--rule); }
.player-opts select:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent);
}

/* The voice drawer is grouped by how the voices sound; the headings carry
   that, so they have to read as headings and not as choices. */
.player-opts optgroup { font-style: normal; font-weight: 600; }
.player-opts option { font-weight: 400; }

/* Try-this-voice sits with the drawer it belongs to, and smaller than the
   transport: it is a convenience, not a control. */
.player-try { width: 2.1rem; height: 2.1rem; }
.player-try .i { width: 1.05rem; height: 1.05rem; }

/* Only shown when the device has nothing good installed, which is a fact
   about the device and not a failure of the page. It opens rather than
   hovering: the phone that most needs it has no tooltips. */
.player-hint {
  margin: 0; font-size: .72rem; line-height: 1.45; color: var(--ink-faint);
  max-width: 42rem;
}
.player-hint[hidden] { display: none; }
.player-hint > summary { cursor: pointer; }
.player-hint > summary:hover { color: var(--ink); }
.player-hint > p { margin: .4rem 0 0; }
.player-hint > p strong { color: var(--ink-soft); font-weight: 600; }
/* Opened, it is a paragraph or five, and the player is already at the
   bottom of the screen. */
.player-hint[open] { max-height: 40vh; overflow-y: auto; }

/* The player covers the last inch of the page; the reader gets that back.
   Measured rather than assumed, because the player is a different height
   with the hint up, and on a narrow phone where the options wrap. */
body.listening { padding-bottom: calc(var(--player-h, 7.5rem) + 1rem); }

@media (max-width: 620px) {
  .player-where { font-size: .82rem; }
  /* One setting per row on a phone: two columns of a label plus a select is
     how the voice name came to be clipped mid-word. */
  .player-opts { grid-template-columns: 1fr; }
  /* The transport keeps its touch targets; the gap goes instead. */
  .player-transport { gap: 0; }
}

@media print { .player { display: none; } }


/* ---------- the timeline ----------
   Every work as a bar on one axis. The length of a bar is the length of the
   claim behind it, which is the thing a list of dates cannot show. */

.tl-axis {
  position: relative; height: 1.4rem; margin: .3rem 0 .2rem;
  border-bottom: 1px solid var(--rule);
}

.tl-tick {
  position: absolute; bottom: .15rem; transform: translateX(-50%);
  font-family: var(--sans); font-size: .66rem; color: var(--ink-faint);
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.tl-tick::after {
  content: ""; position: absolute; left: 50%; bottom: -.35rem;
  width: 1px; height: .3rem; background: var(--rule);
}

.tl-rows { display: flex; flex-direction: column; }

.tl-row {
  display: grid; align-items: center; gap: .5rem; padding: .12rem .25rem;
  grid-template-columns: 13rem 1fr 8rem;
  color: inherit; text-decoration: none; border-radius: 4px;
}
.tl-row:hover, .tl-row:focus-visible { background: var(--paper-2); }

.tl-name {
  font-size: .78rem; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis;
}

.tl-track {
  position: relative; height: .62rem; border-radius: 2px;
  background: var(--rule-soft);
}

.tl-bar {
  position: absolute; top: 0; bottom: 0; border-radius: 2px;
  background: var(--accent); min-width: 2px;
}

/* A span read from a century or a period commits to less than one read from
   a year, and a work placed by its era commits to less still. Both are drawn
   as the looser claims they are rather than being levelled up. */
.tl-bar.century, .tl-bar.period, .tl-bar.decade { opacity: .7; }
.tl-row.placed-section .tl-bar { background: var(--ink-faint); opacity: .45; }

/* A book the volume's own position calls composite -- the Torah, the Psalms,
   Isaiah 1-39 with the apocalypse inside it -- is not one composition, and a
   solid bar across it is the most confident mark on the page and the least
   true. Hatched, so the drawing says what the method page says. The stripe
   runs at 45 degrees in the bar's own colour against a lighter cut of it, so
   it reads as texture on both themes rather than as a second colour, and the
   row's own opacity rules still apply on top of it. */
.tl-bar.composite {
  background-image: repeating-linear-gradient(
    45deg, transparent 0 3px, var(--paper) 3px 5px);
}

/* An open end runs off the edge instead of stopping at a year nobody stated. */
.tl-bar.open-before {
  border-top-left-radius: 0; border-bottom-left-radius: 0;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 3rem);
  mask-image: linear-gradient(to right, transparent, #000 3rem);
}
.tl-bar.open-after {
  border-top-right-radius: 0; border-bottom-right-radius: 0;
  -webkit-mask-image: linear-gradient(to left, transparent, #000 3rem);
  mask-image: linear-gradient(to left, transparent, #000 3rem);
}

.tl-when {
  font-family: var(--sans); font-size: .68rem; color: var(--ink-faint);
  text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums;
}

.tl-note {
  margin: .9rem 0 0; padding-top: .6rem; border-top: 1px solid var(--rule-soft);
  font-size: .8rem; color: var(--ink-soft);
}
.tl-note a { color: inherit; }

.tl-layered { margin-top: .8rem; font-size: .82rem; }
.tl-layered summary { cursor: pointer; color: var(--ink-soft); }
.tl-layered-list { margin: .5rem 0 0; padding-left: 1.1rem; }
.tl-layered-list li { margin-bottom: .25rem; }

.tl-undated { margin-top: .8rem; font-size: .82rem; }
.tl-undated summary { cursor: pointer; color: var(--ink-soft); }
.tl-undated-list { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .5rem; }

/* On a phone the title needs the width more than the figure does, so the
   figure goes under the bar. */
@media (max-width: 40rem) {
  .tl-row { grid-template-columns: 8.5rem 1fr; row-gap: .1rem; }
  .tl-when { grid-column: 2; text-align: left; font-size: .64rem; }
  .tl-name { font-size: .72rem; }
}

/* ---------- the chapter map ----------
   Land, water, and the places the chapter names. The canvas is the picture;
   the list under it is the content, and is what a keyboard and a screen
   reader use. Both are always present. */

/* The count beside "Where this chapter happens", so a chapter with nothing
   to draw says so on the closed panel rather than after a click. */
.map-tally {
  font-family: var(--sans); font-size: .74rem; color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.map-tally.none { font-style: italic; }

.chapter-map {
  margin: 1.4rem 0 .4rem;
  border-top: 1px solid var(--rule-soft);
  padding-top: .7rem;
}

.chapter-map > summary {
  cursor: pointer;
  display: flex; align-items: baseline; gap: var(--gap-tight);
  font-family: var(--sans);
  font-size: .82rem;
  color: var(--ink-soft);
}
.chapter-map > summary:hover { color: var(--ink); }

.map-body { margin-top: .7rem; }

/* --map-aspect is set from the frame being drawn. Capping the height and
   then deriving the width from it is what keeps the box the shape of the
   map, rather than a wide letterbox with the map adrift in the middle. */
.map-stage {
  position: relative;
  border: 1px solid var(--rule);
  border-radius: 6px;
  overflow: hidden;
  background: var(--paper);
  --map-aspect: 1.2;
  --map-height: 26rem;
  aspect-ratio: var(--map-aspect);
  max-height: var(--map-height);
  max-width: calc(var(--map-height) * var(--map-aspect));
  width: 100%;
  margin: 0 auto;
}

.map-canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;   /* the canvas handles its own drag */
  cursor: grab;
}
.map-canvas:active { cursor: grabbing; }

@media (max-width: 40rem) {
  .map-stage { --map-height: 17rem; }
}

.map-tools {
  display: flex;
  gap: .4rem;
  margin: .5rem 0 .2rem;
}

.map-caption {
  font-size: .76rem;
  color: var(--ink-faint);
  line-height: 1.5;
  margin: .5rem 0 .7rem;
}

.map-detail { margin: .7rem 0; }
.map-detail-name {
  font-family: var(--sans);
  font-size: .9rem;
  margin: 0 0 .3rem;
}

.map-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: .3rem;
}

.map-place {
  display: inline-flex;
  align-items: baseline;
  gap: .35rem;
  font-family: var(--sans);
  font-size: .74rem;
  padding: .2rem .5rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--raised);
  color: var(--ink);
  cursor: pointer;
}
.map-place:hover, .map-place:focus-visible {
  border-color: var(--accent);
  color: var(--accent);
}

/* The same three grades the canvas draws, so the list is not a lesser copy:
   solid for identified, dashed for approximate, faint for a region. */
.map-place::before {
  content: "";
  width: .5rem; height: .5rem;
  border-radius: 50%;
  background: var(--accent);
  flex: none;
}
.map-place.map-approximate::before {
  background: transparent;
  border: 1px dashed var(--accent);
}
.map-place.map-region::before {
  background: var(--accent);
  opacity: .3;
}
.map-place.map-within::before {
  background: var(--accent);
  opacity: .6;
}

.map-place-kind, .map-place-first {
  /* Named for the screen reader, not shown: the dot carries it for the eye. */
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* The list's copy of the canvas's dotted outer ring. A ring rather than a
   fill, so it reads as something added around the grade rather than as a
   fourth grade of its own -- the place is still whatever it was. */
.map-place.map-first {
  box-shadow: 0 0 0 1px var(--accent);
}

/* ---- what a place is named beside ---- */

.map-kin { margin-top: .9rem; }
.map-kin h4 {
  font-family: var(--sans);
  font-size: .72rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0 0 .4rem;
}
.map-kin-list {
  list-style: none;
  margin: 0; padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: .3rem;
}
.map-kin-list li { display: flex; }
.map-kin-here, .map-kin-name {
  font-family: var(--sans);
  font-size: .74rem;
  padding: .2rem .5rem;
  border-radius: 999px;
  border: 1px solid var(--rule);
  background: var(--raised);
  color: var(--ink);
}
/* Only the ones already on this canvas are buttons; the rest are named but
   not offered, because there is nothing here to point at. */
.map-kin-here { cursor: pointer; }
.map-kin-here:hover, .map-kin-here:focus-visible {
  border-color: var(--accent);
  color: var(--accent);
}
.map-kin-name {
  border-style: dashed;
  color: var(--ink-faint);
  text-transform: capitalize;
}
.map-kin-count { color: var(--ink-faint); }
.map-kin-note, .map-kin-none, .map-arrival {
  font-size: .76rem;
  line-height: 1.5;
  color: var(--ink-faint);
  margin: .5rem 0 0;
}
.map-arrival a { color: var(--accent); }
.map-arrival-note { color: var(--ink-faint); }

/* The offline notice, added by assets/sw-register.js when the network goes.
   Fixed to the foot rather than the head: the header already steps out of
   the way on a scroll down, and a bar arriving at the top would push the
   line somebody is reading. Colours are tokens, so it follows the theme. */
.offline-note {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: 1rem;
  z-index: 60;
  margin: 0;
  max-width: min(34rem, calc(100vw - 2rem));
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
  background: var(--raised);
  color: var(--ink-soft);
  font-size: 0.9rem;
  line-height: 1.4;
  box-shadow: 0 2px 10px rgb(0 0 0 / 12%);
}

/* ---------- the fold, on a phone ----------

   Measured rather than felt: on a 568px iPhone SE the first verse of Genesis
   sat 743px down, which is a Bible you arrive at and see no Bible on. Nothing
   was at fault by itself -- the header 121, the note 109, the strip 74, the
   dating block 55, the controls 53, the crumbs 41 -- it was the sum, and the
   strip had already been cut from four rows to two by somebody fighting the
   same thing.

   So this is the apparatus giving up its spacing rather than its content. It
   takes nothing off the page: the note, the dating, the controls and every
   chapter number are all still there and still say the same thing. */
@media (max-width: 620px) {
  .reader-head { padding-bottom: var(--gap-tight); margin-bottom: var(--gap-tight); }
  .crumbs { margin-bottom: .2rem; }
  .note-block {
    font-size: .95rem; line-height: 1.5;
    margin-bottom: var(--gap); padding-left: var(--gap-tight);
  }
  .reader-controls { margin-top: var(--gap-tight); }

  /* Free height, which is the reason to do it here rather than everywhere:
     the strip is a box of a fixed maximum height that scrolls, so a taller
     link inside it pushes nothing down the page. These were 25px against
     the 44 that Apple asks for and the 48 Android does -- the smallest
     targets on the site, on the control a thumb uses most, and the one
     place where making them bigger is free. */
  /* The box keeps its height. Raising it to fit two rows of the taller links
     would hand back exactly what the spacing above just saved, and the half
     row showing under the first still says what it is there to say. */
  .chapter-strip a {
    min-height: 44px; min-width: 44px;
    display: inline-flex; align-items: center; justify-content: center;
    padding: .18rem .5rem;
  }
}

/* ---------- the transport, under a thumb ----------

   The player's buttons were 2.2rem, which measures 35px on a phone, and the
   play button 42. These are the controls somebody uses one-handed while
   listening, often without looking straight at the screen -- a stronger case
   for a thumb-sized target than anything else on the site, and they were the
   smallest things on it after the chapter numbers.

   Keyed on the pointer rather than on the width, because the thing that
   decides this is what is touching the screen and not how wide it is: a
   tablet is a coarse pointer at desk width and wants the same 44px, and a
   narrow window on a laptop has a mouse and does not. A mouse keeps the
   smaller buttons, which are easier to fit and no harder to hit with a
   cursor.

   Free of the fold, like the chapter numbers and for the same reason: the
   player is position: fixed, so its height is drawn over the page rather
   than pushed into it, and nothing here moves the scripture down. */
@media (pointer: coarse) {
  .player-btn { width: 44px; height: 44px; }
  .player-play { width: 52px; height: 52px; }

  /* The settings row is not the transport. Rate, pace, voice, the sample and
     the sleep timer are chosen once and then left alone, and sizing them like
     the controls a thumb reaches for every verse took the panel from 177px to
     254 -- which, fixed over a 568px phone, is nearly half the screen covered
     while the highlight is trying to show somebody where the voice has got
     to. They keep the size a settings row can have. */
  .player-opts .player-btn { width: 2.2rem; height: 2.2rem; }
}

/* ---------------- the diary ----------------

   A page that holds a reader's own paragraphs rather than the text, so it is
   set like something written rather than something published: the same
   measure as the reader, and no ornament competing with what they wrote. */

.diary-where {
  color: var(--ink-soft);
  border-left: 3px solid var(--rule);
  padding-left: 0.9rem;
  margin: 1rem 0 1.4rem;
  max-width: 46rem;
}

.diary-shape {
  color: var(--ink-soft);
  margin: 0 0 1.6rem;
}

.diary-keep {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 1.2rem 0 0;
}

.diary-day {
  font-size: 1.05rem;
  color: var(--ink-faint);
  border-bottom: 1px solid var(--rule-soft);
  padding-bottom: 0.3rem;
  margin: 2rem 0 1rem;
}

.diary-entry {
  border-left: 3px solid var(--rule-soft);
  padding: 0 0 0 1rem;
  margin: 0 0 1.6rem;
  max-width: 46rem;
}

.diary-ref a { font-weight: 600; }

.diary-why {
  color: var(--ink-soft);
  font-style: italic;
  margin: 0.2rem 0 0.6rem;
}

/* The reader's own words. Kept at reading measure and left to breathe --
   white-space: pre-wrap so the paragraphs they typed stay the paragraphs
   they typed. */
.diary-took {
  white-space: pre-wrap;
  margin: 0 0 0.6rem;
}

.diary-mark {
  display: inline-block;
  font-size: 0.85rem;
  color: var(--ink-faint);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0.1rem 0.7rem;
  margin: 0 0 0.4rem;
}

.diary-form { max-width: 46rem; }

.diary-form label {
  display: block;
  font-size: 0.9rem;
  color: var(--ink-soft);
  margin: 0.9rem 0 0.25rem;
}

.diary-form textarea {
  width: 100%;
  font: inherit;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 4px;
  padding: 0.55rem 0.7rem;
  resize: vertical;
}

.diary-reading {
  color: var(--ink-faint);
  font-size: 0.9rem;
  margin: 0;
}

.diary-marks {
  border: 0;
  padding: 0;
  margin: 1rem 0 0;
}

.diary-marks legend {
  font-size: 0.9rem;
  color: var(--ink-soft);
  padding: 0;
}

.diary-marks label {
  display: inline-block;
  margin: 0 0.9rem 0 0.25rem;
  color: var(--ink-soft);
}

.diary-actions {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  margin: 1.1rem 0 0;
}

.diary-status { color: var(--ink-soft); margin: 0; }

.diary-inline {
  border-left: 3px solid var(--accent);
  padding-left: 1rem;
  margin: 1rem 0 1.5rem;
}

.diary-kept { color: var(--ink-soft); margin: 0; }

.diary-new { margin: 0 0 1.5rem; }

.diary-new summary {
  cursor: pointer;
  color: var(--accent);
  padding: 0.3rem 0;
}

/* Entries not yet in a file. Marked rather than alarmed: this is a fact
   about what is at stake, not an error, and a page that shouts at somebody
   for writing in it is a page they stop writing in. */
.diary-unsaved { color: var(--accent); }

/* Always-visible sleep link to the YouTube live stream. */
.live-now-links a.live-now-primary { font-weight: 700; }
.foot .sleep-yt a { font-weight: 600; text-decoration: underline; text-underline-offset: .15em; }
