/* ===== SpeedyE — Redesign: Cinematic Scrollytelling ===== */

:root {
  --bg: #0a0a0a;
  --surface: #141414;
  --surface-2: #1a1a1a;
  --border: #262626;
  --fg: #ededed;
  --muted: #8a8a8a;
  --accent: #d4a574;
  --accent-soft: rgba(212, 165, 116, 0.16);
  --serif: "DM Serif Display", "Iowan Old Style", serif;
  --sans: "DM Sans", -apple-system, BlinkMacSystemFont, sans-serif;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img, video { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
h1, h2, h3 { font-family: var(--serif); font-weight: 400; margin: 0; }
p { margin: 0; line-height: 1.7; color: #cfcfcf; }

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
  html { scroll-behavior: auto; }
}

/* ---- Top progress bar ---- */
.progress-bar {
  position: fixed; top: 0; left: 0; height: 3px; width: 0%;
  background: var(--accent); z-index: 200; transition: width 0.1s linear;
}

/* ---- Fixed chrome ---- */
.pill-btn {
  position: fixed; z-index: 150; display: flex; align-items: center; gap: 8px;
  padding: 10px 18px; border-radius: 999px;
  /* Solid background, no backdrop-filter: this element stays fixed on screen while
     full-bleed videos scroll underneath it, and blurring a live video every frame
     is what caused the strobing. */
  background: rgba(10, 10, 10, 0.75); border: 1px solid rgba(255,255,255,0.1);
  font-size: 0.85rem; color: rgba(255,255,255,0.85); transition: all 0.3s var(--ease);
}
.pill-btn:hover { background: rgba(10,10,10,0.92); color: #fff; border-color: rgba(255,255,255,0.2); }
.back-btn { top: 20px; left: 20px; }
.social-btn { top: 20px; right: 20px; width: 40px; height: 40px; padding: 0; justify-content: center; }

/* ---- Story page: fixed ambient map background ----
   Sits behind the whole page (photos are opaque and simply cover it; text
   sections have no background of their own, so it shows through around
   them). Negative z-index puts it below all normal in-flow content
   regardless of DOM order or other elements' own positioning. */
.story-map-bg { position: fixed; inset: 0; z-index: -1; background: var(--bg); }
/* Esri's World_Dark_Gray_Base (see generate.py) reads noticeably lighter
   than CartoDB's old dark_all tiles — darken and punch up contrast to get
   back to that near-black look instead of the raw grey. */
#story-map {
  position: absolute; inset: 0; filter: saturate(0.85) brightness(0.55) contrast(1.2);
  /* Stays invisible over .story-map-bg's near-black backdrop until its
     tile layer fires 'load' (see map_script in generate.py) — a black
     screen that fades into the map instead of a flash of half-loaded
     tiles. */
  opacity: 0; transition: opacity 0.6s var(--ease);
}
#story-map.tiles-loaded { opacity: 1; }
.story-map-overlay {
  position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse at center, rgba(10,10,10,0.35) 0%, rgba(10,10,10,0.72) 68%, rgba(10,10,10,0.85) 100%);
}
/* Non-interactive: this map is driven entirely by scroll position, not touch/wheel/drag */
/* Leaflet's default tile-pane background is light gray, which flashes
   visibly (reads as "white") in the gap before a newly-scrolled-to tile has
   painted in — especially noticeable on mobile during the flyTo zoom
   animation, which requests many tiles across zoom levels in quick
   succession. Dark fallback makes that gap invisible against the theme. */
.leaflet-container { background: var(--bg); }
.story-map-bg .leaflet-container { cursor: default; }
.story-map-bg .leaflet-control-zoom,
.story-map-bg .leaflet-control-attribution { display: none; }
.story-map-bg .pulse-marker.visited .core,
.explore-map-sticky .pulse-marker.visited .core { background: rgba(212, 165, 116, 0.5); }
.story-map-bg .pulse-marker.visited .ring,
.explore-map-sticky .pulse-marker.visited .ring { display: none; }
.story-map-path { stroke: var(--accent); stroke-opacity: 0.55; }
/* A static schematic image that can fade in on top of the live map (see
   the "schematic" story option in generate.py / onChapterChange's
   schematicOn) — a one-way switch for the back half of a story where an
   architectural drawing reads better than the map, e.g. a stadium's floor
   plan once the story moves inside the building. Sits between the map and
   its dimming overlay, so the same overlay gradient dims it too. */
.story-schematic-bg {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain;
  background: var(--bg); opacity: 0; filter: brightness(0.92);
  transition: opacity 1.8s var(--ease);
}
.story-schematic-bg.active { opacity: 1; }

/* ---- Chapter side nav ---- */
.chapter-nav {
  position: fixed; right: 28px; top: 50%; transform: translateY(-50%);
  /* Negative, not the usual positive UI z-index: sits above the ambient
     map background (z-index -1, but declared earlier in the DOM so this
     paints over it) yet below any real photo — the hero, a media-break,
     anything full-bleed — rather than floating on top of one wherever a
     dot happens to land on it. */
  z-index: -1; display: flex; flex-direction: column; gap: 14px;
}
@media (max-width: 900px) { .chapter-nav { display: none; } }
.chapter-dot {
  width: 8px; height: 8px; border-radius: 50%; background: rgba(255,255,255,0.2);
  border: none; cursor: pointer; position: relative; transition: all 0.3s var(--ease);
}
.chapter-dot::after {
  content: attr(data-label); position: absolute; right: 20px; top: 50%; transform: translateY(-50%);
  white-space: nowrap; font-size: 0.75rem; color: var(--muted); opacity: 0; transition: opacity 0.2s;
  background: rgba(10,10,10,0.85); padding: 4px 10px; border-radius: 6px; pointer-events: none;
}
.chapter-dot:hover::after { opacity: 1; }
.chapter-dot.active { background: var(--accent); transform: scale(1.4); }

/* ---- Hero ---- */
.hero {
  position: relative; height: 100vh; width: 100%; overflow: hidden;
  display: flex; align-items: flex-end;
}
.hero-img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  animation: kenburns 22s ease-out forwards;
}
@keyframes kenburns { from { transform: scale(1.0); } to { transform: scale(1.14) translate(-1%, -1%); } }
/* Some architectural hero frames depend on strict symmetry. They can still
   breathe with the slow zoom, but never drift away from their chosen focal
   point as the standard Ken Burns treatment does. */
.hero-img.hero-center-focus { animation-name: kenburns-centered; }
@keyframes kenburns-centered { from { transform: scale(1); } to { transform: scale(1.14); } }
.hero-gradient {
  position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(10,10,10,1) 0%, rgba(10,10,10,0.35) 45%, rgba(10,10,10,0.05) 70%);
}
.hero-content { position: relative; z-index: 5; padding: 6vh 6vw 8vh; max-width: 900px; }
.hero-pill {
  display: inline-block; font-size: 0.75rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--accent); border: 1px solid var(--accent-soft); background: var(--accent-soft);
  padding: 5px 14px; border-radius: 999px; margin-bottom: 20px; opacity: 0;
  animation: rise 0.8s var(--ease) forwards; animation-delay: 0.2s;
}
.hero-title { font-size: clamp(2.4rem, 6vw, 5.2rem); line-height: 1.05; margin-bottom: 22px; }
.hero-title .word { display: inline-block; overflow: hidden; }
.hero-title .word span {
  display: inline-block; opacity: 0; transform: translateY(110%);
  animation: rise 0.9s var(--ease) forwards;
}
.hero-meta {
  display: flex; flex-wrap: wrap; gap: 14px; align-items: center;
  font-size: 0.85rem; color: rgba(255,255,255,0.55); opacity: 0;
  animation: rise 0.8s var(--ease) forwards; animation-delay: 1.1s;
}
.hero-meta .dot { opacity: 0.4; }
@keyframes rise { to { opacity: 1; transform: translateY(0); } }
.scroll-cue {
  position: absolute; bottom: 28px; left: 50%; transform: translateX(-50%);
  z-index: 5; opacity: 0.5; animation: bob 2s ease-in-out infinite;
}
@keyframes bob { 0%,100% { transform: translate(-50%, 0); } 50% { transform: translate(-50%, 10px); } }

/* ---- Chapters ---- */
.chapter { padding: 10vh 0; position: relative; }
.chapter-inner { max-width: 720px; margin: 0 auto; padding: 0 6vw; }
.chapter-label { display: flex; align-items: baseline; gap: 18px; margin-bottom: 34px; }
.chapter-num { font-family: var(--serif); font-size: 1rem; color: var(--accent); opacity: 0.7; }
.chapter-heading { font-size: clamp(1.8rem, 3.4vw, 2.6rem); }
.chapter-text p { margin-bottom: 20px; font-size: 1.05rem; }
.chapter-text p:last-child { margin-bottom: 0; }
.chapter-text a { color: var(--accent); text-decoration: underline; text-decoration-color: rgba(212, 165, 116, 0.4); text-underline-offset: 2px; transition: text-decoration-color 0.2s var(--ease); }
.chapter-text a:hover { text-decoration-color: var(--accent); }
/* A quoted line (e.g. a historical quote dropped into a post) is a real
   break in the reading flow, not just an indented paragraph — an oversized
   accent-colored quote mark, big serif type, no left-aligned run-on with
   body text. Its attribution (if any) sits well below, small and quiet. */
.chapter-text blockquote {
  margin: 56px 0; padding: 0; border: none; font-family: var(--serif); color: #fff;
  /* Wider than the normal text column (not just full-column italics) — caps
     out at 900px so it doesn't overrun on a wide viewport. */
  width: calc(100% + 140px); max-width: 900px; margin-left: -70px; margin-right: -70px;
}
.chapter-text blockquote::before {
  content: "“"; display: block; font-size: 4.5rem; line-height: 1; color: var(--accent);
  opacity: 0.85; margin-bottom: -0.2em;
}
.chapter-text blockquote p { font-size: 1.7rem; line-height: 1.35; font-style: italic; margin-bottom: 16px; }
.chapter-text blockquote cite {
  display: block; font-family: var(--sans); font-style: normal; font-size: 0.78rem;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted);
}
/* The 70px-a-side breakout above assumes room to spare — on a narrow
   viewport it would overrun the page, so just match the text column there. */
@media (max-width: 700px) {
  .chapter-text blockquote { width: 100%; max-width: none; margin-left: 0; margin-right: 0; }
}

.reveal { opacity: 0; transform: translateY(28px); transition: opacity 0.9s var(--ease), transform 0.9s var(--ease); }
.reveal.in-view { opacity: 1; transform: translateY(0); }

/* ---- Media blocks ---- */
.media-break {
  margin: 6vh 0; width: 100vw; height: 82vh; position: relative; left: 50%; margin-left: -50vw;
  overflow: hidden;
}
.media-break > img, .media-break > video { width: 100%; height: 82vh; object-fit: cover; }
.media-break.section-hero-landscape { height: min(54vh, 620px); }
.media-break.section-hero-landscape > img {
  width: 100%; height: min(54vh, 620px); object-fit: cover;
}
.media-break.reveal-scale { opacity: 0; transform: scale(1.06); transition: opacity 1.1s var(--ease), transform 1.4s var(--ease); }
.media-break.reveal-scale.in-view { opacity: 1; transform: scale(1); }
.media-caption {
  position: absolute; bottom: 0; left: 0; right: 0; padding: 22px 6vw;
  font-size: 0.85rem; color: rgba(255,255,255,0.7);
  background: linear-gradient(to top, rgba(0,0,0,0.55), transparent);
}

.media-duo, .media-trio, .media-quad { display: grid; gap: 3px; margin: 6vh 0; }
.media-duo { grid-template-columns: 1fr 1fr; }
.media-trio { grid-template-columns: 1.4fr 1fr; grid-template-rows: 1fr 1fr; }
/* first-child spans both rows, so its own height needs to fill that full
   spanned track (100%) — the generic 46vh below is a per-row height, and
   without this override the tall column would only fill the top half,
   leaving a gap under it. */
.media-trio figure:first-child { grid-row: 1 / 3; height: 100%; }
/* Variant for a trio with one landscape photo among two portraits (the
   default layout above assumes all-landscape source media) — the landscape
   shot spans the full row instead of being squeezed into a tall column. */
.media-trio.media-trio-landscape { grid-template-columns: 1fr 1fr; grid-template-rows: auto auto; }
.media-trio.media-trio-landscape figure:first-child { grid-row: auto; }
.media-trio .media-span-full { grid-column: 1 / -1; }
/* Variant for a trio as one even row of three (see trio_row_photos in
   generate.py) instead of the plain tall-left + stacked-right trio —
   same treatment as media-quad-row below, one column short. */
.media-trio.media-trio-row { grid-template-columns: repeat(3, 1fr); grid-template-rows: 1fr; }
.media-trio.media-trio-row figure:first-child { grid-row: auto; height: 52vh; }
.media-trio.media-trio-row figure { height: 52vh; }
/* Three equal portrait frames in one row. Matching the source photos' 2:3
   orientation keeps their compositions intact rather than forcing them into
   shallow landscape cells or the asymmetric stacked trio. */
.media-trio.media-trio-portrait-row {
  grid-template-columns: repeat(3, 1fr); grid-template-rows: 1fr;
}
.media-trio.media-trio-portrait-row figure,
.media-trio.media-trio-portrait-row figure:first-child {
  grid-row: auto; height: auto; aspect-ratio: 2 / 3;
}
.media-quad { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }
/* Variant for a quad with two portrait photos among two landscape ones (see
   two_tall_quad_photos in generate.py) — the two portrait shots each get
   their own full-height column instead of being squeezed into the plain
   2x2 grid, with the landscape pair stacked in a third column. */
.media-quad.media-quad-2tall { grid-template-columns: 1fr 1fr 1.4fr; grid-template-rows: 1fr 1fr; }
.media-quad.media-quad-2tall figure.media-quad-tall { grid-row: 1 / 3; height: 100%; }
/* Variant for a quad as one even row of four instead of the plain 2x2. */
.media-quad.media-quad-row { grid-template-columns: repeat(4, 1fr); grid-template-rows: 1fr; }
/* Four portrait frames in one upright row. Their 2:3 cells preserve the
   native composition instead of cropping them into the default quad's
   shallow landscape cells. */
.media-quad.media-quad-portrait-row {
  grid-template-columns: repeat(4, 1fr); grid-template-rows: 1fr;
}
/* A whole carousel as one unified grid of even squares (see
   square_grid_photos in generate.py) instead of being chunked into
   several separate quad/trio/duo grids stacked vertically. */
.media-square-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 3px; margin: 6vh 0;
}
.media-square-grid figure {
  margin: 0; position: relative; overflow: hidden; aspect-ratio: 1 / 1;
  opacity: 0; transform: translateY(30px); transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
}
.media-square-grid figure.in-view { opacity: 1; transform: translateY(0); }
.media-square-grid img, .media-square-grid video { width: 100%; height: 100%; object-fit: cover; }

/* Five-photo portrait-led spread: three upright anchors with two cropped
   details stacked between them. This keeps the source orientation readable
   while giving an all-portrait carousel a less repetitive editorial rhythm. */
.media-editorial-five {
  display: grid;
  grid-template-columns: 1.08fr 0.86fr 1.08fr 1.08fr;
  grid-template-rows: repeat(2, minmax(0, clamp(150px, 18vw, 290px)));
  gap: 3px; margin: 6vh 0;
}
.media-editorial-five figure {
  margin: 0; position: relative; overflow: hidden;
  opacity: 0; transform: translateY(30px);
  transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
}
.media-editorial-five figure.in-view { opacity: 1; transform: translateY(0); }
.media-editorial-five img, .media-editorial-five video {
  width: 100%; height: 100%; object-fit: cover;
}
.media-editorial-five figure:nth-child(1) { grid-column: 1; grid-row: 1 / 3; }
.media-editorial-five figure:nth-child(2) { grid-column: 2; grid-row: 1; }
.media-editorial-five figure:nth-child(3) { grid-column: 2; grid-row: 2; }
.media-editorial-five figure:nth-child(4) { grid-column: 3; grid-row: 1 / 3; }
.media-editorial-five figure:nth-child(5) { grid-column: 4; grid-row: 1 / 3; }

/* Changing the Guard: shift the second portrait one full panel left, then
   stack the two landscape frames in the newly opened third column. */
.media-editorial-five.media-editorial-five-stack-right figure:nth-child(1) { grid-column: 1; grid-row: 1 / 3; }
.media-editorial-five.media-editorial-five-stack-right figure:nth-child(2) { grid-column: 2; grid-row: 1 / 3; }
.media-editorial-five.media-editorial-five-stack-right figure:nth-child(3) { grid-column: 3; grid-row: 1; }
.media-editorial-five.media-editorial-five-stack-right figure:nth-child(4) { grid-column: 3; grid-row: 2; }
.media-editorial-five.media-editorial-five-stack-right figure:nth-child(5) { grid-column: 4; grid-row: 1 / 3; }

/* Six-photo mixed-orientation spread: two full-height portrait anchors,
   each paired with two landscape details. The pattern reads as one composed
   sequence rather than a quad followed by a disconnected duo. */
.media-editorial-six {
  display: grid;
  grid-template-columns: 1fr 1.25fr 1fr 1.25fr;
  grid-template-rows: repeat(2, minmax(0, clamp(150px, 18vw, 290px)));
  gap: 3px; margin: 6vh 0;
}
.media-editorial-six figure {
  margin: 0; position: relative; overflow: hidden;
  opacity: 0; transform: translateY(30px);
  transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
}
.media-editorial-six figure.in-view { opacity: 1; transform: translateY(0); }
.media-editorial-six img, .media-editorial-six video { width: 100%; height: 100%; object-fit: cover; }
.media-editorial-six figure:nth-child(1) { grid-column: 1; grid-row: 1 / 3; }
.media-editorial-six figure:nth-child(2) { grid-column: 2; grid-row: 1; }
.media-editorial-six figure:nth-child(3) { grid-column: 2; grid-row: 2; }
.media-editorial-six figure:nth-child(4) { grid-column: 3; grid-row: 1 / 3; }
.media-editorial-six figure:nth-child(5) { grid-column: 4; grid-row: 1; }
.media-editorial-six figure:nth-child(6) { grid-column: 4; grid-row: 2; }

/* Editorial eight-photo spread. The first band echoes the Lacock collage:
   two tall lead images, then a landscape over two smaller detail frames.
   Three tall portraits form a calmer second band beneath it. */
.media-editorial-eight {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-template-rows: repeat(4, minmax(0, clamp(120px, 14vw, 220px)));
  gap: 3px;
  margin: 6vh 0;
}
.media-editorial-eight figure {
  margin: 0; position: relative; overflow: hidden;
  opacity: 0; transform: translateY(30px);
  transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
}
.media-editorial-eight figure.in-view { opacity: 1; transform: translateY(0); }
.media-editorial-eight img, .media-editorial-eight video {
  width: 100%; height: 100%; object-fit: cover;
}
.media-editorial-eight figure:nth-child(1) { grid-column: 1 / 3; grid-row: 1 / 3; }
.media-editorial-eight figure:nth-child(2) { grid-column: 3 / 5; grid-row: 1 / 3; }
.media-editorial-eight figure:nth-child(3) { grid-column: 5 / 7; grid-row: 1; }
.media-editorial-eight figure:nth-child(4) { grid-column: 5; grid-row: 2; }
.media-editorial-eight figure:nth-child(5) { grid-column: 6; grid-row: 2; }
.media-editorial-eight figure:nth-child(6) { grid-column: 1 / 3; grid-row: 3 / 5; }
.media-editorial-eight figure:nth-child(7) { grid-column: 3 / 5; grid-row: 3 / 5; }
.media-editorial-eight figure:nth-child(8) { grid-column: 5 / 7; grid-row: 3 / 5; }
.media-duo figure, .media-trio figure, .media-quad figure { margin: 0; position: relative; overflow: hidden; height: 46vh; opacity: 0; transform: translateY(30px); transition: opacity 0.9s var(--ease), transform 0.9s var(--ease); }
.media-quad figure { height: 34vh; }
.media-quad.media-quad-portrait-row figure { height: auto; aspect-ratio: 2 / 3; }
/* A mixed portrait+landscape duo (see media_block() in generate.py) is
   taller than the plain 46vh duo — the portrait side reads cramped at that
   height once its width is narrowed to match its own aspect ratio. */
.media-duo-native figure { height: 58vh; }
.media-duo figure.in-view, .media-trio figure.in-view, .media-quad figure.in-view { opacity: 1; transform: translateY(0); }
.media-duo img, .media-trio img, .media-quad img,
.media-duo video, .media-trio video, .media-quad video { width: 100%; height: 100%; object-fit: cover; }

@media (max-width: 700px) {
  .media-break.section-hero-landscape { height: 44vh; }
  .media-break.section-hero-landscape > img { height: 44vh; }
  .media-quad.media-quad-portrait-row {
    grid-template-columns: 1fr 1fr; grid-template-rows: auto auto;
  }
  .media-editorial-eight {
    grid-template-columns: 1fr 1fr;
    grid-template-rows: repeat(9, minmax(0, 36vw));
  }
  .media-editorial-eight figure:nth-child(1) { grid-column: 1; grid-row: 1 / 3; }
  .media-editorial-eight figure:nth-child(2) { grid-column: 2; grid-row: 1 / 3; }
  .media-editorial-eight figure:nth-child(3) { grid-column: 1 / 3; grid-row: 3; }
  .media-editorial-eight figure:nth-child(4) { grid-column: 1; grid-row: 4 / 6; }
  .media-editorial-eight figure:nth-child(5) { grid-column: 2; grid-row: 4 / 6; }
  .media-editorial-eight figure:nth-child(6) { grid-column: 1; grid-row: 6 / 8; }
  .media-editorial-eight figure:nth-child(7) { grid-column: 2; grid-row: 6 / 8; }
  .media-editorial-eight figure:nth-child(8) { grid-column: 1 / 3; grid-row: 8 / 10; }
  .media-editorial-five {
    grid-template-columns: 1fr 1fr;
    grid-template-rows: repeat(4, minmax(0, 36vw));
  }
  .media-editorial-five figure:nth-child(1) { grid-column: 1; grid-row: 1 / 3; }
  .media-editorial-five figure:nth-child(2) { grid-column: 2; grid-row: 1; }
  .media-editorial-five figure:nth-child(3) { grid-column: 2; grid-row: 2; }
  .media-editorial-five figure:nth-child(4) { grid-column: 1; grid-row: 3 / 5; }
  .media-editorial-five figure:nth-child(5) { grid-column: 2; grid-row: 3 / 5; }
  .media-editorial-five.media-editorial-five-stack-right {
    grid-template-rows: repeat(5, minmax(0, 36vw));
  }
  .media-editorial-five.media-editorial-five-stack-right figure:nth-child(1) { grid-column: 1; grid-row: 1 / 3; }
  .media-editorial-five.media-editorial-five-stack-right figure:nth-child(2) { grid-column: 2; grid-row: 1 / 3; }
  .media-editorial-five.media-editorial-five-stack-right figure:nth-child(3) { grid-column: 1; grid-row: 3; }
  .media-editorial-five.media-editorial-five-stack-right figure:nth-child(4) { grid-column: 2; grid-row: 3; }
  .media-editorial-five.media-editorial-five-stack-right figure:nth-child(5) { grid-column: 1 / 3; grid-row: 4 / 6; }
  .media-editorial-six {
    grid-template-columns: 1fr 1fr;
    grid-template-rows: repeat(4, minmax(0, 36vw));
  }
  .media-editorial-six figure:nth-child(1) { grid-column: 1; grid-row: 1 / 3; }
  .media-editorial-six figure:nth-child(2) { grid-column: 2; grid-row: 1; }
  .media-editorial-six figure:nth-child(3) { grid-column: 2; grid-row: 2; }
  .media-editorial-six figure:nth-child(4) { grid-column: 2; grid-row: 3 / 5; }
  .media-editorial-six figure:nth-child(5) { grid-column: 1; grid-row: 3; }
  .media-editorial-six figure:nth-child(6) { grid-column: 1; grid-row: 4; }
}

.parallax-img { will-change: transform; }

/* ---- Split layout experiment (Norway): text left, map visible on the
   right, instead of the map as a subtle full-bleed wash behind everything.
   Scoped under body.split-layout so it doesn't affect TMB's existing look.
   --split: how much of the viewport the text column (incl. its own left
   gutter) takes; the map starts exactly there. */
/* --split is where the map starts — text/photo column (left of it) gets
   2/3 of the viewport, map gets the remaining 1/3. */
.split-layout { --split: 66.67vw; --split-gutter: 6vw; }
.split-layout .story-map-bg { left: var(--split); }
.split-layout .story-map-overlay {
  background: linear-gradient(to right, var(--bg) 0%, var(--bg) 7%, rgba(10,10,10,0.75) 22%, rgba(10,10,10,0.15) 50%, transparent 66%);
}
.split-layout main, .split-layout .chapter-inner { max-width: calc(var(--split) - var(--split-gutter)); margin: 0; padding: 0 0 0 var(--split-gutter); }
.split-layout .closer { max-width: calc(var(--split) - var(--split-gutter)); margin: 0; padding-left: var(--split-gutter); padding-right: var(--split-gutter); text-align: left; }
.split-layout .stats-row { justify-content: flex-start; }
.split-layout .chapter-nav { right: auto; left: calc(var(--split) + 24px); }
/* Cancel the 100vw full-bleed breakout for media inside the left column —
   photos sit within the column instead of spanning under the map. */
.split-layout .media-break { width: 100%; left: auto; margin-left: 0; height: 60vh; }
.split-layout .media-break > img, .split-layout .media-break > video { height: 60vh; }
.split-layout .media-duo, .split-layout .media-trio, .split-layout .media-quad { max-width: 100%; }
.split-layout .media-duo figure, .split-layout .media-trio figure, .split-layout .media-quad figure { height: 32vh; }
.split-layout .media-duo-native figure { height: 40vh; }
.split-layout .media-quad figure { height: 24vh; }
/* Narrow screens can't fit both a text column and a map panel — fall back
   to the default full-bleed-background behavior below the same breakpoint
   the chapter-nav already disappears at. */
@media (max-width: 900px) {
  .split-layout .story-map-bg { left: 0; }
  .split-layout .story-map-overlay { background: radial-gradient(ellipse at center, rgba(10,10,10,0.35) 0%, rgba(10,10,10,0.72) 68%, rgba(10,10,10,0.85) 100%); }
  .split-layout main, .split-layout .chapter-inner, .split-layout .closer { max-width: 720px; margin: 0 auto; padding: 0 6vw; text-align: left; }
  .split-layout .media-break { width: 100vw; left: 50%; margin-left: -50vw; height: 82vh; }
  .split-layout .media-break > img, .split-layout .media-break > video { height: 82vh; }
}

/* ---- Portrait media in a full-bleed (landscape-shaped) slot ----
   object-fit:cover on a portrait photo in a wide/short box crops away most
   of the vertical content. Instead: a blurred, dimmed cover copy fills the
   box as backdrop, and the real photo sits on top fully visible. */
.portrait-fill { position: relative; width: 100%; height: 100%; overflow: hidden; background: #000; display: flex; align-items: center; justify-content: center; }
.portrait-fill .pf-backdrop {
  position: absolute; inset: -6%; width: 112%; height: 112%; object-fit: cover;
  filter: blur(60px) brightness(0.42) saturate(1.15);
}
.portrait-fill .pf-fg { position: relative; z-index: 1; height: 100%; width: auto; max-width: 100%; object-fit: contain; }
.hero-fill .pf-fg { height: 92%; }

/* ---- Stats / closing ---- */
.closer { padding: 14vh 6vw; text-align: center; max-width: 760px; margin: 0 auto; }
.stats-row { display: flex; justify-content: center; gap: 60px; margin: 60px 0; flex-wrap: wrap; }
.stat { text-align: center; }
.stat-num { font-family: var(--serif); font-size: clamp(2.2rem, 4vw, 3.2rem); color: var(--accent); }
.stat-label { font-size: 0.8rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.08em; margin-top: 6px; }
.closer-text { font-size: 1.4rem; font-family: var(--serif); line-height: 1.5; color: #ddd; }
.cta-btn {
  display: inline-flex; align-items: center; gap: 10px; margin-top: 44px;
  padding: 14px 28px; border-radius: 999px; border: 1px solid rgba(255,255,255,0.18);
  font-size: 0.9rem; transition: all 0.3s var(--ease);
}
.cta-btn:hover { background: var(--accent); color: #0a0a0a; border-color: var(--accent); }

/* ---- Shared header (homepage) ---- */
.site-header {
  position: fixed; top: 0; left: 0; right: 0; z-index: 100; height: 72px;
  display: flex; align-items: center; justify-content: space-between; padding: 0 28px;
  background: rgba(10,10,10,0.7); backdrop-filter: blur(18px); border-bottom: 1px solid var(--border);
}
.logo { font-family: var(--serif); font-size: 1.5rem; }

/* ---- Homepage hero ---- */
.home-hero { position: relative; height: 50vh; display: flex; align-items: center; justify-content: center; overflow: hidden; }
.home-hero-bg { position: absolute; inset: 0; }
.home-hero-bg img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; animation: crossfade 24s infinite; }
.home-hero-bg img:nth-child(1) { animation-delay: 0s; }
.home-hero-bg img:nth-child(2) { animation-delay: 6s; }
.home-hero-bg img:nth-child(3) { animation-delay: 12s; }
.home-hero-bg img:nth-child(4) { animation-delay: 18s; }
@keyframes crossfade {
  0% { opacity: 0; transform: scale(1.0); }
  4% { opacity: 1; }
  20% { opacity: 1; transform: scale(1.06); }
  26% { opacity: 0; }
  100% { opacity: 0; }
}
.home-hero-overlay { position: absolute; inset: 0; background: radial-gradient(ellipse at center, rgba(10,10,10,0.3) 0%, rgba(10,10,10,0.92) 85%); }
/* One caption per crossfading cover photo, absolutely stacked in the same
   bottom-right spot and faded on the same 24s/6s-stagger schedule as the
   photos themselves (see .home-hero-bg img above) so the name/date/location
   always matches whichever photo is currently showing. */
.home-hero-captions { position: absolute; right: 6vw; bottom: 52px; z-index: 3; text-align: right; }
.home-hero-caption {
  /* pointer-events is stepped through the same keyframes as opacity so only
     the currently-visible caption's link is ever clickable — the other
     three are stacked in the same spot but invisible, not just transparent. */
  position: absolute; right: 0; bottom: 0; opacity: 0; pointer-events: none; white-space: nowrap;
  animation: heroCaptionFade 24s infinite;
}
.home-hero-caption:nth-child(1) { animation-delay: 0s; }
.home-hero-caption:nth-child(2) { animation-delay: 6s; }
.home-hero-caption:nth-child(3) { animation-delay: 12s; }
.home-hero-caption:nth-child(4) { animation-delay: 18s; }
@keyframes heroCaptionFade {
  0% { opacity: 0; pointer-events: none; }
  4% { opacity: 1; pointer-events: auto; }
  20% { opacity: 1; pointer-events: auto; }
  26% { opacity: 0; pointer-events: none; }
  100% { opacity: 0; pointer-events: none; }
}
.home-hero-caption-title {
  display: block; font-family: var(--serif); font-size: 1.3rem; color: #fff; margin-bottom: 4px;
  text-decoration: none; transition: color 0.2s var(--ease);
}
.home-hero-caption-title:hover { color: var(--accent); }
.home-hero-caption-meta {
  font-size: 0.78rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted);
}
@media (max-width: 700px) {
  .home-hero-captions { right: 20px; bottom: 44px; }
  .home-hero-caption-title { font-size: 1.05rem; }
}
.home-hero-content { position: relative; z-index: 3; text-align: center; }
.home-hero-content .eyebrow {
  font-size: 0.8rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--accent);
  opacity: 0; animation: rise 1s var(--ease) forwards; animation-delay: 0.1s;
}
.home-hero-content h1 { font-size: clamp(3rem, 9vw, 7rem); margin: 18px 0; }
.home-hero-content h1 .word { display: inline-block; overflow: hidden; margin-right: 0.25em; }
.home-hero-content h1 .word span { display: inline-block; opacity: 0; transform: translateY(100%); animation: rise 1s var(--ease) forwards; }
.home-hero-content .tagline {
  font-size: 1.05rem; color: var(--muted); opacity: 0; animation: rise 1s var(--ease) forwards; animation-delay: 0.9s;
}
.home-scroll-cue { position: absolute; bottom: 32px; left: 50%; transform: translateX(-50%); z-index: 3; opacity: 0.5; animation: bob 2s ease-in-out infinite; }

/* ---- Map section ---- */
.section-heading { text-align: center; margin-bottom: 8px; font-size: clamp(1.8rem, 3vw, 2.4rem); }
.section-sub { text-align: center; color: var(--muted); margin-bottom: 50px; font-size: 0.95rem; }

/* ---- Explore section: story list over a full-bleed live map ----
   The map is now a single ambient backdrop for the whole section — behind
   the list too, not just its own column — running edge-to-edge and, since
   .explore-section carries no padding of its own, right up against the
   hero's bottom edge with no gap/seam. Same idea as the story pages'
   fixed/inset background map, just position:sticky (bounded to this one
   section's scroll range) instead of position:fixed (bound to the whole
   page). The list sits on top as a normal in-flow positioned element with
   its own higher z-index — an absolutely-positioned sibling would
   otherwise paint above static content regardless of DOM order. */
.atlas-section { position: relative; }
.atlas-content { position: relative; z-index: 1; }
.atlas-feature {
  --portal-x: 0px; --portal-y: 0px;
  position: relative; z-index: 1; min-height: 100svh; max-width: 760px;
  padding: calc(72px + 7vh) 6vw 8vh; display: flex; flex-direction: column;
  justify-content: center;
  background: linear-gradient(to right, var(--bg) 0%, var(--bg) 64%, transparent 100%);
}
.atlas-feature-card { display: block; width: min(100%, 590px); color: var(--fg); }
.atlas-feature-image-wrap {
  position: relative; overflow: hidden; height: clamp(330px, 50vh, 470px); background: var(--surface);
  box-shadow: 0 32px 80px rgba(0,0,0,0.48);
  transition: box-shadow 0.55s var(--ease);
}
.atlas-feature-image-wrap::before {
  content: ""; position: absolute; z-index: 3; top: 8%; right: 0; bottom: 8%; width: 1px;
  background: var(--accent); opacity: 0; box-shadow: 0 0 26px 8px rgba(212,165,116,0.38);
  transition: opacity 0.45s var(--ease);
}
.atlas-feature-image {
  display: block; width: 100%; height: 100%; object-fit: cover;
  transform: translate3d(var(--portal-x), var(--portal-y), 0) scale(1);
  transition: transform 0.65s var(--ease), filter 0.65s var(--ease);
  will-change: transform;
}
.atlas-feature.portal-active .atlas-feature-image-wrap {
  box-shadow: 0 38px 100px rgba(0,0,0,0.58), 20px 0 70px rgba(212,165,116,0.08);
}
.atlas-feature.portal-active .atlas-feature-image-wrap::before { opacity: 0.9; }
.atlas-feature.portal-active .atlas-feature-image {
  transform: translate3d(var(--portal-x), var(--portal-y), 0) scale(1.055);
  filter: brightness(1.08) saturate(1.05);
}
.atlas-feature-index {
  position: absolute; z-index: 2; top: 18px; left: 18px; padding: 7px 12px; border-radius: 999px;
  background: rgba(10,10,10,0.72); backdrop-filter: blur(12px);
  font-size: 0.7rem; letter-spacing: 0.1em; text-transform: uppercase; color: #fff;
}
.atlas-feature-copy { padding-top: 24px; transition: transform 0.5s var(--ease); }
.atlas-feature.portal-active .atlas-feature-copy { transform: translateY(-5px); }
.atlas-feature-cat {
  display: block; color: var(--accent); font-size: 0.74rem; letter-spacing: 0.12em;
  text-transform: uppercase; margin-bottom: 10px;
}
.atlas-feature-copy h1 { font-size: clamp(2.35rem, 4.4vw, 4.6rem); line-height: 0.98; max-width: 680px; }
.atlas-feature-meta { margin-top: 13px; color: var(--muted); font-size: 0.82rem; }
.atlas-feature-link {
  display: inline-flex; gap: 10px; align-items: center; margin-top: 22px;
  color: #fff; font-size: 0.88rem;
}
.atlas-feature-link span { transition: transform 0.25s var(--ease); }
.atlas-feature-card:hover .atlas-feature-link span { transform: translateX(5px); }
.atlas-scroll-note {
  position: absolute; left: 6vw; bottom: 26px; color: var(--muted);
  font-size: 0.7rem; letter-spacing: 0.1em; text-transform: uppercase;
}
.atlas-scroll-note::before {
  content: ""; display: inline-block; width: 34px; height: 1px; margin-right: 10px;
  vertical-align: middle; background: var(--accent);
}
.explore-list {
  position: relative; z-index: 1; max-width: 760px; padding: 12vh 6vw 8vh;
  /* Fades the map out from behind the text itself (instead of a fixed-%
     cut on the old separate map column) so it tracks the list's own
     rendered width at any viewport size, staying opaque long enough for
     the text to read cleanly, then opening up into the fully-visible map
     by its own right edge. */
  background: linear-gradient(to right, var(--bg) 0%, var(--bg) 65%, transparent 100%);
}
/* Straight from the hero into the list itself — no "Where we've been"
   heading or sub-copy above it, on any screen size. */
.explore-list .section-heading, .explore-list .section-sub { display: none; }
.explore-map-col {
  position: sticky; top: 0; z-index: 0; height: 100vh; overflow: hidden;
  /* Keep the pinned map in normal flow so sticky positioning works, then
     pull the atlas content back over the same viewport. An absolutely
     positioned parent prevents the child sticky element from pinning once
     the page has scrolled beyond the opening screen. */
  margin-bottom: -100vh;
}
.explore-map-sticky {
  position: absolute; inset: 0; overflow: hidden; background: var(--bg);
}
/* See #story-map above — same darken/contrast punch-up for Esri's lighter
   grey tiles, and the same black-until-loaded fade-in. */
#home-map {
  position: absolute; inset: 0; opacity: 0; transition: opacity 0.6s var(--ease);
}
#home-map.tiles-loaded { opacity: 1; }
#home-map .leaflet-tile-pane {
  filter: saturate(0.85) brightness(0.55) contrast(1.2);
  transition: filter 0.75s var(--ease);
}
#home-map.portal-active .leaflet-tile-pane { filter: saturate(0.98) brightness(0.72) contrast(1.16); }
.explore-map-sticky .leaflet-container { cursor: default; background: var(--bg); }
.explore-map-overlay {
  /* Just a soft vertical feather into the hero above/content below, plus a
     light overall vignette — the list's own gradient (above) now handles
     the horizontal fade, so there's no hard left-edge cut here any more. */
  position: absolute; inset: 0; pointer-events: none; transition: opacity 0.6s var(--ease);
  background:
    linear-gradient(to bottom, var(--bg) 0%, transparent 6%, transparent 90%, var(--bg) 100%),
    radial-gradient(ellipse at center, rgba(10,10,10,0.05) 0%, rgba(10,10,10,0.5) 100%);
}
#home-map.portal-active + .explore-map-overlay { opacity: 0.48; }
#home-map .featured-route { filter: drop-shadow(0 0 4px rgba(241,189,131,0.7)); }
.explore-item {
  /* Fade-in on scroll comes from the shared .reveal/.reveal.in-view rules
     (it carries that class too) — just the layout lives here. */
  display: flex; align-items: center; gap: 18px; padding: 22px 0;
  border-top: 1px solid var(--border); color: var(--fg);
}
.explore-items > .explore-item:first-of-type { border-top: none; }
.explore-filters { display: flex; gap: 10px; margin-bottom: 30px; }
.filter-btn {
  font-family: var(--sans); font-size: 0.78rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted); background: transparent; border: 1px solid var(--border); border-radius: 999px;
  padding: 8px 18px; cursor: pointer; transition: color 0.2s var(--ease), border-color 0.2s var(--ease), background 0.2s var(--ease);
}
.filter-btn:hover { color: var(--fg); border-color: var(--muted); }
.filter-btn.active { color: var(--bg); background: var(--accent); border-color: var(--accent); }
.explore-item-num { font-family: var(--serif); color: var(--accent); opacity: 0.7; font-size: 0.95rem; flex: 0 0 auto; }
.explore-item-body { flex: 1 1 auto; min-width: 0; }
.explore-item-cat {
  display: inline-block; font-size: 0.72rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--accent); margin-bottom: 6px;
}
.explore-item-title { font-size: 1.3rem; margin-bottom: 4px; transition: color 0.2s var(--ease); }
.explore-item:hover .explore-item-title { color: var(--accent); }
.explore-item-meta { font-size: 0.8rem; color: var(--muted); }
.explore-item-thumb {
  flex: 0 0 auto; width: 84px; height: 84px; object-fit: cover; border-radius: 10px;
}
@media (max-width: 900px) {
  .atlas-section { background: var(--bg); }
  .atlas-feature {
    min-height: 58svh; max-width: none; padding: 0; justify-content: flex-end;
    background: var(--bg);
  }
  .atlas-feature-card { position: relative; width: 100%; }
  .atlas-feature-image-wrap { height: 58svh; aspect-ratio: auto; box-shadow: none; }
  .atlas-feature-image { animation: atlasMobileDrift 14s ease-in-out infinite alternate; }
  @keyframes atlasMobileDrift {
    from { transform: scale(1.035) translate3d(-0.7%, 0, 0); }
    to { transform: scale(1.075) translate3d(0.7%, -0.5%, 0); }
  }
  .atlas-feature-image-wrap::after {
    content: ""; position: absolute; inset: 0;
    background: linear-gradient(to bottom, rgba(10,10,10,0.05) 35%, rgba(10,10,10,0.92) 100%);
  }
  .atlas-feature-copy { position: absolute; z-index: 2; left: 6vw; right: 6vw; bottom: 34px; padding: 0; }
  .atlas-feature-copy h1 { font-size: clamp(2.1rem, 10vw, 3.5rem); }
  .atlas-feature-meta { color: rgba(255,255,255,0.68); }
  .atlas-feature-link { margin-top: 14px; }
  .atlas-scroll-note { display: none; }
  /* The list already carries its own full padding (see .explore-list
     above) — just let it stretch full width now that there's no map
     column beside it to share space with. */
  .explore-list { max-width: none; padding-top: 7vh; }
  /* No room for a useful map on a phone screen, and (separately) the map
     column's #home-map is position:absolute/inset:0 relying on
     .explore-map-sticky for its containing block — that block previously
     went position:static here, which broke the containing block entirely
     and let the map balloon to cover the full viewport, overlapping the
     hero above it. Simplest fix on both counts: no map at all on mobile,
     straight from hero into the list. */
  .explore-map-col { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .atlas-feature-image { animation: none !important; transform: none !important; }
}

.pulse-marker { position: relative; width: 16px; height: 16px; }
.pulse-marker .core { position: absolute; inset: 4px; background: var(--accent); border-radius: 50%; border: 2px solid #0a0a0a; }
.pulse-marker .ring { position: absolute; inset: 0; border-radius: 50%; background: var(--accent); opacity: 0.5; animation: pulse-ring 2.2s ease-out infinite; }
@keyframes pulse-ring { 0% { transform: scale(0.4); opacity: 0.6; } 100% { transform: scale(2.2); opacity: 0; } }
/* Small traveling marker for "cruise" routes (see CRUISE in generate.py's
   map script) — a boat animating along a route's line, e.g. the Flåm fjord
   cruise, rather than the route just appearing instantly. */
.cruise-marker { font-size: 14px; line-height: 20px; text-align: center; filter: drop-shadow(0 1px 2px rgba(0,0,0,0.8)); }

/* ---- Story grid ---- */
.grid-section { padding: 6vh 6vw 14vh; }
.story-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 22px; max-width: 1400px; margin: 0 auto; }
.story-card {
  position: relative; border-radius: 16px; overflow: hidden; height: 380px;
  background: var(--surface); border: 1px solid var(--border);
  opacity: 0; transform: translateY(40px);
  transition: opacity 0.8s var(--ease), transform 0.8s var(--ease), box-shadow 0.4s var(--ease);
  transform-style: preserve-3d; will-change: transform;
}
.story-card.in-view { opacity: 1; transform: translateY(0); }
.story-card:hover { box-shadow: 0 24px 50px -12px rgba(0,0,0,0.6); }
.story-card img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform 0.6s var(--ease), filter 0.6s var(--ease); }
.story-card:hover img { transform: scale(1.08); filter: brightness(0.75); }
.card-overlay { position: absolute; inset: 0; background: linear-gradient(to top, rgba(0,0,0,0.85), rgba(0,0,0,0.1) 55%); }
.card-content { position: absolute; bottom: 0; left: 0; right: 0; padding: 22px; }
.card-cat { font-size: 0.7rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent); margin-bottom: 8px; display: block; }
.card-title { font-family: var(--serif); font-size: 1.35rem; margin-bottom: 6px; }
.card-meta { font-size: 0.78rem; color: rgba(255,255,255,0.55); }

footer.site-footer { text-align: center; padding: 40px; color: var(--muted); font-size: 0.8rem; border-top: 1px solid var(--border); }

/* ---- Photo lightbox ---- */
.lightbox-trigger { cursor: zoom-in; }
.lightbox {
  position: fixed; inset: 0; z-index: 300; display: flex; align-items: center; justify-content: center;
  background: rgba(5, 5, 5, 0.92); padding: 6vh 6vw;
  opacity: 0; pointer-events: none; transition: opacity 0.3s var(--ease);
}
.lightbox.open { opacity: 1; pointer-events: auto; }
.lightbox-content {
  /* Definite width/height (not max-*): the img/video below cap themselves
     with percentage max-height, and a percentage against a parent whose
     height is auto resolves as "none" — portrait photos then rendered at
     full natural height, overflowing way past the viewport. */
  width: 100%; height: 100%; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 14px;
}
.lightbox-img, .lightbox-video {
  max-width: 100%; max-height: calc(100% - 3.5rem); width: auto; height: auto;
  object-fit: contain; border-radius: 6px; box-shadow: 0 24px 70px rgba(0, 0, 0, 0.6);
  opacity: 0; transform: scale(0.97); transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
}
.lightbox-img, .lightbox-video { cursor: zoom-out; }
.lightbox.open .lightbox-img, .lightbox.open .lightbox-video { opacity: 1; transform: scale(1); }
.lightbox-caption {
  max-width: min(760px, 84vw); color: rgba(255,255,255,0.82); text-align: center;
  font-family: var(--serif); font-size: 1rem; line-height: 1.45;
}
.lightbox-caption[hidden] { display: none; }
.lightbox-close {
  position: fixed; top: 20px; right: 24px; width: 44px; height: 44px; border-radius: 50%;
  background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.15); color: #fff;
  font-size: 26px; line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center;
  transition: background 0.2s var(--ease);
}
.lightbox-close:hover { background: rgba(255, 255, 255, 0.18); }
.lightbox-nav {
  position: fixed; top: 50%; transform: translateY(-50%); width: 52px; height: 52px; border-radius: 50%;
  background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.15); color: #fff;
  font-size: 22px; line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center;
  transition: background 0.2s var(--ease);
}
.lightbox-nav:hover { background: rgba(255, 255, 255, 0.18); }
.lightbox-prev { left: 24px; }
.lightbox-next { right: 24px; }
@media (max-width: 700px) {
  .lightbox-nav { width: 40px; height: 40px; font-size: 18px; }
  .lightbox-prev { left: 10px; }
  .lightbox-next { right: 10px; }
}
