/* Article typography + shared theme tokens — used by the prerendered
 * standalone article pages AND the SPA (index.css maps its Tailwind theme to
 * the --sp-* tokens defined here; both render the same .sp-article markup).
 *
 * The visuals are a 1:1 copy of the EDITOR's reader view
 * (apps/editor/src/ArticleReader.tsx + ArticleBlock.tsx + index.css):
 * Lora serif body, Figtree sans for headings/byline/captions/quotes/factboxes,
 * 720px measure (wider than the editor's 592 — a deliberate reader choice),
 * per-article accent (hsl hue cycle, theme-tuned lightness),
 * the pullquote ornament, the bordered byline strip.
 *
 * Theme: `.light`/`.dark` on <html> wins (the SPA's toggle); with neither
 * class the OS preference applies. Standalone pages have no JS → OS pref.
 * Each block below also declares `color-scheme` (form controls / scrollbars,
 * plus the Android system nav bar via the browser's own UI negotiation); the
 * SPA additionally mirrors the resolved value as a `<meta name="color-scheme">`
 * tag (theme.ts::syncThemeMeta) since that's the same early, explicit channel
 * `theme-color` already uses for the status bar. */

@font-face { font-family: "Lora"; font-style: normal; font-weight: 400; font-display: swap; src: url("/fonts/lora/lora-v37-latin-regular.woff2") format("woff2"); }
@font-face { font-family: "Lora"; font-style: italic; font-weight: 400; font-display: swap; src: url("/fonts/lora/lora-v37-latin-italic.woff2") format("woff2"); }
@font-face { font-family: "Lora"; font-style: normal; font-weight: 500; font-display: swap; src: url("/fonts/lora/lora-v37-latin-500.woff2") format("woff2"); }
@font-face { font-family: "Lora"; font-style: normal; font-weight: 600; font-display: swap; src: url("/fonts/lora/lora-v37-latin-600.woff2") format("woff2"); }
@font-face { font-family: "Lora"; font-style: normal; font-weight: 700; font-display: swap; src: url("/fonts/lora/lora-v37-latin-700.woff2") format("woff2"); }
@font-face { font-family: "Figtree"; font-style: normal; font-weight: 300; font-display: swap; src: url("/fonts/figtree/figtree-v9-latin-300.woff2") format("woff2"); }
@font-face { font-family: "Figtree"; font-style: italic; font-weight: 300; font-display: swap; src: url("/fonts/figtree/figtree-v9-latin-300italic.woff2") format("woff2"); }
@font-face { font-family: "Figtree"; font-style: normal; font-weight: 400; font-display: swap; src: url("/fonts/figtree/figtree-v9-latin-regular.woff2") format("woff2"); }
@font-face { font-family: "Figtree"; font-style: italic; font-weight: 400; font-display: swap; src: url("/fonts/figtree/figtree-v9-latin-italic.woff2") format("woff2"); }
@font-face { font-family: "Figtree"; font-style: normal; font-weight: 500; font-display: swap; src: url("/fonts/figtree/figtree-v9-latin-500.woff2") format("woff2"); }
@font-face { font-family: "Figtree"; font-style: italic; font-weight: 500; font-display: swap; src: url("/fonts/figtree/figtree-v9-latin-500italic.woff2") format("woff2"); }
@font-face { font-family: "Figtree"; font-style: normal; font-weight: 600; font-display: swap; src: url("/fonts/figtree/figtree-v9-latin-600.woff2") format("woff2"); }
@font-face { font-family: "Figtree"; font-style: italic; font-weight: 600; font-display: swap; src: url("/fonts/figtree/figtree-v9-latin-600italic.woff2") format("woff2"); }
@font-face { font-family: "Figtree"; font-style: normal; font-weight: 700; font-display: swap; src: url("/fonts/figtree/figtree-v9-latin-700.woff2") format("woff2"); }
@font-face { font-family: "Figtree"; font-style: italic; font-weight: 700; font-display: swap; src: url("/fonts/figtree/figtree-v9-latin-700italic.woff2") format("woff2"); }

/* ── Theme tokens (editor palette, index.css) ─────────────────────────── */
:root,
:root.light {
  --sp-bg: #f5f5f4;
  --sp-fg: #1c1c1c;
  --sp-surface: #ffffff;
  --sp-muted-bg: #e8e8e6;
  --sp-muted: #6b6b6b;
  --sp-border: #d4d4d0;
  --sp-primary: #1a6ec9;
  --sp-accent-l: 38%;
  --sp-serif: "Lora", Georgia, "Times New Roman", serif;
  --sp-sans: "Figtree", ui-sans-serif, system-ui, sans-serif;
  color-scheme: light;
}
:root.dark {
  --sp-bg: #1a1a1a;
  --sp-fg: #e8e8e8;
  --sp-surface: #232323;
  --sp-muted-bg: #2a2a2a;
  --sp-muted: #888888;
  --sp-border: #333333;
  --sp-primary: #4a9eff;
  --sp-accent-l: 55%;
  color-scheme: dark;
}
@media (prefers-color-scheme: dark) {
  :root:not(.light):not(.dark) {
    --sp-bg: #1a1a1a;
    --sp-fg: #e8e8e8;
    --sp-surface: #232323;
    --sp-muted-bg: #2a2a2a;
    --sp-muted: #888888;
    --sp-border: #333333;
    --sp-primary: #4a9eff;
    --sp-accent-l: 55%;
    color-scheme: dark;
  }
}

/* Standalone article pages only (the SPA styles its own shell). */
body.sp-standalone {
  margin: 0;
  background: var(--sp-bg);
  color: var(--sp-fg);
}
body.sp-standalone .sp-article {
  padding: 2rem 1.25rem 6rem;
}

/* ── Article scope ────────────────────────────────────────────────────── */
.sp-article {
  /* Per-article accent: hue set inline (--accent-h) by the publish script,
   * lightness by theme — mirrors the editor's articleColor(). */
  --article-accent: hsl(var(--accent-h, 210) 70% var(--sp-accent-l));
  max-width: 720px;
  margin: 0 auto;
  font-family: var(--sp-serif);
  /* Text-size control (views/ArticleView.tsx sets --article-font-scale on this
   * element from prefs.ts's persisted pref; every scaled rule below reads it
   * off inheritance). The standalone prerendered pages never set the var, so
   * every use falls back to 1 — identical to today's fixed sizes. */
  font-size: calc(1rem * var(--article-font-scale, 1));
  line-height: 1.625;
  color: var(--sp-fg);
  /* Inherited by every descendant (title, subtitle, body, captions): break an
   * over-long token — a URL, a long Norwegian compound — only when it would
   * otherwise overflow the 720px measure. Normal wrapping is untouched.
   * min-width:0 lets the article flex-shrink inside the split-view column so
   * this measure is the real width, not the content's intrinsic max. */
  overflow-wrap: break-word;
  min-width: 0;
}
.sp-article ::selection {
  background-color: color-mix(in srgb, var(--article-accent) 30%, transparent);
}

/* Deep-link arrival flash: a page-region click in the spread view scrolls the
 * reader to the target block (ArticleView.tsx) and adds .block-flash — a brief
 * accent wash so it's obvious where the jump landed. Ends transparent. */
@keyframes sp-block-flash {
  from {
    background-color: color-mix(in srgb, var(--article-accent) 32%, transparent);
  }
  to {
    background-color: transparent;
  }
}
.sp-article .block-flash {
  animation: sp-block-flash 1.3s ease-out;
  border-radius: 4px;
}
@media (prefers-reduced-motion: reduce) {
  .sp-article .block-flash {
    animation: none;
  }
}

/* Kicker: editor's label-caps in the article accent. */
.sp-article .kicker {
  font-family: var(--sp-sans);
  font-size: calc(11px * var(--article-font-scale, 1));
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--article-accent);
  margin: 0 0 0.75rem;
}

/* Supertitle: a small sans all-caps label ABOVE the title (kicker-like). */
.sp-article .supertitle {
  font-family: var(--sp-sans);
  font-size: calc(0.875rem * var(--article-font-scale, 1));
  line-height: 1.3;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--sp-fg) 70%, transparent);
  margin: 0 0 0.5rem;
}

/* Title: serif 36px bold (editor: mt-0 mb-3 text-4xl leading-tight font-bold). */
.sp-article h1 {
  font-size: calc(2.25rem * var(--article-font-scale, 1));
  line-height: 1.25;
  font-weight: 700;
  margin: 0 0 0.75rem;
}

/* Subtitle: serif 20px, fg/80 (editor: mb-4 text-xl font-normal text-foreground/80). */
.sp-article .subtitle {
  font-size: calc(1.25rem * var(--article-font-scale, 1));
  line-height: 1.4;
  font-weight: 400;
  color: color-mix(in srgb, var(--sp-fg) 80%, transparent);
  margin: 0 0 1rem;
}

/* Byline strip: bordered, sans 13px muted (editor: mb-5 flex flex-col gap-3
 * border-y py-3 font-sans text-[13px] text-muted-foreground). */
.sp-article .byline {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  border-top: 1px solid var(--sp-border);
  border-bottom: 1px solid var(--sp-border);
  padding: 0.75rem 0;
  margin-bottom: 1.25rem;
  font-family: var(--sp-sans);
  font-size: calc(13px * var(--article-font-scale, 1));
  line-height: 1.45;
  color: var(--sp-muted);
}
.sp-article .byline .author {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
}
.sp-article .byline .portrait {
  width: 3.5rem;
  height: 3.5rem;
  flex-shrink: 0;
  border-radius: 2px;
  object-fit: cover;
}
.sp-article .byline .author-text {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.sp-article .byline .author-name {
  font-weight: 700;
  color: var(--sp-fg);
}
.sp-article .byline .author-affiliation {
  margin-top: 0.125rem;
  font-style: italic;
  white-space: pre-wrap;
}

.sp-article .narration {
  width: 100%;
  margin-bottom: 1.25rem;
}

/* Blurb: sans 18px light, fg/90 (editor: mb-6 font-sans text-lg leading-normal
 * font-light whitespace-pre-wrap text-foreground/90). Sits AFTER the header. */
.sp-article .blurb {
  font-family: var(--sp-sans);
  font-size: calc(1.125rem * var(--article-font-scale, 1));
  line-height: 1.5;
  font-weight: 300;
  color: color-mix(in srgb, var(--sp-fg) 90%, transparent);
  margin: 0 0 1.5rem;
  white-space: pre-wrap;
}

/* Body paragraphs (editor: mt-0 mb-3.5 whitespace-pre-wrap, serif base). */
.sp-article p {
  margin: 0 0 0.875rem;
  white-space: pre-wrap;
}

/* Emphasized paragraph (editor: mt-0 mb-3.5 font-sans text-[1.1em] accent
 * whitespace-pre-wrap) — sans-serif, slightly larger, in the article accent. */
.sp-article p.emphasis {
  font-family: var(--sp-sans);
  font-size: calc(1.1em * var(--article-font-scale, 1));
  line-height: 1.45;
  color: var(--article-accent);
}

/* Body heading: SANS 22px bold (editor h2: mt-7 mb-2 font-sans text-[22px] font-bold). */
.sp-article h2 {
  font-family: var(--sp-sans);
  font-size: calc(22px * var(--article-font-scale, 1));
  font-weight: 700;
  line-height: 1.3;
  margin: 1.75rem 0 0.5rem;
  white-space: pre-wrap;
}

/* Container sub-heading: caps 15px bold (editor child heading h3:
 * label-caps mt-0 mb-2 text-[15px] font-bold tracking-wide). */
.sp-article h3 {
  font-family: var(--sp-sans);
  font-size: calc(15px * var(--article-font-scale, 1));
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin: 0 0 0.5rem;
}

/* Pullquote: sans 22px in the article accent with the ornament above
 * (editor: relative mx-0 my-4 pt-12 text-[22px] leading-snug accent). */
.sp-article blockquote {
  position: relative;
  font-family: var(--sp-sans);
  font-size: calc(22px * var(--article-font-scale, 1));
  line-height: 1.375;
  color: var(--article-accent);
  margin: 1rem 0;
  padding: 3rem 0 0;
}
.sp-article blockquote .quote-ornament {
  position: absolute;
  top: 0;
  left: 0;
  height: 42px;
  width: auto;
  color: var(--article-accent);
  pointer-events: none;
}
.sp-article blockquote p {
  margin: 0 0 0.5rem;
}

/* Factbox: sans on a faint panel (editor aside: my-5 rounded-md border
 * bg-white/3 px-5 py-4 text-base leading-relaxed). */
.sp-article aside.factbox {
  font-family: var(--sp-sans);
  font-size: calc(1rem * var(--article-font-scale, 1));
  line-height: 1.625;
  border: 1px solid var(--sp-border);
  border-radius: 6px;
  background: color-mix(in srgb, #ffffff 3%, transparent);
  padding: 1rem 1.25rem;
  margin: 1.25rem 0;
}
.sp-article aside.factbox p {
  margin: 0 0 0.5rem;
}
.sp-article aside.factbox p:last-child {
  margin-bottom: 0;
}

/* Images (editor figure: mx-0 my-4; img h-auto max-h-[70vh] w-full rounded-xs
 * object-contain; figcaption my-3 font-sans text-xs centered). */
.sp-article figure {
  position: relative;
  margin: 1rem 0;
  /* Clips the hover-scale below to a clean rounded edge (mirrors the
   * overflow-hidden rounded-md thumbnail wrapper in Library.tsx). */
  overflow: hidden;
  border-radius: 4px;
}
.sp-article figure img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 70vh;
  object-fit: contain;
  border-radius: 4px;
  /* Affordance: figure images open a lightbox. Signal it with a zoom cursor
   * and, on real hover devices, the same subtle scale-up the library's issue
   * thumbnails use on hover (Library.tsx: transition-transform duration-300
   * group-hover:scale-[1.02]) — touch has no hover, so the tap itself is the
   * affordance there. */
  cursor: zoom-in;
  transition:
    opacity 0.25s ease,
    transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
@media (hover: hover) {
  .sp-article figure img:hover {
    transform: scale(1.02);
  }
}
/* SPA-managed loading state: the reserved box (width/height attrs) shows as a
 * muted placeholder; the image fades in when complete (ArticleView.tsx).
 * Applies to every article image incl. byline portraits. */
.sp-article figure:has(> img.img-loading) {
  background: var(--sp-muted-bg);
  border-radius: 4px;
}
.sp-article img {
  transition: opacity 0.25s ease;
}
.sp-article img.img-loading {
  opacity: 0;
}
/* Broken-image fallback (ArticleView.tsx's fadeInWhenLoaded): a failed figure
 * image is fully hidden (opacity:0 — a plain `display:none`/visibility hack
 * still leaves the browser's native broken-image glyph+alt-text painted) and
 * the figure — already sized by the image's reserved width/height attrs —
 * gets a muted placeholder background with a centered message instead. */
.sp-article img.img-broken {
  opacity: 0;
}
.sp-article figure:has(> img.img-broken) {
  background: var(--sp-muted-bg);
}
.sp-article figure:has(> img.img-broken)::after {
  content: "Image unavailable";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.75rem;
  text-align: center;
  font-family: var(--sp-sans);
  font-size: 0.8125rem;
  color: var(--sp-muted);
}
/* Byline portraits aren't wrapped in a <figure> — same idea, scoped to the
 * fixed 3.5rem portrait box via the `.author` row (portrait is always its
 * first child; see articleHtml.ts). No text: too small to be legible. */
.sp-article .byline .author {
  position: relative;
}
.sp-article .byline .author:has(> img.portrait.img-broken)::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 2px;
  background: var(--sp-muted-bg);
}
.sp-article .byline .portrait.img-broken {
  opacity: 0;
}
.sp-article figcaption {
  font-family: var(--sp-sans);
  font-size: calc(12px * var(--article-font-scale, 1));
  line-height: 1.5;
  color: var(--sp-fg);
  text-align: center;
  margin: 0.75rem 0;
}

/* Image galleries: a `gallery` CONTAINER block (task #270e/#270f/#287d). Core
 * auto-groups runs of 2+ images into a gallery at import; the editor AND the
 * reader lay it out with the SAME shared pure packer (`packGallery` in
 * @steinerpublish/core), aspect-preserved, never cropped.
 *
 * The pack is BAKED once and rendered VERBATIM (task #303): the prerender projects the
 * gallery's STORED arrangement (`layoutFromArrangement` → `galleryFlexTree`) into a
 * NESTED-FLEX tree (task #313) — `.sp-gallery-split` flex rows/columns with a leaf
 * `.sp-gallery-tile` figure per image; `flex-grow`/`aspect-ratio` come inline. With
 * `flex-basis: 0` native flex subtracts the gutter BEFORE distributing, so every child
 * of a row ends at the same height → rows/cols are gap-free by construction (this fixes
 * the visible gaps the earlier absolute-rect render, #311, produced). The root
 * `.sp-gallery` reserves height via `aspect-ratio`, so the whole gallery is laid out
 * with ZERO client JS and correct with JS disabled — the crawler / shared-link
 * standalone page matches the SPA, AND matches the editor (same stored tree, same
 * flex projection, same gap). The gallery-level caption renders as a normal figcaption
 * BELOW the gallery (styled by the generic figcaption rule above). */
.sp-article .sp-gallery-figure {
  margin: 1.25rem 0;
  /* The generic figure rule clips + rounds; a gallery manages its own tile
   * radii, so opt out here. */
  overflow: visible;
  border-radius: 0;
}
.sp-article .sp-gallery {
  /* Flex wrapper for the nested-flex tree (task #313); its inline `aspect-ratio`
   * reserves the whole gallery's height once. `--sp-gallery-gap` is a PURELY VISUAL
   * gutter — native flex subtracts it before distributing, so rows/cols stay gap-free
   * for any value (it need not match the packer's baked gap fraction). MUST equal the
   * editor's `--stpub-gallery-gap` so the two apps are pixel-identical. */
  --sp-gallery-gap: 6px;
  display: flex;
  width: 100%;
  margin: 0;
}
/* An internal split node: a flex row (children side by side) or column (stacked);
 * `flex-direction` + `flex` come inline. Children size from flex-grow (∝ aspect for a
 * row, ∝ 1/aspect for a column). min-*:0 lets a child shrink below its content. */
.sp-article .sp-gallery-split {
  display: flex;
  gap: var(--sp-gallery-gap);
  min-width: 0;
  min-height: 0;
}
.sp-article .sp-gallery-tile {
  /* Each tile figure IS a flex leaf: `flex` + `aspect-ratio` come inline. It stays
   * `position: relative` as the containing block for its caption scrim. */
  position: relative;
  margin: 0;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  border-radius: 2px;
  /* No background: each box matches its image's aspect very closely, and the tile
   * image COVER-fills it (task #317), so there is nothing to letterbox — keep any
   * residual margin INVISIBLE (the article bg shows through) not a gray frame (#282). */
  background: transparent;
}
.sp-article .sp-gallery-tile img {
  display: block;
  width: 100%;
  height: 100%;
  /* Override the generic figure-img cap: a gallery tile fills its packed box. */
  max-height: none;
  /* COVER, not contain (task #317): the leaf box aspect matches the (already
   * build-time-cropped) image aspect to sub-pixel, so cover absorbs the residual
   * rounding as an imperceptible crop rather than a letterbox gap or a stretch. */
  object-fit: cover;
}
/* Per-tile caption overlaid at the bottom with a gradient scrim (reader
 * decision, task #270e): a packed tile can't stack a figcaption BELOW its fixed
 * box, and dropping captions would lose content the old gallery showed — so keep
 * them as a subtle overlay. The full caption also appears in the lightbox on tap
 * (Lightbox reads the figcaption). The GALLERY-LEVEL caption is a separate
 * figcaption below the whole gallery, NOT scoped here. */
.sp-article .sp-gallery-tile figcaption {
  position: absolute;
  inset: auto 0 0 0;
  margin: 0;
  padding: 1.75rem 0.6rem 0.5rem;
  font-size: 0.7rem;
  line-height: 1.3;
  text-align: left;
  color: #fff;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.72), rgba(0, 0, 0, 0));
  pointer-events: none;
}

/* Lists (editor: pl-7 items, absolute muted marker, mb-1 per item). */
.sp-article ol,
.sp-article ul {
  margin: 0 0 0.875rem;
  padding-left: 1.75rem;
}
/* Restore markers: the SPA injects this fragment under Tailwind's preflight,
 * which resets `list-style: none` on ul/ol. */
.sp-article ul {
  list-style: disc;
}
.sp-article ol {
  list-style: decimal;
}
.sp-article li {
  margin-bottom: 0.25rem;
  white-space: pre-wrap;
}
.sp-article li::marker {
  color: var(--sp-muted);
  font-variant-numeric: tabular-nums;
}

/* Footnote markers render sans (editor: [data-footnote-start] → font-sans). */
.sp-article sup.footnote {
  font-family: var(--sp-sans);
  color: var(--article-accent);
}

.sp-article mark {
  background: color-mix(in srgb, var(--article-accent) 25%, transparent);
  color: inherit;
  border-radius: 2px;
}

/* ── Print ─────────────────────────────────────────────────────────────
 * Shared by the SPA's injected fragment AND the standalone prerendered page
 * (see the file banner) — the standalone page is the more likely target
 * (no chrome to begin with), but the SPA route must print sanely too.
 *
 * 1. The SPA is a fixed-height scrolling shell (index.css: html/body/#root
 *    pinned to 100svh; the article pane is `sm:h-full sm:overflow-y-auto`),
 *    which would clip a printed article to one screen's worth of content.
 *    Forcing the shell to `height: auto` mirrors the existing
 *    `sp-mobile-article` trick (index.css) — a percentage height against an
 *    auto-height ancestor resolves to `auto` per spec, so every `h-full`/
 *    `flex-1` box down the chain relaxes to plain flow and `overflow-y-auto`
 *    has nothing left to scroll.
 * 2. Hide app chrome around the article: the ViewHeader / split-view pane
 *    toolbar, the ToC sidebar, the prev/next gutter arrows + nav cards, the
 *    swipe-hint chips, and any open dialog (lightbox / search / mobile ToC).
 *    None of this exists on the standalone page, so these are no-ops there.
 * 3. Force the light palette regardless of `.dark`/OS preference — print is
 *    always white paper.
 * 4. Print-scale serif sizing (pt, not the screen rem/px scale) + page-break
 *    rules: a heading never ends a page (stays with what follows), figures/
 *    factboxes/the byline strip never split mid-block.
 * 5. Drop the 70vh screen cap on images — meaningless on a printed page;
 *    `break-inside: avoid` on the figure keeps it whole instead. */
@media print {
  html,
  body,
  #root {
    height: auto !important;
  }

  /* App chrome (SPA route only). The header selector excludes the ARTICLE's
   * own <header> (kicker/supertitle/title/subtitle/byline/narration, emitted
   * by articleHtml.ts) — only the outer ViewHeader chrome above it is hidden.
   * `[data-print-hide]` is a STABLE hook the chrome elements carry directly
   * (the ToC aside, the article nav, the prev/next gutter arrows) — it used to
   * be aria-label selectors, but those labels are translated (nb/da) while
   * this rule must stay locale-independent. */
  header:not(.sp-article header),
  [data-print-hide],
  .swipe-hint,
  .article-actions-row,
  [role="dialog"],
  /* SplitView's slim pane toolbar (Expand/Close) — no dedicated hook, matched
   * by its exact (currently unique) Tailwind class set. */
  div.justify-end.border-b.border-border.bg-surface.px-2 {
    display: none !important;
  }

  :root {
    --sp-bg: #ffffff !important;
    --sp-fg: #000000 !important;
    --sp-muted-bg: #eeeeee !important;
    --sp-muted: #555555 !important;
    --sp-border: #cccccc !important;
    --sp-accent-l: 32% !important;
    color-scheme: light !important;
  }

  .sp-article {
    max-width: 100%;
    padding: 0 !important;
    font-size: 11pt;
    line-height: 1.5;
  }
  .sp-article .kicker {
    font-size: 8.5pt;
  }
  .sp-article .supertitle {
    font-size: 10pt;
  }
  .sp-article h1 {
    font-size: 22pt;
  }
  .sp-article .subtitle {
    font-size: 13pt;
  }
  .sp-article .byline {
    font-size: 9pt;
  }
  .sp-article .blurb {
    font-size: 12pt;
  }
  .sp-article h2 {
    font-size: 15pt;
  }
  .sp-article h3 {
    font-size: 10.5pt;
  }
  .sp-article blockquote {
    font-size: 14pt;
  }
  .sp-article figcaption {
    font-size: 9pt;
  }

  /* Never split these; never end a page on a heading/kicker. */
  .sp-article header,
  .sp-article h1,
  .sp-article h2,
  .sp-article h3,
  .sp-article .byline,
  .sp-article figure,
  .sp-article aside.factbox,
  .sp-article blockquote {
    page-break-inside: avoid;
    break-inside: avoid;
  }
  .sp-article header,
  .sp-article h1,
  .sp-article h2,
  .sp-article h3,
  .sp-article .kicker {
    page-break-after: avoid;
    break-after: avoid;
  }
  .sp-article p,
  .sp-article li {
    orphans: 3;
    widows: 3;
  }

  .sp-article figure img {
    max-height: none;
  }

  /* Inert on paper. */
  .sp-article .narration {
    display: none;
  }
}
