/* The default design (the original is at ?v=1). Self-contained: this page does not
   load escala.css. */

:root {
  --surface-0: #f5f4f0;
  --surface-1: #ffffff;
  --surface-2: #f7f6f3;
  --border: #dcd9d3;
  --border-strong: #b8b4ac;
  --border-success: #4c9a6a;
  --radius: 8px;
  /* Every control in the toolbar — number fields, the Incremento select, the Orden
     arrows — is exactly this size. Width fits "100.00"; the select fits "0.25" + caret. */
  --control-w: 60px;
  --control-h: 28px;
  /* Shared by the scale's column-gap and the header strip's grid gap; they must match or
     the headers drift out of line with their columns. */
  --col-gap: 16px;
  --text-primary: #1f1e1c;
  --text-secondary: #5f5c56;
  --text-muted: #8a867e;
  --text-accent: #b05730;
  /* Secondary buttons: the accent desaturated into a fill, so they read as buttons rather
     than as entry fields, while Generar tabla stays the saturated one. */
  --btn-soft-bg: #eaddd6;
  --btn-soft-bg-hover: #e0cec4;
  --btn-soft-fg: #8a4526;
  --bg-success: #e3f0e7;
  --text-success: #276745;
  --bg-danger: #f7e3e0;
  --text-danger: #a13a2c;
  --font-sans: -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

:root { color-scheme: light; }

/* The dark palette appears twice and the two copies must stay in sync. A media query can't
   be written as a selector, so there's no way to combine "system is dark" with "the user
   hasn't overridden to light" in one rule.
   First copy: system preference, unless the user explicitly picked light. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --surface-0: #191817;
    --surface-1: #232220;
    --surface-2: #2b2a27;
    --border: #3a3936;
    --border-strong: #55534e;
    --text-primary: #ece9e4;
    --text-secondary: #b3afa8;
    --text-muted: #807c75;
    --bg-success: #1f3a2b;
    --text-success: #8fd0a8;
    --bg-danger: #442520;
    --text-danger: #e39a8c;
    --btn-soft-bg: #46312a;
    --btn-soft-bg-hover: #523a31;
    --btn-soft-fg: #e2a98d;
  }
}

/* Second copy: the user's explicit choice, whatever the system says. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --surface-0: #191817;
  --surface-1: #232220;
  --surface-2: #2b2a27;
  --border: #3a3936;
  --border-strong: #55534e;
  --text-primary: #ece9e4;
  --text-secondary: #b3afa8;
  --text-muted: #807c75;
  --bg-success: #1f3a2b;
  --text-success: #8fd0a8;
  --bg-danger: #442520;
  --text-danger: #e39a8c;
  --btn-soft-bg: #46312a;
  --btn-soft-bg-hover: #523a31;
  --btn-soft-fg: #e2a98d;
}

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

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--surface-0);
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: 14px;
}

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

input, button, select {
  font-family: inherit;
  color: inherit;
  font-size: 14px;
}

input[type=number] {
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  padding: 6px 8px;
  width: 100%;
}

.wrap {
  max-width: 900px;
  margin: 0 auto;
  padding: 0 16px 32px;
}

header {
  background: var(--surface-1);
  border-bottom: 1px solid var(--border);
  margin-bottom: 16px;
}

/* Text block on the left, toggle on the right, both centred on the same line — `center`
   rather than `baseline`, and no wrap, so the toggle can't drop onto the byline's row when
   the text takes two lines. Equal top and bottom padding centres the pair in the bar. */
.head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-top: 12px;
  padding-bottom: 12px;
}

.head-text {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 2px 6px;
  min-width: 0;
}

h1 {
  font-size: 18px;
  margin: 0;
}

/* Carries the contact address now, so it shouldn't read as fine print. */
.autor {
  font-size: 13px;
  color: var(--text-secondary);
}

/* Still real links (mailto: included) — just not underlined at rest. */
.autor a {
  color: inherit;
  text-decoration: none;
}

.autor a:hover { text-decoration: underline; }

/* Small print under the title and byline: the way back to the original design, and the
   contact address. flex-basis puts the row on its own line — .head-text already wraps, so no
   second container is needed — and it stays a step smaller than the byline, since these are
   escape hatches rather than actions of the page. The margin is on top of .head-text's 2px
   row gap, which is tuned for a wrapped byline rather than a separate line of its own. Only
   the plain text is muted; the links keep the accent colour they get everywhere else. */
.head-alt {
  flex: 0 0 100%;
  margin: 2px 0 0;
  font-size: 12px;
  color: var(--text-secondary);
}

/* Theme toggle. The icon shows the theme in force — moon while dark, sun while light — and
   clicking it switches to the other one. The swap keys off [data-theme] alone, with no
   prefers-color-scheme copy, because the button only exists when JS is on and the inline
   script in <head> always sets that attribute to the effective theme. */
.theme-toggle {
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius);
  color: var(--text-secondary);
  cursor: pointer;
}

.theme-toggle:hover {
  background: var(--surface-2);
  border-color: var(--border);
  color: var(--text-primary);
}

.theme-toggle svg { display: block; }
.theme-toggle .icon-moon { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: block; }


.btn {
  display: inline-flex;
  align-items: center;
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  padding: 6px 12px;
  font-size: 13px;
  color: var(--text-primary);
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
}

.btn:hover { background: var(--surface-2); }

.btn.primary {
  background: var(--text-accent);
  border-color: var(--text-accent);
  color: #fff;
  font-weight: 600;
}

/* Same hue as primary, desaturated, and left at normal weight so the hierarchy is
   unmistakable: filled = action, bordered-and-white = input. */
.btn.soft {
  background: var(--btn-soft-bg);
  border-color: transparent;
  color: var(--btn-soft-fg);
}

.btn.soft:hover { background: var(--btn-soft-bg-hover); }

/* Generar tabla sits first in the DOM so Enter triggers it; this puts it back on the
   right. Tab order therefore reaches it before Excel and Imprimir. */
.actions .generar { order: 1; }

.card {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px 16px;
  margin-bottom: 12px;
}

/* Parameters */

/* The whole card is one grid of label+control pairs at every width — `display: contents`
   on the groups and their children flattens them into it. That's what puts Incremento
   under Puntaje máx. and Orden under Exigencia (%) instead of each group sizing its own
   columns. Five pairs per row here; the media queries below drop to two, then one. */
.params {
  display: grid;
  grid-template-columns: repeat(5, max-content var(--control-w));
  align-items: center;
  gap: 10px 14px;
}

.fields,
.controls,
.field,
.control { display: contents; }

.field label {
  font-size: 13px;
  color: var(--text-secondary);
  white-space: nowrap;
}

/* Sized for "100.00" and nothing more. Spinners are useless here — teachers type a
   number they already know — and they ate a third of the field's width. */
.field input[type=number] {
  width: var(--control-w);
  height: var(--control-h);
  padding: 0 6px;
  font-size: 13px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  appearance: textfield;
  -moz-appearance: textfield;
}

.field input[type=number]::-webkit-inner-spin-button,
.field input[type=number]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  appearance: none;
  margin: 0;
}

/* Excel and Imprimir sit beside Generar tabla but read as quiet links, so the primary
   action stays the obvious one. Placed in the tracks after the two selectors, right
   aligned; can't land in row 1 because the five fields fill every track there. */
.actions {
  display: flex;
  align-items: center;
  gap: 12px;
  grid-column: 5 / -1;
  justify-self: end;
}

/* Same size as the field labels — these are peers, not fine print. */
.control-label {
  font-size: 13px;
  color: var(--text-secondary);
}

/* A grid, not a wrapping flex row: with 8 increments on a narrow screen the flex version
   spilled into a ragged second line. Equal-width cells reflow into a tidy 2x4 instead.
   The 1px gaps show the container's background, which draws the dividers. */
.seg {
  display: grid;
  gap: 1px;
  min-width: 0;
  background: var(--border);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  overflow: hidden;
}

/* Explicit track count, not auto-fit: inside a shrink-to-fit flex item, auto-fit with
   1fr tracks has no definite width to work from and collapses to a single column. */
.seg-orden {
  grid-template-columns: repeat(2, 1fr);
  width: var(--control-w);
  height: var(--control-h);
}

.seg-orden label {
  display: grid;
  place-items: center;
  padding: 0;
}

/* appearance: none is the point — mobile Safari otherwise imposes its own native select
   chrome (oversized, its own font and padding) and ignores most of what's set here. That
   means drawing the caret ourselves. A data: URI can't use currentColor, so rather than keep
   a per-theme copy in sync with the palette, the caret is one mid-grey that reads on both
   the light and the dark surface. */
.control select {
  appearance: none;
  -webkit-appearance: none;
  background-color: var(--surface-1);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%238a867e' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 5px center;
  background-size: 8px 5px;
  background-clip: padding-box;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  margin: 0;
  padding: 0 17px 0 7px;
  font-size: 13px;
  line-height: 1.3;
  width: var(--control-w);
  height: var(--control-h);
  justify-self: start;
}


.seg input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.seg label {
  padding: 5px 8px;
  font-size: 13px;
  text-align: center;
  cursor: pointer;
  white-space: nowrap;
  background: var(--surface-1);
}

.seg input:checked + label {
  background: var(--surface-2);
  font-weight: 600;
  box-shadow: inset 0 -2px 0 var(--text-accent);
}

.seg input:focus-visible + label { outline: 2px solid var(--text-accent); outline-offset: -2px; }


/* Scale */

/* Column flow, each column read top-to-bottom, with a rule between them.
   `column-count` rather than `column-width`: the count has to be a number this stylesheet
   knows, so the header strip can be told the same number. A media query changes both
   together — see "Responsive column count" at the bottom.

   The header strip is multicol too, not a grid of 1fr tracks. Both were meant to divide the
   width identically, but they round fractional column widths differently and the error
   accumulates across columns — 3px of drift by the fourth header at 431px. Running the same
   algorithm on both makes them agree exactly. Eight single-line items in eight columns
   balance to one per column. */
.escala,
.escala-head {
  column-count: 8;
  column-gap: var(--col-gap);
}

.escala { column-rule: 1px solid var(--border); }

.escala-head { margin-bottom: 3px; }

.escala-head > div {
  display: flex;
  justify-content: space-between;
  gap: 6px;
  padding: 0 7px 3px;
  border-bottom: 1px solid var(--border-strong);
  font-size: 11px;
  font-weight: 600;
  color: var(--text-secondary);
  white-space: nowrap;
  break-inside: avoid;
}

/* Rows butt against each other — no gaps, no rounded corners — so a run of them reads as
   one continuous table rather than a column of loose pills. The only separator is a
   hairline in the page's own background colour: it reads as a divider between two tinted
   rows and disappears above the first row, where there's no tint behind it. */
.cell {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 6px;
  padding: 3px 7px;
  border-top: 1px solid var(--surface-0);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  text-decoration: none;
  break-inside: avoid;
}

.cell.ok { background: var(--bg-success); color: var(--text-success); }
.cell.no { background: var(--bg-danger); color: var(--text-danger); }

.cell .n { font-weight: 600; }

/* No rule at the cut on screen — the colour change already marks it. The .cut class is
   still emitted because print drops the tints and needs the rule to show the split. */

/* Explanation page. Same tokens and cards as the table, plus prose and formula handling. */

.back { margin: 0 0 12px; }

/* Latin Modern Roman: the direct successor to Computer Modern, which is what KaTeX's own fonts
   clone. So the prose and the formulas are one type family, and unlike KaTeX's fonts this one
   carries the accented glyphs Spanish needs — KaTeX_Main and KaTeX_Math have none of
   á é í ó ú ñ ¿ ¡ ü, which is why the body couldn't simply be set in them.
   Self-hosted rather than pulled from a CDN: the only distribution that had it was latex.css,
   which embeds the faces as base64 inside a 3.1 MB stylesheet. These four files were extracted
   from it — 45-51 KB each, and a browser only fetches the ones a page actually uses.
   Latin Modern is GUST Font License; latex.css is MIT. */
@font-face {
  font-family: 'Latin Modern Roman';
  src: url('latinmodern-roman-regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Latin Modern Roman';
  src: url('latinmodern-roman-italic.woff2') format('woff2');
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: 'Latin Modern Roman';
  src: url('latinmodern-roman-bold.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Latin Modern Roman';
  src: url('latinmodern-roman-bolditalic.woff2') format('woff2');
  font-weight: 700;
  font-style: italic;
  font-display: swap;
}

/* Serif because the formulas are: a sans body beside Computer Modern maths reads as two
   documents stapled together. The reverse — forcing KaTeX into the UI sans — is worse than it
   sounds: KaTeX positions glyphs from its own fonts' metrics, so substituting a family breaks
   fraction bars, delimiter sizing and spacing. The rest of the site stays sans; that's
   interface, this is a document.
   Size stays at 14px: .katex is 1.21em of its parent, so raising this would grow every formula
   and undo the widths the media queries below are measured against. */
.prose {
  font-family: 'Latin Modern Roman', Georgia, 'Times New Roman', serif;
  font-size: 14px;
  line-height: 1.55;
  color: var(--text-secondary);
}

.prose h2 {
  margin: 0 0 12px;
  font-size: 16px;
  color: var(--text-primary);
}

.prose p { margin: 0 0 12px; }
.prose > :last-child { margin-bottom: 0; }

.prose ul {
  margin: 0 0 12px;
  padding-left: 20px;
}

.prose li { margin-bottom: 6px; }
.prose strong, .prose em { color: var(--text-primary); }

/* Inline maths matches the prose size. KaTeX defaults to 1.21em, which went unnoticed while the
   two used different faces but became obvious once prose digits came from KaTeX_Main as well —
   "60%" in text sat visibly smaller than "0,6" in maths right beside it. Same face plus same
   size makes the digits identical. The selector picks inline maths only: a display formula's
   .katex sits inside .katex-display, so it keeps its larger size, which is what a block formula
   is for, and the media queries below still tune it. */
.prose :not(.katex-display) > .katex { font-size: 1em; }

/* Kept as a safety net: if a formula still can't fit, it scrolls inside its own box rather
   than widening the page. The steps below are meant to make that unnecessary on phones. */
.prose .katex-display {
  overflow-x: auto;
  overflow-y: hidden;
  padding: 2px 0;
}

/* Shrink display maths on narrow screens instead of leaving the widest formulas to scroll.
   Sized from measurement, not taste: the two \begin{cases} blocks need 412px at KaTeX's
   default 16.94px, and the content box is 349px at 430, 329 at 390, 314 at 375 and 259 at
   320 — so each step is the largest size whose worst case still fits. Inline maths is left
   alone; shrinking it too would make it smaller than the prose around it. */
@media (max-width: 480px) {
  .prose .katex-display .katex { font-size: 0.95em; }
}

@media (max-width: 380px) {
  .prose .katex-display .katex { font-size: 0.85em; }
}

@media (max-width: 340px) {
  .prose .katex-display .katex { font-size: 0.72em; }
}

/* Inline SVG diagram. Takes its colours from the theme, so it needs no light plate the way the
   old PNG did, and 1:1 at its natural size on desktop while scaling down on a phone. */
.diagrama {
  display: block;
  width: 100%;
  max-width: 440px;
  height: auto;
  margin: 4px auto 16px;
  color: var(--text-secondary);
}

.diagrama .eje {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
}

.diagrama .flecha { stroke-linejoin: round; stroke-linecap: round; }

.diagrama .guia {
  fill: none;
  stroke: var(--border-strong);
  stroke-width: 1;
  stroke-dasharray: 3 3;
}

.diagrama .curva {
  fill: none;
  stroke: var(--text-accent);
  stroke-width: 2.5;
  stroke-linejoin: round;
}

.diagrama .punto { fill: var(--text-accent); }

/* The labels are the same symbols as the formulas, so they use KaTeX's own faces rather than the
   page font — KaTeX_Math is italic by design, which is how single-letter variables render in the
   maths. Safe here because every label is ASCII; KaTeX's fonts don't carry the accented glyphs
   Spanish prose needs, which is why the body text uses a system serif instead. Falls back to a
   serif if KaTeX's stylesheet doesn't load. */
.diagrama text {
  fill: currentColor;
  font-family: KaTeX_Math, KaTeX_Main, Georgia, serif;
  font-size: 15px;
}

.diagrama .sub { font-size: 11px; }

/* FAQ */

#faq {
  margin-top: 32px;
  border-top: 1px solid var(--border);
  padding-top: 8px;
  font-size: 13px;
  color: var(--text-secondary);
}

#faq .pregunta {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 12px 16px;
  margin: 12px 0;
}

#faq .pregunta p:first-child {
  font-weight: 600;
  color: var(--text-primary);
}

/* Ads: format 1 is a fixed 750px unit; clip it rather than let it widen the page. */

.ads { margin: 12px 0; }

/* Measured on the live site: the unit is a hardcoded 750px box and the creative inside it a
   728px leaderboard, so anything under an 800px viewport clips it. Centre the box so what
   survives the clip is the middle of the ad rather than a lopsided slice. `flex: 0 0 auto`
   matters — a flex item shrinks by default, which would collapse the box to the wrapper's
   width and push the clipping back onto one side. */
.ad-wide {
  overflow-x: hidden;
  display: flex;
  justify-content: center;
}

.ad-wide > div { flex: 0 0 auto; }

/* Imprimir needs window.print(); without JS the browser's own print command still works. */

.js-only { display: none; }
html.js .js-only { display: inline-flex; }

.print-only { display: none; }

/* Below the width where all five pairs fit on one line, drop to two per row — seven pairs
   in four rows. Exactly four tracks and no trailing spacer: auto-placement fills a fifth
   track with the next label rather than leaving it empty. */
@media (max-width: 880px) {
  .params {
    grid-template-columns:
      max-content var(--control-w)
      max-content var(--control-w);
    /* 10px rather than 12: the wider touch controls push the two-pair row to 331px, and a
       380px phone only offers 336px of content box. */
    gap: 8px 10px;
    /* Two narrow columns leave a lot of slack in a wide card; centre the pairs rather than
       stranding them against the left edge. */
    justify-content: center;
  }

  /* Own row, spanning: beside Orden there isn't room for three buttons without forcing
     the grid wider than the screen. */
  .actions { grid-column: 1 / -1; }
}

/* iOS Safari zooms the viewport whenever a focused control's text is under 16px, and there's
   no honest way to suppress that — `user-scalable=no` is ignored by Safari and would break
   pinch-zoom anyway. So touch devices get 16px text, which needs 4px more width ("100.00"
   measures 48.9px at 16px against 39.8px at 13px) and a slightly taller box. Pointer devices
   keep the tighter 13px. The width half of the query covers narrow desktop windows, which
   get the same layout and are the testable half. */
@media (pointer: coarse), (max-width: 880px) {
  :root {
    --control-w: 64px;
    --control-h: 30px;
  }

  .field input[type=number],
  .control select { font-size: 16px; }
}

/* Trim the frame to buy the two columns room on a phone. */
@media (max-width: 413px) {
  .wrap { padding-left: 10px; padding-right: 10px; }
  .card { padding: 12px; }
}

/* Narrowest phones: two pairs need 329px ("Puntaje máx." 76 + "Nota aprobación" 97, two
   60px controls, three 12px gaps) and 375px only offers 314px of content box, so fall back
   to one pair per row. 380px is the first width where two pairs fit without overflowing. */
@media (max-width: 379px) {
  .params { grid-template-columns: max-content var(--control-w); }
}

/* The responsive column counts live in a generated <style> in the page head — they depend on
   this scale's row count, which CSS can't know. See COLUMN_STEPS in escala.rb. */

/* Without an explicit page box, Chrome and Safari each apply their own default margins, so
   the printable width — and therefore the column widths and how much lands on page one —
   differs between them. Fixing the margin here makes the sheet the same in both. */
@page {
  margin: 12mm;
}

@media print {
  /* An allow-list, because naming the ad elements never keeps up. The scripts append their
     own nodes straight to <body> — a fixed-position 300x250 with a close button, which is how
     stray X glyphs landed mid-table and under the footer in Safari — and they invent the ids
     themselves (Moneytizer's 33670-*, Smart AdServer's sas_*). So: of <body>'s own children,
     only <main> prints. <header> is .chrome and would go anyway.
     Whatever gets injected at body level next is covered without another patch. */
  body > *:not(main) { display: none !important; }

  /* And by id for anything injected *inside* main, which the allow-list can't reach. */
  .chrome,
  .card,
  iframe,
  [id^="33670-"],
  [id^="sas_"] { display: none !important; }

  .print-only { display: block; }

  body { background: #fff; color: #000; font-size: 11px; }
  .wrap { max-width: none; padding: 0; }

  /* WebKit needs the prefixed property to keep a row from splitting across a column or page
     break; the unprefixed one alone leaves Safari free to slice a row in half. */
  .cell, .escala-head > div {
    -webkit-column-break-inside: avoid;
    break-inside: avoid;
  }


  .print-meta {
    border-bottom: 2px solid #000;
    padding-bottom: 6px;
    margin-bottom: 8px;
    font-size: 11px;
  }

  /* Grid, not multicol. WebKit does not fragment a multi-column element in paged media:
     Safari collapsed the whole scale into one long single column and stacked the four
     headers on top of each other, while Chrome rendered four proper columns. Grid works in
     both. It's only viable here because the print column count is fixed, so the server can
     state the row count a column-flow grid needs (see the generated <style> in <head>).
     Both elements use grid with identical templates, so they can't drift apart the way grid
     and multicol did on screen. */
  .escala, .escala-head {
    display: grid;
    column-gap: 18px;
  }

  .escala {
    grid-auto-flow: column;
    column-rule: none;
  }

  .escala-head > div { border-bottom: 1px solid #000; color: #000; padding: 0 2px 2px; }

  .cell {
    background: none !important;
    color: #000 !important;
    border-top: 0.5px solid #ddd;
    padding: 1.5px 2px;
    font-size: 11.5px;
  }

  .cell.no { color: #444 !important; }
  .cell.ok .n { font-weight: 700; }
  .cell.cut { border-top: 2px solid #000; }

  .print-footer {
    border-top: 0.5px solid #999;
    margin-top: 10px;
    padding-top: 6px;
    font-size: 10px;
    color: #555;
  }

  /* ---- Explanation page ----------------------------------------------------
     Without these it printed blank: the rule at the top of this block hides .card, and the
     explanation's whole content sits in .card.prose. Higher specificity brings it back. */
  .card.prose {
    display: block !important;
    margin: 0 0 18px;
    padding: 0;
    background: none;
    border: 0;
    border-radius: 0;
  }

  /* Nothing on paper can be navigated. */
  .back { display: none !important; }

  .prose {
    color: #000;
    font-size: 10.5pt;
    line-height: 1.45;
  }

  .prose h2 {
    margin: 0 0 6px;
    color: #000;
    font-size: 12pt;
    /* A heading alone at the foot of a page is worse than a slightly short page. */
    break-after: avoid;
  }

  .prose p,
  .prose li {
    orphans: 3;
    widows: 3;
  }

  /* Two separate hazards. Splitting a formula across a page break makes it unreadable; and the
     screen rule turns these into scroll containers, which on paper silently clips whatever
     overflows instead of scrolling. Both are undone here. */
  .prose .katex-display {
    overflow: visible;
    break-inside: avoid;
  }

  /* Ink-safe: the accent becomes black, the guides stay grey so they read as construction
     lines. Smaller than on screen, since paper doesn't need it big to be legible. */
  .diagrama {
    max-width: 78mm;
    margin: 6px auto 14px;
    color: #000;
    break-inside: avoid;
  }

  .diagrama .curva { stroke: #000; }
  .diagrama .punto { fill: #000; }
  .diagrama .guia { stroke: #999; }
}
