/* Shell, header, passage view, sentence view, in-text marks. */

*, *::before, *::after { box-sizing: border-box; }
/* No scroll anchoring: the reader rebuilds the article on every re-render and
   places the margin blocks by script afterwards, and the browser, anchoring on
   a node that then moved, used to carry a page deep in a chapter to its end.
   reader.js keeps the scroll offset across a render and puts the current
   sentence back on its line itself. */
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: var(--ui-md) / 1.5 var(--face-ui);
  min-height: 100dvh;
  touch-action: manipulation;
}
::selection { background: var(--selection); }
[hidden] { display: none !important; }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: var(--radius-sm); }
button, select, input { font: inherit; color: inherit; }
button { cursor: pointer; }
button:disabled, button[aria-disabled="true"] { cursor: default; opacity: 0.45; }   /* aria-disabled: a no-op that keeps its place in the tab order (the size steppers at either end) */
a { color: var(--rubric-ink); text-underline-offset: 0.15em; }

.visually-hidden {
  position: absolute !important; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
.skip {
  position: absolute; left: var(--s-4); top: -100px; z-index: var(--z-tooltip);
  background: var(--bg-raised); color: var(--ink); padding: var(--s-2) var(--s-4);
  border: 1px solid var(--line-strong); border-radius: var(--radius);
}
.skip:focus { top: var(--s-2); }

/* ---------------------------------------------------------- buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-1);
  min-height: var(--tap); padding: 0 var(--s-4);
  background: var(--bg-raised); border: 1px solid var(--line-strong); border-radius: var(--radius);
  color: var(--ink); font-size: var(--ui-md); line-height: 1.2; text-decoration: none;
  transition: background-color var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out);
}
.btn:hover:not(:disabled, [aria-disabled="true"]) { border-color: var(--ink-3); }
.btn:active:not(:disabled, [aria-disabled="true"]) { background: var(--bg-chrome); }
.btn--primary { background: var(--rubric); border-color: var(--rubric); color: var(--bg); }   /* page colour on the rubric: ≥ 6:1 in both palettes */
.btn--primary:hover:not(:disabled, [aria-disabled="true"]) { border-color: var(--rubric-ink); background: var(--rubric-ink); }
.btn--quiet { background: transparent; border-color: transparent; color: var(--ink-2); }
.btn--quiet:hover:not(:disabled, [aria-disabled="true"]) { background: var(--bg-chrome); border-color: transparent; color: var(--ink); }
.btn--icon { min-width: var(--tap); padding: 0; }
.btn__aa { font-family: var(--face-serif); font-size: 1.1rem; letter-spacing: 0.01em; }

/* ---------------------------------------------------------- header */
.bar {
  position: sticky; top: 0; z-index: var(--z-sticky);
  background: var(--bg-chrome); border-bottom: 1px solid var(--line);
  padding: 0 max(var(--s-3), env(safe-area-inset-left)) 0 max(var(--s-3), env(safe-area-inset-right));
}
.bar__main {
  /* Phones: week | view switch, then the five display toggles | settings. The
     settings button gets its own narrow column so the toggles keep one row. */
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto; grid-template-areas: "week view view" "tools tools settings";
  align-items: center; gap: var(--s-1) var(--s-2); padding: var(--s-1) 0;
}
.week { grid-area: week; min-width: 0; display: flex; flex: 1 1 auto; }
.week__btn {
  flex: 1 1 auto; width: 100%;
  display: flex; align-items: baseline; gap: 0.5em; min-width: 0; max-width: 100%;
  min-height: var(--tap); padding: 0 1.8rem 0 var(--s-2);
  background: transparent; border: 1px solid transparent; border-radius: var(--radius);
  font-family: var(--face-serif); font-size: var(--ui-lg); color: var(--ink); cursor: pointer; text-align: left;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 0.95rem) 50%, calc(100% - 0.65rem) 50%;
  background-size: 0.3rem 0.3rem; background-repeat: no-repeat;
}
.week__btn:hover { border-color: var(--line-strong); background-color: var(--bg-raised); }
.week__btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.week__num { color: var(--ink-3); white-space: nowrap; }
.week__title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
#settings-btn { grid-area: settings; justify-self: end; }
.seg {
  grid-area: view; display: inline-flex; justify-self: start;
  border: 1px solid var(--line-strong); border-radius: var(--radius); overflow: hidden; background: var(--bg-raised);
}
.seg__btn {
  min-height: 40px; padding: 0 var(--s-4); background: transparent; border: 0; color: var(--ink-2);
  transition: background-color var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out);
}
.seg__btn + .seg__btn { border-left: 1px solid var(--line-strong); }
.seg__btn[aria-pressed="true"] { background: var(--ink); color: var(--bg); }
.seg__btn:focus-visible { outline-offset: -3px; border-radius: 0; }

.bar__tools { grid-area: tools; display: flex; flex-wrap: wrap; gap: 0 var(--s-1); min-width: 0; margin-left: calc(-1 * var(--s-2)); }
/* Display toggles: a small switch (track + knob) before the label, the same
   for all five, so on/off reads at a glance. --toggle-pad places the knob. */
.toggle {
  --toggle-pad: var(--s-2);
  position: relative; display: inline-flex; align-items: center; gap: var(--s-2);
  min-height: 40px; padding: 0 var(--toggle-pad); background: transparent; border: 0; border-radius: var(--radius);
  color: var(--ink-2); font-size: var(--ui-sm);
  transition: color var(--t-fast) var(--ease-out), background-color var(--t-fast) var(--ease-out);
}
.toggle::before {
  content: ""; width: 1.75em; height: 1em; border-radius: 999px; flex: none;
  border: 1.5px solid var(--ink-3); background: transparent;
  transition: background-color var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out);
}
.toggle::after {
  content: ""; position: absolute; top: 50%; left: calc(var(--toggle-pad) + 0.25em);
  width: 0.55em; height: 0.55em; border-radius: 50%; background: var(--ink-3);
  transform: translateY(-50%);
  transition: transform var(--t-base) var(--ease-out), background-color var(--t-fast) var(--ease-out);
}
.toggle[aria-pressed="true"] { color: var(--ink); }
.toggle[aria-pressed="true"]::before { background: var(--ink); border-color: var(--ink); }
.toggle[aria-pressed="true"]::after { background: var(--bg); transform: translate(0.72em, -50%); }
.toggle:hover { color: var(--ink); background: var(--bg-raised); }
/* Phones and small tablets: short labels — except Translation, which keeps its
   full name from 480px up (the one toggle a first-time reader must find). */
@media (max-width: 767.98px) {
  .toggle__label { font-size: 0; }
  .toggle__label::after { content: attr(data-short); font-size: var(--ui-sm); }
  .seg__btn { padding-inline: var(--s-3); }
  /* A slightly smaller switch and tighter padding so the five toggles keep one row from 360px up. */
  .toggle { --toggle-pad: 5px; gap: 5px; }
  .toggle::before { width: 1.5em; height: 0.9em; }
  .toggle::after { width: 0.5em; height: 0.5em; left: calc(var(--toggle-pad) + 0.2em); }
  .toggle[aria-pressed="true"]::after { transform: translate(0.6em, -50%); }
  .bar__tools { gap: 0 2px; }
}
@media (min-width: 480px) and (max-width: 767.98px) {
  .toggle[data-toggle="english"] .toggle__label { font-size: var(--ui-sm); }
  .toggle[data-toggle="english"] .toggle__label::after { content: none; }
}
@media (max-width: 479.98px) {
  .toggle { --toggle-pad: 4px; gap: 4px; }
  .toggle::before { width: 1.35em; height: 0.85em; }
  .toggle::after { width: 0.45em; height: 0.45em; }
  .toggle[aria-pressed="true"]::after { transform: translate(0.5em, -50%); }
}
@media (max-width: 420px) { .seg__btn { padding-inline: var(--s-2); } }
@media (prefers-reduced-motion: reduce) { .toggle::after { transition: none; } }

/* First-run hint under the Translation toggle: a row of the (sticky) header,
   so it pushes the text down instead of covering the part heading; main.js
   sets margin-left + --hint-caret so the caret points at the toggle. */
.hint {
  position: relative; margin: var(--s-1) 0 var(--s-2);
  display: flex; align-items: center; gap: var(--s-3); width: max-content;
  max-width: min(22rem, 100%); padding: var(--s-2) var(--s-2) var(--s-2) var(--s-4);
  background: var(--ink); color: var(--bg); border-radius: var(--radius); box-shadow: var(--shadow-pop);
  font-family: var(--face-ui); font-size: var(--ui-sm); line-height: 1.4; text-wrap: pretty;
  animation: rise-down var(--t-base) var(--ease-out);
}
.hint::before {
  content: ""; position: absolute; top: -6px; left: calc(var(--hint-caret, 1.5rem) - 6px);
  border: 6px solid transparent; border-top: 0; border-bottom-color: var(--ink);
}
.hint__text { margin: 0; }
.hint__text b { font-weight: 600; }
.hint__ok {
  flex: none; min-height: 32px; padding: 0 var(--s-3); border-radius: var(--radius-sm);
  background: var(--bg); color: var(--ink); border: 0; font-size: var(--ui-sm); font-weight: 600;
}
.hint__ok:hover { background: var(--bg-chrome); }
.hint__ok:focus-visible { outline-color: var(--bg); }
@keyframes rise-down { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .hint { animation: none; } }

@media (min-width: 768px) {
  .bar { padding-inline: var(--s-5); }
  .bar__main {
    grid-template-columns: minmax(0, 1fr) auto auto; grid-template-areas: "week view settings" "tools tools tools";
    gap: var(--s-1) var(--s-4); padding: var(--s-2) 0;
  }
  .week__btn { max-width: 32rem; }
  .bar__tools { gap: 0 var(--s-2); padding-bottom: var(--s-1); }
}

/* ---------------------------------------------------------- layout */
/* --gutter-r: the line-number gutter resolved at the reading size once, so
   the part title (0.95em) and the prose share the same left edge. */
@property --gutter-r { syntax: "<length>"; inherits: true; initial-value: 0px; }
.layout { display: block; }
#main { min-width: 0; overflow-x: clip; }
/* The two largest sizes: a 13-letter word can be wider than a phone column; break it rather than pan the page. */
html[data-size="7"] .la, html[data-size="8"] .la, html[data-size="7"] .sentence__la, html[data-size="8"] .sentence__la { overflow-wrap: anywhere; hyphens: manual; }
.reader {
  --gutter-r: var(--gutter);
  font-family: var(--face-reading);
  font-size: var(--reading-size);
  line-height: var(--reading-leading);
  padding: var(--s-5) var(--s-4) calc(var(--s-7) + env(safe-area-inset-bottom));
  max-width: calc(var(--measure) + var(--gutter-r) + 2rem);
  margin-inline: auto;
  text-wrap: pretty;
  hanging-punctuation: first;
}
/* Phones: the line-number gutter stops growing at 14vw so the largest sizes keep a readable column. */
@media (max-width: 767.98px) { .reader { --gutter-r: min(var(--gutter), 14vw); } }
/* With the gutter in use the text column narrows to the book's own proportion
   (Ørberg sets ~55 characters beside a wide margin): the glosses then keep
   pace with the lines instead of stacking below them. */
.reader[data-margin="on"][data-margin-mode="gutter"] { --measure: 26em; max-width: calc(var(--measure) + var(--gutter-r) + 2rem + var(--margin-col) + var(--margin-gap)); }
.reader__loading, .empty { color: var(--ink-3); font-family: var(--face-ui); font-size: var(--ui-md); }
.boot-error { margin: var(--s-6); color: var(--danger); font-family: var(--face-ui); }

/* The side panel column (tablet: only while open; desktop: always) with a
   draggable divider in between. --panel-w is set by main.js from settings. */
.divider { display: none; }
@media (min-width: 768px) {
  .reader { padding: var(--s-6) var(--s-6) var(--s-7); }
}
/* Tablet only: the panel column exists while the panel is open. Bounded above
   so the desktop column below keeps one width whether the panel is open or not. */
@media (min-width: 768px) and (max-width: 1099.98px) {
  .layout[data-panel="open"] { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, var(--panel-w, min(22rem, 44vw))); align-items: start; }
  .layout[data-panel="open"] .divider { display: block; }
}
@media (min-width: 1100px) {
  .reader { --margin-col: max(15rem, 11em); }   /* grows with the type so the column keeps pace at sizes 4–5 */
  .layout { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, var(--panel-w, var(--panel-default))); align-items: start; }
  .layout .divider { display: block; }
  .reader { padding-inline: var(--s-7); }
}
.divider {
  position: sticky; top: var(--bar-h, 0px); align-self: start;
  width: 14px; height: calc(100dvh - var(--bar-h, 0px)); margin-inline: -1px;
  cursor: col-resize; touch-action: none; user-select: none; -webkit-user-select: none;
  background: transparent; border: 0; padding: 0; border-radius: 0;
  z-index: 1;
}
.divider::before {
  content: ""; position: absolute; top: 0; bottom: 0; left: 50%; width: 1px; margin-left: -0.5px;
  background: var(--line); transition: background-color var(--t-fast) var(--ease-out), width var(--t-fast) var(--ease-out), margin var(--t-fast) var(--ease-out);
}
.divider:hover::before, .divider.is-dragging::before { width: 3px; margin-left: -1.5px; background: var(--line-strong); }
.divider:focus-visible { outline: none; }
.divider:focus-visible::before { width: 3px; margin-left: -1.5px; background: var(--focus); }
body.is-resizing { cursor: col-resize; user-select: none; -webkit-user-select: none; }
body.is-resizing .panel { pointer-events: none; }

/* ---------------------------------------------------------- margin notes */
/* Ørberg's marginal glosses. Two copies are rendered per unit: .mnotes inside
   the sentence (phones, or a tablet with no room) and .mnote in the .margin
   gutter beside the prose (positioned by reader.js). CSS shows one of them. */
.margin-probe { position: absolute; visibility: hidden; height: 0; width: calc(var(--margin-col) + var(--margin-gap)); }
.mnotes, .mnote {
  font-family: var(--face-serif); font-style: normal; color: var(--ink-3);
  font-size: calc(0.78em * var(--note-scale)); line-height: 1.45; text-wrap: pretty;
}
.mnotes { display: block; margin: 0.2em 0 0.35em; }
.mnotes__item { display: block; padding-left: 1.15em; text-indent: -1.15em; }
.mnotes__mark { display: inline-block; width: 1.15em; text-indent: 0; color: var(--ink-3); opacity: 0.7; }
.mnote .mnotes__mark { display: none; }
.mnotes__line { font-family: var(--face-ui); font-size: 0.78em; color: var(--ink-3); font-variant-numeric: tabular-nums; margin-right: 0.45em; }
.mnotes .w, .mnote .w { color: var(--ink-2); padding-block: 0.3em; }   /* ≥ 24px tap height at the smallest note size */
.mnotes--sentence { margin: calc(-1 * var(--s-2)) 0 var(--s-4); font-size: calc(0.9em * var(--note-scale)); }
.reader[data-margin="off"] .mnotes, .reader[data-margin="off"] .margin { display: none; }
/* Each .part carries its own data-margin-mode (reader.js): a part too dense for the column is inline while its neighbours keep the gutter. */
.part[data-margin-mode="gutter"] .mnotes { display: none; }
.reader[data-margin="on"] .part:not([data-margin-mode="gutter"]) .unit.has-margin { display: block; margin-bottom: var(--s-3); }   /* only while the notes are shown: switched off, the sentences flow (and the printed lines hold in book mode) */
.margin { display: none; }
/* --prose-pr: the prose's right padding, named so the book-mode marks column (below) can find the text column's right edge. */
.reader[data-margin="on"] .part[data-margin-mode="gutter"] .prose { --prose-pr: calc(var(--margin-col) + var(--margin-gap)); padding-right: var(--prose-pr); }
.reader[data-margin="on"] .part[data-margin-mode="gutter"] .margin {
  display: block; position: absolute; top: 0; right: 0; bottom: 0; width: var(--margin-col); pointer-events: none;
}
/* The blocks reader.js positions on every reflow are never scroll anchors:
   anchoring on one, then moving it, used to carry a page deep in a chapter to
   the chapter's end on any re-render. The page keeps anchoring everywhere else
   (a picture decoding above the viewport, a summary toggled, fonts arriving). */
.margin, .mnote, .mpic { overflow-anchor: none; }
.mnote { position: absolute; left: 0; width: 100%; pointer-events: auto; line-height: 1.25; }   /* tight leading: a two-line gloss stays inside 1.3 text lines */

.mnote .mnotes__item { padding-left: 0; text-indent: 0; }
/* A gloss with an English rendering (margin[].en): a small "en" chip after the
   Latin opens the English beneath it, in the UI face. The button itself is
   the target (≥ 24px either way; negative block margins keep it out of the
   line box) and the pill is drawn inside it (::before), small enough for the
   gutter. Settings → Reading "English under margin notes" shows every
   rendering and drops the chips. */
.mnotes__en-btn {
  position: relative; isolation: isolate; display: inline-flex; align-items: center; justify-content: center; vertical-align: middle;
  min-width: 24px; height: 24px; margin: -6px 0 -6px 0.4em; padding: 0 0.5em; text-indent: 0;
  font: 600 0.68rem/1 var(--face-ui); letter-spacing: 0.05em;
  color: var(--ink-3); background: transparent; border: 0; border-radius: 999px;
  transition: color var(--t-fast) var(--ease-out);
}
.mnotes__en-btn::before {
  content: ""; position: absolute; inset: 4px 0; z-index: -1;
  border: 1px solid var(--line-strong); border-radius: 999px;
  transition: background-color var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out);
}
.mnotes__en-btn:hover { color: var(--ink); }
.mnotes__en-btn:hover::before { border-color: var(--ink-3); }
.mnotes__en-btn[aria-expanded="true"] { color: var(--bg); }
.mnotes__en-btn[aria-expanded="true"]::before { background: var(--ink-2); border-color: var(--ink-2); }
.mnotes__en-btn:focus-visible { outline-offset: 0; }
.mnotes__en {
  display: none; margin: 0.15em 0 0.2em; text-indent: 0;
  font-family: var(--face-ui); font-size: 0.92em; line-height: 1.4; color: var(--ink-2); text-wrap: pretty;
}
.mnotes__item.is-en-open .mnotes__en, .reader[data-gloss-en="on"] .mnotes__en { display: block; }
.reader[data-gloss-en="on"] .mnotes__en-btn { display: none; }
.mnotes--sentence .mnotes__en { font-size: 0.95em; }

/* ---------------------------------------------------------- pictures */
/* The book's illustrations beside their sentence (CONTRACT.md "Pictures").
   Two copies per unit in passage view, like the margin notes: .pic--inline
   inside the unit (phones, or a part shown inline — full width, at most 60%
   of the column, centred, above the sentence) and .mpic in the gutter,
   positioned by reader.js above that sentence's notes. Sentence view puts
   .pic--sentence above the Latin. The caption is the reading face, tappable;
   its English behind the same "en" chip as a margin gloss. */
.pic { display: block; margin: 0; }
.pic__btn {
  display: block; width: 100%; padding: 0; margin: 0; background: transparent; border: 0; border-radius: var(--radius-sm);
  cursor: zoom-in; line-height: 0;
}
.pic__btn:hover .pic__img { border-color: var(--ink-3); }
.pic__btn:focus-visible { outline-offset: 3px; }
.pic__img {
  display: block; width: 100%; height: auto; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--bg-raised);
  transition: border-color var(--t-fast) var(--ease-out);
}
/* A scan's paper white blends into the page in the light palette; dark keeps the plate as it is. */
.pic__img { mix-blend-mode: multiply; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .pic__img { mix-blend-mode: normal; } }
:root[data-theme="dark"] .pic__img { mix-blend-mode: normal; }
/* The image did not load (an expired signed URL, offline): the alt text keeps the frame. */
.pic__missing {
  display: flex; align-items: center; justify-content: center; text-align: center;
  min-height: 4.5em; padding: var(--s-3); border: 1px dashed var(--line-strong); border-radius: var(--radius-sm);
  font-family: var(--face-ui); font-size: var(--ui-sm); line-height: 1.4; color: var(--ink-3);
}
.pic__cap {
  margin: 0.4em 0 0; font-family: var(--face-reading); font-size: calc(0.78em * var(--note-scale)); line-height: 1.4; color: var(--ink-2); text-wrap: pretty;
}
.pic__la .w { padding-block: 0.3em; }   /* ≥ 24px tap height at the caption size */
.pic__en { display: none; margin-top: 0.15em; font-family: var(--face-ui); font-size: 0.92em; line-height: 1.4; color: var(--ink-2); text-wrap: pretty; }
.pic.is-en-open .pic__en, .reader[data-pictures="on"][data-gloss-en="on"] .pic__en { display: block; }
.reader[data-pictures="off"] .pic, .reader[data-pictures="off"] .mpic { display: none; }
/* Inline copy: in the prose just before its sentence, 60% of the column, centred. Hidden while the part uses the gutter. */
.pic--inline { width: 60%; margin: var(--s-3) auto; }
.prose > .pic--inline:first-child { margin-top: 0; }
.part[data-margin-mode="gutter"] .pic--inline { display: none; }
/* Gutter copy: positioned like a .mnote; the notes stack beneath it. */
.mpic { position: absolute; left: 0; width: 100%; pointer-events: auto; }
.mpic .pic__cap { font-size: calc(0.78em * var(--note-scale)); line-height: 1.25; }
/* Sentence view: above the Latin, the same proportion as the inline copy. */
.pic--sentence { width: 60%; max-width: 22rem; margin: 0 auto var(--s-5); }
@media (max-width: 479.98px) { .pic--inline, .pic--sentence { width: 72%; } }   /* a narrow phone: the plate stays readable */

/* The illustration at full size: a native dialog, closed by Escape, the backdrop or the button. */
.lightbox {
  margin: auto; padding: 0; border: 0; border-radius: var(--radius-lg); max-width: min(96vw, 64rem); max-height: 96dvh;
  background: var(--bg-raised); color: var(--ink); box-shadow: var(--shadow-pop); overflow: hidden;
  display: none;
}
.lightbox[open] { display: grid; grid-template-rows: minmax(0, 1fr) auto; animation: pop var(--t-base) var(--ease-out); }
.lightbox::backdrop { background: oklch(0 0 0 / 0.55); }
.lightbox__img { display: block; width: auto; max-width: 96vw; height: auto; max-height: calc(96dvh - 4rem); margin: 0 auto; object-fit: contain; background: var(--bg-raised); }
.lightbox__cap { margin: 0; padding: var(--s-3) calc(var(--tap) + var(--s-3)) var(--s-3) var(--s-4); font-family: var(--face-reading); font-size: 1.05rem; line-height: 1.4; color: var(--ink); text-wrap: pretty; }
.lightbox__cap:empty { display: none; }
.lightbox__en, .lightbox__page { font-family: var(--face-ui); font-size: var(--ui-sm); color: var(--ink-2); }
.lightbox__sep { color: var(--ink-3); }
.lightbox__close {
  position: absolute; top: var(--s-2); right: var(--s-2); width: var(--tap); height: var(--tap);
  border: 0; border-radius: 50%; background: oklch(0 0 0 / 0.45); color: oklch(1 0 0); font-size: 1.5rem; line-height: 1;
  transition: background-color var(--t-fast) var(--ease-out);
}
.lightbox__close:hover { background: oklch(0 0 0 / 0.65); }
.lightbox__close:focus-visible { outline-color: oklch(1 0 0); }
@media (prefers-reduced-motion: reduce) { .lightbox[open] { animation: none; } }

/* ---------------------------------------------------------- passage */
.part + .part { margin-top: var(--s-7); }
.part__title {
  font-family: var(--face-serif); font-weight: 400; font-size: 0.95em; letter-spacing: 0.01em;
  color: var(--ink-2); margin: 0 0 var(--s-4) var(--gutter-r);
  border-bottom: 1px solid var(--line); padding-bottom: var(--s-2);
}
.part__lines { color: var(--ink-3); font-size: 0.85em; }

/* Section summary: a disclosure under the part heading (week.parts[].summary_en
   / summary_la). Closed by default; reader.js remembers open/closed per week
   and part. The Latin inside is tokenised like the text, so its words open the
   panel and take the yellow underline. Hidden by Settings → Reading. */
.summary {
  margin: calc(-1 * var(--s-2)) 0 var(--s-4) var(--gutter-r);
  font-family: var(--face-ui); font-size: var(--ui-sm); line-height: 1.5; color: var(--ink-2);
  hanging-punctuation: none;
}
.reader[data-margin="on"] .part[data-margin-mode="gutter"] .summary { margin-right: calc(var(--margin-col) + var(--margin-gap)); }   /* keeps to the text column */
.summary__toggle {
  display: inline-flex; align-items: center; gap: var(--s-2);
  min-height: 36px; margin-left: calc(-1 * var(--s-2)); padding: 0 var(--s-2); border-radius: var(--radius-sm);
  color: var(--ink-3); cursor: pointer; list-style: none; user-select: none; -webkit-user-select: none;
  transition: color var(--t-fast) var(--ease-out), background-color var(--t-fast) var(--ease-out);
}
.summary__toggle::-webkit-details-marker { display: none; }
.summary__toggle::before {
  content: ""; flex: none; width: 0.4em; height: 0.4em; margin-top: -0.15em;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg); transition: transform var(--t-fast) var(--ease-out);
}
.summary[open] .summary__toggle::before { transform: rotate(45deg); margin-top: -0.3em; }
.summary__toggle:hover { color: var(--ink); background: var(--bg-raised); }
.summary[open] .summary__toggle { color: var(--ink-2); }
.summary__toggle:focus-visible { outline: 2px solid var(--focus); outline-offset: 0; }
.summary__body { padding: var(--s-1) 0 var(--s-2); max-width: 60ch; animation: rise-down var(--t-base) var(--ease-out); }
.summary__en { margin: 0 0 var(--s-3); font-size: calc(1em * var(--note-scale)); color: var(--ink); text-wrap: pretty; }   /* the English follows the notes size; the Latin (.summary__p) keeps its own */
.summary__h { font-family: var(--face-ui); font-size: var(--ui-xs); font-weight: 600; letter-spacing: 0.02em; color: var(--ink-3); margin: 0 0 var(--s-1); }
.summary__p { margin: 0; font-family: var(--face-reading); font-size: 1rem; line-height: 1.5; color: var(--ink); text-wrap: pretty; }
.summary__p .w { padding-block: 0.2em; }   /* ≥ 24px tap height at the UI size */
.reader[data-summaries="off"] .summary, .reader[data-summaries="off"] .sentence__summary, .reader[data-summaries="off"] .sentence__meta-sep { display: none; }
@media (prefers-reduced-motion: reduce) { .summary__body { animation: none; } }
.prose { position: relative; padding-left: var(--gutter-r); margin: 0; }
.unit { position: static; }
.lineno--dup { visibility: hidden; }
.lineno {
  position: absolute; left: 0; width: calc(var(--gutter-r) - 0.75rem);
  text-align: right; font-family: var(--face-ui); font-size: max(0.62em, 0.7rem); line-height: inherit;
  color: var(--ink-3); font-variant-numeric: tabular-nums; padding-top: 0.42em; user-select: none;
}
/* Book lines (CONTRACT.md "Book lines", settings.lineMode = 'book'): reader.js
   renders the passage as printed — a <br class="lb"> before the token that
   opens each printed line, a .lineno (data-line) before that token, and a
   trailing .lb--unit on a sentence that ends a printed line. Units without
   line data keep the flow layout. A line longer than the column simply wraps,
   its number staying with its first screen line. The trailing break is
   redundant inside a block unit (verse, a speaker turn, the interleaved
   translation, inline margin notes) and would open an empty line there. */
.reader[data-english="interleaved"] .lb--unit, .unit--verse .lb--unit, .unit--turn .lb--unit,
.reader[data-margin="on"] .part:not([data-margin-mode="gutter"]) .unit.has-margin .lb--unit { display: none; }
/* Book mode's column is sized from the data by reader.js (sizeBookColumn):
   the article's max-width holds the week's widest printed line in the
   reading face, and --marks-room (n × --marks-w) reserves a column at the
   line's right end for the † and play buttons, which CSS takes out of the
   text (below) so a line never wraps for its marks. The margin-notes gutter
   is kept whatever the density of the glosses (they are placed by line
   number and may stack below their line). A sentence on a printed line
   (.unit--line) is never a block: its inline notes (.mnotes--line) are
   rendered under the printed line, its inline pictures above it. */
.reader[data-line-mode="book"] .prose { --prose-pr: var(--marks-room, 0px); padding-right: var(--prose-pr); }
.reader[data-line-mode="book"][data-margin="on"] .part[data-margin-mode="gutter"] .prose { --prose-pr: calc(var(--margin-col) + var(--margin-gap) + var(--marks-room, 0px)); }
.mnotes--line { margin: 0.15em 0 var(--s-3); }
@media (min-width: 768px) {
  .reader[data-line-mode="book"] { padding-inline: var(--s-5); }   /* the printed line gets the room first; the article stays centred */
  /* The marks column: each set of marks leaves the text for the slot at the
     line's right (its static position keeps it on the sentence's last line);
     --mark-i is a second sentence ending on the same line. Compact here:
     the † and the play button at their 24 px minimum, tight together. */
  .reader[data-line-mode="book"] .unit--line .marks {
    position: absolute; left: calc(100% - var(--prose-pr, 0px) + 0.4em + var(--mark-i, 0) * var(--marks-w, 0px));
  }
  .reader[data-line-mode="book"] .unit--line .marks.marks--inline { position: static; }   /* a set without a slot (reader.js) stays in the text */
  .reader[data-line-mode="book"] .unit--line .notemark { margin-left: 0; }
  .reader[data-line-mode="book"] .unit--line .playbtn { --playbtn-size: clamp(24px, 1.2em, 2.75rem); margin-left: 0; }
  .reader[data-line-mode="book"] .unit--line .notemark + .playbtn { margin-left: 0.15em; }
}
.speaker { font-variant: small-caps; letter-spacing: 0.04em; color: var(--ink-2); }
.speaker::after { content: ":"; }

/* English: hidden by default, interleaved beneath its sentence when on. */
.reader[data-english="hidden"] .en { display: none; }
.reader[data-english="interleaved"] .unit { display: block; margin-bottom: var(--s-4); }
.reader[data-english="interleaved"] .unit .en {
  display: block; margin-top: 0.15em;
  font-family: var(--face-ui); font-size: clamp(0.9rem, 0.78em, 1.75rem); line-height: 1.5; color: var(--ink-2);
}
.unit--verse { display: block; }
.unit--turn { display: block; margin-bottom: var(--s-3); }

/* Words */
.w {
  cursor: pointer; border-radius: 2px;
  transition: background-color var(--t-fast) var(--ease-out);
  text-decoration-skip-ink: none;
}
.w:hover { background: var(--rubric-soft); }
.w:focus-visible { outline-offset: 1px; }
/* Latin that was drawn as plain text — a picture caption enlarged, a cited form, a chapter title —
   cut into words the first time the pointer crosses it (hovergloss.js). It answers the pointer like
   a reading word, and it takes the same tint so the learner can see that it will; it is not a
   control, though, so a click on it is still whatever the click on what holds it always was. */
.r-wx { border-radius: 2px; transition: background-color var(--t-fast) var(--ease-out); }
.r-wx:hover { background: var(--rubric-soft); }
@media (prefers-reduced-motion: reduce) { .w, .r-wx { transition: none; } }
/* The finger's dictionary (GRAMMAR-CONTRACT.md §17.4): holding a word opens its entry, so the
   browser's own long press must not put a share sheet or a pair of selection handles over the top
   of it. The iOS callout is refused outright — touch-only, a mouse never meets it — and selection
   only while a finger is actually down on a word (`<html data-hold>`, set by hovergloss.js off a
   real pointer event), so a mouse can still select and copy a passage on a touchscreen laptop. */
.w, .r-wx { -webkit-touch-callout: none; }
:root[data-hold="1"] .w, :root[data-hold="1"] .r-wx { user-select: none; -webkit-user-select: none; }
.reader[data-underlines="on"] .w--seen, .lookups .w--seen, .note--summary .w--seen {
  text-decoration: underline; text-decoration-color: var(--underline);
  text-decoration-thickness: 0.16em; text-underline-offset: 0.14em;
}

/* Grammar-focus glow */
.reader[data-highlights="on"] .hl {
  background: var(--glow-bg); color: var(--glow-ink);
  box-shadow: 0 0 0 0.18em var(--glow-bg), 0 0 0.6em 0.2em var(--glow-ring);
  border-radius: 3px; box-decoration-break: clone; -webkit-box-decoration-break: clone;
}
.reader[data-highlights="on"] .hl .w:hover { background: var(--glow-ring); }

/* Note marker: a dagger, like a footnote sign. The button itself is the target
   (≥ 24px either way; the negative block margins keep it out of the line box)
   with the glyph raised inside it, so it and the play button that may follow
   never share a pixel. */
.notemark {
  display: inline-flex; align-items: center; justify-content: center; vertical-align: middle;
  min-width: 24px; height: 24px; margin: -8px 0 -8px 0.15em; padding: 0 0.2em;
  font: 0.6em var(--face-serif); line-height: 1; color: var(--ink-3);
  background: transparent; border: 0; border-radius: var(--radius-sm);
}
.notemark > span { transform: translateY(-0.4em); }
.marks { white-space: nowrap; }   /* the dagger and the play button never split across lines */
.notemark:hover { color: var(--rubric-ink); background: var(--rubric-soft); }

/* Audio (E toggles .is-playing via reader.setPlayingUnit) */
/* The per-sentence play button: a circle that grows with the type (24–44px;
   32px from size 3 up) and stands 0.6em clear of a dagger before it. */
.playbtn {
  --playbtn-size: clamp(24px, 1.6em, 2.75rem);
  display: inline-flex; align-items: center; justify-content: center; vertical-align: middle;
  width: var(--playbtn-size); height: var(--playbtn-size); margin: -0.3em 0 -0.3em 0.35em; border-radius: 50%;
  background: transparent; border: 1px solid var(--line-strong); color: var(--ink-2); padding: 0;
  transition: border-color var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out);
}
.notemark + .playbtn { margin-left: 0.6em; }
.playbtn::before { content: ""; width: 0; height: 0; margin-left: 0.1em; border-left: 0.42em solid currentColor; border-block: 0.28em solid transparent; }   /* the triangle */
.playbtn:hover { border-color: var(--ink); color: var(--ink); background: var(--rubric-soft); }
.unit.is-playing .playbtn { border-color: var(--rubric); color: var(--rubric-ink); }
.unit.is-playing .la, .sentence.is-playing .sentence__la { background: var(--rubric-soft); border-radius: 3px; box-decoration-break: clone; }
/* The word being spoken (audio.js → reader.setPlayingWord): a deeper wash of
   the playing sentence's rubric plus a rule under the word — distinct from the
   yellow lookup underline and the blue grammar glow, and it wins inside both. */
.reader .w--now, .reader[data-highlights="on"] .hl .w--now {
  background: var(--cursor-bg); box-shadow: 0 0.12em 0 0 var(--cursor-line); border-radius: 2px 2px 0 0;
}
/* Audio toggle off: nothing audio-related in the text (main.js also stops playback, which drops the highlight, and removes the buttons). */
.reader[data-audio="off"] .playbtn { display: none; }

/* ---------------------------------------------------------- reading progress */
/* "42 of 93 read · Continue →" (CONTRACT.md "Reading progress"): one quiet
   line in the text, placed by reader.js under the first part title (above
   the listen bar) and under sentence view's meta line; main.js paints it.
   Continue is a text button in the "Section summary" idiom. The read
   sentences themselves carry no mark — the page stays calm. */
.progress {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--s-1);
  margin: 0 0 var(--s-3) var(--gutter-r);
  font-family: var(--face-ui); font-size: var(--ui-sm); line-height: 1.4; color: var(--ink-3);
  font-variant-numeric: tabular-nums; hanging-punctuation: none;
}
.reader[data-margin="on"] .part[data-margin-mode="gutter"] .progress { margin-right: calc(var(--margin-col) + var(--margin-gap)); }   /* keeps to the text column */
.sentence .progress { margin-left: 0; margin-bottom: var(--s-3); }
.progress[data-state="done"] .progress__text { color: var(--success); }
.progress__continue {
  display: inline; min-height: 0; padding: 0.35em 0; margin: -0.35em 0;   /* block padding: a ≥ 24px target without moving the line */
  background: transparent; border: 0; border-radius: var(--radius-sm);
  color: var(--rubric-ink); font: inherit;
}
.progress__sep { color: var(--ink-3); }
.progress__word { text-decoration: underline; text-decoration-color: transparent; text-underline-offset: 0.15em; transition: text-decoration-color var(--t-fast) var(--ease-out); }
.progress__continue:hover .progress__word { text-decoration-color: currentColor; }
/* Sentence view's meta line: a faint "read ✓" once the sentence counts as read. */
.sentence__read { color: var(--ink-3); }

/* ---------------------------------------------------------- listen bar */
/* "Play passage" / "Play sentence", the speed and a status line, in the text
   itself: reader.js places #listen under the first part title (passage view)
   and above the sentence (sentence view); main.js paints it. Not sticky. With
   nothing to play, or Audio off, one quiet line takes its place. */
.listen {
  display: grid; gap: var(--s-2);
  margin: 0 0 var(--s-5) var(--gutter-r);
  font-family: var(--face-ui); font-size: var(--ui-sm); line-height: 1.4; color: var(--ink-2);
  hanging-punctuation: none;
}
.reader[data-margin="on"] .part[data-margin-mode="gutter"] .listen { margin-right: calc(var(--margin-col) + var(--margin-gap)); }   /* keeps to the text column */
.sentence .listen { margin-left: 0; margin-bottom: var(--s-4); }
.listen__main { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); }
.listen .btn { min-height: 36px; font-size: var(--ui-sm); }
@media (pointer: coarse) { .listen .btn { min-height: var(--tap); } }   /* a finger needs the full 44px */
.listen__play { gap: 0.55em; padding-inline: var(--s-3) var(--s-4); font-weight: 600; }
.listen__tri { width: 0; height: 0; border-left: 0.6em solid currentColor; border-block: 0.38em solid transparent; }
.listen__rate { font-variant-numeric: tabular-nums; min-width: 3.4em; padding-inline: var(--s-2); }
.listen__rate[aria-expanded="true"] { background: var(--bg-chrome); color: var(--ink); }
.listen__status { margin: 0; color: var(--ink-3); font-variant-numeric: tabular-nums; text-wrap: pretty; }
.listen__status:empty { display: none; }
.listen__rates { padding-bottom: var(--s-2); border-bottom: 1px solid var(--line); }
.listen__synth { margin: calc(-1 * var(--s-1)) 0 0; color: var(--ink-3); font-size: var(--ui-xs); }   /* "…read by a synthesised voice" (rows with synth: true) */
.listen__quiet { margin: 0; color: var(--ink-3); }
.listen[data-state="quiet"] .listen__main, .listen[data-state="quiet"] .listen__rates { display: none; }
.listen:not([data-state="quiet"]) .listen__quiet { display: none; }

/* While the transport is shown (main.js: html[data-transport="on"] + --transport-h,
   its measured height) the text gets that much room at the bottom, so the last
   sentence and sentence view's Next / Previous are never under it — and a
   scrollIntoView() of the playing sentence lands above it too. */
html[data-transport="on"] #main { padding-bottom: calc(var(--transport-h, 0px) + var(--s-4) + max(var(--s-4), env(safe-area-inset-bottom))); }
html[data-transport="on"] { scroll-padding-bottom: calc(var(--transport-h, 0px) + var(--s-6)); }

/* Chapter playback bar: only while a whole chapter plays. */
.transport {
  position: fixed; left: 50%; bottom: max(var(--s-4), env(safe-area-inset-bottom)); transform: translateX(-50%);
  z-index: var(--z-popover); display: grid; gap: var(--s-2);
  width: max-content; max-width: calc(100vw - 2 * var(--s-4)); padding: var(--s-2) var(--s-2) var(--s-2) var(--s-4);
  background: var(--bg-raised); border: 1px solid var(--line-strong); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-pop); font-family: var(--face-ui); font-size: var(--ui-sm); color: var(--ink);
  animation: rise var(--t-base) var(--ease-out);
}
.transport__main { display: flex; align-items: center; gap: var(--s-3); min-width: 0; }
.transport__text { margin: 0; line-height: 1.3; min-width: 0; }
.transport__title { font-weight: 600; }
.transport__pos { color: var(--ink-2); font-variant-numeric: tabular-nums; }
.transport__pos:not(:empty)::before { content: "· "; }
.transport__btns { display: flex; gap: var(--s-1); flex: none; }
.transport__rate { font-variant-numeric: tabular-nums; min-width: 3.4em; padding-inline: var(--s-2); }
.transport__rate[aria-expanded="true"] { background: var(--bg-chrome); color: var(--ink); }
/* The speed row opens above the buttons (the same chips as Settings → Audio). */
.transport__rates { order: -1; padding-bottom: var(--s-2); margin-right: var(--s-2); border-bottom: 1px solid var(--line); }
@keyframes rise { from { opacity: 0; transform: translate(-50%, 6px); } to { opacity: 1; transform: translateX(-50%); } }

/* Playback-speed chips (transport + Settings → Audio): 0.5 … 1.2, one pressed. */
.rate-chips { display: flex; flex-wrap: wrap; gap: var(--s-1); }
.rate-chip {
  min-width: 2.4em; min-height: 32px; padding: 0 var(--s-2); border-radius: 999px;
  border: 1px solid var(--line-strong); background: transparent; color: var(--ink-2);
  font-size: var(--ui-sm); font-variant-numeric: tabular-nums; line-height: 1;
  transition: background-color var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out);
}
.rate-chip:hover { border-color: var(--ink-3); color: var(--ink); }
.rate-chip[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--bg); }
@media (max-width: 420px) { .rate-chip { min-width: 2.1em; padding-inline: 5px; } .transport { padding-left: var(--s-3); } }

/* Short status line (play errors and the like); mirrored to #live for screen readers. */
.notice {
  position: fixed; left: 50%; bottom: max(var(--s-4), env(safe-area-inset-bottom)); transform: translateX(-50%);
  z-index: var(--z-tooltip); margin: 0; max-width: calc(100vw - 2 * var(--s-4)); padding: var(--s-2) var(--s-4);
  background: var(--ink); color: var(--bg); border-radius: var(--radius); box-shadow: var(--shadow-pop);
  font-family: var(--face-ui); font-size: var(--ui-sm); line-height: 1.4; text-wrap: pretty;
  animation: rise var(--t-base) var(--ease-out);
}
.transport:not([hidden]) ~ .notice { bottom: calc(max(var(--s-4), env(safe-area-inset-bottom)) + var(--tap) + var(--s-5)); }
@media (prefers-reduced-motion: reduce) { .transport, .notice { animation: none; } }

/* ---------------------------------------------------------- sentence */
.sentence { max-width: var(--measure); scroll-margin-top: calc(var(--bar-h, 0px) + var(--s-5)); outline: none; }
.unit, .part__title { scroll-margin-top: calc(var(--bar-h, 0px) + var(--s-5)); }
.sentence__meta { font-family: var(--face-ui); font-size: var(--ui-sm); color: var(--ink-3); margin: 0 0 var(--s-4); }
/* "Section summary" in the meta line: a quiet text button, styled like a link, opening the part's summary in the panel. */
.sentence__summary {
  display: inline; min-height: 0; padding: 0.35em 0; margin: -0.35em 0;   /* the block padding makes the target ≥ 24px without moving the line */
  background: transparent; border: 0; border-radius: var(--radius-sm);
  color: var(--rubric-ink); font-size: inherit; text-decoration: underline; text-decoration-color: transparent; text-underline-offset: 0.15em;
  transition: text-decoration-color var(--t-fast) var(--ease-out);
}
.sentence__summary:hover { text-decoration-color: currentColor; }
.sentence__la { font-size: clamp(1em, 1.3em, 1em + 0.5rem); line-height: 1.45; margin: 0 0 var(--s-4); text-wrap: pretty; }
.sentence__en { font-family: var(--face-ui); font-size: clamp(0.9rem, 0.85em, 1.75rem); line-height: 1.55; color: var(--ink-2); margin: 0 0 var(--s-5); }
.sentence__h { font-family: var(--face-ui); font-size: var(--ui-xs); font-weight: 600; letter-spacing: 0.02em; color: var(--ink-3); margin: 0 0 var(--s-2); }
.sentence__note { margin: 0 0 var(--s-5); padding-top: var(--s-4); border-top: 1px solid var(--line); }
.sentence__note p { font-family: var(--face-ui); font-size: calc(0.8em * var(--note-scale)); line-height: 1.55; color: var(--ink); margin: 0; }
/* The "In plain words" disclosure (panels.css) at the note's own size. */
.sentence__note .plain { font-size: calc(0.8em * var(--note-scale)); margin-top: var(--s-2); }
.sentence__note .plain__text { font-size: 1em; margin: var(--s-1) 0 0; }
/* "Words you looked up in this sentence": phones only — from 768px the side
   panel's stack for the sentence (panels.css "stack") replaces the list. */
.sentence__lookups { margin: 0 0 var(--s-6); padding-top: var(--s-4); border-top: 1px solid var(--line); font-family: var(--face-ui); font-size: 0.78em; line-height: 1.5; }
@media (min-width: 768px) { .sentence__lookups { display: none; } }
.sentence__hint { color: var(--ink-3); margin: 0; }
.lookups { list-style: none; margin: 0; padding: 0; }
.lookups__item { display: grid; grid-template-columns: auto 1fr; gap: 0 var(--s-3); padding: var(--s-2) 0; border-top: 1px dotted var(--line); align-items: baseline; }
.lookups__item:first-child { border-top: 0; }
.lookups__form { grid-row: 1 / span 3; min-height: var(--tap); font-family: var(--face-reading); font-size: 1.25em; background: transparent; border: 0; padding: 0; color: var(--ink); text-align: left; }
.lookups__meaning { color: var(--ink); }
.lookups__meaning--miss { color: var(--ink-3); font-style: italic; }
.lookups__parse { color: var(--ink-2); }
.lookups__lemma { color: var(--ink-3); font-family: var(--face-reading); font-style: italic; }
.lookups__item.is-learned .lookups__meaning::after { content: " · learned"; color: var(--success); }
.sentence__nav { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); font-family: var(--face-ui); font-size: var(--ui-md); }
.sentence__keys { color: var(--ink-3); font-size: var(--ui-xs); font-family: var(--face-mono); }
@media (hover: none) { .sentence__keys { visibility: hidden; } }

/* Signed in, nothing seeded */
.empty-library { max-width: 34rem; margin: var(--s-7) auto; padding: 0 var(--s-4); font-family: var(--face-ui); color: var(--ink-2); line-height: 1.55; }
.empty-library h2 { font-family: var(--face-serif); font-weight: 400; font-size: 1.4rem; color: var(--ink); margin: 0 0 var(--s-3); }
.empty-library code { font-size: 0.95em; background: var(--bg-chrome); padding: 0.1em 0.4em; border-radius: var(--radius-sm); }
