/* Scroll-driven layer (October 20, 2026). Applies only when js/scenes.js has
   started (html.scenes). Every scroll position is one frame: chapters pin, the
   page colour flips between paper and ink, lines draw, and the stage canvas
   behind the content keeps changing one drawing. Only transform, opacity,
   clip-path and stroke properties animate. */

/* Theme changes are physical edges: every chapter keeps its own solid colour
   (css/site.css), and a chapter that follows a pinned one slides up over it for
   the last screen of the hold (data-overlap / data-covered, set by build.py). */
html.scenes .ch[data-pin] { height: calc(var(--pin) * 1vh); }
html.scenes .ch[data-covered] { height: calc((var(--pin) + 100) * 1vh); }
html.scenes .ch[data-overlap] { margin-top: -100vh; }
/* A pinned chapter whose content is taller than the screen flows instead (set by
   scenes.js), and so does the panel that would have slid over it. */
html.scenes .ch.no-pin { height: auto !important; }
html.scenes .ch.no-pin .ch__frame--pin { position: relative; height: auto; overflow: visible; }
html.scenes .ch.no-overlap { margin-top: 0 !important; }
/* A layered chapter that has to flow shows its layers one under another. */
html.scenes .ch.no-pin .lys__stack { height: auto; display: grid; gap: 3rem; }
html.scenes .ch.no-pin .ly { position: static; }
/* The stage draws the measuring rule itself. */
html.scenes .ruler__ticks, html.scenes .ruler__fill { visibility: hidden; }
html.scenes .ch__frame--pin { position: sticky; top: 0; height: 100vh; overflow: hidden; padding-block: calc(var(--hdr) + 5vh) 7vh; }
html.scenes .ch--hero .ch__frame--pin, html.scenes .ch--people .ch__frame--pin, html.scenes .ch--top:not(.ch--head) .ch__frame--pin { padding: 0; }
html.scenes .hero__grid, html.scenes .cph { min-height: 100vh; height: 100vh; }
html.scenes .pp { padding-top: 30vh; }

/* Line masks: each line sits in a clip-path window and slides through it. */
.ln { display: block; clip-path: inset(-.06em -.3em -.16em -.3em); }
.ln > span { padding-bottom: .02em; }
.ln > span { display: block; will-change: auto; }
html.scenes [data-fx="rise"] { will-change: auto; }

/* Rules draw from the left (the script sets their clip). */
html.scenes .rule[data-draw] { clip-path: inset(0 100% 0 0); }

/* Timeline: one row that travels while the chapter pins (desktop). */
@media (min-width: 761px) {
  html.scenes .ch--timeline .tl__track { position: absolute; left: 0; right: 0; top: 58vh; margin: 0; }
  html.scenes .tl__list { display: flex; gap: 0; width: max-content; margin: 0; padding-left: calc((100vw - min(100vw - var(--gutter) * 2, var(--frame))) / 2); }
  html.scenes .tl__item { width: max(22vw, 260px); padding: 2.2rem 2.5rem 0 0; border-top: 0; }
  html.scenes .lys__stack { position: relative; height: min(46vh, calc(100vh - 25rem)); margin-top: min(7vh, 4rem); }
  html.scenes .ly { position: absolute; inset: 0 0 auto 0; }
  html.scenes .ch--map .pins { margin-top: 5vh; }
  html.scenes .ch--impact .ruler { margin-top: 12vh; }
  html.scenes .sts__list { margin-top: 26vh; }
  html.scenes .st { transition: opacity .6s var(--ease-out); }
  html.scenes .st:not(.is-on) { opacity: .72; }
}

/* Hero media closes into a frame as the hero hands over. */
html.scenes [data-hero-media] { will-change: auto; }

/* Header: the current chapter's index fades between labels. */
.hdr__where { transition: opacity .35s var(--ease-out); }
.hdr__where.is-swapping { opacity: 0; }

/* Page transitions (js/scenes.js + js/world.js): the page fades away while the camera
   flies to the next page's branch, and the next page fades in over the same shot. */
html.leaving main, html.leaving .foot, html.leaving .banner, html.leaving .world-labels, html.leaving .menu { opacity: 0; transition: opacity .38s var(--ease-soft); pointer-events: none; }
html.arriving main, html.arriving .foot, html.arriving .banner, html.arriving .world-labels { opacity: 0; }
html.world main, html.world .foot { transition: opacity .6s var(--ease-soft); }

/* Menu opens as a wipe from the top. */
html.scenes .menu { clip-path: inset(0 0 100% 0); transition: clip-path .6s var(--ease-io); }
html.scenes .menu.is-open { clip-path: inset(0 0 0 0); }

/* Home intro ------------------------------------------------------------------- */
.intro { display: none; }
html.intro-play .intro { display: block; position: relative; height: 380vh; margin-bottom: -200vh; }
@media (min-width: 761px) { html.intro-play.scenes .ch--hero { height: calc((var(--pin) + 100) * 1vh); } }
.intro__stage { position: sticky; top: 0; z-index: 3; height: 100vh; display: grid; place-items: center; overflow: hidden; background: var(--paper); color: #314b6e; }
.intro__word { width: min(52vw, 56rem); height: auto; overflow: visible; fill: currentColor; fill-opacity: 0; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.intro__word .fill { stroke: none; }
.intro__word .line { fill: none; }
.intro__cue { position: absolute; left: 50%; bottom: max(4vh, 24px); display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 8px 12px; color: var(--navy); font: 600 .8125rem/1 var(--f-sub); letter-spacing: .04em; transform: translateX(-50%); }
.intro__cue i { position: relative; display: block; width: 1px; height: 44px; overflow: hidden; background: rgba(49, 75, 110, .18); }
.intro__cue i::after { content: ""; position: absolute; left: 0; top: 0; width: 1px; height: 100%; background: #314b6e; transform: translateY(-100%); animation: cue 2.2s var(--ease-io) infinite; }
@keyframes cue { 0% { transform: translateY(-100%); } 55% { transform: translateY(0); } 100% { transform: translateY(100%); } }
.intro__skip { position: absolute; left: 50%; bottom: calc(max(4vh, 24px) + 76px); padding: .6rem 1rem; border: 1px solid rgba(13, 17, 23, .3); background: var(--paper); color: var(--ink); font: 600 .875rem/1 var(--f-display); transform: translateX(-50%); clip-path: inset(50%); white-space: nowrap; }
.intro__skip:focus-visible { clip-path: none; }
.intro__lockup { position: fixed; inset: 0; z-index: 70; pointer-events: none; visibility: hidden; color: var(--ink); }
html.intro-play.intro-ready .intro__lockup { visibility: visible; }
.intro__mark, .intro__name { position: absolute; left: 0; top: 0; transform-origin: 0 0; opacity: 0; }
.intro__mark { display: block; width: 44px; height: 44px; }
.intro__mark svg { display: block; width: 100%; height: 100%; overflow: visible; }
.intro__fill { fill: #314b6e; fill-opacity: 0; transition: fill .5s var(--ease-out); }
.intro__lines { fill: none; stroke: #314b6e; transition: stroke .5s var(--ease-out); }
.intro__name { transition: color .5s var(--ease-out); }
.intro__name > span { opacity: 0; }
.intro__lockup.is-dark { color: var(--paper); }
.intro__lockup.is-dark .intro__fill { fill: var(--paper); }
.intro__lockup.is-dark .intro__lines { stroke: var(--paper); }
html.intro-play:not(.intro-ready) .hdr { opacity: 0; visibility: hidden; }
.site-hero-gap { display: none; }

@media (max-width: 760px) {
  /* Phones: nothing pins. Chapters flow, and their copy still arrives on its timers. */
  html.scenes .ch[data-pin], html.scenes .ch[data-covered] { height: auto; }
  html.scenes .ch[data-overlap] { margin-top: 0; }
  html.scenes .ch__frame--pin { position: relative; height: auto; overflow: visible; padding-block: calc(var(--hdr) + 6vh) 10vh; }
  html.scenes .ch--hero .ch__frame--pin, html.scenes .ch--people .ch__frame--pin, html.scenes .ch--top:not(.ch--head) .ch__frame--pin { padding: 0; }
  html.scenes .hero__grid, html.scenes .cph { min-height: 100svh; height: auto; }
  html.intro-play .intro { height: 380svh; margin-bottom: -200svh; }
  /* Under the intro the phone hero stays held, so the hand-over is full-bleed. */
  html.intro-play.scenes .ch--hero { height: 200svh; }
  html.intro-play.scenes .ch--hero .ch__frame--pin { position: sticky; top: 0; height: 100svh; overflow: hidden; }
  html.intro-play.scenes .ch--hero .hero__grid { height: 100svh; }
  .intro__stage { height: 100svh; }
  .intro__word { width: 82vw; }
  html.scenes .ch--timeline .tl__track { margin-top: 4vh; }
  html.scenes .ruler__ticks, html.scenes .ruler__fill { visibility: visible; }
  html.scenes .lys__stack { position: static; height: auto; display: grid; gap: 3rem; }
  html.scenes .ly { position: static; }
  html.scenes .pp { padding-top: 22svh; }
}

@media (prefers-reduced-motion: reduce) {
  .intro { display: none !important; }
  html.scenes .ch[data-pin] { height: auto; }
  html.scenes .ch__frame--pin { position: relative; height: auto; overflow: visible; }
  html.scenes .rule[data-draw] { clip-path: none; }
  html.scenes .menu, .hdr, .btn, .go { transition: none !important; }
}

/* 3D world (js/world.js) ----------------------------------------------------------------
   The canvas sits where the 2D stage was: above chapter backgrounds, below chapter
   content. Chapters that show the world are transparent; the footer is a solid panel. */
.world-canvas { position: fixed; inset: 0; z-index: 0; display: block; width: 100%; height: 100%; pointer-events: none; }
html.world .stage { display: none; }
html.world .ch[data-world], html.world .ch--hero { background: transparent; }
/* A page opened from another page's flight shows that flight's last frame (this page's
   first shot) until its own world has drawn over it. */
html.has-shot .ch[data-world], html.has-shot .ch--hero { background: transparent; }
html.world .foot { position: relative; z-index: 3; }
/* Chapters outside the world are solid panels that pass over it. */
html.world .ch:not([data-world]):not(.ch--hero), html.world .notes { position: relative; z-index: 3; }
/* In the impact chapter the world's giant word is the heading; the text heading stays
   for screen readers. */
html.world .ch--impact[data-world] .ip__title, html.world .ch[data-world] .head__title.ip__title { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
html.world .ch[data-world] .head__media { position: relative; z-index: 1; }
html.world .foot__close { display: none; } /* no gap above the footer: nothing is drawn there now */
/* Labels projected from points in the world (milestones, pins). */
/* above the held world chapters (so the labels' links work), below the solid panels */
.world-labels { position: fixed; inset: 0; z-index: 2; pointer-events: none; overflow: hidden; }
.wl { position: absolute; left: 0; top: 0; opacity: 0; transition: opacity .5s var(--ease-out); will-change: transform; }
.wl.is-on { opacity: 1; }
.wl.is-on.is-blocked { opacity: 0; }
.wl.is-on a { pointer-events: auto; }
.wl.is-on.is-blocked a { pointer-events: none; }
.wl__in { position: absolute; left: 0; bottom: 0; transition: transform .45s var(--ease-out); width: max-content; max-width: 15rem; color: var(--ink); }
.wl--tl .wl__in { transform: translate(-50%, -14px); text-align: center; }
.wl--tl .tl__date { font-size: 1.125rem; }
.wl--tl .tl__what { margin-top: .3rem; font-size: .875rem; }
.wl--tl .tl__where { font-size: .75rem; }
.wl--tl.is-next .tl__date { color: var(--navy); }
/* an article's paragraphs, floating in the scene one at a time */
.wl--para .wl__in { bottom: auto; top: 0; transform: translate(-50%, -50%); width: min(34rem, 78vw); max-width: none; text-align: center; }
.wl--para .wl__in::before { content: ""; position: absolute; inset: -3.5rem -5rem; z-index: -1; background: radial-gradient(closest-side, rgba(242, 240, 234, .9), rgba(242, 240, 234, .78) 55%, rgba(242, 240, 234, 0)); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); -webkit-mask-image: radial-gradient(closest-side, #000 55%, transparent); mask-image: radial-gradient(closest-side, #000 55%, transparent); }
.wl--para .wl__p { font: 500 clamp(1.25rem, 1.7vw, 1.625rem)/1.42 var(--f-display); letter-spacing: -.012em; color: var(--ink); text-shadow: 0 0 22px var(--paper), 0 0 8px var(--paper), 0 0 2px var(--paper); }
.wl--para .wl__p a { text-decoration: underline; text-underline-offset: 3px; }
.wl--cap .wl__in { bottom: auto; top: 0; transform: translate(-50%, 0); width: min(28rem, 78vw); max-width: none; text-align: center; }
.wl--cap .wl__cap { font: 500 .8125rem/1.4 var(--f-sub); color: var(--grey); }
.wl--post .wl__in { bottom: auto; top: 0; transform: translate(-50%, 10px); width: 15rem; max-width: none; text-align: center; }
.wl--post .po__a { display: block; padding: 0; border: 0; }
.wl--post .po__meta { margin: 0; }
.wl--post .po__t { margin-top: .4rem; font-size: 1.375rem; line-height: 1.08; letter-spacing: -.02em; }
.wl--post .go { margin-top: .6rem; justify-self: center; }
.wl--pin .wl__in { bottom: auto; top: 0; transform: translate(16px, -50%); padding-left: .9rem; border-left: 1px solid rgba(13, 17, 23, .35); max-width: 17rem; }
.wl--pin.is-left .wl__in { transform: translate(calc(-100% - 16px), -50%); padding: 0 .9rem 0 0; border-left: 0; border-right: 1px solid rgba(13, 17, 23, .35); text-align: right; }
.wl--pin .pin__name { font: 500 1rem/1.15 var(--f-display); }
.wl--pin .pin__city { font-size: .75rem; color: var(--grey); }
.wl--pin .pin__list { margin-top: .4rem; font-size: .8125rem; }
.wl--pin .pin__list li { padding: .1rem 0; }
/* What the world draws is not drawn twice: the flat versions stay for screen readers
   and come back when they hold keyboard focus. */
html.has-shot .ch[data-world] :is(.tl__track, .pins, .ruler, .rds, .pos__list, .cw__when),
html.world .ch[data-world] .tl__track:not(:focus-within),
html.world .ch[data-world] .pins:not(:focus-within),
html.world .ch[data-world] .ruler,
html.world .ch[data-world] .pos__list:not(:focus-within),
html.world .ch--when[data-world] .cw__when,
html.world .ch[data-world] .ar__body:not(:focus-within), html.world .ch[data-world] .ar__fig,
html.world .ch[data-world] .rds:not(:focus-within) { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
html:has(.tl__track:focus-within) .wl--tl { opacity: 0 !important; }
@media (min-width: 761px) {
  html.world .ch--story .story__lead { grid-column: 1 / 8; font-size: clamp(2.1rem, min(4.6vw, 7.4vh), 4.75rem); }
  html.world .ch--story .story__body { grid-column: 1 / 7; margin-top: min(2.25rem, 4vh); font-size: clamp(.95rem, min(1.25vw, 2.1vh), 1.25rem); }
  html.world .ch--story .story__go { grid-column: 1 / 6; }
  html.world .ch--story .lbl { grid-column: 1 / -1; }
}
@media (max-width: 760px) {
  /* Phones with the world (after Hut 8's phone site): every world chapter holds the whole
     screen, the scene fills it, and a short caption sits at its foot on a soft paper veil.
     Labels in the scene and the 3D figures are used as on desktop; long copy is trimmed. */
  html.world.scenes .ch[data-world][data-pin]:not(.no-pin) { height: calc(var(--pin) * 1vh); }
  html.world.scenes .ch[data-world] .ch__frame--pin { position: sticky; top: 0; height: 100svh; overflow: hidden; display: flex; flex-direction: column; justify-content: flex-end; padding: var(--hdr) 0 max(5svh, 1.75rem); }
  html.world .ch[data-world] .ch__frame { background: linear-gradient(to top, rgba(242, 240, 234, .95) 0%, rgba(242, 240, 234, .88) 30%, rgba(242, 240, 234, .5) 46%, rgba(242, 240, 234, 0) 60%); }
  html.world .ch[data-world] .frame { row-gap: 0; }
  html.world .ch[data-world] :is(h1, h2):not(.ip__title) { font-size: clamp(1.75rem, 8.4vw, 2.25rem) !important; line-height: 1.02; margin-bottom: .6rem; }
  html.world .ch[data-world] .story__lead { font-size: clamp(1.75rem, 8.4vw, 2.25rem) !important; }
  html.world .ch[data-world] :is(.head__lede, .story__body, .ly__p, .pg__p, .ev__p, .cta__p, .cw__about p, .mp__p, .pp__p, .st__p, .ar__dek, .po__p, .qt__p) {
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; font-size: .9375rem; line-height: 1.45; margin-top: .4rem; max-width: 34ch;
  }
  html.world .ch[data-world] :is(.pg__list, .pg__note, .ev__thumb, .pjl__ph, .head__media, .ar__by, .tl__note, .ev__when, .head__lbl) { display: none !important; }
  html.world .ch[data-world] .cw__about p + p { display: none; }
  html.world .ch[data-world] :is(.head__go, .story__go, .ly__go, .pg__go, .ev__go, .cta__go, .pp__go, .ip__go, .pjs__go) { margin-top: 1rem; gap: .75rem 1.25rem; }
  /* layered chapters keep their layers in one place, one at a time, as on desktop */
  html.world.scenes .ch[data-world] .lys__stack { position: relative; display: block; height: 11.5rem; margin-top: .5rem; }
  html.world.scenes .ch[data-world] .ly { position: absolute; inset: 0 0 auto 0; }
  html.world.scenes .ch[data-world] .ly__i { margin-bottom: .4rem; }
  /* steps: only the one the camera is at */
  html.world .ch[data-world] .sts__list { display: grid; grid-template-columns: minmax(0, 1fr); margin-top: .75rem; }
  html.world .ch[data-world] .sts__list .st { grid-area: 1 / 1; border-top: 0; padding-top: 0; opacity: 0; transition: opacity .5s var(--ease-out); }
  html.world .ch[data-world] .sts__list .st.is-cur { opacity: 1; }
  html.world.scenes .pp { padding-top: 0; }
  /* the hero is held and the first scene slides up over it, as on desktop (no dark slab) */
  html.world.scenes .ch--hero[data-covered] { height: calc((var(--pin) + 100) * 1svh); }
  html.world.scenes .ch--hero .ch__frame--pin { position: sticky; top: 0; height: 100svh; overflow: hidden; }
  html.world.scenes .ch--hero .hero__grid { height: 100svh; }
  html.world.scenes .ch--hero + .ch[data-overlap] { margin-top: -100svh; }
  /* the report banner waits under the header, clear of the caption at the foot */
  html.world .banner { top: calc(var(--hdr) + 6px); bottom: auto; }
  /* labels in the scene, a size smaller */
  .wl__in { max-width: 12rem; font-size: .875rem; }
  .wl--tl .tl__date { font-size: 1rem; }
  /* an article's paragraph and caption hold the middle of the screen while the camera moves */
  .wl--para, .wl--cap { transform: translate3d(50vw, 36svh, 0) !important; }
  .wl--cap { transform: translate3d(50vw, 64svh, 0) !important; }
  .wl--para .wl__in { width: 84vw; }
  .wl--para .wl__p { font-size: 1.125rem; }
  .wl--post .wl__in { width: 7.75rem; }
  .wl--post .po__t { font-size: .9375rem; }
  .wl--post .po__meta { font-size: .625rem; }
}

/* Short laptop screens: held chapters tighten so they still fit and hold. */
@media (min-width: 761px) and (max-height: 820px) {
  html.scenes .ch__frame--pin { padding-block: calc(var(--hdr) + 1.5vh) 3vh; }
  html.scenes .lys__title, html.scenes .tl__title, html.scenes .mp__title, html.scenes .pp__title { font-size: clamp(2rem, 6.2vh, 3.75rem); }
  html.scenes .lys__stack { margin-top: 2.5vh; height: calc(100vh - 17rem); }
  html.scenes .ch .rule { margin-bottom: 1rem; }
  html.scenes .ch .lbl { margin-bottom: 1rem; }
  .pjl__ph { width: min(190px, 60%); margin-bottom: .75rem; }
  .ly__p { margin-top: .75rem; }
  .ly__go { margin-top: .9rem; }
}

/* The menu over the world: a solid ink panel; the world stays still behind it. */
html.world .menu { background: var(--ink); }
html.world body.menu-open main, html.world body.menu-open .foot, html.world body.menu-open .banner { opacity: 0; transition: opacity .45s var(--ease-soft); }
html.world .menu__nav a { transition: color .3s var(--ease-out), transform .45s var(--ease-out); }
html.world .menu__nav a:hover, html.world .menu__nav a:focus-visible { color: var(--paper); }

/* Branch pages: the copy keeps to the left half and the model owns the right. */
@media (min-width: 761px) {
  html.world .ch--left[data-world] .frame > :not(.rule):not(.lbl):not(.sts__list) { grid-column: 1 / 7 !important; }
  /* the date chapter: the date is in the scene; the about copy sits on a soft veil */
  html.world .ch--when[data-world] .ch__frame { background: linear-gradient(90deg, rgba(242, 240, 234, .9) 0%, rgba(242, 240, 234, .8) 36%, rgba(242, 240, 234, 0) 47%); }
  html.world .ch--when[data-world] .cw__about { margin-top: 0; align-self: center; }
  html.world .ch--when[data-world] .cw__about p { color: var(--ink); font-size: 1.125rem; line-height: 1.6; max-width: 46ch; }
  html.world .ch--left[data-world] .head__title { font-size: clamp(2.5rem, 5.4vw, 5.5rem); }
  html.world .ch--left[data-world] .head__lede { margin-top: 1.5rem; max-width: 40ch; }
  html.world .ch--left[data-world] .ev__thumb { width: min(220px, 50%); margin-top: 1.5rem; }
}
.ev__thumb .ph { aspect-ratio: 4 / 3; }


/* Steps held over the world (how support works, how the day runs): only the step the
   camera is at is shown, under its model. */
@media (min-width: 761px) {
  html.world .ch[data-world] .sts__list { position: absolute; left: 50%; top: 60vh; width: min(44ch, 80vw); margin: 0; transform: translateX(-50%); display: grid; grid-template-columns: minmax(0, 1fr); justify-items: center; text-align: center; }
  html.world .ch[data-world] .sts__list .st { grid-area: 1 / 1; max-width: 42ch; border-top: 0; opacity: 0; transform: translateY(12px); transition: opacity .5s var(--ease-out), transform .6s var(--ease-out); }
  html.world .ch[data-world] .sts__list .st.is-cur { opacity: 1; transform: none; }
}
/* inside the venue, the step's words sit under the element on the floor */
@media (min-width: 761px) { html.world .ch--day[data-world] .sts__list { top: 69vh; } }
/* Impact events: the copy sits on the same soft paper veil as the date shots, so the
   neighbouring venues on the arc never show through behind it */
@media (min-width: 761px) { html.world .ch[data-world^="ev-"] .ch__frame { background: linear-gradient(90deg, rgba(242, 240, 234, .92) 0%, rgba(242, 240, 234, .84) 36%, rgba(242, 240, 234, 0) 50%); } }

/* Copy over the world (not the words in the scene): a darker grey and a soft paper halo,
   so the dotted ground and the models never show through the letters. */
html.world .ch[data-world] { --fg-soft: #3f444b; }
html.world .ch[data-world] :is(p, li, dt, dd, .go, .lbl) { text-shadow: 0 0 3px var(--paper), 0 0 14px var(--paper); }
html.world .ch[data-world] :is(h1, h2, h3) { text-shadow: 0 0 18px var(--paper); }
html.world .ch[data-world] .lbl { color: #4a4f56; }

