/* ============================================================
   SCROLL & STONE - Outlived
   A scroll-driven telling: empire monuments rise and crumble
   in a fixed stage while one lamp flame stays lit throughout.
   Type: reuses the site display/body/utility stacks from style.css
   (var(--display) / var(--body) / var(--utility)). Palette here is
   deliberately narrower than the house palette - near-black stone,
   ink-grey text, cold bronze accents. The flame is the only warm
   colour on the page.
   Sections: tokens, stage, flame, movement anatomy, per-kind
   variants, paged (reduced-motion) mode, wide-screen refinements.
   ============================================================ */

:root {
  --ol-bg:        #0d0c0a;   /* near-black stone */
  --ol-bg-2:      #16140f;   /* slightly raised, for placards/cards */
  --ol-ink:       #c9c4ba;   /* ink-grey text */
  --ol-ink-dim:   #8a857a;   /* secondary/caption text */
  --ol-bronze:    #7a6a4f;   /* cold bronze accent, rules and borders */
  --ol-bronze-hi: #a3906c;   /* bronze on hover/active */
  --ol-flame-core:#ffb347;   /* the only warm colour on the page */
  --ol-flame-glow:#ff7b2e;

  --ol-display: var(--display, "Frank Ruhl Libre", Georgia, serif);
  --ol-body:    var(--body, "Newsreader", Georgia, serif);
  --ol-utility: var(--utility, "Inter", -apple-system, sans-serif);
}

#ol-main { background: var(--ol-bg); color: var(--ol-ink); }

/* ------------------------------------------------------------
   Scroll driver + fixed stage
   ------------------------------------------------------------ */

#ol-scroll {
  position: relative;
  width: 100%;
  /* height is set by outlived.js (Task 3) once movement count and
     per-movement scroll budget are known; a generous fallback keeps
     the page usable if that script has not run yet. */
  min-height: 1000vh;
  background: var(--ol-bg);
}

#ol-stage {
  position: fixed;
  inset: 0;
  overflow: hidden;
  background: var(--ol-bg);
}

/* Faint vignette so the stage reads as a chamber, not a flat rectangle */
#ol-stage::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(ellipse at 50% 42%, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 55%, rgba(0, 0, 0, 0.55) 100%);
  z-index: 5;
}

/* ------------------------------------------------------------
   The flame - persistent, direct child of #ol-stage
   ------------------------------------------------------------ */

#ol-flame {
  position: absolute;
  bottom: calc(18px + env(safe-area-inset-bottom));
  right: 12vw;
  width: 40px;
  height: 40px;
  z-index: 20;
  pointer-events: none;
}

.ol-flame-halo {
  position: absolute;
  /* pooled at the flame's base, not floating above it */
  left: 50%;
  bottom: -38px;
  width: 104px;
  height: 104px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 179, 71, 0.38) 0%, rgba(255, 123, 46, 0.17) 40%, rgba(255, 123, 46, 0) 72%);
  transition: opacity 0.6s ease;
}

.ol-flame-glow {
  /* flood overlay for .ol-flame-flood - scales up via transform only */
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 40px;
  height: 40px;
  transform: translateX(-50%) scale(1);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 179, 71, 0.9) 0%, rgba(255, 123, 46, 0.55) 45%, rgba(255, 123, 46, 0) 75%);
  opacity: 0;
  transform-origin: 50% 85%;
  transition: opacity 0.9s ease, transform 0.9s ease;
  will-change: transform, opacity;
}

/* The flame body: three stacked teardrops, each a square with one square
   corner (border-radius: 0 50% 50% 50%) rotated so that corner points up,
   then stretched vertically. Layered heat - a deep red-orange rim, an amber
   body, and a small pale-yellow core sitting low in the flame - with soft
   transparent gradient edges instead of a blur filter (no filters here: the
   engine transforms #ol-flame itself every frame in the finale).
   Only ONE element animates: .ol-flame-body, transform and opacity. */
.ol-flame-body {
  --ol-fs: 1;             /* one knob for the whole flame's size */
  position: absolute;
  left: 50%;
  bottom: 2px;
  width: 26px;
  height: 34px;
  transform: translateX(-50%) scale(1);
  transform-origin: 50% 100%;
  animation: ol-breathe 3.2s ease-in-out infinite;
  will-change: transform, opacity;
}

/* Each layer is a square with one square corner; rotating ~46deg stands
   that corner up as the tip and the other three corners' 50% radii form the
   rounded base, then scaleY draws the whole thing tall and narrow so the tip
   is genuinely acute. The rotation slightly past 45deg gives the lean. */
.ol-flame-body span {
  position: absolute;
  left: 50%;
  display: block;
  border-radius: 0 50% 50% 50%;
}

/* rim - deep red-orange, feathered to nothing at its edge */
.ol-flame-outer {
  bottom: 6.75px;
  width: 15px;
  height: 15px;
  margin-left: -7.5px;
  transform: scaleY(1.9) rotate(47deg);
  background: radial-gradient(circle at 52% 76%,
    rgba(255, 172, 78, 0.98) 0%,
    rgba(242, 110, 32, 0.96) 40%,
    rgba(198, 62, 16, 0.82) 70%,
    rgba(152, 40, 12, 0.34) 89%,
    rgba(152, 40, 12, 0) 100%);
}

/* body - amber, narrower and seated a touch higher off the base */
.ol-flame-mid {
  bottom: 6.5px;
  width: 9.5px;
  height: 9.5px;
  margin-left: -4.75px;
  transform: scaleY(1.75) rotate(44.5deg);
  background: radial-gradient(circle at 52% 76%,
    rgba(255, 222, 145, 0.98) 0%,
    rgba(255, 173, 68, 0.94) 46%,
    rgba(255, 140, 44, 0.4) 82%,
    rgba(255, 140, 44, 0) 100%);
}

/* core - small, pale, low in the flame */
.ol-flame-core {
  bottom: 5.25px;
  width: 5px;
  height: 5px;
  margin-left: -2.5px;
  transform: scaleY(1.5) rotate(45deg);
  background: radial-gradient(circle at 50% 72%,
    rgba(255, 250, 228, 0.97) 0%,
    rgba(255, 233, 166, 0.72) 55%,
    rgba(255, 220, 130, 0) 100%);
}

@keyframes ol-breathe {
  0%,   100% {
    transform: translateX(-50%) scale(var(--ol-fs)) rotate(0deg);
    opacity: 0.94;
  }
  50% {
    transform: translateX(-50%) scale(calc(var(--ol-fs) * 1.05), calc(var(--ol-fs) * 1.13)) rotate(-2deg);
    opacity: 1;
  }
}

/* .ol-flame-dim: the flame bends low (the dark movement) - slower
   breathing, and its light physically recedes via opacity transitions
   rather than an animated shadow. */
#ol-flame.ol-flame-dim .ol-flame-body {
  animation-duration: 6s;
}
#ol-flame.ol-flame-dim .ol-flame-body,
#ol-flame.ol-flame-dim .ol-flame-halo {
  opacity: 0.5;
  transition: opacity 1.2s ease;
}

/* .ol-flame-flood: the finale - the glow overlay scales up (transform
   only) to flood the frame warm. */
#ol-flame.ol-flame-flood .ol-flame-glow {
  opacity: 1;
  transform: translateX(-50%) scale(22);
}
#ol-flame.ol-flame-flood .ol-flame-halo {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .ol-flame-body { animation: none; opacity: 1; }
}

/* Narrow phones (360x740, 390x844): the full-size halo's 104px footprint
   reaches into the CTA pill and the counter copy that sit stacked above it
   in the single mobile column. The flame stays fully lit and visible here -
   only its footprint shrinks, and its anchor pulls tighter into the corner -
   so it never overlaps interactive elements or copy. Still honours the
   safe-area inset. Scaled down with the same halo/flame proportions as the
   1400px-up rule below, just smaller instead of bigger. */
@media (max-width: 430px) {
  #ol-flame {
    width: 28px;
    height: 28px;
    right: 5vw;
    bottom: calc(14px + env(safe-area-inset-bottom));
  }
  .ol-flame-body { --ol-fs: 0.74; }
  .ol-flame-halo {
    width: 72px;
    height: 72px;
    bottom: -26px;
  }
  .ol-flame-glow {
    width: 28px;
    height: 28px;
  }
}

/* #ol-veil: single persistent overlay for the dark movement, appended
   once by outlived.js (Task 3). Its opacity is written per-frame in
   the scroll-driven engine; stays at 0 (invisible) the rest of the time.
   It sits BELOW .ol-mv (z-index 10) so it darkens the ground and not the
   words - the dark movement's copy has to stay readable while the frame
   empties out around it. */
#ol-veil {
  position: absolute;
  inset: 0;
  background: #000;
  opacity: 0;
  pointer-events: none;
  z-index: 8;
  will-change: opacity;
}

/* ------------------------------------------------------------
   The site nav - present at rest, gone for the cinema
   ------------------------------------------------------------
   The topbar is the brightest thing on a near-black stage, so it fades
   out once the piece begins (html.ol-immersed, toggled at phase
   boundaries by outlived.js) and returns for the finale's doorways.
   visibility:hidden after the fade keeps it out of the tab order; the
   delayed visibility transition lets the fade finish first. In paged
   (reduced-motion) mode the class is never set, so the bar always stays. */
.topbar {
  transition: opacity 0.7s ease, visibility 0s linear 0s;
}

html.ol-immersed:not(.ol-paged) .topbar {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.7s ease, visibility 0s linear 0.7s;
}

/* ------------------------------------------------------------
   Movement anatomy - section.ol-mv[data-id]
   ------------------------------------------------------------ */

.ol-mv {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  /* clamp() rather than a height breakpoint - on a short coarse-pointer
     viewport (e.g. 390x844) the fixed stage clips anything the flex
     column pushes past its bounds, so padding/gap must shrink with vh
     directly or the CTA link and doorway links can land off-screen. */
  gap: clamp(0.8rem, 2.4vh, 1.6rem);
  padding:
    calc(env(safe-area-inset-top) + clamp(2.5rem, 7vh, 5rem))
    1.25rem
    calc(env(safe-area-inset-bottom) + clamp(1.75rem, 5vh, 3rem));
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s ease;
  z-index: 10;
}

.ol-mv.ol-active {
  opacity: 1;
  pointer-events: auto;
}

/* .ol-text: the single text column - boast, placard, counter, link (and,
   in the finale, doorways and share) stacked in that order. Built once in
   outlived.js so a wide screen composes as TWO columns (stone | text)
   rather than scattering every child into its own squeezed flex track. */
.ol-text {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(0.8rem, 2.4vh, 1.6rem);
  width: 100%;
  min-width: 0;
  /* no measure here on purpose - .ol-boast and .ol-counter each carry
     their own max-width in their own type size, which is the only place a
     ch measure means anything. The wide two-column rule below is the one
     case where the COLUMN itself needs a width. */
}

/* .ol-stone: the fragment container that holds the artefact image,
   built as clip-path polygon fragments so the engine can crumble it.
   Sized by HEIGHT so every artefact carries similar visual weight on
   stage regardless of shape; width follows from --ol-aspect (the
   image's real width/height, set once at build time in outlived.js -
   see buildStone). max-width keeps a wide artefact (e.g. the Cyrus
   Cylinder) from overflowing the column on a narrow viewport. The
   0.8 fallback only applies to prologue/dark/finale, which have no
   image and hide .ol-stone entirely (see below). */
.ol-stone {
  position: relative;
  /* 42vh, not 52vh - the whole column (stone, boast, placard, counter,
     link) has to fit inside a 390x844 and a 360x740 viewport with room to
     breathe at both ends; the fixed stage clips anything that overruns.
     --ol-max-h (set once at build time, only for low-resolution artefact
     photos - see buildStone in outlived.js) additionally caps the height so
     a small source image is never upscaled past ~1.2x its native size; the
     width follows it via aspect-ratio, so the box never distorts. */
  height: min(42vh, 380px, var(--ol-max-h, 380px));
  max-width: 88vw;
  aspect-ratio: var(--ol-aspect, 0.8);
  flex: 0 0 auto;
}

/* Short phone viewports: the counter can wrap to six lines at 360px wide,
   so the stone gives up height to keep real breathing room at both ends.
   Declared before the wide two-column block, which overrides it. */
@media (max-height: 800px) {
  .ol-stone {
    height: min(36vh, 300px, var(--ol-max-h, 300px));
  }
}

.ol-frag {
  position: absolute;
  inset: 0;
  overflow: hidden;
  will-change: transform;
  clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
}

/* A handful of jagged, shard-like default fragment shapes so a stone
   still reads as "broken stone" before Task 3 assigns per-node clips. */
.ol-frag:nth-child(1) { clip-path: polygon(0% 0%, 62% 0%, 48% 38%, 0% 46%); }
.ol-frag:nth-child(2) { clip-path: polygon(62% 0%, 100% 0%, 100% 58%, 48% 38%); }
.ol-frag:nth-child(3) { clip-path: polygon(0% 46%, 48% 38%, 58% 100%, 0% 100%); }
.ol-frag:nth-child(4) { clip-path: polygon(48% 38%, 100% 58%, 100% 100%, 58% 100%); }

.ol-frag img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Museum-furniture suppression. Every artefact photograph arrives with
   gallery walls, floors, chairs and neighbouring exhibits around it, and a
   bright museum background fights a near-black stage. This overlay is drawn
   in STONE coordinates (a .ol-frag is inset:0 of .ol-stone, so each
   fragment's clip-path shows exactly its slice of one continuous vignette):
   the seven artefacts read as one lit system, the tiling stays seamless,
   and the treatment travels with each piece through the crumble. Static -
   no per-frame cost, no filter.

   Four edge gradients, not an ellipse. An elliptical mask cuts the top and
   bottom off a tall stele and leaves a visible porthole rim plus bare
   photograph corners on a wide viewport. These four one-sided falloffs each
   run from solid ground colour exactly at their own edge to nothing a short
   way in, so the photograph dissolves into the black on all four sides, the
   whole artefact stays visible and lit, and no ellipse and no rectangle
   boundary is perceptible anywhere. The bands are kept narrow (a fifth of
   the box at most) so the falloff bites the photograph's margin, not the
   subject. */
.ol-frag::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to right,
      rgba(13, 12, 10, 1) 0%,
      rgba(13, 12, 10, 0.86) 3%,
      rgba(13, 12, 10, 0.5) 9%,
      rgba(13, 12, 10, 0.2) 16%,
      rgba(13, 12, 10, 0.05) 23%,
      rgba(13, 12, 10, 0) 30%),
    linear-gradient(to left,
      rgba(13, 12, 10, 1) 0%,
      rgba(13, 12, 10, 0.86) 3%,
      rgba(13, 12, 10, 0.5) 9%,
      rgba(13, 12, 10, 0.2) 16%,
      rgba(13, 12, 10, 0.05) 23%,
      rgba(13, 12, 10, 0) 30%),
    linear-gradient(to bottom,
      rgba(13, 12, 10, 1) 0%,
      rgba(13, 12, 10, 0.86) 2%,
      rgba(13, 12, 10, 0.5) 6%,
      rgba(13, 12, 10, 0.2) 11%,
      rgba(13, 12, 10, 0.05) 16%,
      rgba(13, 12, 10, 0) 21%),
    linear-gradient(to top,
      rgba(13, 12, 10, 1) 0%,
      rgba(13, 12, 10, 0.86) 2%,
      rgba(13, 12, 10, 0.5) 6%,
      rgba(13, 12, 10, 0.2) 11%,
      rgba(13, 12, 10, 0.05) 16%,
      rgba(13, 12, 10, 0) 21%),
    /* Lighting falloff, not a mask: it never exceeds 0.72 alpha anywhere, so
       nothing is ever cut out of the picture; it simply lets the museum room
       sink towards the ground colour while the lit subject in the middle
       keeps full brightness. */
    radial-gradient(ellipse 76% 68% at 50% 46%,
      rgba(13, 12, 10, 0) 26%,
      rgba(13, 12, 10, 0.2) 56%,
      rgba(13, 12, 10, 0.5) 80%,
      rgba(13, 12, 10, 0.72) 100%),
    /* a flat sit-back so a bright gallery wall never out-shines the stage */
    linear-gradient(rgba(13, 12, 10, 0.16), rgba(13, 12, 10, 0.16));
}

/* .ol-boast: the empire's own words, chiselled */
.ol-boast {
  max-width: 40ch;
  text-align: center;
  font-family: var(--ol-display);
  font-weight: 700;
  font-size: clamp(1.15rem, 4vw, 1.7rem);
  line-height: 1.35;
  letter-spacing: 0.03em;
  color: var(--ol-ink);
  /* chiselled inset look: two static text-shadows, never animated */
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.85), 0 -1px 0 rgba(255, 255, 255, 0.06);
}

.ol-boast p { margin: 0 0 0.35em; }
.ol-boast p:last-child { margin-bottom: 0; }

/* .ol-quote-open: the real opening curly mark (outlived.js), wrapping only
   the mark itself so it can be nudged independently of the line it opens -
   inert by default (centred mobile layout), hung into the margin only on
   the wide-screen left-aligned layout below. */
.ol-quote-open { display: inline-block; }

/* .ol-placard: small-caps letter-spaced museum label */
.ol-placard {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0 1em;
  font-family: var(--ol-utility);
  font-variant: small-caps;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  color: var(--ol-ink-dim);
  border-top: 1px solid rgba(122, 106, 79, 0.4);
  border-bottom: 1px solid rgba(122, 106, 79, 0.4);
  padding: 0.5em 0;
}

.ol-placard .ol-year,
.ol-placard .ol-risen,
.ol-placard .ol-fallen {
  white-space: nowrap;
}

/* .ol-year: when the empire's own boast was carved - sits between the
   artefact name and the risen/fallen dates, same museum-label voice. */
.ol-placard .ol-year {
  color: var(--ol-ink-dim);
}

.ol-placard .ol-fallen {
  font-weight: 700;
  color: var(--ol-bronze-hi);
}

/* .ol-thud: one-shot 120ms settle animation, added by outlived.js (Task 3)
   to .ol-fallen the instant a stone movement's hold phase crosses t=0.5,
   then removed on animationend so it can refire on a later pass. */
@keyframes ol-thud {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.22); }
  100% { transform: scale(1); }
}

.ol-placard .ol-fallen.ol-thud {
  animation: ol-thud 0.12s ease;
}

@media (prefers-reduced-motion: reduce) {
  .ol-placard .ol-fallen.ol-thud { animation: none; }
}

/* .ol-counter: our reply, shown as the stone crumbles */
.ol-counter {
  max-width: 46ch;
  text-align: center;
  font-family: var(--ol-body);
  font-style: italic;
  font-size: clamp(1rem, 2.6vw, 1.2rem);
  line-height: 1.55;
  color: var(--ol-ink);
}

.ol-counter p { margin: 0 0 0.5em; }
.ol-counter p:last-child { margin-bottom: 0; }

/* .ol-link: doorway to the fuller article/object page for the artefact */
.ol-link {
  /* Sized to its own content and never wrapped - a restrained single-line
     pill, not a stretched ellipse. flex:0 0 auto stops a flex parent from
     ever stretching or squeezing it. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  align-self: center;
  flex: 0 0 auto;
  white-space: nowrap;
  font-family: var(--ol-utility);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--ol-bronze-hi);
  border: 1px solid var(--ol-bronze);
  border-radius: 999px;
  padding: 0.55em 1.3em;
  min-height: 44px;
  box-sizing: border-box;
  text-decoration: none;
  transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}

.ol-link:hover,
.ol-link:focus-visible {
  color: var(--ol-bg);
  background: var(--ol-bronze-hi);
  border-color: var(--ol-bronze-hi);
}

/* ------------------------------------------------------------
   Per-kind variants
   ------------------------------------------------------------ */

/* prologue / finale: no stone, text carries the whole frame */
.ol-mv[data-kind="prologue"] .ol-stone,
.ol-mv[data-kind="finale"] .ol-stone,
.ol-mv[data-kind="dark"] .ol-stone {
  display: none;
}

.ol-mv[data-kind="prologue"] .ol-boast,
.ol-mv[data-kind="finale"] .ol-boast {
  font-size: clamp(1.4rem, 5.5vw, 2.3rem);
  max-width: 34ch;
}

/* The opening line arrives rather than snapping on: a one-shot keyframe that
   outranks the engine's inline opacity while it runs and lands on the same
   value, so nothing pops when it finishes. No fill-mode, deliberately - the
   engine owns the property again the moment the animation is over. */
@keyframes ol-arrive {
  from { opacity: 0; transform: translate3d(0, 8px, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

.ol-mv[data-kind="prologue"] .ol-boast p:first-child {
  animation: ol-arrive 1.1s ease;
}

/* ------------------------------------------------------------
   The scroll cue - the piece has to say, on arrival, that it moves
   ------------------------------------------------------------
   Near the foot of the prologue, in the page's own register: one small line
   of bronze utility type and a hairline chevron that falls a short distance
   and fades, over and over. Compositor-only (transform and opacity), a CSS
   keyframe with no JS timer. The engine fades the whole cue out on the first
   real scroll (applyPrologue) and never brings it back mid-piece. */
.ol-cue {
  position: absolute;
  left: 50%;
  bottom: calc(env(safe-area-inset-bottom) + clamp(1.4rem, 5vh, 2.6rem));
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  pointer-events: none;
  font-family: var(--ol-utility);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--ol-bronze-hi);
  animation: ol-arrive 1.1s ease 0.5s backwards;
}

.ol-cue-mark {
  display: block;
  width: 11px;
  height: 11px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translate3d(0, -3px, 0) rotate(45deg);
  animation: ol-cue-fall 2.1s ease-in-out infinite;
}

@keyframes ol-cue-fall {
  0%   { transform: translate3d(0, -3px, 0) rotate(45deg); opacity: 0; }
  30%  { opacity: 1; }
  70%  { opacity: 0.85; }
  100% { transform: translate3d(0, 9px, 0) rotate(45deg); opacity: 0; }
}

/* Reduced motion: the cue still has to be there and still has to read - it
   simply stops moving. */
@media (prefers-reduced-motion: reduce) {
  .ol-cue,
  .ol-mv[data-kind="prologue"] .ol-boast p:first-child { animation: none; }
  .ol-cue-mark {
    animation: none;
    opacity: 1;
    transform: rotate(45deg);
  }
}

/* Paged (reduced-motion) mode puts every movement back in document flow, so
   the cue cannot be pinned to a stage that no longer exists. */
html.ol-paged .ol-cue {
  position: static;
  transform: none;
  margin: 1.6rem auto 0;
}

/* breath (Cyrus): the reply is kinder, not adversarial - softer bronze rule */
.ol-mv[data-kind="breath"] .ol-placard {
  border-color: rgba(163, 144, 108, 0.55);
}

/* dark (the twelve years without a monument): the frame goes emptier
   and the flame is the whole point - text alone, low and close.
   Quiet is a matter of pace and space, not of dimming the words below
   legibility: the veil darkens the ground beneath the copy (see #ol-veil),
   and the copy itself keeps comfortable contrast. */
.ol-mv[data-kind="dark"] {
  gap: 1rem;
}

.ol-mv[data-kind="dark"] .ol-counter {
  font-style: normal;
  font-family: var(--ol-display);
  font-weight: 400;
  font-size: clamp(1.15rem, 3.6vw, 1.5rem);
  line-height: 1.6;
  color: #d5cfc3;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.9);
}

/* "It held." - the payoff of the movement, and the brightest thing in it
   apart from the flame */
.ol-mv[data-kind="dark"] .ol-counter p:last-child {
  margin-top: 0.6em;
  font-size: 1.4em;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: #f4efe3;
}

/* ------------------------------------------------------------
   finale - the flood is LIGHT BEHIND the words
   ------------------------------------------------------------
   The flame and its flood live at z-index 20; the finale section is raised
   above them so every closing line and every doorway sits in front of the
   light rather than under it. A soft dark scrim behind the text column
   holds contrast where the flood is brightest. */
.ol-mv[data-kind="finale"] {
  z-index: 30;
}

/* The scrim is a wash across the whole frame, not a shape behind the words.
   Sized to the stage (not to the text box) and falling off to nothing well
   inside the frame's own edges, it has no perceptible boundary: the copy
   simply sits in the deeper part of the light. It is drawn on the section,
   beneath .ol-text, so the words keep their full contrast on top of it. */
.ol-mv[data-kind="finale"]::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 88% 72% at 50% 50%,
      rgba(8, 7, 5, 0.66) 0%,
      rgba(8, 7, 5, 0.58) 34%,
      rgba(8, 7, 5, 0.38) 58%,
      rgba(8, 7, 5, 0.16) 78%,
      rgba(8, 7, 5, 0.04) 91%,
      rgba(8, 7, 5, 0) 100%),
    linear-gradient(to bottom,
      rgba(8, 7, 5, 0) 0%,
      rgba(8, 7, 5, 0.18) 26%,
      rgba(8, 7, 5, 0.22) 62%,
      rgba(8, 7, 5, 0) 100%);
}

.ol-mv[data-kind="finale"] .ol-text {
  position: relative;
  z-index: 1;
  /* shrink-wrapped, so the closing lines hold together as one block */
  width: auto;
  max-width: min(94vw, 46rem);
  padding: clamp(1rem, 3vh, 2rem) clamp(0.75rem, 4vw, 3rem);
}

.ol-mv[data-kind="finale"] .ol-boast {
  color: #f6f1e6;
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.85), 0 1px 0 rgba(0, 0, 0, 0.6);
}

.ol-mv[data-kind="finale"] .ol-counter {
  color: #ece5d6;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.8);
}

/* the share row has to survive the flood too */
.ol-mv[data-kind="finale"] .ol-share {
  color: #d8d0c0;
}

.ol-mv[data-kind="finale"] .ol-link {
  color: #f2e3c4;
  border-color: var(--ol-bronze-hi);
  background: rgba(11, 10, 8, 0.7);
}

/* finale: doorways to the rest of the site, and a plain share row
   mirroring the tone of the article share-tray (no widget dependency).

   Five doorways in total (Thread / Atlas / Library / Calendar /
   Constellation). Left to a single flex-wrap row, five pills of varying
   label length wrap wherever the content happens to run out of room -
   at some in-between widths that lands on an accidental 4 + 1 split,
   which reads as broken rather than composed. Instead outlived.js builds
   two explicit rows (3 then 2, see .ol-doorway-row below) and each row
   centres ITSELF, independently, at every viewport - so the composition
   is the same deliberate 3-over-2 block from 360px up to a wide desktop,
   never a content-driven accident. The two longer labels ("The Calendar",
   "The Constellation") sit together in the shorter row, where each pill
   gets more width to work with. */
.ol-doorways {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
}

.ol-doorway-row {
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  gap: 0.75rem;
  width: 100%;
}

.ol-doorway {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  min-height: 44px;
  box-sizing: border-box;
  font-family: var(--ol-utility);
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: #f2ece0;
  text-decoration: none;
  border: 1px solid var(--ol-bronze-hi);
  border-radius: 6px;
  padding: 0.6em 1.1em;
  /* opaque, so a doorway stays readable wherever the flood is brightest */
  background: rgba(11, 10, 8, 0.92);
  transition: color 0.2s ease, border-color 0.2s ease;
}

/* Phone width: each row shares its own width evenly between its own
   pills - three tighter pills up top, two roomier ones (with the longer
   labels) below - rather than wrapping on content and risking an orphan.
   "The Constellation" is the longest label on the page; it gets a
   two-up row precisely so it never has to wrap inside its own pill. */
@media (max-width: 560px) {
  .ol-doorways {
    gap: 0.4rem;
  }

  .ol-doorway-row {
    gap: 0.4rem;
  }

  .ol-doorway {
    flex: 1 1 0;
    min-width: 0;
    padding: 0.6em 0.35em;
    font-size: 0.74rem;
    letter-spacing: 0.01em;
  }
}

.ol-doorway:hover,
.ol-doorway:focus-visible {
  color: var(--ol-flame-core);
  border-color: var(--ol-flame-core);
}

/* ------------------------------------------------------------
   The finale share row - the site's own share tray, dressed for the flood
   ------------------------------------------------------------
   The controls are built by share-tray.js (copy link, WhatsApp, X on the row;
   Facebook and email behind the overflow menu). Everything here is scoped to
   .ol-share-tray so no other page's tray is touched. It sits on the warm
   flood, so each control gets the same near-opaque dark ground and bronze
   edge the doorways use, and stays one deliberate row at phone widths. */
.ol-share {
  display: flex;
  flex-wrap: nowrap;
  gap: 0.6rem;
  align-items: center;
  justify-content: center;
  max-width: 100%;
  font-family: var(--ol-utility);
  font-size: 0.72rem;
  color: var(--ol-ink-dim);
}

.ol-share-label {
  flex: 0 0 auto;
  letter-spacing: 0.06em;
  white-space: nowrap;
}

.ol-share-tray {
  /* the status line and the overflow menu are both positioned against this,
     so neither can reflow the row when it appears */
  position: relative;
  flex: 0 1 auto;
  min-width: 0;
}

.ol-share-tray .st-row {
  flex-wrap: nowrap;
  gap: 0.45rem;
  justify-content: center;
}

.ol-share-tray .st-btn {
  width: 44px;
  height: 44px;
  border: 1px solid var(--ol-bronze-hi);
  background: rgba(11, 10, 8, 0.92);
  color: #f2e3c4;
}

.ol-share-tray .st-btn:hover,
.ol-share-tray .st-btn:focus-visible {
  color: var(--ol-flame-core);
  border-color: var(--ol-flame-core);
  background: rgba(11, 10, 8, 0.98);
}

.ol-share-tray .st-btn:focus-visible {
  outline: 2px solid var(--ol-flame-core);
  outline-offset: 3px;
}

.ol-share-tray .st-icon {
  width: 19px;
  height: 19px;
}

/* The X glyph ships as a flat #000000 fill (img/social/x.svg, shared with
   every other page - not touched here) - on this page's dark ground (the
   flood behind the finale, or the near-black paged fallback) that is a
   black glyph on a near-black button, effectively invisible. WhatsApp's
   green and Facebook's blue are brand colours with real contrast against
   this ground and stay as shipped, on the owner's steer; "copy link" and
   "more" are inline currentColor SVGs that already pick up the row's
   cream ink. X is the one glyph that needs help, so it alone is flipped
   to white with a plain, static CSS filter (not an SVG filter primitive,
   not animated) - scoped to this page's own .ol-share-tray so no other
   page's share row (which sits on a light ground) is touched. */
.ol-share-tray .st-icon img[src$="/x.svg"] {
  filter: invert(1);
}

/* The overflow menu is absolutely positioned so opening it can never reflow
   the row, and it opens DOWNWARDS: the share row is the last thing in the
   finale column, so the empty frame below it is the only place a menu can go
   without covering the closing lines or the doorways. */
.ol-share-tray .st-more {
  position: absolute;
  top: calc(100% + 0.5rem);
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  flex-wrap: nowrap;
  gap: 0.45rem;
  padding: 0.4rem;
  border: 1px solid var(--ol-bronze);
  border-radius: 10px;
  background: rgba(11, 10, 8, 0.96);
  /* opaque and above the transient status line, which shares this space */
  z-index: 2;
}

.ol-share-tray .st-status {
  position: absolute;
  top: calc(100% + 0.35rem);
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  white-space: nowrap;
  color: var(--ol-flame-core);
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.85);
  z-index: 1;
}

/* Phone width: label and four controls on one row, nothing orphaned. */
@media (max-width: 560px) {
  .ol-share { gap: 0.45rem; }
  .ol-share-label { font-size: 0.68rem; }
  .ol-share-tray .st-row { gap: 0.3rem; }
}

/* ------------------------------------------------------------
   Portrait-first layout / wide layout
   ------------------------------------------------------------ */

/* Two columns on a wide screen: the stone on one side, ONE text column on
   the other holding boast, placard, counter and link stacked and vertically
   centred, at a comfortable measure with a generous gutter. Only the
   artefact movements go to two columns - prologue, dark and finale have no
   stone, so their text stays a centred single column at every width. */
@media (min-width: 900px) {
  .ol-mv[data-kind="stone"],
  .ol-mv[data-kind="breath"] {
    flex-direction: row;
    justify-content: center;
    align-items: center;
    gap: clamp(3rem, 6vw, 6rem);
    padding: 5rem clamp(2.5rem, 5vw, 6rem);
  }

  .ol-mv[data-kind="stone"] .ol-stone,
  .ol-mv[data-kind="breath"] .ol-stone {
    /* Cinematic on wide screens - height-driven, bounded by the column
       layout rather than the old flat 480px cap. --ol-max-h still applies
       here (see .ol-stone above) - a low-resolution artefact stays capped
       at its ~1.2x ceiling even in the widescreen two-column layout. */
    height: min(68vh, 640px, var(--ol-max-h, 640px));
    max-width: 46vw;
  }

  .ol-mv .ol-stone {
    flex: 0 0 auto;
  }

  .ol-mv[data-kind="stone"] .ol-text,
  .ol-mv[data-kind="breath"] .ol-text {
    flex: 0 1 auto;
    align-items: flex-start;
    /* a real measure - the boast must never drop to a word a line */
    width: 44ch;
    max-width: 44ch;
    gap: 1.7rem;
  }

  .ol-mv[data-kind="stone"] .ol-boast,
  .ol-mv[data-kind="breath"] .ol-boast,
  .ol-mv[data-kind="stone"] .ol-counter,
  .ol-mv[data-kind="breath"] .ol-counter {
    text-align: left;
    max-width: none;
    width: 100%;
  }

  /* Hang the opening mark slightly into the left margin so the first
     line's actual text keeps the same left edge as the lines below it -
     optical alignment, a nudge of about half the glyph's own width. */
  .ol-mv[data-kind="stone"] .ol-boast .ol-quote-open,
  .ol-mv[data-kind="breath"] .ol-boast .ol-quote-open {
    margin-left: -0.5em;
  }

  /* Sized so the longest boast in the piece - "The scrolls of the Law were
     torn and burned;" - sits comfortably on two lines in a 44ch column. */
  .ol-mv[data-kind="stone"] .ol-boast,
  .ol-mv[data-kind="breath"] .ol-boast {
    font-size: clamp(1.35rem, 2.1vw, 1.85rem);
  }

  .ol-mv[data-kind="stone"] .ol-placard,
  .ol-mv[data-kind="breath"] .ol-placard {
    justify-content: flex-start;
    width: 100%;
  }

  .ol-mv[data-kind="stone"] .ol-link,
  .ol-mv[data-kind="breath"] .ol-link {
    align-self: flex-start;
  }

  .ol-mv[data-kind="prologue"] .ol-text,
  .ol-mv[data-kind="dark"] .ol-text,
  .ol-mv[data-kind="finale"] .ol-text {
    gap: 1.7rem;
  }
}

@media (min-width: 1400px) {
  #ol-flame { right: 8vw; width: 48px; height: 48px; }
  .ol-flame-body { --ol-fs: 1.25; }
  .ol-flame-halo { width: 124px; height: 124px; bottom: -46px; }
  .ol-mv[data-kind="stone"],
  .ol-mv[data-kind="breath"] { padding-inline: 7vw; gap: 6rem; }
}

/* ------------------------------------------------------------
   html.ol-paged - reduced-motion document-flow variant
   ------------------------------------------------------------ */

html.ol-paged #ol-scroll {
  min-height: 0;
  height: auto;
}

html.ol-paged #ol-stage {
  position: static;
  inset: auto;
  overflow: visible;
  height: auto;
}

html.ol-paged #ol-stage::before {
  display: none;
}

html.ol-paged .ol-mv {
  position: static;
  inset: auto;
  display: flex;
  opacity: 1;
  pointer-events: auto;
  transition: none;
  padding: 4rem 1.5rem;
  border-bottom: 1px solid rgba(122, 106, 79, 0.3);
}

html.ol-paged .ol-mv:last-child {
  border-bottom: none;
}

html.ol-paged #ol-flame {
  position: sticky;
  bottom: calc(18px + env(safe-area-inset-bottom));
  right: 12vw;
  margin-left: auto;
  z-index: 20;
}

@media (min-width: 900px) {
  html.ol-paged .ol-mv[data-kind="stone"],
  html.ol-paged .ol-mv[data-kind="breath"] {
    padding: 5rem clamp(2.5rem, 5vw, 6rem);
  }
}

@media (prefers-reduced-motion: reduce) {
  html:not(.ol-paged) .ol-mv { transition: none; }
}
