@charset "UTF-8";

/* ==========================================================================
   qadil.css — single stylesheet for QaDiL-generated HTML math textbooks.
   Replaces Bootstrap + iLaTeXen.css + index.css.
   Fonts ('STIX Two Text', 'PT Mono') are loaded via a Google Fonts
   <link> in the HTML head, not via @import.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Design tokens
   -------------------------------------------------------------------------- */

:root {
  --accent: #76323f;
  --border: #ccc;
  --bg-subtle: #f4f4f4;
  --bg-tint: #f8f2ed;

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 1rem;
  --space-4: 1.5rem;
  --space-5: 2.5rem;

  /* STIX Two Text: the STIpub consortium's serif for scientific
     publishing; pairs with KaTeX's Computer Modern math.  To revert,
     restore 'PT Serif' / 'PT Serif Caption' here and the matching
     Google Fonts <link> in templates/html/document.html. */
  --font-serif: 'STIX Two Text', 'PT Serif', Georgia, 'Times New Roman', serif;
  --font-heading: 'STIX Two Text', 'PT Serif Caption', Georgia, 'Times New Roman', serif;
  --font-mono: 'PT Mono', ui-monospace, monospace;

  --sidebar-width: 19rem;
  --radius: 0.25rem;
}

/* --------------------------------------------------------------------------
   2. Base & typography
   -------------------------------------------------------------------------- */

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

body {
  margin: 0;
  background-color: #fff;
  color: #222;
  font-family: var(--font-serif);
  font-size: 1rem;
  line-height: 1.6;
  text-align: left;          /* not justified: better for math text */
  letter-spacing: normal;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading);
  font-weight: normal;
  color: var(--accent);
  line-height: 1.25;
}

h1 { font-size: 2.25rem; padding-top: var(--space-3); padding-bottom: var(--space-4); }
h2 { font-size: 1.6rem; padding-top: var(--space-3); padding-bottom: var(--space-2); }
h3 { font-size: 1.3rem; }

/* Chapter heading: number + title on a single unbroken line */
.chaptertitle { white-space: nowrap; }
.chaptertitle .chapno { font-weight: 700; margin-right: 0.35em; }

@media (max-width: 64rem) {
  .chaptertitle { white-space: normal; }  /* let phones wrap */
}

/* Title page (\maketitle) */
.doctitle { text-align: center; }
.docauthor { text-align: center; color: #333; font-size: 1.4rem; }

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

blockquote {
  margin: var(--space-3) var(--space-3) var(--space-3) 0;
  padding: var(--space-1) var(--space-3);
  border-left: 3px solid var(--border);
}

code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  background-color: var(--bg-subtle);
  padding: 0.1em 0.3em;
  border-radius: 3px;
}

pre {
  font-family: var(--font-mono);
  font-size: 0.9em;
  line-height: 1.45;
  background-color: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-2) var(--space-3);
  overflow-x: auto;
}

pre code { background: none; padding: 0; border-radius: 0; }

/* Lists */
ol > li { margin-bottom: 0.375rem; padding: var(--space-1) 0; }

ol.alpha       { list-style-type: lower-alpha; }
ol.greek       { list-style-type: lower-greek; }
ol.number      { list-style-type: decimal; }
ol.lowerroman  { list-style-type: lower-roman; }
ol.upperroman  { list-style-type: upper-roman; }

.bibliography { counter-reset: list; }
.bibliography > li { list-style: none; }
.bibliography > li::before {
  content: "[" counter(list) "] ";
  counter-increment: list;
}

/* Media */
img {
  display: inline-block;
  max-width: 100%;
  height: auto;
  vertical-align: middle;
}

.centerimg { text-align: center; }

figcaption { text-align: center; padding: var(--space-2) 0; font-size: 0.9em; }

video { display: block; margin: 0 auto; max-width: 100%; }

audio {
  display: block;
  width: 100%;
  max-width: 20rem;
  margin: var(--space-2) 0 var(--space-2) auto;
}

/* KaTeX */
.katex-render { font-size: 1em; }   /* old 85% was too small to read */

.katex-render .katex-display {
  position: relative;
  overflow-x: auto;          /* wide equations scroll, not overflow */
  overflow-y: hidden;
  padding: var(--space-1) 0;
}

.katex-render .katex-display .katex .eqno {
  display: inline-block;
  position: absolute;
  right: 0;
}

.katex-render .katex-display .accent .accent-body.accent-vec > span {
  left: 0.5em !important;
}

/* Workaround: transparent-colored spans from the LaTeX source
   (https://github.com/nielsthl/QaDiL/issues/9) */
span[style*="transparent"] { visibility: hidden; }

/* --------------------------------------------------------------------------
   3. Layout: content column & slides
   -------------------------------------------------------------------------- */

.main {
  max-width: 90ch;
  margin-inline: auto;
  padding-inline: var(--space-3);
  padding-block: 3.5rem 4rem;   /* top clears the fixed .menu-toggle */
  font-size: 1.1875rem;
  line-height: 1.6;
  hyphens: auto;
  overflow-wrap: break-word;
}

/* [slides] documentclass option: a presentation.  slides.js shows one
   .slide at a time; navigation is click / arrows / PageUp-PageDown /
   swipe, with a progress bar and counter (see slides.js). */
.slides-container .slide {
  display: none;
  position: fixed;
  inset: 0;
  overflow-y: auto;
  background: #fff;
  padding: 9vh 11vw 12vh;
  font-size: 1.45em;
  line-height: 1.55;
  cursor: default;
}

.slides-container .slide.active {
  display: block;
  animation: qadil-slidein 0.28s ease-out;
}

@keyframes qadil-slidein {
  from { opacity: 0; transform: translateX(2.2rem); }
  to   { opacity: 1; transform: none; }
}

/* slide headings carry the accent, with a thin rule underneath */
.slides-container .slide h1,
.slides-container .slide h2,
.slides-container .slide h3 {
  color: var(--accent);
  border-bottom: 2px solid var(--accent);
  padding-bottom: 0.15em;
  margin-top: 0;
}

.slides-container .slide .chaptertitle {
  border-bottom: none;
}

/* YouTube click-to-load facade (\youtube in v2): thumbnail + play
   button; clicking swaps in the player (see html2.youtube on why an
   eager iframe breaks from file://) */
.yt-embed { max-width: 40rem; margin: var(--space-3) auto; }
.yt-facade { position: relative; display: block; line-height: 0; }
.yt-facade img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--radius);
}
.yt-play {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 68px;
  height: 48px;
  background: rgba(0, 0, 0, 0.65);
  border-radius: 12px;
}
.yt-play::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 12px 0 12px 20px;
  border-color: transparent transparent transparent #fff;
}
.yt-facade:hover .yt-play,
.yt-facade:focus-visible .yt-play { background: var(--accent); }
.yt-embed .yt-player { width: 100%; aspect-ratio: 16 / 9; border: 0; }

/* a one-page deck has nothing to search */
.slides-mode .searchIcon { display: none; }

#slideprogress {
  position: fixed;
  left: 0;
  bottom: 0;
  height: 4px;
  width: 0;
  background: var(--accent);
  transition: width 0.25s ease;
  z-index: 40;
}

#slidecounter {
  position: fixed;
  right: 1rem;
  bottom: 0.55rem;
  font-size: 0.8rem;
  color: #999;
  font-variant-numeric: tabular-nums;
  z-index: 40;
  user-select: none;
}

/* --------------------------------------------------------------------------
   4. Sidebar (off-canvas drawer on small screens, persistent on desktop)
   -------------------------------------------------------------------------- */

.menu-toggle {
  position: fixed;
  top: var(--space-1);
  left: var(--space-1);
  z-index: 120;
  min-width: 44px;
  min-height: 44px;
  background: transparent;
  border: none;
  cursor: pointer;
  font-size: 28px;
  line-height: 1;
  color: inherit;
}

.sidenav {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  z-index: 100;
  width: var(--sidebar-width);
  max-width: 85vw;
  background-color: var(--bg-subtle);
  overflow-y: auto;
  overflow-x: hidden;
  padding-bottom: var(--space-5);
  transform: translateX(-100%);      /* hidden drawer by default (mobile) */
  transition: transform 0.25s ease;
}

body.sidebar-open .sidenav { transform: translateX(0); }

/* Full-screen overlay behind the open drawer */
.backdrop {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 90;
  background: rgba(0, 0, 0, 0.4);
}

body.sidebar-open .backdrop { display: block; }

/* Screen-reader-only text (e.g. the PDF link label) */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

/* Tool row inside the sidebar: +/− buttons and PDF link */
.sidebar-tools {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-2) var(--space-1) 3.25rem; /* clears toggle */
}

.sidebar-tools button,
.sidebar-tools a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  background: transparent;
  border: none;
  cursor: pointer;
  font-size: 1.25rem;
  color: inherit;
  text-decoration: none;
}

.openbs,
.closebs {
  font-size: 20px;
  background-color: transparent;
  border: none;
  cursor: pointer;
}

/* Table of contents */
.leftmenu {
  list-style: none;
  margin: 0;
  padding: var(--space-2) var(--space-1);
  font-size: 1rem;
}

.leftmenu ul {
  list-style: none;
  padding-left: 0;
  margin: 0 0 var(--space-1);
}

.leftmenu a {
  display: block;
  padding: 0.5rem 0.75rem;   /* comfortable tap targets */
  border-radius: 6px;
  color: #444;
  text-decoration: none;
}

.leftmenu a:hover,
.leftmenu a:focus-visible {
  background-color: var(--bg-subtle);
  color: var(--accent);
}

/* Chapter row: title link + expander button side by side */
.tocchapter { margin-top: 2px; }

.tocrow { display: flex; align-items: stretch; }

.tocrow .kap {
  flex: 1;
  display: flex;
  gap: 0.5rem;
  font-weight: 600;
  color: #1a1a1a;
}

.tocrow .tocno {
  color: var(--accent);
  font-weight: 700;
  min-width: 1.1em;
  text-align: right;
}

.tocexpand {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: #777;
  cursor: pointer;
}

.tocexpand:hover,
.tocexpand:focus-visible { background-color: var(--bg-subtle); color: var(--accent); }

.tocexpand svg { transition: transform 0.18s ease; }

.tocexpand.open svg { transform: rotate(90deg); }

/* Section links: indented under the chapter title, slightly quieter */
.leftmenu .collapse a {
  font-size: 0.9375rem;
  padding-left: 2.35rem;
}

/* Current chapter (set by sidebar.js): accent tint + marker */
.leftmenu .kap.current {
  background-color: var(--bg-subtle);
  color: var(--accent);
}

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

/* The sidebar is an overlay drawer at every width: it slides over the
   content, which stays centered and never shifts horizontally. */

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

/* --------------------------------------------------------------------------
   5. Collapse mechanic & buttons (replaces Bootstrap)
   -------------------------------------------------------------------------- */

.collapse { display: none; }
.collapse.open { display: block; }

/* Collapsed content in the text gets the old "opened block" look */
.main .collapse.open {
  border-left: 4px solid #d3d3d3;
  padding: 0 var(--space-3) var(--space-2) var(--space-3);
  margin-bottom: var(--space-3);
}

.btn {
  display: inline-block;
  padding: 0.375rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background-color: #fff;
  color: inherit;
  font-family: inherit;
  font-size: 1rem;
  text-decoration: none;
  cursor: pointer;
}

.btn:hover,
.btn:focus-visible { background-color: #f0f0f0; }

/* --------------------------------------------------------------------------
   6. Math environments
   Rendered as <div class="NAME" data-count="X.Y"> with a ::before label.
   -------------------------------------------------------------------------- */

.theorem,
.definition,
.lemma,
.proposition,
.corollary,
.conjecture,
.example,
.exercise,
.quizexercise,
.quiz,
.remark,
.figure,
.figurestar,
.video,
.frameit,
.emphasize,
.genericenv {
  display: block;
  margin: 12px 0;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background-color: #fff;
}

.emphasize { background-color: var(--bg-tint); }

/* Shared label style */
.theorem::before,
.definition::before,
.lemma::before,
.proposition::before,
.corollary::before,
.conjecture::before,
.example::before,
.exercise::before,
.quizexercise::before,
.quiz::before,
.remark::before,
.figure::before,
.video::before,
.genericenv::before {
  display: block;
  font-weight: bold;
  font-style: normal;
  margin-bottom: var(--space-3);
}

.theorem::before      { content: "(" attr(data-count) ") THEOREM."; }
.definition::before   { content: "(" attr(data-count) ") DEFINITION."; }
.lemma::before        { content: "(" attr(data-count) ") LEMMA."; }
.proposition::before  { content: "(" attr(data-count) ") PROPOSITION."; }
.corollary::before    { content: "(" attr(data-count) ") COROLLARY."; }
.conjecture::before   { content: "(" attr(data-count) ") CONJECTURE."; }
.example::before      { content: "(" attr(data-count) ") EXAMPLE."; }
.exercise::before     { content: "(" attr(data-count) ") Exercise."; }
.quizexercise::before { content: "(" attr(data-count) ") Quiz."; }
/* Interactive quiz cards carry no data-count (the numbered quizexercise
   shell owns the number) — show the bare heading, not "()" . */
.quiz::before              { content: "QUIZ."; }
.quiz[data-count]::before  { content: "(" attr(data-count) ") QUIZ."; }
.remark::before       { content: "(" attr(data-count) ") REMARK."; }
.figure::before       { content: "(" attr(data-count) ") FIGURE."; }
.video::before        { content: "(" attr(data-count) ") VIDEO."; }
.genericenv::before   { content: "(" attr(data-count) ") " attr(data-name) "."; }

/* Titled envs (v2) carry a real DOM header so math in titles can
   render; suppress the CSS-generated one. */
.genericenv.titledenv::before { content: none; }
.envheader {
  display: block;
  font-weight: bold;
  font-style: normal;
  margin-bottom: var(--space-3);
}

/* Proof and hint: unboxed, italic inline labels */
.proof,
.hint {
  display: block;
  padding-bottom: var(--space-3);
}

.proof::before { content: "Proof."; font-style: italic; }
.hint::before  { content: "Hint.";  font-style: italic; }

/* QED mark (legacy float; also on the show/hide variant .Proof) */
.proof::after,
.Proof::after {
  content: "QED.";
  font-weight: bold;
  float: right;
}

/* Show/hide variants:
   <a class="Theoremno" data-count="…"></a>
   <a class="btn Theorembutton" data-toggle="collapse"></a>
   and the generic <a class="Genericenvno" data-count data-name>. */
.Theoremno::before,
.Exampleno::before,
.Exerciseno::before,
.Quizexerciseno::before,
.Quizno::before,
.Remarkno::before,
.Sageno::before,
.Genericenvno::before {
  font-weight: bold;
  font-style: normal;
  content: "(" attr(data-count) ") ";
}

.Theorembutton::before      { content: " Theorem"; }
.Examplebutton::before      { content: " Example"; }
.Exercisebutton::before     { content: " Exercise"; }
.Quizexercisebutton::before { content: " Quiz"; }
.Quizbutton::before         { content: " Quiz"; }
.Remarkbutton::before       { content: " Remark"; }
.Sagebutton::before         { content: " Sage"; }
.Pythonbutton::before       { content: "Python"; }
.Promptingbutton::before    { content: "LLM prompt"; }
.Hintbutton::before         { content: "Hint"; }
.Proofbutton::before        { content: "Proof"; }
.Genericenvbutton::before   { content: attr(data-name); }

/* \link buttons carry the environment counter */
.linkbutton {
  padding-top: var(--space-2);
  font-weight: bold;
  counter-increment: envco;
}

.linkbutton::before {
  content: "(" counter(chapter) "." counter(envco) ") ";
}

/* --------------------------------------------------------------------------
   7. Quizzes
   -------------------------------------------------------------------------- */

.quizquestion {
  padding: var(--space-2) 0;
  margin-bottom: var(--space-3);
}

.quizboxes { display: flex; flex-wrap: wrap; gap: 0.75rem; }

.quizanswer {
  flex: 1 1 45%;
  min-height: 44px;
  padding: var(--space-3);
  border: 2px solid var(--border);
  border-radius: 8px;
  background-color: #fff;
  cursor: pointer;
}

.quizanswer.toggle {
  border-color: var(--accent);
  background-color: var(--bg-tint);
}

.quizcheck { margin-top: var(--space-3); }

/* Feedback borders: two classes to outrank .quizanswer.toggle (which
   would otherwise keep the accent color on selected answers), and the
   extra 2px of border comes out of the padding so box geometry -- and
   hence the flex row layout -- does not change on Check. */
.quizanswer.quizrightanswer {
  border: 4px solid green;
  padding: calc(var(--space-3) - 2px);
}
.quizanswer.quizwronganswer {
  border: 4px dashed red;
  padding: calc(var(--space-3) - 2px);
}

/* orderquiz: items to drag (left) and the ordered slots (right)
   side by side; stacked only on narrow screens */
.orderquizcolumns {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
}

.orderquizcolumn {
  flex: 1 1 0;
  min-width: 0;
}

@media (max-width: 40rem) {
  .orderquizcolumns { flex-direction: column; }
  .orderquizcolumn { width: 100%; }
}

/* orderquiz / paragraphquiz drag targets */
.orderquizanswer {
  min-height: 44px;
  padding: var(--space-3);
  margin: 0 var(--space-2) var(--space-2) var(--space-2);
  border: 2px solid var(--border);
  border-radius: 8px;
  background-color: #fff;
  cursor: move;
}

.orderquizanswer.empty {
  border-style: dashed;
  background-color: var(--bg-subtle);
}

/* Legacy parser artifact: strip stray inner <div> boxes in drag targets */
.orderquizanswer div { display: none; }

.orderquizfeedback.hidden { display: none; }

.orderquizresult { display: block; margin-top: var(--space-2); }

.orderquizresult .correct { color: green; }
.orderquizresult .wrong   { color: red; }

/* paragraphquiz inline slots */
.inlineblock {
  display: inline-block;
  min-width: 100px;
  padding: var(--space-2);
  margin: var(--space-2);
}

.inlineblock::after { content: "\00A0"; }

/* formatquiz (number-entry quizzes) */
.formatquiz-numbers .number-field { width: 80px; }

.formatquizanswer .hidden { display: none; }

.formatquiz-numbers .result .correct { color: green; }
.formatquiz-numbers .result .wrong   { color: red; }

/* --------------------------------------------------------------------------
   8. Bubbles (footnote / cross-reference popups)
   NOTE: bubble.js measures the bubble's offsetWidth/offsetHeight while it
   is hidden, so it must be hidden with opacity + pointer-events (not
   display:none). Hover-show and the .bubblestick pin are driven from here.
   -------------------------------------------------------------------------- */

.bubblelabel { color: var(--accent); cursor: pointer; }
.footnotecolor { color: var(--accent); }

.bubblecontent {
  position: absolute;
  z-index: 99998;
  width: min(90vw, 400px);
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background-color: #fff;
  color: #000;
  font-size: 0.85rem;
  text-align: left;
  filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.2))
          drop-shadow(0 6px 20px rgba(0, 0, 0, 0.19));
  opacity: 0;
  pointer-events: none;   /* don't block text selection while hidden */
  transform: translateY(-25px);
  transition: opacity 0.15s ease-in, transform 0.15s ease-in;
}

.bubblelabel:hover + .bubblecontent,
.bubblecontent.bubblestick {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

/* Pointer arrow; bubble.js picks the bubbleleft/right/flipped variant */
.bubblecontent::before {
  content: "";
  position: absolute;
  top: 100%;
  right: 50%;
  transform: rotate(45deg) translateY(-8px);
  border-width: 10px 10px 0 0;
  border-style: solid;
  border-color: transparent #fff;
}

.bubbleleft::before  { right: 92%; }
.bubbleright::before { right: 8%; }

.bubbleflipped::before {
  top: 0;
  transform: rotate(-135deg) translateY(7px);
  border-color: transparent #fff;
}

.bubbleinnercontent { overflow-x: auto; }

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

/* --------------------------------------------------------------------------
   9. Prompt card (prompting.html)
   -------------------------------------------------------------------------- */

.prompt-card {
  width: 100%; max-width: 980px; margin: 0 auto 1.25rem;
  box-sizing: border-box;
  background: #ffffff; color: #374151;
  border: 1px solid #E5E7EB; border-radius: 14px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Arial, "Noto Sans";
  overflow: hidden;
  font-size: 1rem;
}
.prompt-card * { box-sizing: inherit; }

.pc-header {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 1.2rem 1.5rem; border-bottom: 1px solid #E5E7EB;
}
.pc-title { display: flex; align-items: center; gap: 0.75rem; min-width: 0; }
.pc-title h2 {
  margin: 0; font-size: 1.15rem; font-weight: 600; letter-spacing: 0.01em;
  color: #374151;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pc-icon { width: 28px; height: 28px; color: #2563eb; flex: 0 0 28px; }

.pc-body { padding: 1.5rem; }

pre.pc-text {
  margin: 0; width: 100%;
  background: #F8FAFC;
  border: 1px solid #E5E7EB; border-radius: 12px;
  padding: 1.1rem 1.3rem;
  font-family: var(--font-mono);
  font-size: 0.95rem;      /* mono runs optically larger than the serif */
  line-height: 1.65;
  color: #374151;
  white-space: pre-wrap; word-break: break-word;
  max-height: clamp(260px, 50vh, 640px); overflow: auto;
}
pre.pc-text code { font: inherit; background: none; padding: 0; }

.pc-footer {
  display: flex; align-items: center; gap: 0.8rem; flex-wrap: wrap;
  padding: 1rem 1.5rem 1.3rem; border-top: 1px solid #E5E7EB;
}
.pc-meta { color: #6B7280; font-size: 1rem; }
.pc-pills { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.pc-pill {
  display: inline-flex; align-items: center; padding: 0.45rem 0.8rem;
  font: 600 1rem/1 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Arial, "Noto Sans";
  color: #374151; text-decoration: none;
  background: #F8FAFC; border: 1px solid #E5E7EB; border-radius: 999px;
}
.pc-pill:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }

@media (max-width: 520px) {
  .pc-header { padding: 1rem 1.25rem; }
  .pc-footer { padding: 0.8rem 1.25rem 1rem; }
}

/* --------------------------------------------------------------------------
   10. Live Python cells (sage.html template + livepython.js)
   -------------------------------------------------------------------------- */

.py-card { margin: var(--space-3) 0; }

.py-card .py-src {
  width: 100%;
  box-sizing: border-box;
  background: #F8FAFC;
  border: 1px solid #E5E7EB;
  border-radius: 12px;
  padding: 1.1rem 1.3rem;
  font-family: var(--font-mono);
  font-size: 0.95rem;
  line-height: 1.5;
  color: #374151;
  resize: vertical;
}

.py-controls {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  margin-top: 0.7rem;
}

button.py-run { cursor: pointer; }

.py-status { color: #6B7280; font-size: 0.9rem; }

/* The Explain button sits opposite Run and folds out a pc-footer row
   of chatbot pills (chatbotscript.js).  [hidden] must beat the
   .pc-footer flex display, hence the explicit rule. */
button.py-explain { margin-left: auto; cursor: pointer; }
.py-explain-row[hidden] { display: none; }
.py-explain-row .pc-pill { cursor: pointer; }
.py-explain-row .pc-pill:hover {
  border-color: var(--accent, #76323F);
  color: var(--accent, #76323F);
}

.py-out {
  background: #1f2430;
  color: #d8dee9;
  border-radius: 12px;
  padding: 1rem 1.2rem;
  font-family: var(--font-mono);
  font-size: 0.9rem;
  line-height: 1.5;
  margin-top: 0.7rem;
  overflow-x: auto;
}

.py-fig { margin-top: 0.7rem; }
/* The backend sizes its wrappers and canvases in fixed pixels; scale
   oversized figures down to the card instead of cropping them. */
.py-fig div { max-width: 100%; }
.py-fig canvas, .py-fig img { max-width: 100%; height: auto; }

/* Hide the matplotlib backend's toolbar (pan/zoom/save buttons and the
   format dropdown) -- students just see the figure. */
.py-fig button,
.py-fig select { display: none; }

/* Step-through widget (show_steps in live python cells): Previous/Next
   buttons over a sequence of matrix snapshots.  The buttons must escape
   the toolbar-hiding rule above. */
.py-fig .qadil-steps button { display: inline-flex; }
.qadil-steps { margin-top: 0.4rem; }
.qadil-steps-nav {
  display: flex; align-items: center; gap: var(--space-3);
  flex-wrap: wrap;
}
.qadil-steps-nav button { cursor: pointer; }
.qadil-steps-nav button:disabled { opacity: 0.4; cursor: default; }
.qadil-steps-count { color: #6B7280; font-size: 1rem; }
.qadil-steps-caption {
  margin-top: 0.6rem;
  min-height: 2.6em;   /* two lines, so the matrix does not jump */
}
.qadil-steps-matrix { overflow-x: auto; }
.qadil-steps-matrix table {
  border-collapse: collapse;
  margin: 0.4rem 0 0.2rem;
}
.qadil-steps-matrix td {
  padding: 0.28rem 0.9rem;
  text-align: center;
  font-family: var(--font-mono, ui-monospace, monospace);
  border-left: 2px solid currentColor;
  border-right: 2px solid currentColor;
}
.qadil-steps-matrix td:not(:first-child) { border-left: none; }
.qadil-steps-matrix td:not(:last-child) { border-right: none; }
.qadil-steps-split td { border-top: 2px dashed #9CA3AF; }
.qadil-steps-hot { background: rgba(246, 200, 80, 0.45); }

/* Numbered label on collapsed (showhide) cells */
.Sage {
  padding-top: var(--space-2);
  font-weight: bold;
  counter-increment: envco;
}

.Sage::before {
  content: "(" counter(chapter) "." counter(envco) ") ";
}

/* --------------------------------------------------------------------------
   11. Misc
   -------------------------------------------------------------------------- */

.indent { text-indent: 25px; }

/* Editor preview wrapper */
#wrapper {
  text-align: left;
  border: 1px solid var(--border);
  margin: 0 auto;
  width: 90%;
  padding: 30px;
}

/* Editor: clickable \label markers */
.ltxlabel {
  font-size: 25px;
  color: red;
  cursor: pointer;
}

/* Editor: show image notes on hover */
.imgnote { cursor: crosshair; }

.imgnote .imgnotetext {
  z-index: -1;
  opacity: 0;
  position: fixed;
  left: 15px;
  bottom: 20px;
  margin-right: 18px;
  padding: 14px 20px;
  border-radius: 4px;
  border: 2px solid #DCA;
  color: #000;
  background-color: #fff;
}

.imgnote:hover .imgnotetext {
  z-index: 9;
  opacity: 1;
}

/* --------------------------------------------------------------------------
   12. Search palette (search2.js -- v2 books)
   -------------------------------------------------------------------------- */

.sp-icon {
  position: fixed;
  left: 14px;
  bottom: 14px;
  z-index: 40;
  width: 42px;
  height: 42px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: #fff;
  color: #4b5563;
  border: 1px solid #e5e7eb;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
  cursor: pointer;
  transition: color 120ms ease, box-shadow 120ms ease,
              transform 120ms ease;
}
.sp-icon svg { width: 21px; height: 21px; }
.sp-icon:hover {
  color: var(--accent, #76323F);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.14);
  transform: translateY(-1px);
}

.sp-backdrop {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: none;
  background: rgba(17, 24, 39, 0.42);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  padding: 11vh 16px 0;
}
.sp-backdrop.sp-open { display: block; }

.sp-card {
  margin: 0 auto;
  width: min(660px, 94vw);
  max-height: 72vh;
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid #e5e7eb;
  border-radius: 14px;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.16),
              0 30px 70px rgba(0, 0, 0, 0.22);
  overflow: hidden;
  animation: sp-drop 130ms ease-out;
}
@keyframes sp-drop {
  from { opacity: 0; transform: translateY(-10px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .sp-card { animation: none; }
}

.sp-inputrow {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid #eef0f3;
}
.sp-glass { width: 19px; height: 19px; color: #9ca3af; flex: none; }
.sp-input {
  flex: 1;
  border: none;
  outline: none;
  font: 400 1.06rem/1.4 "STIX Two Text", serif;
  color: #111827;
  background: transparent;
}
.sp-input::placeholder { color: #9ca3af; font-style: italic; }
.sp-input::-webkit-search-cancel-button { display: none; }

.sp-results {
  overflow-y: auto;
  overscroll-behavior: contain;
  flex: 1;
}
.sp-chapter {
  padding: 9px 18px 4px;
  font: 600 0.72rem/1 ui-sans-serif, system-ui, sans-serif;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #9ca3af;
}
.sp-row {
  display: flex;
  gap: 12px;
  align-items: baseline;
  padding: 8px 16px 8px 14px;
  border-left: 3px solid transparent;
  cursor: pointer;
}
.sp-row.sp-selected {
  background: #f7f3ec;
  border-left-color: var(--accent, #76323F);
}
.sp-main { min-width: 0; }
.sp-title {
  display: block;
  font-size: 1rem;
  color: #1f2937;
}
.sp-snippet {
  display: block;
  margin-top: 1px;
  font-size: 0.86rem;
  color: #6b7280;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sp-row mark,
.sp-empty mark {
  background: none;
  color: var(--accent, #76323F);
  font-weight: 600;
}

.sp-badge {
  flex: none;
  min-width: 40px;
  text-align: center;
  padding: 2px 7px;
  border-radius: 999px;
  font: 600 0.7rem/1.5 ui-sans-serif, system-ui, sans-serif;
  color: #fff;
  background: #9ca3af;
  transform: translateY(-2px);
}
.sp-section     { background: #6b7280; }
.sp-theorem, .sp-lemma, .sp-proposition, .sp-corollary
                { background: var(--accent, #76323F); }
.sp-definition  { background: #1f4e79; }
.sp-example     { background: #3d7a4e; }
.sp-exercise    { background: #a86412; }
.sp-remark, .sp-conjecture
                { background: #7c6f9b; }
.sp-quiz        { background: #b0567a; }
.sp-figure, .sp-video
                { background: #4a8a94; }

.sp-empty {
  padding: 26px 18px;
  text-align: center;
  color: #6b7280;
  font-style: italic;
}
.sp-suggest {
  color: var(--accent, #76323F);
  font-weight: 600;
  font-style: normal;
  cursor: pointer;
  text-decoration: underline;
}

.sp-footer {
  display: flex;
  gap: 18px;
  align-items: center;
  padding: 8px 16px;
  border-top: 1px solid #eef0f3;
  font: 400 0.78rem/1 ui-sans-serif, system-ui, sans-serif;
  color: #9ca3af;
}
.sp-footer .sp-status { margin-left: auto; }
.sp-footer kbd, .sp-esc {
  display: inline-block;
  padding: 2px 6px;
  margin-right: 2px;
  border: 1px solid #e5e7eb;
  border-bottom-width: 2px;
  border-radius: 5px;
  background: #f9fafb;
  color: #6b7280;
  font: 500 0.72rem/1 ui-sans-serif, system-ui, sans-serif;
}

/* arrival highlights on the target page */
mark.sp-hl {
  background: rgba(246, 200, 80, 0.55);
  border-radius: 3px;
  padding: 0 1px;
}

/* --------------------------------------------------------------------------
   13. Link previews (bubble2.js -- v2 books)
   -------------------------------------------------------------------------- */

#pv-card {
  position: fixed;
  z-index: 55;
  display: none;
  flex-direction: column;
  background: #fff;
  border: 1px solid #e2e5e9;
  border-radius: 12px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.09),
              0 18px 44px rgba(0, 0, 0, 0.18);
  overflow: visible;
  font-size: 0.94rem;
  line-height: 1.5;
}
#pv-card.pv-open {
  display: flex;
  animation: pv-in 140ms ease-out;
}
@keyframes pv-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}
#pv-card.pv-above { animation-name: pv-in-up; }
@keyframes pv-in-up {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  #pv-card.pv-open { animation: none; }
}

.pv-thumb {
  width: 100%;
  max-height: 150px;
  object-fit: cover;
  border-radius: 11px 11px 0 0;
}
/* the base img rule sets display and would defeat the UA's
   [hidden] { display: none } -- restate it at author level */
.pv-thumb[hidden] { display: none; }

.pv-bodywrap { position: relative; overflow: hidden; }
.pv-body {
  max-height: 300px;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 10px 14px 12px;
}
.pv-body > div > .katex-display { margin: 0.4em 0; }
.pv-body img { max-width: 100%; height: auto; }
/* clipped content fades out instead of cutting off */
.pv-fade {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 26px;
  background: linear-gradient(rgba(255, 255, 255, 0), #fff);
  pointer-events: none;
}

.pv-wikititle {
  font-weight: 700;
  font-size: 1.02rem;
  margin-bottom: 3px;
}
.pv-extract { color: #374151; }
.pv-ytthumb {
  display: block;
  width: calc(100% + 28px);
  margin: -10px -14px -12px;
  border-radius: 11px 11px 0 0;
}

.pv-footer {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px;
  border-top: 1px solid #eef0f3;
  font: 400 0.78rem/1.3 ui-sans-serif, system-ui, sans-serif;
  color: #6b7280;
  background: #fafbfc;
  border-radius: 0 0 11px 11px;
}
.pv-footer:empty { display: none; }
.pv-badge { transform: none; }
.pv-num { font-weight: 600; color: #374151; }
.pv-ctx { margin-left: auto; font-style: italic; }

.pv-notch {
  position: absolute;
  width: 14px;
  height: 14px;
  background: #fff;
  border: 1px solid #e2e5e9;
  transform: rotate(45deg);
}
#pv-card.pv-below .pv-notch {
  top: -8px;
  border-right: none;
  border-bottom: none;
}
#pv-card.pv-above .pv-notch {
  bottom: -8px;
  border-left: none;
  border-top: none;
}

/* previews are a pointer affordance, not a touch one */
@media (hover: none) {
  #pv-card { display: none !important; }
}

/* ask-the-book (search palette, requires `qadil embed`) */
.sp-tabs {
  display: flex;
  gap: 4px;
  padding: 10px 14px 0;
}
.sp-tab {
  border: none;
  background: none;
  padding: 5px 12px 8px;
  font: 600 0.85rem/1.2 ui-sans-serif, system-ui, sans-serif;
  color: #6b7280;
  border-bottom: 2px solid transparent;
  cursor: pointer;
}
.sp-tab:hover { color: #374151; }
.sp-tab.sp-active {
  color: var(--accent, #76323F);
  border-bottom-color: var(--accent, #76323F);
}

.sp-askstatus {
  padding: 26px 18px;
  text-align: center;
  color: #374151;
}
.sp-askstatus code {
  background: var(--bg-subtle, #f4f4f4);
  padding: 0.1em 0.35em;
  border-radius: 3px;
}
.sp-askpct { font-weight: 700; color: var(--accent, #76323F); }
.sp-asknote {
  display: block;
  margin-top: 5px;
  font-size: 0.8rem;
  color: #9ca3af;
}

.sp-askhead {
  padding: 11px 18px 5px;
  font: 600 0.85rem/1.4 ui-sans-serif, system-ui, sans-serif;
  color: #374151;
}

.sp-askhint {
  padding: 0 18px 7px;
  font: 400 0.8rem/1.45 ui-sans-serif, system-ui, sans-serif;
  color: #9ca3af;
}
.sp-askrow { cursor: pointer; }
.sp-askrow:hover {
  background: #f7f3ec;
  border-left-color: var(--accent, #76323F);
}

.sp-askactions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 12px 16px 14px;
  border-top: 1px solid #eef0f3;
}
.sp-askactions .pc-pill { cursor: pointer; }
.sp-askactions .pc-pill:hover {
  border-color: var(--accent, #76323F);
  color: var(--accent, #76323F);
}
.sp-askactions .sp-asknote {
  display: inline;
  margin: 0 0 0 auto;
}

/* \quizme -- the "Quiz me on this" button (quizme.js) */
.quizme {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: var(--space-3) 0;
}
.quizme-btn {
  border: 1px solid var(--accent, #76323F);
  background: #fff;
  color: var(--accent, #76323F);
  border-radius: 999px;
  padding: 6px 16px;
  font: 600 0.9rem/1.4 ui-sans-serif, system-ui, sans-serif;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease,
              box-shadow 120ms ease;
}
.quizme-btn:hover {
  background: var(--accent, #76323F);
  color: #fff;
  box-shadow: 0 2px 8px rgba(118, 50, 63, 0.25);
}
.quizme-actions { display: inline-flex; flex-wrap: wrap; gap: 6px; }
.quizme-actions .pc-pill { cursor: pointer; }
.quizme-actions .pc-pill:hover {
  border-color: var(--accent, #76323F);
  color: var(--accent, #76323F);
}
.quizme-note {
  font: 400 0.8rem/1.3 ui-sans-serif, system-ui, sans-serif;
  color: #9ca3af;
  font-style: italic;
}

/* --------------------------------------------------------------------------
   14. QaDiL 2 button language
   v2 pages carry class="v2" on <body> (document.html, gated on the
   v2search context key); v1 books keep the original flat buttons and
   are untouched by everything below. One family: rounded sans-serif
   pills, matching the pc-pill / quizme / palette species. Disclosure
   buttons ([data-qadil-toggle]) get a chevron whose direction tracks
   the adjacent .collapse via :has(), so it stays honest no matter who
   opens the fold (click, open-all, search jump, preview). The
   environment number becomes a filled accent badge in the .sp-badge
   family, and the quiz Check button is the one filled primary action.
   -------------------------------------------------------------------------- */

body.v2 .btn {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  padding: 0.38rem 0.95rem;
  border: 1px solid #d8d2cb;
  border-radius: 999px;
  background-color: #fff;
  font: 600 0.9rem/1.4 ui-sans-serif, system-ui, -apple-system,
        "Segoe UI", Roboto, Arial, "Noto Sans";
  color: #44403c;
  vertical-align: middle;
  transition: color .15s ease, border-color .15s ease,
              background-color .15s ease, box-shadow .15s ease;
}

body.v2 .btn:hover,
body.v2 .btn:focus-visible {
  background-color: #fff;
  border-color: var(--accent);
  color: var(--accent);
  box-shadow: 0 1px 4px rgba(118, 50, 63, 0.18);
}

body.v2 .btn:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
}

/* chevron on disclosure buttons only; direction tracks the fold */
body.v2 .btn[data-qadil-toggle]::after {
  content: "";
  flex: none;
  width: 0.4em;
  height: 0.4em;
  border-right: 1.7px solid currentColor;
  border-bottom: 1.7px solid currentColor;
  transform: translateY(-22%) rotate(45deg);
  transition: transform .18s ease;
}

body.v2 .btn[data-qadil-toggle]:has(+ .collapse.open) {
  border-color: #c39ba4;
  background-color: var(--bg-tint);
  color: var(--accent);
}

body.v2 .btn[data-qadil-toggle]:has(+ .collapse.open)::after {
  transform: translateY(18%) rotate(-135deg);
}

/* v1 labels lead with a space (the number sat outside the button);
   the number is a badge now, so the labels lose it */
body.v2 .Theorembutton::before      { content: "Theorem"; }
body.v2 .Examplebutton::before      { content: "Example"; }
body.v2 .Exercisebutton::before     { content: "Exercise"; }
body.v2 .Quizexercisebutton::before { content: "Quiz"; }
body.v2 .Quizbutton::before         { content: "Quiz"; }
body.v2 .Remarkbutton::before       { content: "Remark"; }
body.v2 .Sagebutton::before         { content: "Sage"; }

/* environment number: a filled badge in the .sp-badge family, read
   together with the pill that follows as one labeled control */
body.v2 a[data-count]:not(.btn) {
  display: inline-flex;
  align-items: center;
  padding: 2px 9px;
  border-radius: 999px;
  background-color: var(--accent);
  font: 700 0.72rem/1.6 ui-sans-serif, system-ui, sans-serif;
  color: #fff;
  vertical-align: middle;
}

body.v2 a[data-count]:not(.btn)::before {
  content: attr(data-count);
}

/* the quiz Check is an action, not a disclosure: the filled pill */
body.v2 .quizcheck {
  background-color: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

body.v2 .quizcheck:hover,
body.v2 .quizcheck:focus-visible {
  background-color: #5e2833;
  border-color: #5e2833;
  color: #fff;
  box-shadow: 0 1px 4px rgba(118, 50, 63, 0.3);
}

/* sidebar tools join the family: compact circular pills */
body.v2 .sidebar-tools {
  gap: 0.5rem;
}

body.v2 .sidebar-tools .btn,
body.v2 .sidebar-tools .pdflink {
  min-width: 36px;
  min-height: 36px;
  padding: 0;
  justify-content: center;
  border: 1px solid #d8d2cb;
  border-radius: 999px;
  background-color: #fff;
  color: #57534e;
  font-size: 1.05rem;
  line-height: 1;
}

body.v2 .sidebar-tools .btn:hover,
body.v2 .sidebar-tools .btn:focus-visible,
body.v2 .sidebar-tools .pdflink:hover,
body.v2 .sidebar-tools .pdflink:focus-visible {
  border-color: var(--accent);
  color: var(--accent);
  box-shadow: 0 1px 4px rgba(118, 50, 63, 0.18);
}

/* The prompt/python cards join the family: same warm grays as the
   pills instead of the original blue-gray chrome, claret icon, and
   pc-pills (chatbot pills, Run, quizme/ask actions) on exactly the
   .btn pill metrics. Fold labels renamed to match the card titles. */
body.v2 .Promptingbutton::before { content: "Chatbot Prompt"; }
body.v2 .Pythonbutton::before    { content: "Live Python"; }

body.v2 .prompt-card {
  color: #44403c;
  border-color: #e3ddd5;
  box-shadow: 0 1px 3px rgba(68, 52, 45, 0.06);
}

body.v2 .pc-header { border-bottom-color: #ece6de; }
body.v2 .pc-footer { border-top-color: #ece6de; }
body.v2 .pc-title h2 { color: #44403c; }
body.v2 .pc-icon { color: var(--accent); }

body.v2 pre.pc-text {
  background: #faf7f3;
  border-color: #e9e3da;
  color: #44403c;
}

body.v2 .py-card .py-src {
  background: #faf7f3;
  border-color: #e9e3da;
}

body.v2 .pc-meta,
body.v2 .py-status { color: #8a8178; font-size: 0.9rem; }

body.v2 .pc-pill {
  padding: 0.38rem 0.95rem;
  font: 600 0.9rem/1.4 ui-sans-serif, system-ui, -apple-system,
        "Segoe UI", Roboto, Arial, "Noto Sans";
  color: #44403c;
  background: #fff;
  border-color: #d8d2cb;
  transition: color .15s ease, border-color .15s ease,
              box-shadow .15s ease;
}

body.v2 .pc-pill:hover,
body.v2 .pc-pill:focus-visible {
  border-color: var(--accent);
  color: var(--accent);
  box-shadow: 0 1px 4px rgba(118, 50, 63, 0.18);
}

/* The emph option: one callout, not a box-in-a-box. The wrapper
   (emphasize.html around a rendered environment) becomes the book's
   callout -- warm tint, claret left rail -- and the environment
   inside gives up its own white box so the tint fills everything.
   emphquote shares .emphasize and inherits the same language, and so
   does the legacy frameit wrapper: its base style is a second box,
   which read as a double frame around every framed proposition
   (reported on Proposition 5.15). */
body.v2 :is(.emphasize, .frameit) {
  background-color: var(--bg-tint);
  border: 1px solid #e9dfd3;
  border-left: 4px solid var(--accent);
  border-radius: 6px;
  padding: 4px 16px;
}

body.v2 :is(.emphasize, .frameit) > :is(.theorem, .definition,
    .lemma, .proposition, .corollary, .conjecture, .example,
    .exercise, .quizexercise, .quiz, .remark, .figure, .figurestar,
    .video, .frameit, .genericenv) {
  background-color: transparent;
  border: none;
  margin: 0;
  padding: 8px 0;
}

/* Slim card chrome: the header becomes a small-caps claret eyebrow
   (no separator lines, no box-in-a-box), the chatbot card trades the
   robot svg for a CSS-mask sparkle, and the Live Python terminal
   glyph shrinks to match. Footer flows without a rule. */
body.v2 .pc-header {
  padding: 1rem 1.35rem 0;
  border-bottom: none;
}

body.v2 .pc-title { gap: 0.45rem; }

body.v2 .pc-title h2 {
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
}

body.v2 .pc-icon { width: 16px; height: 16px; flex: 0 0 16px; }

body.v2 .prompt-card:not(.py-card) .pc-icon { display: none; }

body.v2 .prompt-card:not(.py-card) .pc-title::before {
  content: "";
  flex: 0 0 15px;
  width: 15px;
  height: 15px;
  background-color: var(--accent);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 3c.9 4.6 3.4 7.1 8 8-4.6.9-7.1 3.4-8 8-.9-4.6-3.4-7.1-8-8 4.6-.9 7.1-3.4 8-8z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 3c.9 4.6 3.4 7.1 8 8-4.6.9-7.1 3.4-8 8-.9-4.6-3.4-7.1-8-8 4.6-.9 7.1-3.4 8-8z'/%3E%3C/svg%3E") center / contain no-repeat;
}

body.v2 .pc-body { padding: 0.7rem 1.35rem 0; }

body.v2 .py-card .pc-body { padding-bottom: 1.1rem; }

/* The Explain pill wears the same sparkle as the chatbot card title,
   tying it to the prompting family at a glance. */
body.v2 button.py-explain { gap: 0.4rem; }
body.v2 button.py-explain::before {
  content: "";
  flex: 0 0 12px;
  width: 12px;
  height: 12px;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 3c.9 4.6 3.4 7.1 8 8-4.6.9-7.1 3.4-8 8-.9-4.6-3.4-7.1-8-8 4.6-.9 7.1-3.4 8-8z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 3c.9 4.6 3.4 7.1 8 8-4.6.9-7.1 3.4-8 8-.9-4.6-3.4-7.1-8-8 4.6-.9 7.1-3.4 8-8z'/%3E%3C/svg%3E") center / contain no-repeat;
}

body.v2 pre.pc-text {
  border: none;
  border-radius: 8px;
  padding: 0.9rem 1.1rem;
  font-size: 0.88rem;
  line-height: 1.6;
}

body.v2 .pc-footer {
  border-top: none;
  padding: 0.85rem 1.35rem 1.1rem;
}

body.v2 .pc-meta { font-size: 0.85rem; }

/* "Grade my solution" (\grademe) — quizme's sibling: accent pill
   button, then an upload/paste panel in the card family. */
.grademe {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: var(--space-3) 0;
}
.grademe-btn {
  border: 1px solid var(--accent, #76323F);
  background: #fff;
  color: var(--accent, #76323F);
  border-radius: 999px;
  padding: 6px 16px;
  font: 600 0.9rem/1.4 ui-sans-serif, system-ui, sans-serif;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease,
              box-shadow 120ms ease;
}
.grademe-btn:hover {
  background: var(--accent, #76323F);
  color: #fff;
  box-shadow: 0 2px 8px rgba(118, 50, 63, 0.25);
}
.grademe-note {
  font: 400 0.8rem/1.3 ui-sans-serif, system-ui, sans-serif;
  color: #9ca3af;
  font-style: italic;
}
.grademe-panel {
  flex-basis: 100%;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 14px;
  border: 1px solid #e3ddd5;
  border-radius: 12px;
  background: #fff;
  box-shadow: 0 1px 3px rgba(68, 52, 45, 0.06);
}
.grademe-file {
  font: 400 0.85rem/1.4 ui-sans-serif, system-ui, sans-serif;
  color: #57534e;
}
.grademe-src {
  width: 100%;
  box-sizing: border-box;
  border: none;
  border-radius: 8px;
  background: #faf7f3;
  padding: 0.9rem 1.1rem;
  font: 400 0.88rem/1.6 var(--font-mono);
  color: #44403c;
  resize: vertical;
}
.grademe-src:focus-visible { outline: 2px solid #2563eb; }
.grademe-actions { display: flex; flex-wrap: wrap; gap: 6px; }
.grademe-actions .pc-pill { cursor: pointer; }

/* "What to use" (exercise refs=) — a standard fold at the end of the
   exercise body (the .btn pill + chevron come from section 14, the
   toggling from collapse.js). Its collapse holds the dependency
   chips: reference links that bubble2.js previews on hover. */
.usehints { margin-top: var(--space-3); }
.usehints > .collapse.open {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  padding: 10px 14px;
  border: 1px solid #e3ddd5;
  border-radius: 12px;
  background: #faf7f3;
}
.usehints-lead {
  flex-basis: 100%;
  font: 400 0.8rem/1.3 ui-sans-serif, system-ui, sans-serif;
  color: #78716c;
  font-style: italic;
}
.usehints-item a {
  display: inline-block;
  border: 1px solid #d6c9bc;
  border-radius: 999px;
  padding: 2px 12px;
  background: #fff;
  color: var(--accent, #76323F);
  font: 600 0.85rem/1.5 ui-sans-serif, system-ui, sans-serif;
  text-decoration: none;
}
.usehints-item a:hover {
  border-color: var(--accent, #76323F);
  box-shadow: 0 2px 8px rgba(118, 50, 63, 0.15);
}

/* Wide display math already scrolls (overflow-x on .katex-display,
   section 2), but overlay scrollbars (macOS) render nothing until a
   scroll is in flight and a mouse has no way in at all - equations
   wider than the column simply LOOKED truncated. Styling the WebKit
   scrollbar opts out of overlay mode: a slim bar appears exactly
   when an equation overflows, and it is mouse-draggable. Firefox
   gets the thin native bar. Scoped to v2 like the rest of the
   section - v1 rendering stays untouched. */
/* NOTE: setting the standard scrollbar-width property makes
   Blink/WebKit IGNORE the ::-webkit-scrollbar rules below, so it is
   gated to engines without the pseudo-element (Firefox). */
@supports not selector(::-webkit-scrollbar) {
  body.v2 .katex-render .katex-display {
    scrollbar-width: thin;
    scrollbar-color: #d6c9bc transparent;
  }
}
body.v2 .katex-render .katex-display::-webkit-scrollbar {
  height: 8px;
}
body.v2 .katex-render .katex-display::-webkit-scrollbar-thumb {
  background: #d6c9bc;
  border-radius: 4px;
}
body.v2 .katex-render .katex-display::-webkit-scrollbar-thumb:hover {
  background: var(--accent, #76323F);
}
body.v2 .katex-render .katex-display::-webkit-scrollbar-track {
  background: transparent;
}

/* --------------------------------------------------------------------------
   15. QaDiL 2 reading typography
   Scoped to body.v2 like section 14 — v1 books render pixel-identical.
   Evidence and rationale: NLQaDiL/docs/design/typography-audit.md and
   typography-direction.md (measured baseline + eight-book reference
   study, 2026-07-13).
   -------------------------------------------------------------------------- */

/* Metric-matched local fallback: Times New Roman scaled to STIX Two
   Text advance widths (canvas-measured ratio 1.047) so the swap to
   the webfont barely reflows. Referenced only from the v2 stack. */
@font-face {
  font-family: "STIX Two Text Fallback";
  src: local("Times New Roman");
  size-adjust: 104.7%;
  line-gap-override: 0%;
}

body.v2 {
  --font-serif: "STIX Two Text", "STIX Two Text Fallback", "PT Serif",
                Georgia, "Times New Roman", serif;
  --font-heading: var(--font-serif);
}

/* Reading measure: 78ch ≈ 700px of text ≈ 69 characters per line at
   the 19px prose size (was 90ch ≈ 80 cpl). Book-wide cost measured
   before the change: 8 of 1159 display equations become wide enough
   to scroll (they get the section-14 visible scrollbar). */
body.v2 .main {
  max-width: 78ch;
  hyphenate-limit-chars: 6 3 3;   /* no "quick-er"-class breaks */
  text-wrap: pretty;
}

/* No h1 here: the balance shorthand resets text-wrap-mode and would
   override .chaptertitle's white-space: nowrap — chapter titles stay
   on one unbroken line (owner preference), wrapping only under the
   existing 64rem media query. */
body.v2 h2,
body.v2 h3 {
  text-wrap: balance;
}

/* Hierarchy below the chapter title is carried by weight and
   asymmetric space (≈2 lines above, ≈0.5 below), not color alone:
   at weight 400 the old h3 (20.8px) sat 1.8px from the prose size. */
body.v2 .main h2 {
  font-weight: 600;
  padding: 0;
  margin: 2.4rem 0 0.9rem;
}

body.v2 .main h3 {
  font-weight: 600;
  padding: 0;
  margin: 2rem 0 0.6rem;
}

/* Environment grammar: a quiet left rail instead of the uniform 1px
   full box. Solid rail = statement family (content), dotted rail =
   the do-this family (exercises). One rail color: the kind is
   already carried by the label text and the number badge — and
   titled environments render as .genericenv.titledenv with the kind
   only in the .envheader text (pitfall 18), so per-kind rail colors
   cannot be applied consistently. The interactive .quiz card keeps
   its widget box; .emphasize/.frameit (tint + 4px rail) remain the
   emphasized tier and still strip inner-environment chrome. */
body.v2 :is(.theorem, .definition, .lemma, .proposition, .corollary,
    .conjecture, .example, .remark, .figure, .figurestar, .video,
    .genericenv) {
  border: none;
  border-left: 3px solid var(--accent);
  border-radius: 0;
  background-color: transparent;
  padding: 2px 0 2px 18px;
  margin: 1.4rem 0;
}

body.v2 :is(.exercise, .quizexercise) {
  border: none;
  border-left: 3px dotted var(--accent);
  border-radius: 0;
  background-color: transparent;
  padding: 2px 0 2px 18px;
  margin: 1.4rem 0;
}

/* The label keeps its exact text — "(5.26) COROLLARY (Title)." is a
   contract with the PDF and the scanners (ENVHEADER_RE) — but is set
   as a quieter tracked eyebrow in the accent color. */
body.v2 :is(.theorem, .definition, .lemma, .proposition, .corollary,
    .conjecture, .example, .exercise, .quizexercise, .quiz, .remark,
    .figure, .video, .genericenv)::before,
body.v2 .envheader {
  font-size: 0.8em;
  letter-spacing: 0.06em;
  color: var(--accent);
  margin-bottom: 0.5rem;
}

/* Hidden footnote bubbles are bubble.js furniture; v2 pages load
   bubble2.js instead, which shows footnotes in its own preview card.
   Without bubble.js the .bubblecontent spans are never positioned:
   the CSS :hover rule showed them RAW on top of bubble2's card
   (double popup), and while hidden (opacity 0) they still stretched
   the page — a phantom horizontal scroll at ≤1024px on every page
   with a footnote near the column edge. bubble2 only reads their
   innerHTML, so they can simply not display. */
body.v2 .bubblecontent {
  display: none;
}

/* Inline math is atomic in KaTeX (no line breaking); one long inline
   formula can exceed a phone viewport (intro at 320px). The render
   loop in document.html tags exactly the formulas wider than the
   column (.katex-longinline); those scroll in place instead of
   stretching the page. (Display math is a div and already scrolls
   via .katex-display.) */
body.v2 .main span.katex-render.katex-longinline {
  display: inline-block;
  max-width: 100%;
  overflow-x: auto;
}

/* Skip link (document.html, v2 pages only): visually hidden until
   keyboard-focused, then a pill in the top-left above the burger. */
.skip-link {
  position: fixed;
  top: 0.6rem;
  left: -100vw;
  z-index: 200;
  padding: 8px 16px;
  border: 1px solid var(--accent);
  border-radius: 999px;
  background: #fff;
  color: var(--accent);
  font: 600 0.9rem/1.4 ui-sans-serif, system-ui, sans-serif;
  text-decoration: none;
}

.skip-link:focus-visible {
  left: 0.6rem;
}

/* the skip-link target takes programmatic focus; no ring around the
   whole content column */
.main:focus {
  outline: none;
}

/* Previous/next chapter footer (built by sidebar.js on v2 pages from
   the sidebar TOC, which knows chapter order and real titles). */
body.v2 .pagenav {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 3.5rem;
  padding-top: 1.4rem;
  border-top: 1px solid #e3ddd5;
}

body.v2 .pagenav a {
  flex: 0 1 48%;
  min-width: 0;
  padding: 0.7rem 1.1rem;
  border: 1px solid #d8d2cb;
  border-radius: 12px;
  text-decoration: none;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

body.v2 .pagenav a:hover,
body.v2 .pagenav a:focus-visible {
  border-color: var(--accent);
  box-shadow: 0 1px 4px rgba(118, 50, 63, 0.18);
}

body.v2 .pagenav .pn-next {
  margin-left: auto;
  text-align: right;
}

body.v2 .pagenav .pn-dir {
  display: block;
  font: 600 0.72rem/1.5 ui-sans-serif, system-ui, sans-serif;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #9ca3af;
}

body.v2 .pagenav .pn-title {
  display: block;
  margin-top: 2px;
  font: 600 0.95rem/1.35 ui-sans-serif, system-ui, sans-serif;
  color: var(--accent);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   16. Front page (index.html): hero + chapter grid
   The maketitle template renders the hero; the chapter grid is the
   \tableofcontents marker filled by postprocess/toc.py (fronttoc.html).
   -------------------------------------------------------------------------- */

/* The front page trades the prose measure for a wider canvas so the
   hero columns and the chapter grid have room (index only: no other
   page renders a .hero). */
.main:has(.hero),
body.v2 .main:has(.hero) { max-width: 66rem; }

/* The chapter grid IS the navigation here — the sidebar drawer (and
   its burger) would duplicate it, and the floating search magnifier
   would duplicate the hero's "Search & ask the book" button (⌘K and
   / still open the palette). */
body:has(.hero) .menu-toggle,
body:has(.hero) .sidenav,
body:has(.hero) .backdrop,
body:has(.hero) .sp-icon { display: none; }

.hero {
  display: flex;
  align-items: center;
  gap: clamp(2rem, 5vw, 4rem);
  margin: clamp(1.5rem, 6vh, 4rem) 0 var(--space-4);
}

.hero-text { flex: 1 1 20rem; }

.hero .doctitle {
  text-align: left;
  font-size: clamp(2.1rem, 4.5vw, 3.3rem);
  line-height: 1.12;
  padding: 0;
}

.hero .docauthor {
  text-align: left;
  margin-top: var(--space-3);
  font-family: var(--font-heading);
  font-size: 1.2rem;
  color: #555;
}

.hero-cover {
  flex: 0 1 23rem;
  min-width: 0;
  margin: 0;
}

.hero-cover img {
  display: block;
  width: 100%;
  border-radius: 0.75rem;
  box-shadow: 0 24px 48px -20px rgba(0, 0, 0, 0.45);
}

@media (max-width: 860px) {
  .hero { flex-direction: column; text-align: center; }
  /* flex-basis would become HEIGHT in column direction */
  .hero-text { flex: 0 0 auto; }
  .hero .doctitle, .hero .docauthor { text-align: center; }
  .hero-cover { flex-basis: auto; width: min(100%, 22rem); }
}

.fronttoc { margin-bottom: var(--space-5); }

.fronttoc-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin: var(--space-4) 0;
}

@media (max-width: 860px) { .fronttoc-actions { justify-content: center; } }

.fronttoc-btn {
  display: inline-flex;
  align-items: center;
  padding: 0.6rem 1.15rem;
  font: 600 1rem/1 ui-sans-serif, system-ui, -apple-system, "Segoe UI",
    Roboto, Arial, "Noto Sans";
  color: #374151;
  text-decoration: none;
  background: #f8fafc;
  border: 1px solid #e5e7eb;
  border-radius: 999px;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}

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

.fronttoc-btn-primary {
  color: #fff;
  background: var(--accent);
  border-color: var(--accent);
}

.fronttoc-btn-primary:hover { background: #5d2732; }

.fronttoc-btn kbd {
  font: 600 0.8rem/1 var(--font-mono);
  padding: 0.15rem 0.35rem;
  border: 1px solid #d1d5db;
  border-bottom-width: 2px;
  border-radius: 0.3rem;
  background: #fff;
  color: #6b7280;
}

.chaptergrid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14.5rem, 1fr));
  gap: 0.9rem;
}

.chaptergrid li { margin: 0; padding: 0; }

.chaptercard {
  display: block;
  height: 100%;
  padding: 1rem 1.1rem 1.1rem;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 0.6rem;
  text-decoration: none;
  transition: border-color 0.15s, transform 0.15s, box-shadow 0.15s;
}

.chaptercard:hover {
  border-color: var(--accent);
  transform: translateY(-2px);
  box-shadow: 0 12px 28px -18px rgba(118, 50, 63, 0.55);
}

.chapterno {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2rem;
  height: 2rem;
  padding: 0 0.4rem;
  font: 700 1.05rem/1 var(--font-heading);
  color: var(--accent);
  background: var(--bg-tint);
  border-radius: 0.45rem;
}

.chaptercard-title {
  display: block;
  margin-top: 0.6rem;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 1.08rem;
  line-height: 1.3;
  color: var(--accent);
}

.chaptercard-meta {
  display: block;
  margin-top: 0.45rem;
  font-family: var(--font-serif);
  font-size: 0.9rem;
  line-height: 1.45;
  color: #57534e;
}
