/* QuotePull — application styles. Light theme only (spec §9). */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  height: 100%;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100%;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: var(--ui-size);
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--accent);
}

button {
  font: inherit;
  color: inherit;
}

/* Any class that sets `display` beats the browser's own rule for [hidden], so
   a hidden button would still be on screen — visible and doing nothing. Say
   it once, here: hidden means hidden, whatever else an element is. */
[hidden] {
  display: none !important;
}

:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* ---------- header ---------- */

/* The bar carries a mark and the two things a reader can do to the whole
   session. Where they are in the book belongs at the foot of the page. */
.app-header {
  display: flex;
  align-items: center;
  gap: 20px;
  height: var(--header-h);
  padding: 0 20px;
  background: var(--panel);
  border-bottom: 1px solid var(--rule);
}

/* On the screens that centre a card, the bar lines up with it rather than
   sitting out at the window's edge — the mark should start where the card's
   own text starts. The reader keeps the full width, because what is under it
   there is full width too. `.entry-card` is 540px and `.entry-card-wide` is
   860px, and these mirror that. */
body:has(.entry:not([hidden])) .app-header {
  padding-inline: max(20px, calc((100% - 540px) / 2));
}

body:has(.entry:not([hidden]) .entry-card-wide) .app-header {
  padding-inline: max(20px, calc((100% - 860px) / 2));
}

/* The mark: two U+2019 set flush with the second one turned, which is the pair
   the curtain lands on and the same shape the favicon and the social still
   carry. Flush is settled — no letter-spacing between them. Public Sans hangs
   a quote glyph in the top third of the em box, so it is dropped by
   --mark-drop to sit on the bar. Black, no shadow.

   Two elements rather than a two-character string, because the second glyph is
   mirrored and there is no character for that. Nothing substitutes a quotation
   mark for the mark: the link is named in words and the glyphs are hidden from
   the accessibility tree. */
.wordmark {
  flex: none;
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 4rem;
  line-height: 1;
  height: 1em;
  letter-spacing: 0;
  color: var(--accent);
  text-decoration: none;
}

.wordmark-mark {
  display: flex;
  gap: 0;
}

.wordmark-mark > span {
  display: block;
  transform: translateY(var(--mark-drop));
}

.wordmark-mark > .is-turned {
  transform: translateY(var(--mark-drop)) scaleX(-1);
}

.wordmark:hover {
  color: var(--ink-muted);
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  justify-content: flex-end;
}

/* ---------- two-pane layout ---------- */

.app-main {
  display: grid;
  grid-template-columns: minmax(0, 65fr) minmax(320px, 35fr);
  height: calc(100dvh - var(--header-h));
}

.reading-pane {
  position: relative;
  display: grid;
  grid-template-columns: var(--rail-w) minmax(0, 1fr);
  min-height: 0;
  background: var(--panel);
  border-right: 1px solid var(--rule);
}

.theme-rail {
  position: relative;
  overflow: hidden;
  background: var(--paper);
  border-right: 1px solid var(--rule);
}

.rail-view {
  position: absolute;
  left: 0;
  right: 0;
  pointer-events: none;
}

.rail-view {
  background: rgba(31, 36, 48, 0.06);
}

.rail-ticks {
  position: absolute;
  inset: 0;
}

.rail-tick {
  position: absolute;
  left: 3px;
  width: 8px;
  height: 3px;
  padding: 0;
  border: 0;
  border-radius: 2px;
  background: var(--dot);
  box-shadow: 0 0 0 1px rgba(31, 36, 48, 0.22);
  cursor: pointer;
  transform: translateY(-1px);
}

.rail-tick:hover {
  transform: translateY(-1px) scaleX(1.3);
}

.rail-tick.is-active {
  height: 5px;
  box-shadow: 0 0 0 1.5px var(--accent);
}

/* The chapter, the prompt above it and the foot below it. */
.reading-stack {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  min-height: 0;
  min-width: 0;
}

/* Each part keeps its own row, so a task with no prompt does not push the
   page up into the prompt's place. */
.reading-stack > .prompt {
  grid-row: 1;
}

.reading-viewport {
  grid-row: 2;
  position: relative;
  min-height: 0;
}

/* One page wide, overflow hidden: the only way to move is a whole page at a
   time (js/pager.js), so a stray swipe can never land between two. */
.reading-scroll {
  height: 100%;
  overflow: hidden;
  padding: 36px max(56px, calc((100% - var(--reading-measure)) / 2));
}

/* Landing somewhere without turning — opening the chapter, or re-paging
   after a resize — is not a page turn and should not be animated. */
.reading.no-turn {
  transition: none;
}

@media (prefers-reduced-motion: reduce) {
  .reading {
    transition: none;
  }
}

/* A page turn slides, and the edge it came from darkens for the length of
   the slide — enough to read as a page moving, not enough to notice twice. */
.reading-scroll::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 56px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 260ms ease-out;
}

.reading-scroll.is-turning-next::after,
.reading-scroll.is-turning-prev::after {
  opacity: 1;
}

.reading-scroll.is-turning-next::after {
  left: 0;
  background: linear-gradient(to right, rgba(31, 36, 48, 0.09), transparent);
}

.reading-scroll.is-turning-prev::after {
  right: 0;
  background: linear-gradient(to left, rgba(31, 36, 48, 0.09), transparent);
}

@media (prefers-reduced-motion: reduce) {
  .reading-scroll::after {
    display: none;
  }
}

/* The turn targets live in the margins, so they never cover a word. */
.page-turn {
  position: absolute;
  top: 0;
  bottom: 0;
  width: var(--page-margin, 56px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink-muted);
  opacity: 0.4;
  cursor: pointer;
}

.page-prev {
  left: 0;
}

.page-next {
  right: 0;
}

.page-turn:hover,
.page-turn:focus-visible {
  opacity: 1;
  color: var(--accent);
}

.page-turn:disabled {
  opacity: 0.12;
  cursor: default;
}

/* Where you are, at the foot of the page: the book on the left, how far
   through on the right. */
.reading-foot {
  grid-row: 3;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 10px var(--page-margin, 56px) 14px;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.foot-where {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.foot-progress {
  flex: none;
  font-variant-numeric: tabular-nums;
}

/* Bottom centre: which paragraphs this page holds, and a way to any other.
   The label is a button; clicking it swaps in a small "go to ¶" box. */
.foot-para {
  flex: none;
  display: flex;
  align-items: center;
  gap: 2px;
  font-variant-numeric: tabular-nums;
}

.foot-para button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  padding: 2px 6px;
  cursor: pointer;
}

.foot-para button:hover {
  color: var(--accent);
  background: var(--accent-tint);
}

.para-step {
  font-size: 1rem;
  line-height: 1;
}

.para-step:disabled {
  opacity: 0.35;
  pointer-events: none;
}

.para-form {
  display: flex;
  align-items: center;
  gap: 4px;
}

.para-form input {
  width: 5ch;
  padding: 1px 4px;
  font: inherit;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  text-align: center;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--panel);
}

.para-form input:focus-visible {
  border-color: var(--accent);
  outline: none;
}

/* ---------- reading typography ---------- */

.reading {
  position: relative; /* the containing block for .sentence-preview */
  height: 100%;
  /* One column exactly a page wide, filled to the bottom before the next is
     started: the browser breaks the text at a line, as a book does. */
  column-width: var(--page-w, 100%);
  column-gap: var(--page-gap, 128px);
  column-fill: auto;
  /* The turn: the whole chapter slides by one page. */
  transition: transform 280ms cubic-bezier(0.22, 0.61, 0.36, 1);
  will-change: transform;
  font-family: var(--font-reading);
  font-size: var(--reading-size);
  line-height: var(--reading-lh);
  font-optical-sizing: auto;
  color: var(--ink);
}

.reading::selection,
.reading *::selection {
  background: rgba(47, 93, 98, 0.22);
}

.book-label {
  font-family: var(--font-ui);
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0 0 6px;
}

.chapter-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 2.25rem;
  line-height: 1.1;
  margin: 0 0 28px;
}

.para {
  margin: 0 0 1.05em;
  text-wrap: pretty;
  position: relative; /* paints above .sentence-preview, which is the article's first child */
}

/* The paragraph's number, small and faint in the margin — the same number
   the tracker and every citation call ¶. Drawn on a pseudo-element from the
   data-n attribute text.js sets, so the paragraph's own DOM text stays
   byte-identical to the chapter. */
.para::before {
  content: attr(data-n);
  position: absolute;
  right: 100%;
  padding-right: 12px;
  top: 0.45em;
  font-family: var(--font-ui);
  font-size: 0.65rem;
  line-height: 1;
  color: var(--ink-muted);
  opacity: 0.55;
  font-variant-numeric: tabular-nums;
  user-select: none;
}

/* Found on purpose — the paragraph someone just jumped to glows out. */
.para.is-located {
  animation: para-flash 1.6s ease-out;
}

@keyframes para-flash {
  0%,
  35% {
    background: var(--accent-tint);
    box-shadow: 0 0 0 6px var(--accent-tint);
  }
  100% {
    background: transparent;
    box-shadow: 0 0 0 6px transparent;
  }
}

@media (prefers-reduced-motion: reduce) {
  .para.is-located {
    animation: none;
  }
}

/* The sentence under the cursor, tinted before anything is clicked, so that
   "click here, get this" is shown rather than explained. Drawn as boxes
   behind the text: a paragraph's own DOM stays identical to the chapter,
   because that is what a highlight's offsets are anchored to. */
.sentence-preview {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.sentence-preview-box {
  position: absolute;
  background: var(--accent-tint);
  border-radius: 3px;
}

@media (prefers-reduced-motion: no-preference) {
  .sentence-preview-box {
    animation: preview-in 90ms ease-out;
  }
  @keyframes preview-in {
    from {
      opacity: 0;
    }
  }
}

.para.pre {
  white-space: pre;
  font-size: 0.85em;
  line-height: 1.5;
  overflow-x: auto;
}

.para.verse {
  white-space: pre-line;
  padding-left: 1.5em;
}

.chapter-subtitle {
  font-family: var(--font-ui);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  line-height: 1.7;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: -18px 0 28px;
}

.scene-break {
  border: 0;
  height: 1px;
  width: 40%;
  margin: 2.2em auto;
  background: var(--rule);
}

/* ---------- prompt card ---------- */

.prompt {
  max-width: var(--reading-measure);
  margin: 0 auto 32px;
  padding: 14px 18px;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.prompt-label {
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0 0 4px;
}

.prompt-text {
  margin: 0;
  font-size: 1rem;
  line-height: 1.5;
}

.prompt-meta {
  margin: 6px 0 0;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

/* ---------- highlights in the text ---------- */

mark.hl {
  background: var(--hl-color, var(--hl-fog));
  color: inherit;
  padding: 0.1em 0;
  border-radius: 2px;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  cursor: pointer;
}

mark.hl.is-active {
  box-shadow: 0 0 0 2px var(--accent);
}

/* close reading: everything outside the assigned passage stays readable but recedes */

/* ---------- teacher wizard ---------- */

.teacher {
  max-width: 820px;
  margin: 0 auto;
  padding: 32px 20px 140px;
}

.teacher-intro {
  margin: 0 0 24px;
}

.teacher-intro h1 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 2.25rem;
  line-height: 1.1;
  margin: 0 0 6px;
}

.teacher-intro p {
  margin: 0;
  color: var(--ink-muted);
}

/* ---------- a plain page of prose (privacy, 404) ---------- */

.doc {
  max-width: 680px;
  margin: 0 auto;
  padding: 40px 20px 120px;
}

.doc h1 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 2.25rem;
  line-height: 1.15;
  letter-spacing: -0.02em;
  margin: 0 0 10px;
}

.doc h2 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.25rem;
  line-height: 1.25;
  margin: 40px 0 10px;
  padding-top: 20px;
  border-top: 1px solid var(--rule);
}

.doc h3 {
  font-size: var(--ui-size);
  font-weight: 700;
  margin: 22px 0 6px;
}

.doc p,
.doc li {
  line-height: 1.65;
}

.doc > p:first-of-type {
  font-size: 1.125rem;
  color: var(--ink-muted);
  margin-top: 0;
}

.doc ul {
  padding-left: 1.15em;
}

.doc li {
  margin-bottom: 7px;
}

.doc code {
  font-size: 0.9em;
  background: var(--accent-tint);
  border-radius: var(--radius-sm);
  padding: 1px 5px;
}

.doc kbd {
  font: inherit;
  font-size: 0.85em;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-bottom-width: 2px;
  border-radius: var(--radius-sm);
  padding: 1px 6px;
}

/* The one claim on the page that is worth setting apart. */
.doc .doc-lede {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.5rem;
  line-height: 1.3;
  color: var(--ink);
  margin: 24px 0;
}

.doc-updated {
  margin-top: 48px;
  padding-top: 20px;
  border-top: 1px solid var(--rule);
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.my-assignments {
  margin: 0 0 28px;
}

.my-assignments h2 {
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0 0 8px;
}

.assignment-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 8px;
}

.assignment-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.assignment-item .info {
  flex: 1;
  min-width: 0;
}

.assignment-item .title {
  display: block;
  font-weight: 600;
}

.assignment-item .meta {
  display: block;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.item-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.wizard {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.step {
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.step-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
}

.step.is-done .step-head {
  cursor: pointer;
  border-radius: var(--radius);
}
.step.is-done .step-head:hover {
  background: var(--accent-tint);
}

.step-num {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--accent-tint);
  color: var(--accent);
  font-size: 0.75rem;
  font-weight: 600;
}

.step.is-current .step-num {
  background: var(--accent);
  color: var(--accent-ink);
}

.step-title {
  flex: none;
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.35rem;
  line-height: 1.1;
}

.step-summary {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--ink-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.step.is-upcoming {
  opacity: 0.55;
}

.step-body {
  padding: 4px 16px 18px 52px;
}

.step-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 16px;
}

.field {
  display: block;
  margin: 0 0 12px;
}

.field > span {
  display: block;
  margin-bottom: 6px;
  font-size: var(--ui-size-sm);
  font-weight: 600;
}

.field select,
.field input[type='text'],
.field input[type='date'],
.field textarea {
  width: 100%;
  max-width: 460px;
  padding: 8px 10px;
  font: inherit;
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--panel);
}

.field textarea {
  max-width: none;
  min-height: 84px;
  resize: vertical;
}

.hint {
  margin: 6px 0 0;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.error {
  margin: 8px 0 0;
  font-size: var(--ui-size-sm);
  color: var(--danger);
}

.theme-rows {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 12px 0;
}

.theme-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.swatches {
  display: flex;
  gap: 4px;
}

.swatch {
  width: 22px;
  height: 22px;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 50%;
  background: var(--dot);
  box-shadow: inset 0 0 0 1px rgba(31, 36, 48, 0.15);
  cursor: pointer;
}

.swatch[aria-pressed='true'] {
  border-color: var(--ink);
}

.theme-name {
  flex: 1;
  max-width: 260px;
  padding: 7px 10px;
  font: inherit;
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--panel);
}

.icon-btn {
  padding: 2px 6px;
  border: 0;
  border-radius: 4px;
  background: none;
  color: var(--ink-muted);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}

.icon-btn:hover:not(:disabled) {
  color: var(--danger);
  background: #f6ebe7;
}

.icon-btn:disabled {
  opacity: 0.3;
  cursor: default;
}

.preview-wrap {
  margin: 8px 0 16px;
}

.preview-frame-wrap {
  position: relative;
  margin-top: 6px;
  overflow: hidden;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--paper);
}

.preview-wrap iframe {
  display: block;
  width: 1280px;
  height: 800px;
  border: 0;
  transform-origin: top left;
  background: var(--paper);
}

/* ---------- export menu ---------- */

.menu-wrap {
  position: relative;
}

.menu {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  z-index: 40;
  min-width: 280px;
  padding: 6px;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  box-shadow: var(--shadow-pop);
}

.menu[hidden] {
  display: none;
}

.menu-item {
  display: flex;
  flex-direction: column;
  gap: 1px;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  text-align: left;
  cursor: pointer;
}

.menu-item:hover,
.menu-item:focus-visible {
  background: var(--accent-tint);
}

.menu-item strong {
  font-size: var(--ui-size-sm);
  font-weight: 600;
}

.menu-item span {
  font-size: 0.75rem;
  color: var(--ink-muted);
}

.menu-foot {
  margin: 6px 4px 2px;
  padding-top: 6px;
  border-top: 1px solid var(--rule);
  font-size: 0.75rem;
  color: var(--ink-muted);
}

.export-nudge {
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.export-nudge.warn {
  color: var(--danger);
}

.published {
  margin-top: 16px;
  padding: 16px;
  background: var(--accent-tint);
  border-radius: var(--radius);
}

.published .code {
  margin: 0 0 10px;
  font-family: var(--font-display);
  font-size: 2rem;
  letter-spacing: 0.04em;
}

.link-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 4px 0 6px;
}

.qr-row {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin: 4px 0 10px;
}

.qr-art {
  flex: none;
  padding: 8px;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
}

.qr-art svg {
  display: block;
  width: 132px;
  height: 132px;
}

.qr-side {
  flex: 1;
  min-width: 200px;
}

.qr-side p {
  margin: 0 0 8px;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

/* Full screen, for the projector. Nothing on it but the way in. */
.projector {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2vh;
  padding: 4vh 4vw;
  background: var(--panel);
  cursor: zoom-out;
}

.projector-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 3.5vh, 2.5rem);
  text-align: center;
}

.projector-qr svg {
  display: block;
  width: min(62vh, 78vw);
  height: min(62vh, 78vw);
}

.projector-hint {
  margin: 0;
  max-width: 60ch;
  text-align: center;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.link-row input {
  flex: 1;
  min-width: 200px;
  padding: 8px 10px;
  font: inherit;
  font-size: var(--ui-size-sm);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--panel);
}

a.btn {
  text-decoration: none;
}

.step-summary .theme-chip {
  color: var(--ink);
}

/* ---------- the holding slot ---------- */

/* The quote being made lives here, at the top of the panel: a fixed place
   to look, with room for theme names rather than bare colour dots. */
.quote-slot {
  flex: none;
  padding: 14px 20px;
  border-bottom: 1px solid var(--rule);
  background: var(--panel);
  font-size: var(--ui-size-sm);
}

.quote-slot.is-pending {
  box-shadow: inset 3px 0 0 var(--accent);
}

.quote-slot.is-empty {
  color: var(--ink-muted);
}

.slot-hint {
  margin: 0;
}

.slot-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 6px;
}

.slot-label {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.slot-close {
  flex: none;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--ink-muted);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}

.slot-close:hover {
  background: var(--accent-tint);
  color: var(--ink);
}

.slot-text {
  margin: 0;
  max-height: 8.5em;
  overflow-y: auto;
  font-family: var(--font-reading);
  font-size: 0.95rem;
  line-height: 1.45;
}

/* "2 sentences" and the ＋ that takes the next one — no modifier key,
   because Ctrl-click has no answer on a touchscreen. */
.slot-size {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
  color: var(--ink-muted);
}

.size-add {
  flex: none;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: 50%;
  background: transparent;
  color: var(--ink);
  font: inherit;
  line-height: 1;
  cursor: pointer;
}

.size-add:hover:not([disabled]),
.size-add:focus-visible:not([disabled]) {
  background: var(--accent-tint);
  border-color: var(--accent-tint);
}

.size-add[disabled] {
  opacity: 0.35;
  cursor: default;
}

.slot-note {
  display: block;
  margin: 10px 0 0;
}

.slot-note-label {
  display: block;
  margin-bottom: 3px;
  color: var(--ink-muted);
}

.slot-note input {
  width: 100%;
  padding: 6px 8px;
  font: inherit;
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--panel);
}

.slot-note input:focus-visible {
  border-color: var(--accent);
}

.slot-lead {
  margin: 10px 0 4px;
  color: var(--ink-muted);
}

.slot-themes {
  display: flex;
  flex-direction: column;
}

/* A theme is a named row, not a bare dot: the name is the vocabulary. */
.slot-theme {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px 8px;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.slot-theme:hover,
.slot-theme:focus-visible {
  background: var(--accent-tint);
}

.slot-theme .dot,
.theme-chip::before {
  content: '';
  flex: none;
  display: inline-block;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--dot);
  border: 1px solid rgba(31, 36, 48, 0.15);
}

.theme-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 4px 0 2px;
  white-space: nowrap;
  color: var(--ink-muted);
}

/* "+ New theme" under the rows: the legend can grow at the moment of tagging */
.slot-new {
  margin-top: 4px;
  padding: 6px 10px;
  border: 1px dashed var(--rule);
  border-radius: 999px;
  background: transparent;
  color: var(--ink-muted);
  font: inherit;
  cursor: pointer;
  align-self: flex-start;
}

.slot-new:hover,
.slot-new:focus-visible {
  background: var(--accent-tint);
  border-color: var(--accent-tint);
  color: var(--ink);
}

.slot-new-theme {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 4px 0 0;
}

.slot-new-theme .swatch {
  width: 18px;
  height: 18px;
}

.new-theme-name {
  flex: 1 1 120px;
  min-width: 110px;
  padding: 6px 8px;
  font: inherit;
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--panel);
}

.new-theme-name:focus-visible {
  border-color: var(--accent);
}

.slot-error {
  flex-basis: 100%;
  margin: 0;
  font-size: var(--ui-size-sm);
  color: var(--danger);
}

.slot-error[hidden] {
  display: none;
}

.slot-msg {
  margin: 0 0 8px;
}

/* ---------- buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--panel);
  color: var(--ink);
  font-size: var(--ui-size-sm);
  font-weight: 500;
  line-height: 1.2;
  cursor: pointer;
  white-space: nowrap;
}

.btn:hover {
  background: var(--accent-tint);
  border-color: var(--accent-tint);
}

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

.btn-primary:hover {
  background: #264d51;
  border-color: #264d51;
}

.btn-ghost {
  border-color: transparent;
  background: transparent;
  color: var(--accent);
}

/* Disabled buttons keep their tooltip — that is where the reason lives. */
.btn:disabled {
  opacity: 0.45;
  cursor: default;
}

.btn:disabled:hover {
  background: var(--panel);
  border-color: var(--rule);
}

.btn-ghost:disabled:hover {
  background: transparent;
  border-color: transparent;
}

/* ---------- the printed task sheet (sheet.html) ---------- */

.sheet-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 20px;
  border-bottom: 1px solid var(--rule);
  background: var(--panel);
}

.sheet {
  box-sizing: border-box;
  width: min(720px, 100%);
  margin: 28px auto 60px;
  padding: 0 24px;
  color: var(--ink);
}

.sheet-head {
  position: relative;
  padding-bottom: 12px;
  padding-right: 132px;
  border-bottom: 2px solid var(--ink);
}

/* Top right of the sheet, out of the way of the title and clear of the fold. */
.sheet-qr {
  position: absolute;
  top: 0;
  right: 0;
  width: 116px;
  text-align: center;
}

.sheet-qr svg {
  display: block;
  width: 116px;
  height: 116px;
}

.sheet-qr-caption {
  display: block;
  margin-top: 3px;
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.sheet-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 2rem;
  line-height: 1.1;
}

/* Name, course, teacher and period: what a teacher sorts a stack by, so it
   sits directly under the title in the reading colour, not the muted one. */
.sheet-student {
  margin: 6px 0 0;
  font-weight: 600;
}

.sheet-meta {
  margin: 4px 0 0;
  color: var(--ink-muted);
}

.sheet-code {
  margin: 6px 0 0;
  font-size: var(--ui-size-sm);
  letter-spacing: 0.08em;
  color: var(--ink-muted);
}

.sheet-h {
  margin: 22px 0 8px;
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.sheet-prompt-text {
  margin: 0;
  font-family: var(--font-reading);
  font-size: 1.1rem;
  line-height: 1.5;
}

.sheet-legend {
  border-collapse: collapse;
  width: 100%;
  max-width: 420px;
}

.sheet-legend td {
  padding: 5px 10px 5px 0;
  border-bottom: 1px solid var(--rule);
  vertical-align: middle;
}

.sheet-swatch-cell {
  width: 28px;
}

.sheet-swatch {
  display: block;
  width: 20px;
  height: 20px;
  border: 1px solid var(--ink);
  border-radius: 3px;
  background: var(--dot);
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

.sheet-colour {
  width: 90px;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.sheet-theme {
  font-weight: 600;
}

.sheet-steps,
.sheet-rules {
  margin: 0;
  padding-left: 22px;
  line-height: 1.55;
}

.sheet-steps li,
.sheet-rules li {
  margin-bottom: 5px;
}

.sheet-foot {
  margin-top: 26px;
  padding-top: 10px;
  border-top: 1px solid var(--rule);
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.sheet-empty {
  margin: 40px 0;
  color: var(--ink-muted);
}

/* ---------- the printed quote sheet (quotes.html) ---------- */

.quotes-count {
  margin: 10px 0 0;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.quotes-none {
  margin: 24px 0;
  font-family: var(--font-reading);
  color: var(--ink-muted);
}

.quotes-theme {
  margin-top: 26px;
  break-inside: auto;
}

.quotes-theme-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 10px;
  padding-bottom: 5px;
  border-bottom: 1px solid var(--rule);
  font-size: 1.05rem;
  font-weight: 600;
  break-after: avoid;
}

.quotes-theme-name {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.25rem;
}

.quotes-colour,
.quotes-theme-count {
  font-family: var(--font-ui);
  font-size: var(--ui-size-sm);
  font-weight: 400;
  color: var(--ink-muted);
}

.quotes-theme-count {
  margin-left: auto;
}

.quotes-empty-theme {
  margin: 0 0 4px;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.quote-entry {
  display: grid;
  grid-template-columns: 1.9rem 1fr;
  gap: 0 4px;
  margin-bottom: 14px;
  break-inside: avoid;
}

.quote-entry-n {
  font-variant-numeric: tabular-nums;
  color: var(--ink-muted);
}

.quote-entry-text {
  margin: 0;
  font-family: var(--font-reading);
  font-size: 1.05rem;
  line-height: 1.45;
}

.quote-entry-cite,
.quote-entry-note {
  margin: 3px 0 0;
  font-size: var(--ui-size-sm);
  line-height: 1.45;
}

.quote-entry-cite {
  color: var(--ink-muted);
}

.quotes-cited {
  margin: 18px 0 0;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.quotes-cited {
  margin-top: 0;
  color: var(--ink);
}

@media print {
  .no-print,
  .sheet-bar {
    display: none !important;
  }

  body.sheet-page {
    background: #fff;
  }

  .sheet {
    width: auto;
    margin: 0;
    padding: 0;
  }

  .sheet-block,
  .sheet-prompt,
  .sheet-qr {
    break-inside: avoid;
  }

  /* The modules have to print at full contrast or the code will not scan. */
  .sheet-qr svg path {
    fill: #000;
    print-color-adjust: exact;
    -webkit-print-color-adjust: exact;
  }

  @page {
    margin: 16mm;
  }
}

/* ---------- boot failure notice ---------- */

/* The curtain over first paint: solid paper, one breathing quotation mark.
   pointer-events: none, so it can only ever be looked at — a click lands on
   whatever is underneath even while it is fading.

   .is-quiet is the same curtain raised for a click inside the site rather than
   an arrival at it: no mark, no held beat, and a faster fade. The mark is an
   entrance, and an entrance shown on the way back from the quote list is just
   something in the way. */
.boot-veil {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  background: var(--paper);
  pointer-events: none;
  transition: opacity 350ms ease;
}

.boot-veil.is-done {
  opacity: 0;
}

.boot-veil.is-quiet.is-done {
  transition-duration: 160ms;
}

/* The intro: two U+2019 fly in from opposite edges, both upright. They collide,
   and the right one turns — and stays turned. What is left standing is the
   site's mark, the same pair as in the header, on the favicon and on the social
   still: a quote mark and its mirror image, reading as a lowercase q and p.

   The turn is the payoff, so it is the last thing to happen and it does not
   come back. The mark is the turned pair, not the upright one.

   Two nested elements per mark on purpose: the outer one only ever translates,
   the inner one only ever flips. Putting both on one element means the second
   animation overwrites the first, because `transform` is a single property.

   First visit only. `html.skip-intro` is set by a blocking script in the head
   of index.html, before any stylesheet, so a returning student never sees a
   frame of this. */
.boot-curtain {
  /* Curtain and skeleton share the one centred cell; only ever one is up. */
  grid-area: 1 / 1;
  display: flex;
  /* Flush. The two have to read as one mark, so there is no gap and no
     letter-spacing between them — this is settled. */
  gap: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(9rem, 28vw, 18rem);
  line-height: 1;
  /* The same black as the header mark — one mark, one colour, everywhere. */
  color: var(--accent);
  /* Held back until Public Sans is really in. The fallback's quote glyph is a
     different shape, and the mirror of a different shape is the flicker the
     curtain exists to hide. An empty curtain for a moment is the point. */
  opacity: 0;
  transition: opacity 180ms ease;
  /* The offscreen start. Tied to the mark's own size rather than to the
     viewport: everything here scales together, so the impact reads at the same
     speed on a phone and on a projector. */
  --mark-d: 6em;
}

html.skip-intro .boot-curtain,
.boot-veil.is-quiet .boot-curtain {
  display: none;
}

.boot-veil.is-lit .boot-curtain {
  opacity: 1;
}

.boot-curtain .mover {
  display: block;
  transform: translateX(0);
}

.boot-curtain .glyph {
  display: block;
  /* Quote glyphs hang high in the em box; this drops the pair to the middle. */
  transform: translateY(var(--mark-drop));
}

/* The right-hand glyph rests turned, because that is where the intro leaves it
   and it is what the mark is. The animation runs *towards* this, and both the
   resolved state and the reduced-motion path fall back to it by switching the
   animation off — so the mark is what shows in every case. */
.boot-curtain .mover-right .glyph {
  transform: translateY(var(--mark-drop)) scaleX(-1);
}

/* Both timelines start together, on the same class. */
.boot-veil.is-playing .mover-left {
  animation: mark-travel-left 1698ms cubic-bezier(0.2, 0.75, 0.25, 1) both;
}

.boot-veil.is-playing .mover-right {
  animation: mark-travel-right 1698ms cubic-bezier(0.2, 0.75, 0.25, 1) both;
}

.boot-veil.is-playing .mover-right .glyph {
  animation: mark-flip 2642ms cubic-bezier(0.65, 0, 0.2, 1) both;
}

/* 62% overshoots and 76% rebounds: that pair of frames is the collision.
   Without them the marks read as stopping politely rather than hitting. */
@keyframes mark-travel-left {
  0% {
    transform: translateX(calc(-1 * var(--mark-d)));
  }
  62% {
    transform: translateX(14px);
  }
  76% {
    transform: translateX(-6px);
  }
  100% {
    transform: translateX(0);
  }
}

@keyframes mark-travel-right {
  0% {
    transform: translateX(var(--mark-d));
  }
  62% {
    transform: translateX(-14px);
  }
  76% {
    transform: translateX(6px);
  }
  100% {
    transform: translateX(0);
  }
}

/* Both marks arrive upright, and the right one is held that way until 60% —
   well after contact. A turn that overlapped the travel would read as one of
   them tumbling in; held back, it reads as the collision knocking it round.
   It stays turned: 92% and 100% are the mark. */
@keyframes mark-flip {
  0% {
    transform: translateY(var(--mark-drop)) scaleX(1);
  }
  60% {
    transform: translateY(var(--mark-drop)) scaleX(1);
  }
  92% {
    transform: translateY(var(--mark-drop)) scaleX(-1);
  }
  100% {
    transform: translateY(var(--mark-drop)) scaleX(-1);
  }
}

/* Skipped, or finished: the resolved pair, no motion. */
.boot-veil.is-resolved .mover,
.boot-veil.is-resolved .mover-right .glyph {
  animation: none;
}

/* The shape of a page, held back 400ms. Nearly every load inside the site is
   over before that, so the usual quiet curtain is a blink of paper and nothing
   else; the bars are only there so a slow one doesn't read as a dead screen. */
.boot-skeleton {
  grid-area: 1 / 1;
  display: flex;
  flex-direction: column;
  gap: 14px;
  width: min(560px, 78vw);
  opacity: 0;
}

.boot-skeleton span {
  height: 13px;
  border-radius: 7px;
  background: var(--rule);
}

.boot-skeleton span:first-child {
  width: 42%;
  height: 22px;
  margin-bottom: 8px;
}

.boot-skeleton span:nth-child(3) {
  width: 92%;
}

.boot-skeleton span:last-child {
  width: 61%;
}

.boot-veil.is-quiet .boot-skeleton {
  animation: boot-skeleton-in 240ms ease 400ms forwards;
}

@keyframes boot-skeleton-in {
  to {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  /* The mark still shows — it is the site's mark, not decoration. What goes is
     the movement: the resolved pair, held for a beat, then the curtain lifts.
     Belt and braces with the JS, which never adds .is-playing here. */
  .boot-veil .mover,
  .boot-veil .mover-right .glyph {
    animation: none !important;
  }

  /* Still held back — the delay is what keeps the bars off a fast load — but
     arriving without a fade. */
  .boot-veil.is-quiet .boot-skeleton {
    animation-duration: 1ms;
  }
}

.boot-failed {
  max-width: 540px;
  margin: 40px auto;
  padding: 18px 20px;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-left: 4px solid var(--danger);
  border-radius: var(--radius);
}

.boot-failed p {
  margin: 0 0 10px;
}

.boot-failed p:last-child {
  margin-bottom: 0;
}

/* ---------- utilities ---------- */

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* A square button that is only its icon: the header carries two of them. */
.btn-icon {
  padding: 7px;
  border-color: transparent;
  background: none;
  color: var(--ink-muted);
}

.btn-icon:hover {
  color: var(--ink);
  border-color: var(--rule);
  background: var(--panel);
}

.btn-icon.is-on {
  color: var(--accent);
  background: var(--accent-tint);
  border-color: var(--accent-tint);
}

.btn-sm {
  padding: 4px 8px;
}

.badge {
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: 999px;
  background: var(--accent-tint);
  color: var(--accent);
}

.header-status {
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.header-status.warn {
  color: var(--danger);
}

/* ---------- entry screens ---------- */

/* The shelf is short enough to fit a window, so it should never scroll. The
   breathing room above it gives way before the content does: 56px on a roomy
   screen, down to 20px on a laptop with the browser's chrome eating the top,
   which is the difference between fitting and a scrollbar over nothing. */
.entry {
  min-height: calc(100dvh - var(--header-h));
  display: grid;
  place-items: start center;
  padding: clamp(20px, 6vh, 56px) 20px;
}

.entry[hidden],
.app-main[hidden] {
  display: none;
}

.entry-card {
  width: min(540px, 100%);
  padding: 28px 30px;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.entry-card-wide {
  width: min(860px, 100%);
}

/* ---------- a code or a link, or straight on ---------- */

.entry-or {
  margin: 14px 0;
  text-align: center;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.btn-wide {
  width: 100%;
  justify-content: center;
  padding: 10px 12px;
}

/* ---------- who is working (the dialog over the reader at print time) ---------- */

.student-dialog {
  width: min(540px, calc(100% - 40px));
  padding: 24px 26px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--panel);
  color: var(--ink);
  box-shadow: var(--shadow-pop);
}

.student-dialog::backdrop {
  background: rgba(31, 36, 48, 0.4);
}

.student-dialog .entry-title {
  font-size: 1.5rem;
}

.student-dialog .entry-lead {
  font-size: var(--ui-size);
  margin-bottom: 8px;
}

.student-remember {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
  cursor: pointer;
}

/* The stacked entry form stretches its inputs (.entry-form input, below);
   a checkbox stays a checkbox. */
.entry-form .student-remember input {
  flex: none;
}

.student-dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 12px;
}

.student-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 10px;
  margin: 10px 0 4px;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.field-short {
  max-width: 120px;
}

/* ---------- choose a text, then a chapter ---------- */

.entry-back {
  display: inline-block;
  margin: 0 0 10px;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
  text-decoration: none;
}

.entry-back:hover {
  color: var(--accent);
}

.chapter-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 8px;
  margin-top: 16px;
}

.chapter-card {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 12px;
  text-align: left;
  font: inherit;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.chapter-card:hover,
.chapter-card:focus-visible {
  border-color: var(--accent);
  background: var(--accent-tint);
}

.chapter-card.is-current {
  border-color: var(--accent);
}

.chapter-name {
  font-weight: 600;
}

.chapter-words {
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.entry-search {
  margin: 18px 0 0;
}

.entry-search a {
  color: var(--ink-muted);
}

.entry-search a:hover {
  color: var(--accent);
}

/* ---------- search cards (SEARCH-CARDS.md) ---------- */

.cards-cap {
  margin: 0 0 14px;
  color: var(--ink-muted);
}

/* Starters under the search box, and "words like this one" at the end. */
.cards-starters {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 14px;
}

.cards-chip {
  padding: 5px 12px;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--paper);
  color: var(--ink);
  text-decoration: none;
  font-size: var(--ui-size-sm);
}

.cards-chip:hover,
.cards-chip:focus-visible {
  border-color: var(--accent);
  background: var(--accent-tint);
}

.cards-forms {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-top: 10px;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.cards-forms[hidden] {
  display: none;
}

.cards-related {
  margin-top: 22px;
}

.cards-related-lead {
  margin: 0 0 2px;
  color: var(--ink-muted);
}

.deck {
  margin-top: 8px;
}

.deck-stack {
  position: relative;
}

.deck-card,
.deck-edge {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

/* The top card sits in flow so it takes its own height; the edges behind
   it just borrow that height. */
.deck-card {
  position: relative;
  z-index: 1;
  min-height: 200px;
  display: flex;
  flex-direction: column;
  padding: 14px 16px;
  box-shadow: var(--shadow-pop);
  touch-action: pan-y;
  cursor: grab;
  transition: transform 0.18s ease;
}

.deck-edge {
  position: absolute;
  inset: 0;
}

/* The deck has to look finite, or thirty-three feels like an unbounded
   chore; the counter is the honest version of the same thing. */
.deck-edge-1 {
  transform: translate(3px, 3px);
}

.deck-edge-2 {
  transform: translate(6px, 6px);
}

.deck-edge[hidden] {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  .deck-card {
    transition: none;
  }
}

.deck-card.is-out {
  opacity: 0;
}

.card-head {
  display: flex;
  justify-content: space-between;
  margin-bottom: 8px;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

/* Long sentences scroll inside the card rather than growing it: the
   buttons must never leave a Chromebook's screen. */
.card-text {
  margin: 0;
  max-height: 9em;
  overflow-y: auto;
  font-family: var(--font-reading);
  font-size: 1.05rem;
  line-height: 1.5;
}

.card-hit {
  font-style: normal;
  color: var(--accent);
  font-weight: 600;
}

.card-context {
  margin: 6px 0 0;
  font-family: var(--font-reading);
  font-size: 0.9rem;
  line-height: 1.45;
  color: var(--ink-muted);
  max-height: 5em;
  overflow-y: auto;
}

.card-context-toggle {
  align-self: center;
  margin-top: auto;
  padding: 0 12px;
  border: 0;
  background: none;
  color: var(--ink-muted);
  font-size: 1rem;
  cursor: pointer;
}

.card-context-toggle:hover {
  color: var(--accent);
}

.deck-buttons {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  margin-top: 16px;
}

.deck-keep,
.deck-toss {
  flex: 1;
  justify-content: center;
}

.deck-hint {
  margin: 10px 0 0;
  text-align: center;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.keeps-list {
  margin: 0 0 16px;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 8px;
}

.keep-item {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px 8px;
  padding: 8px 10px;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
}

.keep-where {
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.keep-text {
  grid-column: 1;
  font-family: var(--font-reading);
  font-size: 0.95rem;
  line-height: 1.45;
}

.keep-remove {
  grid-row: 1 / span 2;
  grid-column: 2;
  align-self: start;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--ink-muted);
  font-size: 1.05rem;
  cursor: pointer;
}

.keep-remove:hover {
  background: var(--accent-tint);
  color: var(--ink);
}

.keeps-themes {
  margin-bottom: 10px;
}

.keeps-new-theme {
  margin-bottom: 16px;
}

.deck-again {
  margin-top: 8px;
}

.entry-kicker {
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0 0 6px;
}

.entry-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 2rem;
  line-height: 1.1;
  margin: 0 0 8px;
}

.entry-lead {
  margin: 0 0 18px;
  color: var(--ink-muted);
}

.entry-form,
.entry-row {
  display: flex;
  gap: 8px;
}

.entry-form-stacked {
  flex-direction: column;
  gap: 6px;
}

.entry-form input {
  flex: 1;
  min-width: 0;
  padding: 9px 10px;
  font: inherit;
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--panel);
}

.entry-form input:focus-visible {
  border-color: var(--accent);
}

.field-label {
  display: block;
  font-size: var(--ui-size-sm);
  font-weight: 600;
}

.entry-error {
  margin: 10px 0 0;
  font-size: var(--ui-size-sm);
  color: var(--danger);
}

.entry-recent {
  margin-top: 22px;
}

.entry-recent h2 {
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0 0 8px;
}

.entry-recent ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.entry-recent a,
.entry-recent button {
  display: block;
  width: 100%;
  padding: 10px 12px;
  text-align: left;
  text-decoration: none;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.entry-recent a:hover,
.entry-recent button:hover {
  border-color: var(--accent);
}

.entry-recent .title {
  display: block;
  font-weight: 600;
}

.entry-recent .meta {
  display: block;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.entry-foot {
  margin: 22px 0 0;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

/* ---------- tracker panel ---------- */

.tracker {
  min-height: 0;
  overflow-y: auto;
  padding: 20px;
  background: var(--paper);
}

.tracker-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.tracker-heading {
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0;
}

.progress {
  margin: 0;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.tracker-empty {
  color: var(--ink-muted);
  margin: 0 0 14px;
}

.theme-group {
  margin: 0 0 10px;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.theme-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  font-weight: 600;
  list-style: none;
  cursor: pointer;
  border-radius: var(--radius);
}

.theme-summary::-webkit-details-marker {
  display: none;
}

.theme-summary::after {
  content: '';
  margin-left: auto;
  width: 7px;
  height: 7px;
  border-right: 1.5px solid var(--ink-muted);
  border-bottom: 1.5px solid var(--ink-muted);
  transform: translateY(-2px) rotate(45deg);
}

.theme-group[open] > .theme-summary::after {
  transform: translateY(2px) rotate(-135deg);
}

.theme-summary .dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--dot);
  border: 1px solid rgba(31, 36, 48, 0.15);
}

.theme-count {
  font-weight: 500;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.theme-empty {
  margin: 0;
  padding: 0 12px 12px;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

/* ---------- the legend, edited while reading ---------- */

.theme-edit {
  margin-left: auto;
  padding: 2px 8px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--ink-muted);
  font: inherit;
  font-size: var(--ui-size-sm);
  font-weight: 500;
  cursor: pointer;
}

.theme-edit:hover,
.theme-edit:focus-visible {
  background: var(--accent-tint);
  color: var(--ink);
}

/* the chevron sits beside Edit rather than being pushed to the far edge */
.theme-summary:has(.theme-edit)::after {
  margin-left: 4px;
}

.theme-editor {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 0;
  padding: 0 12px 12px;
}

.theme-editor.is-new {
  padding: 10px 12px;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.theme-editor .theme-name {
  flex: 1 1 110px;
  max-width: none;
  padding: 5px 8px;
  font-size: var(--ui-size-sm);
}

.theme-editor .swatch {
  width: 18px;
  height: 18px;
}

.theme-editor .error {
  flex-basis: 100%;
  margin: 0;
}

.add-theme {
  width: 100%;
  justify-content: center;
  border-style: dashed;
  color: var(--ink-muted);
}

.quote-list {
  list-style: none;
  margin: 0;
  padding: 0 8px 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.quote-card {
  position: relative;
  padding: 10px 12px 8px 14px;
  background: var(--paper);
  border-left: 4px solid var(--dot);
  border-radius: var(--radius-sm);
}

.quote-card:focus-visible,
.quote-card.is-active {
  box-shadow: 0 0 0 2px var(--accent);
  outline: none;
}

.quote-card.is-new {
  animation: card-in 0.9s ease-out;
}

@keyframes card-in {
  from {
    background: var(--accent-tint);
  }
}

.quote-main {
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.quote-text {
  flex: 1;
  padding: 0;
  border: 0;
  background: none;
  text-align: left;
  font-family: var(--font-reading);
  font-size: 0.95rem;
  line-height: 1.45;
  cursor: pointer;
}

.quote-text:hover {
  color: var(--accent);
}

.quote-where {
  flex: none;
  font-size: 0.75rem;
  color: var(--ink-muted);
}

.quote-note {
  margin: 6px 0 0;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.quote-tools {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: 8px;
}

/* "Move to…" — a labelled door instead of a row of bare colour dots. */
.retag-btn {
  padding: 2px 6px;
  border: 0;
  border-radius: 4px;
  background: none;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
  cursor: pointer;
}

.retag-btn:hover,
.retag-btn[aria-expanded='true'] {
  color: var(--accent);
  background: var(--accent-tint);
}

.retag-menu {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 6px;
}

.retag-option {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px 3px 6px;
  font-size: var(--ui-size-sm);
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 999px;
  cursor: pointer;
}

.retag-option:hover,
.retag-option:focus-visible {
  border-color: var(--accent);
  background: var(--accent-tint);
}

.retag-option .dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--dot);
  box-shadow: inset 0 0 0 1px rgba(31, 36, 48, 0.15);
}

.quote-remove {
  padding: 2px 6px;
  border: 0;
  border-radius: 4px;
  background: none;
  font-size: 0.75rem;
  color: var(--ink-muted);
  cursor: pointer;
}

.quote-remove:hover,
.quote-remove[data-armed] {
  color: var(--danger);
  background: #f6ebe7;
}

/* tone tally */

/* sentence frame */

/* sort control */

/* pairing in progress */

.quote-card.is-draggable {
  cursor: grab;
}

/* note */
.quote-note {
  display: block;
  width: 100%;
  margin: 6px 0 0;
  padding: 0;
  border: 0;
  background: none;
  text-align: left;
  font-size: var(--ui-size-sm);
  line-height: 1.4;
  color: var(--ink-muted);
  cursor: text;
}

.quote-note:hover {
  color: var(--ink);
}

.quote-note.is-empty {
  color: var(--accent);
  font-style: italic;
}

.note-edit {
  display: flex;
  gap: 6px;
  margin: 6px 0 0;
}

.note-input {
  flex: 1;
  min-width: 0;
  padding: 5px 8px;
  font: inherit;
  font-size: var(--ui-size-sm);
  color: var(--ink);
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--panel);
}

/* strength */

/* pairings */

.quote-actions {
  display: flex;
  align-items: center;
  gap: 2px;
}

/* the quote travelling from the text to the tracker */
.fly-ghost {
  position: fixed;
  z-index: 60;
  pointer-events: none;
  padding: 6px 10px;
  background: var(--panel);
  border-left: 4px solid var(--dot);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-pop);
  font-family: var(--font-reading);
  font-size: 0.95rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- keyboard hint ---------- */

.kb-hint {
  position: absolute;
  bottom: 10px;
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: 100%;
  padding: 6px 12px;
  font-size: 0.75rem;
  color: var(--ink-muted);
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: 999px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s;
}

.reading-viewport:has(.reading:focus-visible) .kb-hint {
  opacity: 1;
}

/* The ring goes round the page, not round the article: the article is the
   whole chapter, and most of it is off to the right. */
.reading-viewport:has(.reading:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: -6px;
}

.reading:focus-visible {
  box-shadow: none;
  outline: none;
  border-radius: 2px;
}

/* ---------- touch ---------- */

@media (pointer: coarse) {
  .slot-theme {
    padding: 9px 12px 9px 8px;
  }
  .retag-option {
    padding: 6px 12px 6px 8px;
  }

  .quote-remove,
  .retag-btn {
    padding: 6px 9px;
  }
  .rail-tick {
    width: 10px;
    height: 6px;
  }
}

/* ---------- narrow screens: the tracker becomes a bottom sheet ---------- */

.sheet-handle {
  display: none;
}

@media (max-width: 900px) {
  .app-main {
    grid-template-columns: 1fr;
    grid-template-rows: minmax(0, 1fr);
  }
  /* The slot docks above the collapsed sheet handle the moment there is
     something in it, so a clicked sentence is answered on screen without
     opening the whole tracker; empty, it takes no room at all. */
  .quote-slot {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 54px;
    z-index: 31;
    max-height: 48dvh;
    overflow-y: auto;
    border-top: 1px solid var(--rule);
    border-bottom: 0;
    box-shadow: 0 -8px 24px rgba(31, 36, 48, 0.12);
  }
  .quote-slot.is-empty {
    display: none;
  }
  .reading-pane {
    border-right: 0;
    /* Room for the collapsed tracker sheet, so the last line of the page and
       the foot are never behind it. */
    padding-bottom: 54px;
  }
  .reading-scroll {
    padding: 20px 44px;
  }
  .reading-foot {
    padding: 8px var(--page-margin, 44px) 10px;
  }
  .tracker {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 30;
    height: min(72dvh, 600px);
    padding: 0 14px 28px;
    border-top: 1px solid var(--rule);
    border-radius: 14px 14px 0 0;
    box-shadow: 0 -8px 30px rgba(31, 36, 48, 0.12);
    transform: translateY(calc(100% - 54px));
    transition: transform 0.25s ease;
  }
  .tracker.is-open {
    transform: translateY(0);
  }
  .sheet-handle {
    position: sticky;
    top: 0;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: 10px;
    width: calc(100% + 28px);
    height: 54px;
    margin: 0 -14px 10px;
    padding: 10px 16px 0;
    border: 0;
    background: var(--paper);
    text-align: left;
    cursor: pointer;
  }
  .sheet-grip {
    position: absolute;
    top: 6px;
    left: 50%;
    width: 36px;
    height: 4px;
    border-radius: 2px;
    background: var(--rule);
    transform: translateX(-50%);
  }
  .sheet-title {
    font-size: 0.75rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ink-muted);
  }
  .sheet-progress {
    margin-left: auto;
    font-size: var(--ui-size-sm);
    color: var(--ink-muted);
  }
  .tracker-top {
    display: none;
  }
  .app-header {
    gap: 12px;
    padding: 0 14px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .tracker {
    transition: none;
  }
}

/* ---------- text step: search + cover gallery ---------- */
.text-search {
  display: block;
  box-sizing: border-box;
  width: 100%;
  max-width: 460px;
  margin: 8px 0 16px;
  padding: 8px 10px;
  font: inherit;
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--panel);
}
.cover-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 14px;
}
.cover-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 6px 6px 4px;
  cursor: pointer;
}
.cover-card[hidden] {
  display: none;
}

/* Start Citing renders the shelf as buttons — one click opens the book. */
button.cover-card {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
}

button.cover-card:focus-visible .book-cover {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}
.cover-card input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}
.cover-card .book-cover {
  transition: transform 0.12s ease-out;
}
.cover-card:hover .book-cover {
  transform: translateY(-2px);
}
.cover-card:has(input:checked) .book-cover {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}
.cover-card:has(input:focus-visible) .book-cover {
  box-shadow: var(--focus-ring);
}
.cover-card-meta {
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}
.cover-empty {
  margin: 4px 0 0;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

/* ---------- book covers (js/covers.js) ---------- */
/* 150px wide is the reference: 17.6px title, 28px mark, 11px author. Everything inside is in em, so --cover-w scales the whole cover — the one line that has to do the scaling is the font-size, and it has to multiply the width by a plain ratio. A length times a length (`/ 150 * 16px`) is not a length, so that calc was thrown out and every cover, at any width, drew its text at the inherited 15px: covers narrower than 150 came out crowded and wrapped where they shouldn't. */
.book-cover {
  --cover-w: 150px;
  box-sizing: border-box;
  width: var(--cover-w);
  height: calc(var(--cover-w) * 1.5);
  padding: 1em 0.875em 0.875em;
  border-radius: 3px;
  background: var(--cover-bg, #4a5563);
  color: #fcfbf7;
  font-size: calc(var(--cover-w) * 16 / 150);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  text-align: left;
  box-shadow: 0 1px 0 rgba(31, 36, 48, 0.06), 0 6px 14px rgba(31, 36, 48, 0.14);
}
@supports (color: oklch(0% 0 0)) {
  .book-cover {
    background: var(--cover-bg-oklch, var(--cover-bg, #4a5563));
  }
}
.book-cover-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.1em;
  letter-spacing: -0.01em;
  line-height: 1.08;
  text-wrap: balance;
}
.book-cover-foot {
  display: flex;
  flex-direction: column;
  gap: 0.5em;
}
.book-cover-mark {
  display: block;
  width: 1.75em;
  height: 1.75em;
}
/* The foot hangs from the bottom of the cover, so anything the name does to
   its own height moves the mark above it — and a shelf whose marks sit at five
   different heights is what everyone sees first. The name gets a strip two
   lines deep whether it needs them or not: it grows downward into room already
   counted, and the mark stays put on every cover at every width. */
.book-cover-author {
  font-family: var(--font-ui);
  font-size: 0.6875em;
  line-height: 1.3;
  min-height: 2.6em;
  letter-spacing: 0.02em;
  opacity: 0.85;
}

/* ---------- translation ---------- */

/* The right column holds the tracker and, under it, the word card. The card
   has a slot of its own so nothing reflows as the pointer sweeps a sentence. */
.side {
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--paper);
}

.side .tracker {
  flex: 1 1 auto;
}

/* Drawn over the article, never inside a paragraph: the chapter's own DOM has
   to stay byte-identical, because that is what highlights anchor to. */
.gloss-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
}

.gloss-underline {
  position: absolute;
  border-bottom: 2px dotted var(--accent);
  opacity: 0.75;
}

/* Turning translation on opens the leading, and the chips live in the space
   that was made for them. At the ordinary 1.7 the chip lands on the line above
   and covers it — the word you are reading hides the word before it, which is
   the opposite of help. Reflowing costs nothing that matters here: a highlight
   is anchored to a character offset, not to a pixel, so wider leading moves
   the lines and moves no quote. */
.reading.is-translating .para {
  line-height: 2.5;
}

/* In the leading above the line, never over the text: the chip is opaque and
   sized to the gap the class above opens. */
.gloss-chip {
  position: absolute;
  transform: translate(-50%, -100%);
  margin-top: -2px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-ink);
  font-family: var(--font-ui);
  font-size: 0.6875rem;
  line-height: 1.45;
  white-space: nowrap;
  max-width: 22ch;
  overflow: hidden;
  text-overflow: ellipsis;
}

.gloss {
  flex: 0 0 auto;
  min-height: 132px;
  padding: 12px 20px 16px;
  border-top: 1px solid var(--rule);
  background: var(--panel);
  font-family: var(--font-ui);
}

.gloss[hidden] {
  display: none;
}

.gloss-word {
  margin: 0 0 6px;
  font-family: var(--font-display);
  font-size: 1.0625rem;
  color: var(--ink);
}

.gloss-form,
.gloss-plain,
.gloss-also {
  margin: 0 0 6px;
  font-size: var(--ui-size-sm);
  color: var(--ink-muted);
}

.gloss-form-kind {
  font-style: italic;
}

.gloss-senses {
  margin: 0;
  padding: 0;
  list-style: none;
}

.gloss-sense {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2px 8px;
  padding: 3px 0;
}

.gloss-pos {
  font-size: 0.6875rem;
  color: var(--ink-muted);
  font-style: italic;
  white-space: nowrap;
  padding-top: 2px;
}

.gloss-x {
  font-size: 0.9375rem;
  color: var(--ink);
}

.gloss-def {
  grid-column: 2;
  font-size: 0.75rem;
  color: var(--ink-muted);
}

.gloss-also {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid var(--rule);
}

.btn.is-on {
  background: var(--accent-tint);
  color: var(--accent);
}

.menu-narrow {
  min-width: 220px;
}

@media (max-width: 900px) {
  .gloss {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 54px;
    z-index: 29;
    min-height: 0;
    border-top: 1px solid var(--rule);
  }
}

