/*
 * Oaks Chronicle — site stylesheet, extracted from oaks_worker.js HTML_PAGE.
 * Served from oaksmagical.com/assets/css/oaks.css (see <link> in oaks_worker.js).
 */

/* Self-hosted fonts (replaces Google Fonts). URLs are relative to this file
   (/assets/css/), so they resolve to /assets/fonts/ on both the public site
   and the editor. */
@font-face {
  font-family: 'Special Elite';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/special-elite-v20-latin-regular.woff2') format('woff2'),
       url('../fonts/special-elite-v20-latin-regular.ttf') format('truetype');
}
@font-face {
  font-family: 'Kalam';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/kalam-v18-latin-regular.woff2') format('woff2'),
       url('../fonts/kalam-v18-latin-regular.ttf') format('truetype');
}
@font-face {
  font-family: 'Kalam';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/kalam-v18-latin-700.woff2') format('woff2'),
       url('../fonts/kalam-v18-latin-700.ttf') format('truetype');
}
:root {
  --paper-bg: #f3f2ea;
  --text-dark: #222;
  --text-mid: #333;
  --text-light: #444;
  --text-muted: #555;
  --text-faded: #666;
  --accent: #8B0000;
  --state-default: #e8e8e8;
  --state-visited: #c0c0c0;
  --scrollbar-thumb: #aaa;
  --scrollbar-track: #d8d7cd;
  --font-vintage: 'Special Elite', cursive;
  --font-handwritten: 'Kalam', cursive;
  --shadow-paper: 8px 20px 40px rgba(0, 0, 0, 0.25),
                  0 8px 16px rgba(0, 0, 0, 0.15);
  --shadow-stack: 6px 15px 30px rgba(0, 0, 0, 0.2),
                  0 6px 12px rgba(0, 0, 0, 0.1);
  --transition-std: 0.3s ease-in-out;
  /* Desktop paper width — the single ruler the desk artifacts scale from.
     Grows with the viewport so the paper + clippings fill larger screens,
     capped at 660px so reading line-length stays comfortable. Raise the cap
     to fill ultra-wide monitors more aggressively (at some readability cost). */
  --paper-w: clamp(460px, 36vw, 660px);
  /* How far the paper (with its stack and paper-anchored clippings) sits to
     the right of center within the right column. Tunable. */
  --paper-shift: clamp(20px, calc(3.5vw + (100vw - 800px) * 0.05), 150px);
}

* {
  touch-action: manipulation;
}

html,
body {
  height: 100%;
  overflow: hidden;
  margin: 0;
  padding: 0;
  font-family: Arial, sans-serif;
  -webkit-user-select: none;
  user-select: none;
}

a {
  color: var(--text-light);
  text-decoration: underline;
}

a:hover {
  color: var(--accent);
  font-weight: bold;
}

.scrollable {
  scrollbar-width: thin;
  scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
  overflow-y: auto;
}

.scrollable::-webkit-scrollbar {
  width: 8px;
}

.scrollable::-webkit-scrollbar-track {
  background: var(--scrollbar-track);
  border-radius: 4px;
}

.scrollable::-webkit-scrollbar-thumb {
  background: var(--scrollbar-thumb);
  border-radius: 4px;
}

.scrollable::-webkit-scrollbar-thumb:hover {
  background: var(--text-light);
}

#page-container {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  gap: 50px;
  padding-top: 50px;
  padding-bottom: 40px;
  min-height: 100vh;
  height: 100vh;
  width: 100vw;
  position: relative;
  /* Keep the 50/40px padding INSIDE the 100vh so the container's bottom edge
     is the viewport bottom. Without this (content-box) the padding is added
     to 100vh, pushing the bottom-anchored footer/links/timeline ~90px below
     the fold (links ended up straddling the bottom edge). */
  box-sizing: border-box;
}

#map-container {
  width: 1000px;
  background: transparent;
  position: absolute;
  left: -450px;
  top: 200px;
  z-index: 0;
  pointer-events: auto;
}

#right-column {
  width: 66vw;
  height: 100vh;
  position: absolute;
  top: 0;
  right: 0;
  padding: 0;
  background: transparent;
  z-index: 20;
  pointer-events: none;
}

#paper-wrapper {
  position: relative;
  left: var(--paper-shift); /* nudge the paper + stack + clippings right of center */
  width: var(--paper-w);
  max-width: 92%;
  margin: 60px auto 80px;
  pointer-events: auto;
  z-index: 20;
}

#title-container {
  position: absolute;
  top: 40px;
  left: 40px;
  text-align: left;
  z-index: 5;
  pointer-events: none;
}

#typewriter-title {
  font-family: var(--font-vintage);
  font-size: 36px;
  color: var(--text-dark);
  white-space: nowrap;
  overflow: hidden;
  width: 0;
  /* Bolder main event title — faux weight via stroke (Special Elite is 400 only). */
  -webkit-text-stroke: 0.7px currentColor;
  paint-order: stroke fill;
  /* Clickable "home" link back to the intro (#title-container is pointer-events:none). */
  pointer-events: auto;
  cursor: pointer;
}
/* The line break after "and" is suppressed on wide screens (one line) and
   enabled by the narrow-desktop media query below (two lines). */
#typewriter-title .title-break { display: none; }

/* The typing animation starts when the intro is dismissed, so the
   visitor actually sees it (it used to start at page load, which the
   opaque intro overlay would hide). */
body.intro-done #typewriter-title {
  animation: typing 2s steps(50) forwards;
}

#description-container {
  position: absolute;
  top: 130px;
  left: 60px;
  text-align: center;
  max-width: 420px;
  min-width: 300px;
  width: 35vw;
  z-index: 5;
  pointer-events: none;
}

#description-text {
  font-family: var(--font-vintage);
  font-size: 16px;
  color: var(--text-light);
  line-height: 1.6;
  max-width: 700px;
  margin: 0 auto;
}

#ref-note-author-container {
  position: absolute;
  bottom: 30px; /* Notes/Author links, just above the copyright */
  top: auto;
  left: 60px;
  text-align: center;
  max-width: 420px;
  min-width: 300px;
  width: 35vw;
  z-index: 5;
  pointer-events: none;
}

#ref-note-author-container a {
  pointer-events: auto;
  cursor: pointer;
}

.footer-text {
  font-family: var(--font-vintage);
  font-size: 14px;
  color: var(--text-light);
  line-height: 1.6;
  margin: 0;
}

.footer-text a {
  text-decoration: none;
}

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

.footer-text span {
  color: #aaa;
}

#paper-sheet {
  pointer-events: auto;
  width: var(--paper-w);
  height: 700px; /* fallback for browsers without min() */
  /* Fit the card to shorter desktop windows so its bottom (and the
     scroll area inside #current-event-desc) never falls below the fold.
     Top offset ~60px (#right-column padding 0 + #paper-wrapper margin 60)
     plus ~30px breathing room. Stays 700px on tall screens. */
  height: min(700px, calc(100vh - 170px));
  background: var(--paper-bg);
  padding: 30px 15px 30px 30px;
  border-radius: 2px;
  box-shadow: var(--shadow-paper);
  transform: perspective(600px) rotateX(1deg);
  transform-origin: center top;
  position: relative;
  filter: url(#paper-noise) drop-shadow(0 7px 14px rgba(0, 0, 0, 0.3));
  transition: transform var(--transition-std);
}

.paper-stack {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: -1;
}

.paper-stack-sheet {
  position: absolute;
  width: var(--paper-w);
  max-width: 92%;
  min-height: 700px;
  background: var(--paper-bg);
  padding: 30px;
  border-radius: 2px;
  box-shadow: var(--shadow-stack);
  filter: url(#paper-noise) drop-shadow(0 5px 10px rgba(0, 0, 0, 0.25));
  opacity: 0;
  transform-origin: center top;
}

.paper-stack-sheet.visible {
  opacity: 0.95;
  transition: opacity 0.6s ease, transform 0.4s ease;
}

.paper-stack-sheet:nth-child(1) { transform: rotate(1.2deg) translate(3px, -6px); }
.paper-stack-sheet:nth-child(2) { transform: rotate(1.6deg) translate(-4px, 0px); }
.paper-stack-sheet:nth-child(3) { transform: rotate(-1.0deg) translate(5px, 18px); }
.paper-stack-sheet:nth-child(4) { transform: rotate(-1.6deg) translate(-3px, 24px); }
.paper-stack-sheet:nth-child(5) { transform: rotate(2.0deg) translate(4px, 6px); }

/* ------------------------------------------------------------------
   Desk clippings — desktop only. Event images are lifted out of the
   paper text and scattered around the sheet like research clippings
   on a desk. The layer sits between the paper stack and the sheet so
   clippings tuck *under* the paper edges and peek out, keeping them
   hugging the paper rather than drifting over the map.
   ------------------------------------------------------------------ */
#clippings-layer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 0;
}

.clipping {
  position: absolute;
  pointer-events: auto;
  cursor: pointer;
  margin: 0;
  background: #fdfcf2;
  padding: 6px 6px 7px;
  border-radius: 1px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
  opacity: 0;
  transform: rotate(var(--clip-rot, 0deg)) translateY(26px) scale(0.96);
  transition: opacity 0.55s ease, transform 0.55s ease, box-shadow 0.3s ease;
}

.clipping.visible {
  opacity: 1;
  transform: rotate(var(--clip-rot, 0deg)) translateY(0) scale(1);
}

.clipping:hover {
  transform: rotate(var(--clip-rot, 0deg)) translateY(0) scale(1.05);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
  z-index: 3;
}

/* First-artifact hint (desktop): pulse the first clipping + a hand-drawn
   "Press to Enlarge" arrow pointing at it, until the visitor opens an artifact.
   The pulse uses the independent `scale` property so it composes with the
   clipping's rotate/scale transform instead of overriding it. */
@keyframes artifactPulse {
  0%, 100% { scale: 1; }
  50%      { scale: 1.05; }
}
.clipping.artifact-pulse {
  /* 1.8s matches #next-tab.pulse-hint .nav-arrow so the two pulses share a
     period; JS phase-aligns them (animation-delay) so they peak together. */
  animation: artifactPulse 1.8s ease-in-out infinite;
  z-index: 4;
}
.artifact-hint {
  position: fixed;
  z-index: 40;
  display: flex;
  flex-direction: column;
  align-items: center;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.7s ease;
}
.artifact-hint.artifact-hint-show { opacity: 1; }
.artifact-hint .hint-text {
  font-family: var(--font-handwritten);
  font-size: 22px;
  color: #000;
  margin: 0 0 1px;
  white-space: nowrap;
  transform: rotate(-4deg);
}
/* The hand-drawn arrow's head points UP at its natural orientation. Default
   (hint sits above the artifact) flips it to point DOWN at the clipping; the
   "below" variant uses the natural upward orientation. */
.artifact-hint svg { height: 78px; width: auto; transform: rotate(180deg); }
.artifact-hint.artifact-hint-below { flex-direction: column-reverse; }
.artifact-hint.artifact-hint-below svg { transform: none; }
.artifact-hint.artifact-hint-below .hint-text { margin: 1px 0 0; }

/* Images are never cropped: width and height budgets from
   layoutClippings() act as max constraints and the image scales down
   proportionally to fit inside both. The figure shrink-wraps the
   scaled image (absolutely positioned, no explicit width). */
.clipping img {
  display: block;
  width: auto;
  height: auto;
  max-height: 280px;
  filter: sepia(0.12);
}

.clipping .clip-caption {
  font-family: var(--font-vintage);
  font-size: 10px;
  line-height: 1.3;
  color: #5a5142;
  padding-top: 4px;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* match the image's width instead of stretching the figure */
  width: 0;
  min-width: 100%;
}

/* Clipping positions and sizes are computed at render time by
   layoutClippings(), which measures the whitespace around the paper
   (bounded by the map column on the left and the viewport edges) and
   spreads the clippings to fill it. Only a small strip of each
   clipping stays tucked under the paper. */

/* While hovering a clipping, lift the layer above the paper so the
   full clipping can be seen before clicking. */
#clippings-layer:hover {
  z-index: 25;
}

#current-event {
  padding-right: 15px;
  height: 100%;
  /* Single scroll container: header + body scroll together (the .scrollable
     class supplies overflow-y:auto). */
  display: block;
}

#current-event-header {
  margin-bottom: 12px;
  padding-right: 15px;
  flex-shrink: 0;
}

#current-event-title {
  font-family: var(--font-vintage);
  font-size: 20px;
  font-weight: normal;
  margin-bottom: 6px;
  color: var(--text-dark);
  letter-spacing: 1px;
  /* Bolder event title on the paper — stroke-thickened (single-weight font). */
  -webkit-text-stroke: 0.45px currentColor;
  paint-order: stroke fill;
}

#current-event-subtitle {
  font-family: var(--font-vintage);
  font-size: 16px;
  color: var(--text-mid);
  margin-top: 4px;
  letter-spacing: 0.5px;
}

#current-event-desc {
  font-family: var(--font-vintage);
  font-size: 14px;
  line-height: 1.9;
  color: var(--text-mid);
  text-align: justify;
  letter-spacing: 0.5px;
}

/* Scroll affordance for the desktop event paper. When #current-event holds
   more text than fits, JS (in oaks_worker.js / oaks_editor.js) toggles
   .is-scrollable-up / .is-scrollable-down on #paper-sheet, softly fading the
   top and/or bottom edge of the page and showing a small triangle cue at the
   foot. Both clear once the reader reaches the matching scroll edge. The cue
   elements are injected by that script, so no markup change is needed here. */
.paper-scroll-fade {
  position: absolute;
  left: 0;
  right: 0;
  height: 62px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.25s ease;
  z-index: 5;
}
.paper-scroll-fade--top {
  top: 0;
  border-radius: 2px 2px 0 0;
  background: linear-gradient(to bottom, var(--paper-bg) 22%, rgba(243, 242, 234, 0));
}
.paper-scroll-fade--bottom {
  bottom: 0;
  border-radius: 0 0 2px 2px;
  background: linear-gradient(to top, var(--paper-bg) 22%, rgba(243, 242, 234, 0));
}
.paper-scroll-cue {
  position: absolute;
  left: 50%;
  bottom: 12px;
  width: 0;
  height: 0;
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
  border-top: 9px solid var(--accent);
  transform: translateX(-50%);
  opacity: 0;
  transition: opacity 0.25s ease;
  z-index: 6;
  cursor: pointer;
  animation: paperCueBob 1.6s ease-in-out infinite;
}
/* Up cue: a static red triangle at the head of the page, shown whenever the
   reader is not at the top (text exists above). Never bobs. */
.paper-scroll-cue-up {
  position: absolute;
  left: 50%;
  top: 12px;
  width: 0;
  height: 0;
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
  border-bottom: 9px solid var(--accent);
  transform: translateX(-50%);
  opacity: 0;
  transition: opacity 0.25s ease;
  z-index: 6;
  cursor: pointer;
}
#paper-sheet.is-scrollable-up .paper-scroll-fade--top { opacity: 1; }
#paper-sheet.is-scrollable-up .paper-scroll-cue-up { opacity: 1; }
#paper-sheet.is-scrollable-down .paper-scroll-fade--bottom { opacity: 1; }
#paper-sheet.is-scrollable-down .paper-scroll-cue { opacity: 1; }
/* Stop the cue bobbing once the reader has begun scrolling this event
   (re-armed on each event change). The triangle stays visible while more
   text is below — it just holds still. */
#paper-sheet.has-scrolled .paper-scroll-cue { animation: none; }
@keyframes paperCueBob {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  50% { transform: translateX(-50%) translateY(4px); }
}
@media (prefers-reduced-motion: reduce) {
  .paper-scroll-cue { animation: none; }
}

#loading-message,
#no-events-message {
  text-align: center;
  padding: 40px;
  font-family: var(--font-vintage);
  font-size: 18px;
  color: var(--text-faded);
}

#no-events-message {
  display: none;
}

.artifact-link {
  color: var(--accent);
  text-decoration: underline;
  font-family: var(--font-vintage);
  cursor: pointer;
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  text-align: left;
}

.artifact-link:hover {
  color: var(--text-dark);
  font-weight: bold;
}

.nav-tab {
  position: absolute;
  /* Near the top of the paper, flanking the title row (desktop only —
     #right-column is hidden under the 800px breakpoint). */
  top: 18px;
  width: 50px;
  /* No background — the arrow sits directly on the desk, with a red NEXT/PREV
     label stacked beneath it (see ::after). */
  color: var(--accent);
  font-family: var(--font-vintage);
  font-size: 44px;
  font-weight: bold;
  line-height: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0;
  cursor: pointer;
  transition: var(--transition-std);
  user-select: none;
  /* Above #clippings-layer's hover z-index (25) so an overlapping clipping
     can't intercept the click — the arrows now live in #paper-wrapper. */
  z-index: 30;
}

/* The arrow glyph, in its own element so the pulse (below) can scale just the
   arrow and not the label. inline-block makes it transformable; the tight
   line-height pulls the label up close beneath it. */
.nav-arrow { display: inline-block; }

/* Red label tucked under each arrow. Its own font-size keeps it steady while
   the arrow pulses; the whole tab scales together on hover. */
.nav-tab::after {
  margin-top: -8px;
  font-family: var(--font-vintage);
  font-size: 13px;
  font-weight: bold;
  letter-spacing: 2px;
  color: var(--accent);
  white-space: nowrap;
}
.nav-tab.prev::after { content: "PREV"; }
.nav-tab.next::after { content: "NEXT"; }

.nav-tab.prev { left: -65px; }
.nav-tab.next { right: -110px; }
/* Pop the whole tab (arrow + label) on hover, the way artifacts scale up. */
.nav-tab:hover { transform: scale(1.18); }

/* First-event hint: the next arrow gently pulses until the visitor advances. */
@keyframes navTabPulse {
  0%, 100% { transform: scale(1); text-shadow: none; }
  50% { transform: scale(1.22); text-shadow: 0 0 14px rgba(139, 0, 0, 0.55); }
}

/* Pre-first-use pulse: scales only the arrow glyph, leaving the NEXT label
   steady. Persists until the visitor presses a nav arrow (see app.js). */
#next-tab.pulse-hint .nav-arrow {
  animation: navTabPulse 1.8s ease-in-out infinite;
}

.state {
  fill: var(--state-default);
  stroke: white;
  stroke-width: 1;
  transition: fill 0.3s, opacity 0.2s;
}

.point {
  fill: var(--text-light);
  opacity: 0;
  transition: opacity 0.3s, fill 0.3s;
  pointer-events: none;
}

.point.visible {
  opacity: 1;
  pointer-events: all;
  cursor: pointer;
}

.point.current {
  fill: var(--accent) !important;
}

.point:not(.star) { r: 3; }
.point.current:not(.star) { r: 4; }

/* Story mode: events that occurred during the story's window but aren't part of
   the story render as faint, non-interactive "context" pins — they show Oaks's
   full path and geography without inviting navigation away from the story. */
.point.context.visible {
  opacity: 0.4;
  pointer-events: none;
  cursor: default;
}
.point.context:not(.star) { r: 2; }

.route-line {
  stroke: var(--text-faded);
  stroke-width: 1;
  fill: none;
  stroke-dasharray: 1, 3;
  transition: stroke 0.3s;
}

.route-line.current {
  stroke-width: 2;
  stroke: var(--accent);
}

#timeline-bar-container {
  position: absolute;
  bottom: 60px; /* lifted up to overlap the map's lower edge; copyright/links stay pinned in the corner */
  top: auto;
  left: 70px;
  width: 460px;
  z-index: 15;
  pointer-events: none;
}

#timeline-bar {
  position: relative;
  height: 12px;
  background: #dbdbdb;
  overflow: hidden;
  display: flex;
  pointer-events: auto;
}

#progress-fill {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  background: #b0b0b0;
  width: 0%;
  transition: width 0.4s ease;
  z-index: 1;
}

#progress-fill.dragging {
  transition: none;
}

#timeline-zones {
  position: relative;
  width: 460px;
  height: 60px;
  margin-bottom: 40px;
  pointer-events: none;
}

.zone {
  position: absolute;
  top: 0;
  width: 25%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  cursor: pointer;
  transition: var(--transition-std);
  z-index: 2;
  pointer-events: auto;
}

.zone:nth-child(2) { left: 0%; }
.zone:nth-child(3) { left: 25%; }
.zone:nth-child(4) { left: 50%; }
.zone:nth-child(5) { left: 75%; }

.zone.zone-active {
  filter: drop-shadow(0 0 15px #888) drop-shadow(0 0 25px rgba(136, 136, 136, 0.7));
}

.zone-label {
  font-family: var(--font-vintage);
  font-size: 18px;
  color: var(--text-mid);
  font-weight: bold;
  text-shadow: 0 1px 3px rgba(255, 255, 255, 0.8);
  z-index: 3;
}

.zone-years {
  font-family: var(--font-vintage);
  font-size: 14px;
  color: var(--text-muted);
  margin-top: 3px;
  text-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);
  z-index: 3;
}

.timeline-marker {
  position: absolute;
  top: 0;
  width: 1px;
  height: 100%;
  z-index: 2;
}

.marker-line {
  width: 2px;
  height: 100%;
  background: #fff;
}

#birth-marker .marker-line {
  background: #b0b0b0;
}

#timeline-labels {
  position: relative;
  width: 460px;
  height: 54px;
  margin-top: 12px;
  pointer-events: none;
}

.timeline-label {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  font-family: var(--font-vintage);
  font-size: 14px;
  color: var(--text-mid);
  text-align: center;
  white-space: nowrap;
  pointer-events: auto;
  cursor: pointer;
  transition: color var(--transition-std);
}

.timeline-label:hover {
  color: var(--accent);
}

.timeline-label small {
  font-size: 11px;
  display: block;
  color: var(--text-muted);
}

.timeline-label.label-active {
  text-shadow: 0 0 15px #888, 0 0 25px rgba(136, 136, 136, 0.7);
}

#scrubber-handle {
  position: absolute;
  top: 50%;
  left: 0%;
  transform: translate(-50%, -50%);
  width: 8px;
  height: 24px;
  background: var(--accent);
  cursor: ew-resize;
  z-index: 10;
  pointer-events: auto;
  touch-action: none;
  transition: left 0.4s ease;
}

#scrubber-handle.dragging {
  transition: none;
}

#scrubber-handle::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 12px;
  height: 12px;
  background: var(--accent);
  border-radius: 50%;
  opacity: 0;
  transition: opacity 0.2s;
}

#scrubber-handle:hover::after,
#scrubber-handle.dragging::after {
  opacity: 1;
}

#age-indicator {
  position: absolute;
  bottom: -35px;
  left: 0;
  font-family: var(--font-vintage);
  font-size: 16px;
  color: var(--text-mid);
  font-weight: bold;
  transform: translateX(-50%);
  white-space: nowrap;
  pointer-events: auto;
  cursor: ew-resize;
  opacity: 0;
  transition: left 0.4s ease, opacity 0.3s ease;
  user-select: none;
  background: none;
}

#age-indicator.visible {
  opacity: 1;
}

#age-indicator.dragging {
  transition: none;
}

#scrubber-handle,
#age-indicator {
  pointer-events: auto !important;
}

.modal {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 450;
  overflow: auto;
  background-color: rgba(0, 0, 0, 0.8);
  align-items: center;
  justify-content: center;
  padding: 20px;
  box-sizing: border-box;
}

.modal-content {
  background: var(--paper-bg);
  width: 600px;
  max-width: 90%;
  max-height: 80vh;
  padding: 20px 40px 40px 40px;
  border-radius: 2px;
  box-shadow: var(--shadow-paper);
  filter: url(#paper-noise) drop-shadow(0 10px 20px rgba(0, 0, 0, 0.3));
  overflow-y: auto;
  position: relative;
  transform: perspective(600px) rotateX(1deg);
  transform-origin: center top;
  box-sizing: border-box;
  word-break: break-word;
  display: flex;
  flex-direction: column;
}

.modal-header {
  flex-shrink: 0;
  position: relative;
}

.modal-header h2 {
  margin-top: 4px;
  text-align: left;
  font-family: var(--font-vintage);
  font-size: 24px;
}

.modal-close {
  position: absolute;
  top: 0;
  right: 0;
  font-family: var(--font-vintage);
  font-size: 46px;
  font-weight: bold;
  color: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 102;
  border: none;
  background: transparent;
}

.modal-close:hover {
  color: rgba(255, 255, 255, 0.7);
}

.modal-body {
  flex: 1;
  overflow-y: auto;
  padding-right: 8px;
  margin-right: -8px;
}

.modal-text {
  line-height: 1.9;
  font-family: var(--font-vintage);
  font-size: 14px;
  color: var(--text-mid);
}

.modal-text p {
  margin-bottom: 16px;
}

.img-container {
  margin: 20px 0;
  clear: both;
}

.img-container::after {
  content: '';
  display: table;
  clear: both;
}

.img-figure {
  margin: 0;
  padding: 0;
  text-align: center;
}

.img-figure img {
  display: block;
  width: 100%;
  height: auto;
  cursor: pointer;
  border-radius: 2px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}

.img-figure figcaption {
  margin-top: 8px;
  font-family: var(--font-vintage);
  font-size: 13px;
  color: var(--text-muted);
  font-style: italic;
}

.img-full { width: 100%; }
.img-half { width: 48%; }
.img-half.img-left { float: left; margin-right: 4%; }
.img-half.img-right { float: right; margin-left: 4%; }

/* Desk clippings carry both classes; without these overrides the
   later .img-figure rules win (equal specificity) and the clip
   collapses to 0x0: padding 0 + img width:100% of a shrink-wrapped
   absolute figure. Restore the .clipping intent. */
.img-figure.clipping {
  padding: 6px 6px 7px;
  width: max-content;
}
.img-figure.clipping img {
  width: auto;
}

/* Mobile only: event artifacts (from the desk-layout table) that aren't
   embedded in the prose are collected below the story — desktop scatters
   these onto the desk, but mobile has no desk. */
.mobile-bottom-artifacts {
  margin-top: 24px;
  padding-top: 4px;
  border-top: 1px solid rgba(0, 0, 0, 0.12);
}

/* YouTube video artifacts ([[youtube:<id>]]). Click-to-play poster that swaps to
   a youtube-nocookie iframe; responsive 16:9, never lifted onto the desk. */
.yt-container {
  margin: 20px auto;
  max-width: 640px;
  clear: both;
}

.yt-embed {
  margin: 0;
  text-align: center;
}

.yt-poster,
.yt-iframe {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: 2px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}

.yt-poster {
  position: relative;
  padding: 0;
  border: 0;
  background: #000;
  cursor: pointer;
  overflow: hidden;
}

.yt-poster img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  margin: 0;
  border-radius: 0;
  box-shadow: none;
  cursor: pointer;
}

.yt-play {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 68px;
  height: 48px;
  transform: translate(-50%, -50%);
  border-radius: 12px;
  background: rgba(0, 0, 0, 0.55);
  transition: background 0.2s ease;
}

.yt-play::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-40%, -50%);
  border-style: solid;
  border-width: 11px 0 11px 19px;
  border-color: transparent transparent transparent #fff;
}

.yt-poster:hover .yt-play,
.yt-poster:focus-visible .yt-play {
  background: #f00;
}

.yt-iframe {
  border: 0;
}

.yt-embed figcaption {
  margin-top: 8px;
  font-family: var(--font-vintage);
  font-size: 13px;
  color: var(--text-muted);
  font-style: italic;
}

#image-modal {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 500;
  background-color: rgba(0, 0, 0, 0.95);
  justify-content: center;
  padding: 20px;
  box-sizing: border-box;
  cursor: zoom-out;
  /* Scroll (instead of clipping) if a tall image + a long caption still exceed
     the viewport — the caption is never cut off. Vertical centering is done by
     margin:auto on the content so overflow stays reachable from the top (an
     align-items:center container would clip the top of overflowing content).
     overscroll-behavior keeps this scroll from chaining to the mobile paper
     modal beneath it (iOS leaves that scroller stuck on close otherwise). */
  overflow-y: auto;
  overscroll-behavior: contain;
}

#image-modal.active {
  display: flex;
}

#image-modal-content {
  position: relative;
  /* Centers when it fits; when it doesn't, the auto margins collapse and the
     content stays scrollable from the top (no clipping). */
  margin: auto;
  max-width: 95vw;
  display: flex;
  flex-direction: column;
  align-items: center;
}

#image-modal-img {
  max-width: 95vw;
  /* Cap the image so a tall one always leaves room for the caption beneath it
     (reserve ~200px); still limited to 80vh on very tall viewports. */
  max-height: min(80vh, calc(100vh - 200px));
  object-fit: contain;
  border: 4px solid var(--paper-bg);
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
}

#image-modal-caption {
  font-family: var(--font-vintage);
  font-size: 16px;
  color: var(--paper-bg);
  text-align: center;
  margin-top: 16px;
  max-width: 600px;
  line-height: 1.6;
  flex-shrink: 0;
}

#image-modal-caption .img-date {
  display: block;
  font-style: italic;
  color: #aaa;
  margin-top: 4px;
}

#image-modal-caption .img-source {
  display: block;
  font-size: 13px;
  color: #888;
  margin-top: 4px;
}

#image-modal-close {
  position: fixed;
  top: 14px;
  right: 18px;
  width: 48px;
  height: 48px;
  z-index: 10;
  font-family: var(--font-vintage);
  font-size: 46px;
  font-weight: bold;
  color: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  transition: color 0.2s;
}

#image-modal-close:hover {
  color: rgba(255, 255, 255, 0.7);
}

#image-modal-nav {
  display: none;
}

.image-modal-nav-btn {
  font-family: var(--font-vintage);
  font-size: 28px;
  color: var(--paper-bg);
  cursor: pointer;
  background: none;
  border: none;
  padding: 10px 20px;
  transition: color 0.2s;
}

.image-modal-nav-btn:hover {
  color: #fff;
}

.image-modal-nav-btn:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

#list-modal-content .list-modal-item {
  padding: 10px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  cursor: pointer;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  transition: background-color 0.2s ease;
}

#list-modal-content .list-modal-item:hover {
  background-color: rgba(255, 255, 255, 0.06);
}

#list-modal-content .list-modal-item:last-child {
  border-bottom: none;
}

#list-modal-content .list-modal-bullet {
  width: 10px;
  height: 10px;
  margin-top: 4px;
  background: rgba(255, 255, 255, 0.5);
  border-radius: 50%;
  flex-shrink: 0;
}

#list-modal-content .list-modal-bullet.star {
  width: 14px;
  height: 14px;
  margin-top: 2px;
  clip-path: polygon(
    50% 0%, 61% 35%, 98% 35%, 68% 57%,
    79% 91%, 50% 70%, 21% 91%, 32% 57%,
    2% 35%, 39% 35%
  );
}

#list-modal-content .list-modal-bullet.current {
  background: #e0594f;
  box-shadow: 0 0 0 3px rgba(224, 89, 79, 0.3);
}

#list-modal-content .list-modal-info {
  flex: 1;
}

#list-modal-content .list-modal-title {
  font-weight: bold;
  color: #f0eee6;
  margin-bottom: 2px;
}

#list-modal-content .list-modal-date {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.55);
}

#audio-controls {
  position: fixed;
  bottom: 25px;
  right: 25px;
  z-index: 100;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  cursor: pointer;
  background: rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
}

#audio-controls button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  padding: 0;
  margin: 0;
  background: none;
  border: none;
  cursor: pointer;
}

#speaker-on,
#speaker-off {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

@keyframes typing {
  from { width: 0; }
  to { width: 100%; }
}

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes mobileBtnPulse {
  0%   { transform: scale(1); }
  50%  { transform: scale(1.05); }
  100% { transform: scale(1); }
}

body:not(.loaded) #description-container,
body:not(.loaded) #ref-note-author-container,
body:not(.loaded) #map-container,
body:not(.loaded) #paper-sheet,
body:not(.loaded) #clippings-layer,
body:not(.loaded) .nav-tab,
body:not(.loaded) #audio-controls,
body:not(.loaded) #timeline-bar-container {
  opacity: 0;
}

body:not(.loaded) #description-container {
  animation: fadeIn 2.5s ease-out 1.75s forwards;
}

body:not(.loaded) #map-container {
  animation: fadeIn 2.25s ease-out 2.0s forwards;
}

body:not(.loaded) #ref-note-author-container {
  animation: fadeIn 2.25s ease-out 2.25s forwards;
}

body:not(.loaded) #timeline-bar-container {
  animation: fadeIn 2.0s ease-out 2.25s forwards;
}

body:not(.loaded) #paper-sheet {
  animation: fadeIn 2s ease-out 2.5s forwards;
}

body:not(.loaded) #clippings-layer {
  animation: fadeIn 2s ease-out 3.0s forwards;
}

body:not(.loaded) .nav-tab,
body:not(.loaded) #audio-controls {
  animation: fadeIn 1s ease-out 3.5s forwards;
}

body.loaded .nav-tab,
body.loaded #audio-controls {
  opacity: 1;
  transition: none;
}
/* Keep a smooth transform transition for the hover pop (the rule above sets
   transition:none to stop the fade re-running). */
body.loaded .nav-tab { transition: transform 0.2s ease; }

/* ============================================================
   Introduction overlay (#intro) — shown to every visitor on every
   visit, desktop and mobile. Replaces the old #mobile-welcome.
   Doubles as the audio-unlock click. Wired by ui/intro.js;
   body.intro-done hides it.
   ============================================================ */
#intro {
  position: fixed;
  inset: 0;
  z-index: 500;
  background: linear-gradient(145deg, #f5f4ec 0%, #e8e7df 50%, #d8d7cd 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  /* Extra bottom padding biases the centered block upward so the title and
     first paragraph sit higher; scroll-safe (adds scrollable space rather
     than clipping when the content is taller than the viewport). */
  padding: 30px 20px 12vh;
  box-sizing: border-box;
  text-align: center;
  overflow-y: auto;
}

body.intro-done #intro {
  display: none;
}

.intro-inner {
  max-width: 600px;
  width: 100%;
  margin: auto; /* keeps it scrollable when taller than the viewport */
}

.intro-title {
  font-family: var(--font-vintage);
  font-size: 30px;
  color: var(--text-dark);
  line-height: 1.3;
  margin-bottom: 18px;
  letter-spacing: 1px;
  /* Special Elite ships only one weight (400); thicken the strokes for a
     bolder hero title. paint-order keeps inner counters open at this size. */
  -webkit-text-stroke: 0.6px currentColor;
  paint-order: stroke fill;
}

.intro-desc {
  font-family: var(--font-vintage);
  font-size: 14.5px;
  color: var(--text-mid);
  line-height: 1.6;
  margin-bottom: 20px; /* greater gap before the "Select a story…" prompt */
  /* Balance the centered lines responsively (no hardcoded <br>): keeps
     phrases like "defining moments" together and avoids a short orphan
     last line. Ignored gracefully by older browsers. */
  text-wrap: balance;
}

/* ---- Redesigned intro: scattered artifact scrapbook + rotating spotlight ---- */
#intro-collage {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

#intro-collage img {
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--w, 16vmin);
  height: auto;                 /* keep each artifact's original proportions */
  transform: translate(-50%, -50%) rotate(var(--rot, 0deg));
  background: #fdfcf2;
  padding: 5px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.28);
  filter: blur(1px) grayscale(1);
  opacity: 1;                   /* opaque, so a front clipping fully hides those behind it */
  transition: filter 0.6s ease, transform 0.6s ease, box-shadow 0.6s ease;
}

/* The middle and back layers recede via a lightening filter (NOT opacity),
   so they look faint without letting clippings behind them bleed through. */
#intro-collage img.ambient {
  z-index: 1;
  filter: blur(1.3px) brightness(1.07) contrast(0.9) grayscale(1);
}

#intro-collage img.far {
  z-index: 0;
  filter: blur(1.9px) brightness(1.18) contrast(0.78) grayscale(1);
}

#intro-collage img.key {
  z-index: 2;
  /* Keep the key clippings on a stable compositing layer so the 0.6s transform
     transition doesn't repaint/flicker on the frame it ends. */
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}

/* Desktop scatter positions for the five key clippings. */
#intro-collage img[data-intro-key="timeline"]          { left: 15%; top: 22%; --rot: -3deg; --w: 15vmin; }
#intro-collage img[data-intro-key="glass-water-tank"]  { left: 85%; top: 23%; --rot:  3deg; --w: 21vmin; }
#intro-collage img[data-intro-key="magical-co"]        { left: 13%; top: 66%; --rot:  5deg; --w: 16vmin; }
#intro-collage img[data-intro-key="escape-challenges"] { left: 50%; top: 81%; --rot: -4deg; --w: 16vmin; }
#intro-collage img[data-intro-key="riverboat"]         { left: 87%; top: 64%; --rot: -3deg; --w: 24vmin; }

#intro-collage img.is-focus {
  filter: blur(0) grayscale(1);
  opacity: 1;
  transform: translate(-50%, -50%) rotate(var(--rot, 0deg)) scale(1.12);
  box-shadow: 0 14px 32px rgba(0, 0, 0, 0.42);
  /* z-index intentionally left at the resting value (2). Previously the focused
     clipping was bumped to 6 — above the .intro-veil (z-index 3). It stayed
     composited above the veil while shrinking, then snapped back below the veil
     the instant the next clipping reached full size, which read as a blink on the
     prior image. A constant stacking order removes that snap; the enlarge still
     reads via scale + sharpen + shadow (the keys sit in the veil's faint edge). */
}

/* Sits inside #intro-collage, above the resting clippings but below a
   focused one, clearing the center for the title/text. */
.intro-veil {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background: radial-gradient(60% 50% at 50% 50%,
    rgba(243, 242, 234, 0.85) 0%,
    rgba(243, 242, 234, 0.62) 48%,
    rgba(243, 242, 234, 0.12) 80%,
    rgba(243, 242, 234, 0) 100%);
}

.intro-inner {
  position: relative;
  z-index: 10;
}

/* Featured "Oaks Breaks Jail" newspaper headline above the title. */
/* Floats in the upper area, centered between the top of the page and the title. */
.intro-topper {
  position: absolute;
  top: 14vh;
  left: 50%;
  z-index: 10;
  margin: 0;
  width: 175px;
  max-width: 44vw;
  background: #fdfcf2;
  padding: 5px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.28);
  transform: translate(-50%, -50%) rotate(-1.5deg);
}

.intro-topper img {
  width: 100%;
  height: auto;
  display: block;
  filter: grayscale(1);
}

.intro-title,
.intro-desc {
  text-shadow: 0 0 12px var(--paper-bg), 0 1px 2px var(--paper-bg);
}

.intro-prompt {
  font-family: var(--font-vintage);
  font-size: 14px;
  color: var(--accent); /* accent red — ties the call-to-action to the active story pill */
  margin: 0 0 12px;
  text-shadow: 0 0 12px var(--paper-bg), 0 1px 2px var(--paper-bg);
  line-height: 1.6;
  font-weight: 400;
}

/* Each clause stays whole, so the prompt breaks only at the space after
   "follow," and only when one line can't hold both phrases — no fixed
   breakpoint. Replaces the former hardcoded <br>. */
.intro-prompt span { white-space: nowrap; }

#intro-buttons {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  width: 100%;
  max-width: 560px;
  margin: 2px auto 0;
}

#intro-stories {
  display: contents;
}

.intro-pill {
  font-family: var(--font-vintage);
  font-size: 13px;
  font-weight: bold;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  background: #111;
  color: #fff;
  border: none;
  border-radius: 2px;
  padding: 12px 18px;
  cursor: pointer;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  transition: background 0.3s ease, color 0.3s ease, transform 0.2s ease, box-shadow 0.2s ease;
}

.intro-pill:hover {
  transform: translateY(-2px);
  box-shadow: 0 7px 16px rgba(0, 0, 0, 0.4);
}

.intro-pill.is-active {
  background: var(--accent);
  color: #fff;
}

.intro-copyright {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 10px;
  z-index: 10;
  margin: 0;
  text-align: center;
  font-family: var(--font-vintage);
  font-size: 11px;
  letter-spacing: 0.5px;
  color: var(--text-muted);
  text-shadow: 0 0 8px var(--paper-bg), 0 0 8px var(--paper-bg);
  pointer-events: none;
}

@media screen and (max-width: 800px) {
  .intro-title {
    font-size: 28px; /* match the mobile map-page title (#typewriter-title) */
  }

  /* Mobile readability: a soft frosted-paper panel behind the title + intro
     paragraph, a denser paper halo on that text, and the "Oaks Breaks Jail"
     topper lifted so it clears the title — all so the dark serif text stays
     legible over the grayscale collage. Tunables: panel alpha/blur below,
     and .intro-topper top. */
  .intro-lede {
    background: linear-gradient(0deg, rgba(245, 244, 236, 0.2), transparent);
    border-radius: 16px;
    padding: 16px 16px 18px;
    margin-bottom: 40px;
  }
  .intro-lede .intro-desc { margin-bottom: 0; }
  .intro-title,
  .intro-desc {
    text-shadow: 0 0 6px var(--paper-bg), 0 0 13px var(--paper-bg), 0 0 22px var(--paper-bg);
  }
  .intro-topper {
    top: 6vh;        /* lifted from 14vh so it clears the title */
    width: 150px;
  }

  .intro-desc {
    font-size: 15px;
  }

  .intro-prompt {
    font-size: 14.5px;
  }

  .intro-veil {
    background: radial-gradient(74% 46% at 50% 50%,
      rgba(243, 242, 234, 0.92) 0%,
      rgba(243, 242, 234, 0.72) 55%,
      rgba(243, 242, 234, 0.2) 85%,
      rgba(243, 242, 234, 0) 100%);
  }

  /* Narrow screens: push the clippings to the top and bottom edges so they
     clear the centered title, text and buttons. */
  #intro-collage img[data-intro-key="timeline"]          { left: 19%; top: 12%; --w: 24vmin; }
  #intro-collage img[data-intro-key="glass-water-tank"]  { left: 80%; top: 13%; --w: 30vmin; }
  #intro-collage img[data-intro-key="magical-co"]        { left: 18%; top: 89%; --w: 24vmin; }
  #intro-collage img[data-intro-key="escape-challenges"] { left: 50%; top: 85%; --w: 24vmin; }
  #intro-collage img[data-intro-key="riverboat"]         { left: 82%; top: 88%; --w: 32vmin; }
}

#mobile-event-btn {
  display: none;
  position: absolute;
  top: 110px;
  left: 20px;
  right: 20px;
  z-index: 25;
  background: var(--paper-bg);
  border: none;
  padding: 14px 18px;
  border-radius: 4px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
  cursor: pointer;
  text-align: left;
  animation: mobileBtnPulse 2.5s ease-in-out infinite;
  transform-origin: center;
}

#mobile-event-btn-title {
  font-family: var(--font-vintage);
  font-size: 18px;
  color: var(--text-dark);
  margin-bottom: 3px;
  display: block;
}

#mobile-event-btn-subtitle {
  margin-top: 8px;
  font-family: var(--font-vintage);
  font-size: 12px;
  color: var(--text-muted);
  display: block;
}

.mobile-nav-tab {
  display: none;
  position: fixed;
  top: 50%;
  transform: translateY(-50%);
  width: 30px;
  height: 80px; /* slightly taller; align-items/justify-content center keep the label centered */
  background: rgba(255, 255, 255, 1.0);
  border: none;
  cursor: pointer;
  z-index: 420;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
  transition: all 0.2s ease;
}

.mobile-nav-tab:active {
  transform: translateY(-50%) scale(0.95);
}

.mobile-nav-tab:disabled {
  opacity: 0;
  cursor: not-allowed;
}

.mobile-nav-tab svg {
  width: 26px;
  height: 26px;
  fill: var(--accent);
}

.mobile-nav-tab.prev {
  left: 0;
  border-radius: 0 6px 6px 0;
}

.mobile-nav-tab.next {
  right: 0;
  border-radius: 6px 0 0 6px;
}

/* Prev/next tabs: a vertically-stacked red label instead of the arrow — each
   letter upright, one above the next. Done in CSS (the arrow SVG stays in the
   markup, hidden) so it ships with the stylesheet, no HTML redeploy needed. */
.mobile-nav-tab.prev svg,
.mobile-nav-tab.next svg { display: none; }
.mobile-nav-tab.prev::after,
.mobile-nav-tab.next::after {
  writing-mode: vertical-rl;
  text-orientation: upright;
  font-family: var(--font-vintage);
  font-weight: bold;
  font-size: 15px;
  line-height: 1.05;
  letter-spacing: 1px;
  color: var(--accent);
}
.mobile-nav-tab.prev::after { content: "PREV"; }
.mobile-nav-tab.next::after { content: "NEXT"; }

.mobile-nav-tab.return {
  right: 50% !important;
  transform: translateX(50%) translateY(0) !important;
  top: 0 !important;
  border-radius: 0 0 6px 6px;
  width: max-content;
  height: 36px;
  font-family: var(--font-vintage);
  color: var(--accent);
  font-size: 16px;
  font-weight: bold;
  display: none;
  opacity: 0;
  transition: opacity 0.2s ease;
  padding: 8px 12px 4px 12px;
}

.mobile-nav-tab.return.visible {
  display: flex;
  opacity: 1;
}

#mobile-footer-links {
  display: none;
  position: fixed;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 25;
  font-family: var(--font-vintage);
  font-size: 16px;
  color: var(--text-muted);
  background: rgba(255, 255, 255, 0.92);
  padding: 6px 6px;
  border-radius: 4px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  width: max-content;
}

#mobile-footer-links a {
  color: var(--text-light);
  text-decoration: none;
}

#mobile-footer-links a:hover {
  text-decoration: underline;
}

#mobile-intro-hint {
  display: none;
  position: absolute;
  top: 180px;
  left: 190px;
  transform: translateX(-50%) rotate(-5deg);
  pointer-events: none;
  z-index: 30;
  opacity: 0;
  transition: opacity 1.2s ease 2s;
}

#mobile-intro-hint.visible {
  display: block;
  opacity: 1;
}

#mobile-intro-hint svg {
  width: auto;
  height: 130px;
}

#mobile-intro-hint .hint-text {
  position: relative;
  top: -5px;
  left: -40px;
  z-index: 30;
  font-family: var(--font-handwritten);
  font-size: 24px;
  font-weight: 400;
  color: #000;
  text-align: left;
  width: max-content;
  padding: 0;
  margin: 0;
}

#mobile-paper-modal {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 300;
  background: rgba(0, 0, 0, 0.85);
  padding: 20px;
  box-sizing: border-box;
  overflow-y: auto;
  overscroll-behavior: contain;
}

#mobile-paper-modal.active {
  display: flex;
  align-items: flex-start;
  justify-content: center;
}

.mobile-paper-wrapper {
  position: relative;
  width: 100%;
  max-width: 400px;
  margin-top: 20px;
}

.mobile-paper-stack {
  position: absolute;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: -1;
}

.mobile-stack-sheet {
  position: absolute;
  width: 100%;
  /* Track the real sheet's height so the peeking paper-stack edges run the full
     length when content is long (the wrapper height follows the sheet). */
  height: 100%;
  background: var(--paper-bg);
  border-radius: 2px;
  box-shadow: var(--shadow-stack);
}

.mobile-stack-sheet:nth-child(1) { transform: rotate(1.2deg) translate(3px, -4px); }
.mobile-stack-sheet:nth-child(2) { transform: rotate(1.6deg) translate(-3px, 0px); }
.mobile-stack-sheet:nth-child(3) { transform: rotate(-1.0deg) translate(4px, 4px); }

.mobile-paper-sheet {
  position: relative;
  background: var(--paper-bg);
  padding: 22px 18px;
  border-radius: 2px;
  box-shadow: var(--shadow-paper);
  /* Single scroller on mobile: the sheet grows to fit its content and the whole
     #mobile-paper-modal scrolls as one page — no nested inner scroll boxes that
     can catch the touch and scroll independently. (overflow must be visible on
     BOTH axes; a hidden axis would force the other back to auto = a scroller.) */
  overflow: visible;
  box-sizing: border-box;
  word-break: break-word;
}

.mobile-paper-sheet #current-event-title {
  font-size: 17px;
}

.mobile-paper-sheet #current-event-subtitle {
  font-size: 13px;
}

.mobile-paper-sheet #current-event-desc {
  font-size: 12px;
  line-height: 1.8;
  overflow-x: hidden;
}

#mobile-paper-content {
  display: flex;
  flex-direction: column;
  min-height: calc(100vh - 70px);
}

.mobile-paper-sheet .mobile-event-header {
  flex-shrink: 0;
  padding-bottom: 12px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  margin-bottom: 16px;
}

.mobile-paper-sheet .mobile-event-title {
  font-family: var(--font-vintage);
  font-size: 18px;
  color: var(--text-dark);
  margin-bottom: 6px;
}

.mobile-paper-sheet .mobile-event-subtitle {
  font-family: var(--font-vintage);
  font-size: 16px;
  color: var(--text-mid);
}

.mobile-paper-sheet .mobile-event-body {
  font-family: var(--font-vintage);
  font-size: 14px;
  line-height: 1.8;
  color: var(--text-mid);
  /* flex:1 keeps the sheet full-height for short entries; overflow:visible lets
     long entries grow so the whole modal scrolls (single scroller). */
  flex: 1;
  overflow: visible;
  padding-right: 8px;
  margin-right: -8px;
}

@media screen and (max-width: 800px) {
  #right-column,
  #ref-note-author-container,
  #description-container,
  #page-container .intro-copyright {
    display: none !important;
  }

  /* The close X sat low: its 46px glyph has a tall line box, so top:0 dropped
     it well below the smaller title. Pin it to a centered 44px box level with
     the title (and a proper tap target). */
  .modal-close {
    top: -6px;
    width: 44px;
    height: 44px;
    line-height: 1;
  }

  #mobile-event-btn,
  #mobile-footer-links,
  #title-container {
    display: block;
    text-align: center;
  }

  .mobile-nav-tab {
    display: flex;
  }

  #audio-controls {
    bottom: 6px;
    right: 6px;
    scale: 0.75;
  }

  /* Anchor the footer links to the left so the References / Notes /
     Author links don't overlap the bottom-right speaker icon
     (#audio-controls) on small screens. */
  #mobile-footer-links {
    left: 20px;
    right: auto;
    transform: none;
    text-align: left;
  }

  body:not(.mobile-started) .mobile-nav-tab,
  body:not(.mobile-started) #mobile-event-btn,
  body:not(.mobile-started) #mobile-footer-links,
  body:not(.mobile-started) #map-container,
  body:not(.mobile-started) #title-container,
  body:not(.mobile-started) #timeline-bar-container {
    display: none !important;
  }

  body.mobile-started #page-container {
    padding-top: 20px;
    padding-bottom: 20px;
  }

  body.mobile-started #title-container {
    display: block;
    position: absolute;
    top: 16px;
    left: 10px;
    right: 10px;
  }

  body.mobile-started #typewriter-title {
    font-size: 28px;
    white-space: normal;
    width: auto;
    animation: none;
  }

  body.mobile-started #map-container {
    display: block;
    position: absolute;
    left: -50%;
    transform: translate(-140px, 0px);
    top: 130px;
    width: 800px;
  }

  body.mobile-started #timeline-bar-container {
    display: block;
    position: fixed;
    bottom: 30px;
    left: 50%;
    transform: translateX(-50%);
    width: calc(100vw - 40px);
    top: auto;
  }

  body.mobile-started #timeline-zones {
    height: 28px;
  }

  body.mobile-started #timeline-zones,
  body.mobile-started #timeline-bar,
  body.mobile-started #timeline-labels {
    width: 100%;
  }

  body.mobile-started #timeline-bar {
    height: 24px;
  }

  body.mobile-started #scrubber-handle {
    width: 12px;
  }

  body.mobile-started #scrubber-handle::after {
    width: 24px;
    height: 30px;
  }

  body.mobile-started #birth-label {
    left: 9% !important;
    text-align: left;
  }

  body.mobile-started #death-label {
    left: 75% !important;
    text-align: center;
  }

  body.mobile-started #map-container,
  body.mobile-started #timeline-bar-container,
  body.mobile-started #title-container {
    opacity: 1 !important;
  }
}

@media screen and (max-width: 500px) {
  /* Reclaim horizontal room so the 28px title fits on its two intended
     lines: the map-page title fits because it spans nearly full width,
     while the intro title was boxed in by #intro + .intro-lede padding. */
  #intro { padding-left: 12px; padding-right: 12px; }
  .intro-lede { padding-left: 8px; padding-right: 8px; }
  .intro-title {
    font-size: 28px; /* keep matched to the mobile map-page title on small phones too */
    letter-spacing: 0;
  }

  #mobile-event-btn-title {
    font-size: 14px;
  }

  body.mobile-started #timeline-zones {
    margin-bottom: 22px;
  }

  body.mobile-started #age-indicator {
    bottom: -18px;
    font-size: 13px;
  }

  body.mobile-started .zone {
    top: -2px;
  }

  body.mobile-started .zone-label,
  body.mobile-started .timeline-label {
    font-size: 14px;
  }

  body.mobile-started .zone-years,
  body.mobile-started .timeline-label small {
    font-size: 12px;
  }

  /* Single scroller on mobile: the .modal overlay is the only scroll container;
     its content grows to fit and the whole modal scrolls as one page. Keeping
     overflow on the inner .modal-content/.modal-body too let the touch land on
     an inner box and scroll it independently. */
  .modal {
    align-items: flex-start;
    justify-content: flex-start;
    padding: 20px 20px 0 20px;
    margin: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
  }

  .modal-content {
    width: 100%;
    max-width: 380px;
    max-height: none;
    margin: 0;
    overflow: visible;
    border-radius: 2px;
    padding: 20px 20px 0 20px !important;
  }

  .modal-header h2 {
    font-size: 20px;
  }

  .modal-body {
    overflow: visible;
    padding-right: 8px;
    margin-right: -8px;
  }

  .modal-text {
    font-size: 13px;
    line-height: 1.8;
  }
}

#orientation-warning {
  display: none;
  position: fixed;
  inset: 0;
  background: var(--paper-bg);
  z-index: 1000;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  font-family: var(--font-vintage);
  font-size: 20px;
  color: var(--text-dark);
  text-align: center;
  padding: 40px;
  box-sizing: border-box;
}

#orientation-warning svg {
  width: 80px;
  height: 80px;
  margin-bottom: 30px;
  fill: var(--text-mid);
}

@media (orientation: landscape) and (max-width: 1024px) {
  body.mobile #page-container,
  body.mobile #map-container,
  body.mobile #right-column,
  body.mobile #paper-wrapper,
  body.mobile #title-container,
  body.mobile #description-container,
  body.mobile #page-container .intro-copyright {
    display: none !important;
  }

  body.mobile #orientation-warning {
    display: flex !important;
  }
}
/* ---- Focused Explorations (story mode) ---- */
/* Story ribbon — red swallowtail banner above the paper while in story mode.
   Body click opens the Stories modal; the X exits the story. */
.story-ribbon {
  display: inline-flex;
  align-items: center;
  gap: 30px;
  max-width: 100%;
  box-sizing: border-box;
  background: var(--accent);
  color: #fff;
  padding: 9px 30px;
  cursor: pointer;
  -webkit-clip-path: polygon(0 0, 100% 0, calc(100% - 16px) 50%, 100% 100%, 0 100%, 16px 50%);
  clip-path: polygon(0 0, 100% 0, calc(100% - 16px) 50%, 100% 100%, 0 100%, 16px 50%);
}
.story-ribbon-label {
  flex: 0 1 auto;
  min-width: 0;
  color: #fff;
  font-family: var(--font-vintage);
  font-size: 15px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.story-ribbon-count {
  flex: 0 0 auto;
  color: #fff;
  font-family: var(--font-vintage);
  font-size: 15px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  white-space: nowrap;
}
/* Optical centering: Special Elite's metrics with line-height:1 make the
   uppercase text ride high in the ribbon. A small translate nudges it down
   without changing the ribbon's height (unlike padding-top). Tune 1.5–2px. */
.story-ribbon-label,
.story-ribbon-count {
  transform: translateY(2px);
}
.story-ribbon-x {
  flex: 0 0 auto;
  position: relative; /* anchor the enlarged hit area below */
  background: none;
  border: none;
  padding: 0;
  color: #fff;
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
}
/* The X glyph alone is a tiny tap target; this invisible overlay grows the
   pressable area to (roughly) the ribbon's full height without changing the
   ribbon's layout or the X's appearance. */
.story-ribbon-x::before {
  content: '';
  position: absolute;
  inset: -12px;
}
.story-ribbon-x:hover { color: #ffe3e3; }

/* "View other featured stories" — the same pasted-label sticker as the
   thread links, sized up and set in caps as the primary call to action. */
.story-more {
  position: relative;
  font-family: var(--font-vintage);
  font-size: 14px;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: #fff;
  background: var(--accent);
  border: 1px solid #6e0000;
  border-radius: 2px;
  padding: 12px 18px;
  box-shadow: 2px 3px 7px rgba(0, 0, 0, 0.22);
  cursor: pointer;
  align-self: flex-start;
  transform: rotate(0.7deg);
  transform-origin: left center;
  transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.3s ease;
}
.story-more:hover {
  background: #6e0000;
  transform: rotate(0.7deg) scale(1.06);
  box-shadow: 3px 5px 12px rgba(0, 0, 0, 0.28);
}

/* Desktop: float the ribbon just above the paper, centered, without disturbing
   the paper / stack / clippings / nav-arrow layout below it. */
#story-banner-desktop {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  display: flex;
  justify-content: center;
  padding-bottom: 8px;
  pointer-events: none;
  z-index: 30;
  /* Pivot near the paper's top edge so the JS-applied rotate/translate keeps
     the ribbon visually attached to the paper (matched in applyRandomShift). */
  transform-origin: center bottom;
  transition: transform var(--transition-std);
}
#story-banner-desktop .story-ribbon { pointer-events: auto; }
#story-banner-desktop:empty { display: none; }

/* Mobile: the ribbon is a flow element above the paper area, so the paper and
   its stack shift down to make room. */
.mobile-paper-area { position: relative; }
#story-banner-mobile {
  display: flex;
  justify-content: center;
  margin-bottom: 14px;
}
#story-banner-mobile:empty { display: none; margin: 0; }
#story-banner-mobile .story-ribbon { padding: 8px 24px; }
#story-banner-mobile .story-ribbon-label { font-size: 13px; letter-spacing: 1px; }
#story-banner-mobile .story-ribbon-count { font-size: 13px; letter-spacing: 1px; }
#story-banner-mobile .story-ribbon { gap: 22px; }
/* Mobile banners drop the "STORY: " prefix — just the title (+ count). */
#story-banner-mobile .story-ribbon-prefix,
#story-banner-map-mobile .story-ribbon-prefix { display: none; }

/* Mobile map page: while a story is active (body.story-active, set in
   renderStoryBanner) its banner overlays the era-label strip above the timeline
   bar. Only the .zone labels are hidden — the strip and its #age-indicator stay,
   so the current age still tracks the scrubber. Hidden by default and on
   desktop, which has its own banner. */
#story-banner-map-mobile { display: none; }
body.mobile-started.story-active #timeline-zones .zone { display: none; }
body.mobile-started.story-active #story-banner-map-mobile:not(:empty) {
  display: flex;
  justify-content: center;
  align-items: center;
  position: absolute;
  left: 0;
  right: 0;
  top: -10px; /* lift it clear of the age text on the scrubber below */
  height: 28px; /* overlay the zones strip; its height (and the age below) stay put */
  z-index: 2;
}
/* #timeline-bar-container is pointer-events:none; re-enable taps on the ribbon
   (and its X) so the banner opens the stories list / exits, like on desktop. */
#story-banner-map-mobile .story-ribbon { padding: 8px 24px; gap: 22px; pointer-events: auto; }
#story-banner-map-mobile .story-ribbon-label,
#story-banner-map-mobile .story-ribbon-count { font-size: 13px; letter-spacing: 1px; }

.story-intro,
.story-outro {
  font-family: var(--font-vintage);
  font-style: italic;
  font-size: 14px;
  line-height: 1.8;
  color: var(--text-mid);
  margin: 0 0 16px;
  padding: 10px 12px;
  border-left: 3px solid var(--accent);
  background: rgba(0, 0, 0, 0.02);
}

/* The intro and outro keep their italic voice but lose the red left border. */
.story-intro,
.story-outro {
  border-left: none;
  background: none;
  padding: 0;
}

.story-outro {
  margin-top: 20px;
  margin-bottom: 0;
}

.story-threads {
  margin-top: 18px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 13px;
}

/* Story-thread links — oxblood "pasted label" stickers: red fill, white
   vintage type, a printed edge and a soft drop shadow, tacked on at a
   slight angle. They grow a touch on hover, like a sticker pressed flat. */
.story-thread {
  position: relative;
  font-family: var(--font-vintage);
  font-size: 13px;
  line-height: 1.3;
  text-align: left;
  color: #fff;
  background: var(--accent);
  border: 1px solid #6e0000;
  border-radius: 2px;
  padding: 10px 14px;
  box-shadow: 2px 3px 7px rgba(0, 0, 0, 0.22);
  cursor: pointer;
  transform: rotate(-1.1deg);
  transform-origin: left center;
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}

.story-thread:nth-child(even) {
  transform: rotate(1deg);
}

.story-thread em {
  font-style: italic;
  color: #f7d9d9;
}

.story-thread:hover {
  transform: rotate(-1.1deg) scale(1.06);
  box-shadow: 3px 5px 12px rgba(0, 0, 0, 0.28);
  z-index: 2;
}

.story-thread:nth-child(even):hover {
  transform: rotate(1deg) scale(1.06);
}

.story-thread:focus-visible {
  outline: 2px solid var(--text-dark);
  outline-offset: 2px;
}

.story-cta {
  margin-top: 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.story-cta-heading {
  font-family: var(--font-vintage);
  font-size: 15px;
  font-weight: bold;
  color: var(--text-dark);
}

.story-card {
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-align: left;
  font-family: var(--font-vintage);
  background: rgba(0, 0, 0, 0.02);
  border: 1px solid var(--text-muted);
  padding: 10px 12px;
  cursor: pointer;
}

.story-card:hover {
  border-color: var(--accent);
}

.story-card.active {
  border-color: var(--accent);
  background: rgba(0, 0, 0, 0.05);
}

.story-card-title {
  font-size: 15px;
  font-weight: bold;
  color: var(--text-dark);
}

.story-card-subtitle {
  font-size: 12px;
  color: var(--text-muted);
}

/* ===== Story modal: full-screen black takeover with a scattered clipping scrapbook ===== */
#stories-modal,
#references-modal,
#author-modal,
#list-modal {
  background: #000;
}

#stories-modal .modal-content,
#references-modal .modal-content,
#author-modal .modal-content,
#list-modal .modal-content {
  background: #000;
  color: #fff;
  width: 100%;
  max-width: 100%;
  height: 100%;
  max-height: 100%;
  border-radius: 0;
  box-shadow: none;
  filter: none;
  padding: 28px 32px 44px;
}

#stories-modal .modal-header h2,
#references-modal .modal-header h2,
#author-modal .modal-header h2,
#list-modal .modal-header h2 {
  color: #fff;
  text-align: center;
  padding: 0 44px;
}

#stories-modal .modal-close,
#references-modal .modal-close,
#author-modal .modal-close,
#list-modal .modal-close {
  color: #fff;
}

#stories-modal .modal-close:hover,
#references-modal .modal-close:hover,
#author-modal .modal-close:hover,
#list-modal .modal-close:hover {
  color: rgba(255, 255, 255, 0.7);
}

/* Dark-modal reading content: a centered light column with red links. */
#references-modal .modal-text,
#author-modal #author-content,
#list-modal .modal-text {
  max-width: 640px;
  margin: 0 auto;
  color: #e8e6dd;
}

#references-modal a,
#author-modal a {
  color: #e07a70;
}

#references-modal a:hover,
#author-modal a:hover {
  color: #ffb3aa;
}

#stories-modal .stories-modal-blurb {
  color: rgba(255, 255, 255, 0.82);
  text-align: center;
}

#stories-modal #stories-modal-content {
  align-items: center;
}

.story-scrapbook {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  gap: 30px;
  max-width: 1000px;
  margin: 0 auto;
  padding: 16px 0 4px;
}

.story-scrapbook .story-card {
  position: relative;
  width: 220px;
  gap: 0;
  background: #fff;
  color: #111;
  border: none;
  padding: 8px 8px 12px;
  border-radius: 2px;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
  cursor: pointer;
  transform: rotate(var(--rot, 0deg));
  transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.25s ease, color 0.25s ease;
}

.story-scrapbook .story-card:nth-child(4n+1) { --rot: -2.4deg; }
.story-scrapbook .story-card:nth-child(4n+2) { --rot: 2.2deg; }
.story-scrapbook .story-card:nth-child(4n+3) { --rot: 1.6deg; }
.story-scrapbook .story-card:nth-child(4n)   { --rot: -1.8deg; }

.story-scrapbook .story-card:hover {
  z-index: 5;
  transform: rotate(var(--rot, 0deg)) translateY(-4px) scale(1.03);
  box-shadow: 0 16px 32px rgba(0, 0, 0, 0.6);
  background: var(--accent);
  color: #fff;
}

.story-scrapbook .story-card.active,
.story-scrapbook .story-card.active:hover {
  z-index: 4;
  background: #fff;
  color: #111;
}

.story-card-frame {
  display: block;
  position: relative;
  background: #fdfcf2;
  padding: 5px;
}

.story-card-badge {
  position: absolute;
  left: 50%;
  bottom: 8px;
  transform: translateX(-50%);
  background: var(--accent);
  color: #fff;
  font-family: var(--font-vintage);
  font-size: 10px;
  letter-spacing: 1px;
  text-transform: uppercase;
  white-space: nowrap;
  padding: 3px 9px;
  border-radius: 2px;
  box-shadow: 0 2px 7px rgba(0, 0, 0, 0.45);
}

.story-scrapbook .story-card-img {
  display: block;
  width: 100%;
  height: auto;
  filter: grayscale(1);
}

.story-scrapbook .story-card-title {
  color: inherit;
  font-size: 14px;
  letter-spacing: 0.5px;
  margin-top: 9px;
}

.story-scrapbook .story-card-subtitle {
  color: inherit;
  opacity: 0.7;
  font-size: 11px;
  margin-top: 2px;
}

.stories-modal-blurb {
  margin-bottom: 14px;
}

#stories-modal-content {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* Slightly larger type on larger screens (>800px in both dimensions);
   mobile keeps the base sizes. Generated, font-size only -- no layout impact. */
@media (min-width: 801px) {
  /* Full size at >=1600px, then scale down smoothly as the viewport narrows
     (until the narrow-desktop rule below stacks it onto two lines). */
  #typewriter-title { font-size: clamp(28px, 2.47vw, 39.5px); }
  #description-text { font-size: 17.5px; }
  .footer-text { font-size: 20px; }
  .clipping .clip-caption { font-size: 11px; }
  #current-event-title { font-size: 22px; }
  #current-event-subtitle { font-size: 17.5px; }
  #current-event-desc { font-size: 15.5px; }
  #loading-message, #no-events-message { font-size: 20px; }
  .nav-tab { font-size: 48.5px; }
  .zone-years { font-size: 15.5px; }
  .timeline-label { font-size: 15.5px; }
  .timeline-label small { font-size: 12px; }
  #age-indicator { font-size: 17.5px; }
  .modal-header h2 { font-size: 26.5px; }
  .modal-close { font-size: 52px; }
  .modal-text { font-size: 15.5px; }
  .img-figure figcaption { font-size: 14.5px; }
  #image-modal-caption { font-size: 17.5px; }
  #image-modal-caption .img-source { font-size: 14.5px; }
  #image-modal-close { font-size: 50px; }
  .image-modal-nav-btn { font-size: 31px; }
  #list-modal-content .list-modal-date { font-size: 13px; }
  .intro-title { font-size: 39.5px; } /* match #typewriter-title on larger screens */
  .intro-desc { font-size: 16px; }
  .intro-prompt { font-size: 15.5px; }
  .intro-pill { font-size: 14.5px; }
  .intro-copyright { font-size: 12px; }
  #mobile-event-btn-title { font-size: 20px; }
  #mobile-event-btn-subtitle { font-size: 13px; }
  .mobile-nav-tab.return { font-size: 17.5px; }
  #mobile-footer-links { font-size: 17.5px; }
  #mobile-intro-hint .hint-text { font-size: 26.5px; }
  .mobile-paper-sheet #current-event-title { font-size: 18.5px; }
  .mobile-paper-sheet #current-event-subtitle { font-size: 14.5px; }
  .mobile-paper-sheet #current-event-desc { font-size: 13px; }
  .mobile-paper-sheet .mobile-event-title { font-size: 20px; }
  .mobile-paper-sheet .mobile-event-subtitle { font-size: 17.5px; }
  .mobile-paper-sheet .mobile-event-body { font-size: 15.5px; }
  #orientation-warning { font-size: 22px; }
  .story-intro, .story-outro { font-size: 15.5px; }
  .story-thread { font-size: 14.5px; }
  .story-cta-heading { font-size: 16.5px; }
  .story-card-title { font-size: 16.5px; }
  .story-card-subtitle { font-size: 13px; }
  .story-card-badge { font-size: 11px; }
  .story-scrapbook .story-card-title { font-size: 15.5px; }
  .story-scrapbook .story-card-subtitle { font-size: 12px; }
  #notes-content,
  #author-content { font-size: 15.5px !important; }
  #author-content figcaption { font-size: 13px !important; }
  #references-modal h3 { font-size: 20px !important; }
  #author-content p:last-child { font-size: 17.5px !important; }
}

/* Narrow desktop: once the single-line title has scaled down, stack it onto
   two lines after "and" (the <br class="title-break"> set in
   updateTitleForDevice) and let the size recover. The width-based typewriter
   reveal can't span two lines, so swap it for a simple fade. */
@media (min-width: 801px) and (max-width: 1200px) {
  /* Match the description block's horizontal box and center the title over it. */
  #title-container {
    left: 60px;
    width: 35vw;
    min-width: 300px;
    max-width: 420px;
    text-align: center;
  }
  #typewriter-title {
    /* The <br> makes exactly two lines; nowrap stops either from wrapping to a
       third. The line is centered (and may extend past the box on the
       narrowest widths) so it stays centered over the description text. */
    white-space: nowrap;
    overflow: visible;
    text-align: center;
    font-size: 34px;
  }
  #typewriter-title .title-break { display: inline; }
  body.intro-done #typewriter-title {
    width: 100%;
    animation: fadeIn 0.8s ease forwards;
  }
}

/* ---- Loading screen — the Oaks signature inks in left-to-right while fonts +
   key intro images load, then crossfades to the intro (ui/loader.js drives the
   reveal). Stays up until the content has actually loaded (no dismiss timer);
   after 2s of waiting loader.js adds .ls-waiting, fading in the "Loading…"
   line under the signature. Removed entirely in the editor. ---- */
#loading-screen {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: var(--paper-bg);
  transition: opacity 0.8s ease;
}
.ls-sig {
  width: min(70vw, 440px);
  height: auto;
  /* Start fully clipped so the signature is hidden until loader.js reveals it
     left-to-right (prevents a flash of the whole signature before JS runs). */
  -webkit-mask-image: linear-gradient(to right, #000 0, rgba(0, 0, 0, 0) 0);
          mask-image: linear-gradient(to right, #000 0, rgba(0, 0, 0, 0) 0);
}
/* "Loading…" under the signature. Always in flow (so nothing shifts when it
   appears) but invisible until #loading-screen gains .ls-waiting at the 2s
   mark; hidden again the moment the reveal finishes. Ink color matches the
   signature. */
#ls-loading {
  margin-top: 20px;
  font-family: 'Special Elite', Georgia, serif;
  font-size: 15px;
  letter-spacing: 0.08em;
  color: #8B0000;
  opacity: 0;
  transition: opacity 0.6s ease;
}
#loading-screen.ls-waiting #ls-loading {
  opacity: 1;
}
body.ls-done #loading-screen {
  opacity: 0;
  pointer-events: none;
}
