:root {
  --title-font: var(--story-title-font, system-ui, sans-serif);
  --body-font: var(--story-body-font, Georgia, serif);
  --bg-main: var(--story-bg-main, #fff);
  --text-main: var(--story-text-main, #222);
  --accent: #2b6cb0;
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

/* Every other reduced-motion override lives next to the rule it turns
   off (search "prefers-reduced-motion" — the header/progress bar are the
   deliberate exception: their continuous scroll-driven updates aren't
   decorative motion, so they're left alone). */

body {
  margin: 0;
  font-family: var(--body-font);
  background: var(--bg-main);
  color: var(--text-main);
  line-height: 1.6;
}

h1,
h2,
h3 {
  font-family: var(--title-font);
  line-height: 1.2;
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}

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

a:focus-visible,
button:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

.skip-link {
  position: absolute;
  left: -999px;
  top: 0;
  background: #fff;
  color: #000;
  padding: 0.75rem 1rem;
  z-index: 100;
}

.skip-link:focus {
  left: 0.5rem;
  top: 0.5rem;
}

/* Progress bar: a 2px line across the very top showing how far through
   the story the reader is (Cascade used progress.js's "blue" theme). */

.story-progress {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  z-index: 1001;
  pointer-events: none;
}

.story-progress-bar {
  height: 100%;
  width: 0;
  background: #3498db;
  transition: width 0.3s ease-out;
}

/* Header: Cascade's fixed 50px black bar. While the cover is on screen it
   is "compact" — transparent, with the title and bookmarks hidden — and
   turns solid as the reader scrolls into the story. */

.story-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  height: 50px;
  padding: 5px;
  background: #000;
  color: #fff;
  box-shadow: 0 3px 5px 0 rgba(0, 0, 0, 0.75);
  transition: background 1s linear, box-shadow 1s linear;
  font-family: var(--title-font);
}

.story-header.compact {
  background: transparent;
  box-shadow: none;
}

.story-brand {
  display: flex;
  align-items: center;
  flex: 0 1 auto;
  max-width: 75%;
  min-width: 0;
}

.story-logo {
  flex: 0 1 auto;
  margin-right: 4px;
}

.story-logo img {
  max-width: 210px;
  max-height: 42px;
  display: block;
}

.story-header.compact .story-logo img {
  filter: drop-shadow(0 0 3px #000);
}

.story-title {
  /* px, not the original's `2.1em` — see the .cover-title comment above
     for why: measured 29.4px against the real viewer (2.1em × an
     effective 14px ancestor basis, not the 16px default root). */
  font-size: 29.4px;
  line-height: 38px;
  height: 42px;
  margin-left: 14px;
  color: #fff;
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  visibility: visible;
  /* `visibility` only flips the instant the *reverse* (fade back in)
     transition starts — so it's gone from the accessibility tree while
     compact, but never disappears mid-fade on the way out. A pa11y pass
     flagged this as 0-contrast (invisible) text without it; the original
     viewer has the exact same gap (plain `opacity: 0`, still fully in the
     tree) — fixed here rather than reproduced, per this project's
     accessibility aim. */
  transition: opacity 1s linear, visibility 0s linear 0s;
}

.story-header.compact .story-title {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 1s linear, visibility 0s linear 1s;
}

.story-bookmarks {
  flex: 1 1 0;
  min-width: 40px;
  margin: 2px 30px 0;
}

.story-bookmarks ul {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  list-style: none;
  margin: 0;
  padding: 0;
}

.story-bookmarks a {
  display: inline-block;
  margin-left: 12px;
  padding-bottom: 1px;
  font-size: 14px;
  color: #ccc;
  text-decoration: none;
  border-bottom: 3px solid transparent;
  white-space: nowrap;
}

.story-bookmarks a:hover,
.story-bookmarks a.is-current {
  border-bottom-color: #3b8fc4;
}

.story-bookmarks a.is-current {
  color: #fff;
}

.story-header.compact .story-bookmarks {
  visibility: hidden;
}

.story-link {
  flex: 0 0 auto;
  margin-right: 8px;
  font-size: 14px;
  color: #cecece;
}

.story-link a {
  color: inherit;
}

.story-link a:hover {
  color: #acacac;
}

.story-header.compact .story-link {
  color: #fff;
  text-shadow: 0 0 8px #000;
}

@media (max-width: 767px) {
  .story-title {
    font-size: 18.2px;
    margin-left: 5px;
  }
  .story-bookmarks {
    margin: 0 10px;
  }
  .story-link {
    display: none;
  }
}

/* Sections stack above the pinned cover (see .hero-cover below), so each
   needs its own opaque background; anchor targets clear the fixed header. */

main > section {
  position: relative;
  z-index: 1;
  scroll-margin-top: 60px;
}

/* titleStyle (cover, title bands, immersive global title): an optional
   translucent box around the text, light or dark text, optional glow. */

.text-background {
  display: inline-block;
}

.text-background.background-light,
.text-background.background-dark {
  padding: 10px 30px;
}

.text-background.background-light {
  background-color: rgba(255, 255, 255, 0.75);
}

.text-background.background-dark {
  background-color: rgba(0, 0, 0, 0.75);
}

.title-text {
  color: #fff;
  text-shadow: none;
}

.title-text.text-shadow {
  text-shadow: 0 0 8px rgba(0, 0, 0, 0.95);
}

.title-text.text-dark {
  color: #000;
}

.title-text.text-dark.text-shadow {
  text-shadow: 0 0 8px rgba(255, 255, 255, 0.95);
}

/* Hero (cover / title sections) */

.hero {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #111;
  background-size: cover;
  background-position: center;
  overflow: hidden;
  text-align: center;
  color: #fff;
}

/* The cover stays pinned while later sections slide up over it — the
   same effect as Cascade's fixed cover wrapper, without any JS. */
.hero-cover {
  position: sticky;
  top: 0;
  z-index: 0;
  height: 100vh;
  height: 100dvh;
  min-height: 0;
}

.hero .background-image,
.hero .background-fit {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hero-content {
  position: relative;
  z-index: 1;
  width: calc(100% - 20px);
  padding: 0;
}

.hero-cover .hero-content {
  margin-top: -50px;
}

.hero-cover .text-background.background-light,
.hero-cover .text-background.background-dark {
  padding: 20px 35px;
  min-width: 300px;
}

/* Pixel values below (not the `7em`/`1.7em` the original template's own
   source uses) — Cascade's cover title/subtitle sizes turn out to depend
   on a compounding chain of ancestor font-sizes (a 10px root plus at
   least one 14px-equivalent ancestor in between), not a clean multiple of
   the 16px default; reproducing the `em` numbers verbatim rendered
   ~1.4x too large (measured on the real viewer: 112px vs the original's
   98px at 1440px wide) and wrapped titles onto an extra line. These are
   the actual computed sizes measured directly against the original
   viewer at each breakpoint (docs/polish.md Phase H). */
.cover-title {
  font-size: 98px;
  line-height: 1.2;
  font-weight: 300;
  margin: 0;
  overflow-wrap: break-word;
}

.cover-subtitle {
  font-size: 27px;
  line-height: 1.4;
  margin: 30px 0 0;
  overflow-wrap: break-word;
}

@media (max-width: 1200px) {
  .cover-title {
    font-size: 70px;
  }
  .cover-subtitle {
    font-size: 23.8px;
  }
}

@media (max-width: 767px) {
  .cover-title {
    font-size: 37.8px;
  }
  .cover-subtitle {
    font-size: 15.4px;
    margin-top: 10px;
  }
  .hero-cover .text-background.background-light,
  .hero-cover .text-background.background-dark {
    min-width: 140px;
    padding: 10px 20px;
  }
}

/* Title band (chapter divider): a short banner, not a hero — 90/200/400px
   by `size`, growing taller only if a long title actually needs more room
   (min-height, not height, exactly like the original). */

.title-band {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 90px;
  overflow: hidden;
  text-align: center;
  color: #fff;
  /* The theme's own background (white unless the story is dark-themed) —
     what actually shows through before/behind a background image, per
     Cascade's `.background{background-color:#FFF}` default. */
  background-color: var(--bg-main);
}

.title-band.size-medium {
  min-height: 200px;
}

.title-band.size-large {
  min-height: 400px;
}

.title-band-bg {
  position: absolute;
  inset: 0;
}

.title-band-bg .background-image,
.title-band-bg .background-fit {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.title-band-content {
  position: relative;
  z-index: 1;
  padding: 10px;
}

.fg-title {
  font-size: 52px;
  line-height: 1.05;
  font-weight: 300;
  margin: 0;
  overflow-wrap: break-word;
}

.fg-credits {
  position: absolute;
  bottom: 5px;
  left: 5px;
  z-index: 1;
  font-size: 14px;
  color: #fff;
  opacity: 0.65;
}

.fg-credits p {
  margin: 0;
}

@media (max-width: 767px) {
  .fg-title {
    font-size: 34px;
  }
}

/* Scroll invite: a pulsing white chevron at the foot of the cover. */

.scroll-invite {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  padding: 15px;
  text-align: center;
}

.scroll-invite-btn {
  display: inline-block;
  height: 52px;
  padding: 0;
  border: 0;
  background: none;
  color: #fff;
  cursor: pointer;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.6));
  animation: scroll-invite-blink 2.5s cubic-bezier(0.5, 0, 1, 1) infinite;
}

.scroll-invite-btn svg {
  display: block;
}

@keyframes scroll-invite-blink {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.6;
  }
}

@media (prefers-reduced-motion: reduce) {
  .scroll-invite-btn {
    animation: none;
  }
}

/* Sequence (linear narrative) */

.sequence {
  position: relative;
  padding: 4rem 1.5rem;
  /* Opaque by default (a story's own color background overrides inline):
     this section slides over the pinned cover. */
  background-color: var(--bg-main);
}

.sequence-content {
  position: relative;
  text-align: center;
}

/* Generic block spacing — the fallback used wherever blocks render outside
   a sequence (immersive panels, credits), which keep their own rhythm.
   Sequence overrides every one of these below with Cascade's real numbers:
   each block *element* (a p/h1/h2/blockquote tag, or a media figure) is
   sized and centered on its own, at a width relative to the full section,
   not a narrowed shared column — so it's styled by tag under
   .sequence-content rather than by wrapping it in a container. */
.block-text {
  margin-bottom: 1.5rem;
}

.block-text :first-child {
  margin-top: 0;
}

figure.block-image,
figure.gallery-image {
  margin: 0 0 1.5rem;
}

figure.block-image figcaption,
figure.gallery-image figcaption,
figure.block-image-gallery figcaption {
  font-size: 0.85rem;
  opacity: 0.75;
  margin-top: 0.5rem;
}

.block-image-gallery .gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 0.75rem;
}

.sequence-content .block-text {
  margin-bottom: 0;
}

.sequence-content .block-text p.block,
.sequence-content .block-text h1.block,
.sequence-content .block-text h2.block,
.sequence-content .block-text h3.block,
.sequence-content .block-text blockquote.block,
.sequence-content figure.block-image,
.sequence-content figure.block-image-gallery {
  margin-top: 30px;
  margin-bottom: 0;
}

.sequence-content .block-text p.block,
.sequence-content .block-text h1.block,
.sequence-content .block-text h2.block,
.sequence-content .block-text h3.block,
.sequence-content .block-text blockquote.block {
  margin-left: auto;
  margin-right: auto;
  text-align: left;
}

.sequence-content .block-text p.block,
.sequence-content .block-text h2.block,
.sequence-content .block-text h3.block {
  width: 40%;
  max-width: 768px;
  font-size: 20px;
  line-height: 150%;
}

.sequence-content .block-text h1.block {
  width: 40%;
  max-width: 768px;
  line-height: 110%;
}

.sequence-content .block-text blockquote.block {
  width: 35%;
  max-width: 672px;
  font-family: var(--body-font);
  font-size: 24px;
  line-height: 130%;
}

.sequence-content .block-text .first-letter-huge::first-letter {
  font-size: 200%;
  color: #a5a948;
  vertical-align: 0;
}

.sequence-content figure.block-image {
  margin-left: auto;
  margin-right: auto;
}

.sequence-content figure.block-image img {
  min-width: 170px;
  margin-left: auto;
  margin-right: auto;
}

.sequence-content figure.block-image.block-size-small {
  width: 40%;
  max-width: 768px;
}

.sequence-content figure.block-image.block-size-small.portrait {
  width: 27%;
  max-width: 518px;
}

.sequence-content figure.block-image.block-size-medium {
  width: 80%;
  max-width: 1536px;
}

.sequence-content figure.block-image.block-size-medium.portrait {
  width: 57%;
  max-width: 1094px;
}

.sequence-content figure.block-image.block-size-large {
  width: 100%;
}

.sequence-content figure.block-image.block-size-large figcaption {
  padding: 0 10px;
}

.sequence-content figure.block-image-gallery {
  width: 80%;
  max-width: 1536px;
  margin-left: auto;
  margin-right: auto;
}

.sequence-content figure.block-image figcaption,
.sequence-content figure.block-image-gallery figcaption {
  font-family: var(--title-font);
  font-size: 14px;
  line-height: 130%;
  opacity: 0.65;
  width: 100%;
  text-align: center;
  margin-top: 5px;
}

/* Scroll-driven fade-in for media blocks only (site.js) — text blocks are
   never hidden, matching Cascade (only its div.block media wrappers fade;
   see docs/polish.md §1.4). */
.sequence-content [data-reveal]:not(.bring-in) {
  opacity: 0;
}

.sequence-content [data-reveal].bring-in {
  animation: fade-in-media 1s;
}

@media (prefers-reduced-motion: reduce) {
  .sequence-content [data-reveal].bring-in {
    animation: none;
  }
}

@media (max-width: 1024px) {
  .sequence-content .block-text p.block,
  .sequence-content .block-text h2.block,
  .sequence-content .block-text h3.block {
    width: 50%;
    font-size: 20px;
  }
  .sequence-content .block-text h1.block {
    width: 50%;
    font-size: 36px;
  }
  .sequence-content .block-text blockquote.block {
    width: 45%;
    padding: 0 12px;
    font-size: 24px;
  }
  .sequence-content figure.block-image.block-size-small {
    width: 50%;
    margin-left: auto;
    margin-right: auto;
  }
}

@media (max-width: 767px) {
  .sequence-content .block-text p.block,
  .sequence-content .block-text h1.block,
  .sequence-content .block-text h2.block,
  .sequence-content .block-text h3.block,
  .sequence-content figure.block-image,
  .sequence-content figure.block-image-gallery {
    margin-top: 20px;
  }
  .sequence-content .block-text p.block,
  .sequence-content .block-text h1.block,
  .sequence-content .block-text h2.block,
  .sequence-content .block-text h3.block {
    width: 90%;
    font-size: 16px;
  }
  .sequence-content .block-text h1.block {
    font-size: 30px;
    line-height: 110%;
  }
  .sequence-content .block-text blockquote.block {
    width: 80%;
    padding: 0 12px;
    font-size: 20px;
  }
  .sequence-content figure.block-image,
  .sequence-content figure.block-image.block-size-small,
  .sequence-content figure.block-image.block-size-medium,
  .sequence-content figure.block-image.block-size-large {
    width: 100%;
  }
  .sequence-content figure.block-image figcaption,
  .sequence-content figure.block-image-gallery figcaption {
    width: 90%;
    margin: 5px auto 0;
    text-align: center;
    padding: 0;
  }
}

/* Immersive (scrollytelling): a sticky full-viewport stage holding every
   unique background, only one visible at a time, with the narrative panels
   scrolling over it. Numbers mirror Cascade's viewer (docs/polish.md §1.5). */

.immersive {
  position: relative;
}

.immersive-stage {
  position: sticky;
  top: 0;
  z-index: 0;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  background-color: var(--bg-main);
}

.immersive-filler {
  position: absolute;
  inset: 0;
  background-color: var(--bg-main);
}

.immersive-bg {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #111;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.immersive-bg.is-active {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

/* The outgoing background stays put underneath while the incoming one
   fades over it (site.js drops the class once the transition ends). */
.immersive-bg.was-active {
  opacity: 1;
  visibility: visible;
}

.immersive-bg[data-transition="fade-fast"] {
  transition: opacity 0.5s ease-in-out, visibility 0.5s ease-in-out;
}

.immersive-bg[data-transition="fade-slow"] {
  transition: opacity 1.5s ease-in-out, visibility 1.5s ease-in-out;
}

.immersive-bg .background-image,
.background-fit {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.background-fit .background-image {
  object-fit: contain;
}

/* The shadow line at the reveal edge of a swipe transition. */
.immersive-swipe-edge {
  position: absolute;
  z-index: 3;
  pointer-events: none;
}

.immersive-swipe-edge.is-vertical {
  left: 0;
  right: 0;
  height: 10px;
  margin-top: -10px;
  box-shadow: inset 0 -24px 26px -30px #000;
}

.immersive-swipe-edge.is-horizontal {
  top: 0;
  bottom: 0;
  width: 10px;
  margin-left: -10px;
  box-shadow: inset -24px 0 26px -30px #000;
}

.storymap-map {
  width: 100%;
  height: 100%;
  background: #ddd;
}

.storymap-map.interaction-disabled {
  pointer-events: none;
}

/* The stage sits under the fixed 50px header; keep Leaflet's controls
   (zoom, top-left) clear of it. */
.immersive-stage .leaflet-top {
  top: 50px;
}

.background-unavailable,
.background-unsupported {
  color: #fff;
  max-width: 30rem;
  padding: 1.5rem;
  text-align: center;
}

/* Global title: pinned 60px from the top of the stage, fades in with the
   section and out as its last view scrolls away. */
.immersive-title {
  position: absolute;
  top: 60px;
  left: 0;
  right: 0;
  z-index: 4;
  text-align: center;
  opacity: 0;
  transition: opacity 1s ease-out;
  pointer-events: none;
}

.immersive.is-active .immersive-title {
  opacity: 1;
}

.immersive.is-leaving .immersive-title {
  opacity: 0;
}

.immersive-title h2 {
  margin: 0;
  font-size: 36px;
  line-height: 1.05;
  font-weight: 300;
  pointer-events: auto;
}

/* Panels: each view is at least a viewport tall plus a 380px dwell so the
   background lingers after the text ends; the stage's own 100vh in flow
   provides the background-only lead-in before the first panel. */
.immersive-panels {
  position: relative;
  z-index: 1;
  pointer-events: none;
  font-size: 1.3em;
  line-height: 1.6;
  color: #fff;
}

.imm-panel {
  min-height: 100vh;
  min-height: 100dvh;
  padding: 20px 0 380px;
}

.imm-card {
  position: relative;
  box-sizing: border-box;
  min-width: 250px;
  max-width: 400px;
  margin-left: 30px;
  padding: 15px;
  min-height: 100px;
  pointer-events: auto;
  color: #fff;
  background-color: rgba(0, 0, 0, 0.75);
}

.imm-panel.size-small .imm-card {
  width: 28%;
  max-width: 300px;
}

.imm-panel.size-medium .imm-card {
  width: 38%;
}

.imm-panel.size-large .imm-card {
  width: 48%;
  max-width: 600px;
}

.imm-panel.placement-right .imm-card {
  margin-left: auto;
  margin-right: 30px;
}

.imm-panel.placement-center .imm-card {
  margin-left: auto;
  margin-right: auto;
}

.imm-panel.theme-black-over-white .imm-card {
  color: #000;
  background-color: rgba(255, 255, 255, 0.75);
}

.imm-panel.style-text-shadow .imm-card {
  background-color: transparent;
  text-shadow: 0 0 8px #000;
}

.imm-panel.style-text-shadow.theme-black-over-white .imm-card {
  text-shadow: 0 0 8px #fff;
}

.imm-card a {
  color: #56a5d8;
}

.imm-card > * + * {
  margin-top: 15px;
}

.imm-card .block-text > :first-child {
  margin-top: 0;
}

.imm-card .block-text > :last-child {
  margin-bottom: 0;
}

.imm-card img {
  max-width: 100%;
  height: auto;
}

.imm-panel.size-large .imm-card figure.block-image.portrait,
.imm-panel.size-medium .imm-card figure.block-image.portrait {
  width: 80%;
  margin-left: auto;
  margin-right: auto;
}

.imm-panel.size-large .imm-card figure.block-image.portrait {
  width: 70%;
}

/* scroll-full: the panel is invisible until the reader is 5% of a
   viewport into the view, then fades in over 1s (replays on scroll-back). */
.imm-panel.layout-scroll-full:not(.bring-in) {
  opacity: 0;
}

.imm-panel.layout-scroll-full.bring-in {
  animation: fade-in-media 1s;
}

/* scroll-partial: site.js drives the card's opacity from its position. */
.imm-panel.layout-scroll-partial .imm-card {
  opacity: 0;
}

@keyframes fade-in-media {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@media (max-width: 767px) {
  .imm-card,
  .imm-panel.size-small .imm-card,
  .imm-panel.size-medium .imm-card,
  .imm-panel.size-large .imm-card,
  .imm-panel.placement-right .imm-card {
    width: auto;
    max-width: none;
    margin: 0 10px;
    padding: 5px;
  }
  .immersive-panels {
    font-size: 14px;
    line-height: 1.5;
  }
  .immersive-title h2 {
    /* px, not the original's `1.4em` (measured against the real viewer —
       see the .cover-title comment). */
    font-size: 23.52px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .immersive-bg,
  .immersive-title {
    transition: none;
  }
  .imm-panel.layout-scroll-full.bring-in {
    animation: none;
  }
}

/* Credits */

.credits {
  padding: 4rem 1.5rem;
  text-align: center;
  /* Fixed, not theme-driven — matches the original, which renders every
     credits section on this same dark band regardless of the story's
     theme or the section's own authored background color. */
  background-color: #323232;
  color: #f8f8f8;
}

.credits-content {
  max-width: 34rem;
  margin: 0 auto;
}

.credits-content a {
  color: #56a5d8;
}

.credits-list {
  list-style: none;
  padding: 0;
}

/* Unsupported content fallback */

.block-unsupported,
.section-unsupported {
  border: 1px dashed #c00;
  background: #fff4f4;
  /* .section-unsupported also stacks over the pinned cover, so stay opaque. */
  padding: 1rem;
  margin: 1rem 0;
  color: #900;
}

.block-unsupported pre,
.section-unsupported pre {
  overflow-x: auto;
  font-size: 0.75rem;
  white-space: pre-wrap;
  word-break: break-word;
}

/* Footer */

.story-footer {
  padding: 2rem 1.5rem 3rem;
  max-width: 40rem;
  margin: 0 auto;
  font-size: 0.85rem;
  opacity: 0.8;
}

.story-footer p {
  /* meta.credit is plain text (unlike block text) and can carry real
     newlines from the source item — preserve them instead of collapsing
     to one line. */
  white-space: pre-line;
}

/* Map popups (Leaflet) */

table.map-popup {
  border-collapse: collapse;
  font-size: 0.8rem;
}

table.map-popup th {
  text-align: left;
  padding: 0.15rem 0.5rem 0.15rem 0;
  color: #555;
}

table.map-popup td {
  padding: 0.15rem 0;
}

/* Leaflet's default attribution control is only 80% opaque, so its text
   contrast depends on whatever map tile happens to be underneath it —
   flagged by a pa11y/WCAG2AA contrast pass (G18.Alpha). Make it fully
   opaque with darker text so contrast holds regardless of the basemap. */
.leaflet-control-attribution {
  background: #fff !important;
  color: #111 !important;
}

.leaflet-control-attribution a {
  /* !important: leaflet.css loads after this stylesheet and has its own
     `.leaflet-container a` color rule of equal specificity, which would
     otherwise win purely on source order. */
  color: #0b4f8a !important;
}

/* Print: nothing here is interactive on paper, so unpin every sticky
   stage, stop hiding backgrounds/panels behind scroll state, and force
   plain black-on-white everywhere a component set its own light-on-dark
   or translucent-over-photo colors for the screen. `100vh` sections
   (cover/title/immersive) get a fixed printed height instead — literal
   100vh would burn a full page on every one of them. */
@media print {
  .skip-link,
  .story-header,
  .story-progress,
  .scroll-invite,
  .immersive-swipe-edge,
  .leaflet-control-container {
    display: none !important;
  }

  /* This was `inset: 0` against the sticky stage as a solid backdrop
     while later views loaded in behind it — once the stage is unpinned
     below, its containing block becomes the whole (now much taller)
     section, so it would stretch to cover the title and panels below it
     with an opaque rectangle. Purely a sticky-stage artifact; print has
     no use for it. */
  .immersive-filler {
    display: none;
  }

  html {
    scroll-behavior: auto;
  }

  .hero-cover {
    position: static;
  }

  .hero,
  .title-band {
    min-height: 0;
    height: 3in;
    page-break-inside: avoid;
  }

  .immersive-stage {
    position: static;
    height: auto;
    overflow: visible;
  }

  /* Was absolutely positioned to fill a sticky stage; `position: relative`
     (not `static`) keeps it in normal flow while still giving its own
     absolutely-positioned image/map child a box to fill. */
  .immersive-bg {
    position: relative;
    height: 3in;
    margin-bottom: 10px;
    opacity: 1 !important;
    visibility: visible !important;
    page-break-inside: avoid;
  }

  .immersive-title {
    position: static;
    opacity: 1 !important;
    margin-bottom: 10px;
  }

  .immersive-panels {
    margin-top: 0;
  }

  .imm-panel {
    min-height: 0;
    padding: 0 0 20px;
  }

  .imm-panel.layout-scroll-full:not(.bring-in),
  .imm-panel.layout-scroll-partial .imm-card,
  .sequence-content [data-reveal]:not(.bring-in) {
    opacity: 1 !important;
  }

  /* These normally rely on a CSS background-color alone for contrast
     (over a photo, or assuming a dark/light theme) — printers commonly
     skip background colors by default, so force one that will actually
     print, and force text dark enough to read on plain paper regardless. */
  .imm-card,
  .text-background {
    background: #fff !important;
    color: #000 !important;
    text-shadow: none !important;
    print-color-adjust: exact;
    -webkit-print-color-adjust: exact;
  }

  .hero,
  .title-band,
  .immersive-panels,
  .background-unavailable,
  .background-unsupported {
    color: #000 !important;
  }
}

