/* motion — the four primitives, scroll-linked scenes, dialogs. docs/DESIGN.md §7, §8.
   Every primitive collapses to a 200ms opacity fade under prefers-reduced-motion or html[data-still]. */

/* ---- 1. Masked line reveal (data-reveal="lines") --------------------------------------------------
   The reveal controller groups .w spans into .ln wrappers after fonts.ready, then adds .is-grouped.
   Only a grouped headline hides its lines; an ungrouped one is always visible. */
[data-reveal="lines"] .ln { display:block; overflow:hidden; padding:.15em .05em .3em; margin:-.15em -.05em -.3em; }
[data-reveal="lines"] .ln__in { display:block; }
[data-reveal="lines"].is-grouped .ln__in { transform:translateY(180%); }   /* glyphs overflow a .92 line box, so go well past the .3em padding */
[data-reveal="lines"].is-grouped.is-revealed .ln__in {
  transform:translateY(0);
  transition:transform var(--d-reveal) var(--ease-out);
  transition-delay:calc(var(--i, 0) * 120ms + var(--li, 0) * 120ms);
}
[data-reveal="lines"].is-grouped .accent { filter:blur(6px); opacity:0; }
[data-reveal="lines"].is-grouped.is-revealed .accent {
  filter:blur(0); opacity:1;
  transition:filter 800ms var(--ease-out), opacity 800ms var(--ease-out);
  transition-delay:calc(var(--i, 0) * 120ms + var(--li, 0) * 120ms + 200ms);
}

/* ---- 2. Hairline draw (data-reveal="line") ------------------------------------------------------- */
[data-reveal="line"] { transform:scaleX(0); transform-origin:left center; }
[data-reveal="line"].is-revealed { transform:scaleX(1); transition:transform var(--d-line) var(--ease-out); transition-delay:calc(var(--i, 0) * 120ms); }
[data-reveal="line"].rule--v, [data-reveal="line"][data-axis="y"] { transform:scaleY(0); transform-origin:top center; }
[data-reveal="line"][data-axis="y"].is-revealed, [data-reveal="line"].rule--v.is-revealed { transform:scaleY(1); }

/* ---- 3. Plate reveal (data-reveal="plate") --------------------------------------------------------
   A cover layer in the mat's colour scales to 0 from one edge; the media inside settles 1.08→1. */
/* Put data-reveal="plate" on the .mat (or on the .plate itself). data-from="top|right|left|bottom". */
[data-reveal="plate"] .plate::after, .plate[data-reveal="plate"]::after {
  content:""; position:absolute; inset:0; z-index:5; pointer-events:none;
  background:var(--cover, var(--mat-bg, var(--bg-2)));
  transform:scale(1); transform-origin:top center;
}
.plate--dark, .mat--dark .plate { --cover:var(--dark-2); }
[data-reveal="plate"][data-from="right"] .plate::after,  .plate[data-reveal="plate"][data-from="right"]::after  { transform-origin:right center; }
[data-reveal="plate"][data-from="left"] .plate::after,   .plate[data-reveal="plate"][data-from="left"]::after   { transform-origin:left center; }
[data-reveal="plate"][data-from="bottom"] .plate::after, .plate[data-reveal="plate"][data-from="bottom"]::after { transform-origin:bottom center; }
[data-reveal="plate"].is-revealed .plate::after, .plate[data-reveal="plate"].is-revealed::after {
  transform:scaleY(0); transition:transform var(--d-plate) var(--ease-slow); transition-delay:calc(var(--i, 0) * 120ms);
}
[data-reveal="plate"][data-from="right"].is-revealed .plate::after, .plate[data-reveal="plate"][data-from="right"].is-revealed::after,
[data-reveal="plate"][data-from="left"].is-revealed .plate::after,  .plate[data-reveal="plate"][data-from="left"].is-revealed::after { transform:scaleX(0); }
[data-reveal="plate"] .plate > img, [data-reveal="plate"] .plate > video, [data-reveal="plate"] .plate__media, [data-reveal="plate"] .plate__poster,
.plate[data-reveal="plate"] > img, .plate[data-reveal="plate"] > video { transform:scale(1.08); }
[data-reveal="plate"].is-revealed .plate > img, [data-reveal="plate"].is-revealed .plate > video, [data-reveal="plate"].is-revealed .plate__media, [data-reveal="plate"].is-revealed .plate__poster,
.plate[data-reveal="plate"].is-revealed > img, .plate[data-reveal="plate"].is-revealed > video { transform:scale(1); transition:transform 2200ms var(--ease-out); transition-delay:calc(var(--i, 0) * 120ms); }

/* ---- 4. Fade-rise (data-reveal="rise") ----------------------------------------------------------- */
[data-reveal="rise"] { opacity:0; transform:translateY(24px); }
[data-reveal="rise"].is-revealed { opacity:1; transform:translateY(0); transition:opacity var(--d-fade) var(--ease-out), transform var(--d-fade) var(--ease-out); transition-delay:calc(var(--i, 0) * 120ms); }
[data-reveal="fade"] { opacity:0; }
[data-reveal="fade"].is-revealed { opacity:1; transition:opacity var(--d-fade) var(--ease-out); transition-delay:calc(var(--i, 0) * 120ms); }
/* Without JS nothing should stay hidden (the definitive rule is at the end of this file, after the
   reduced-motion and Still blocks, so it wins over both). */
html.no-js .plate::after { display:none; }

/* ---- Scroll-linked scenes (CSS where the browser can; the scene controller writes the same vars) -- */
.cover-plate { --mat-t:max(var(--mat), var(--nav-h)); --mat-x:var(--mat); --mat-b:22vh; }
.cover-plate .plate, .cover-plate .h1-bone {
  clip-path:inset(calc(var(--mat-t) * (1 - var(--bleed))) calc(var(--mat-x) * (1 - var(--bleed))) calc(var(--mat-b) * (1 - var(--bleed))) calc(var(--mat-x) * (1 - var(--bleed))));
}
@keyframes bleed { to { --bleed:1; } }
@keyframes parallax { to { transform:translateY(15vh); } }
@keyframes read-progress { to { --read-p:1; } }
@keyframes tissue-lift { to { --tissue-p:1; } }
@supports (animation-timeline: scroll()) {
  [data-scene-var="--bleed"][data-scene-css] { animation:bleed linear both; animation-timeline:scroll(); animation-range:0 100svh; }
  .parallax[data-scene-css] { animation:parallax linear both; animation-timeline:scroll(); animation-range:0 100svh; }
  [data-scene-var="--read-p"][data-scene-css] { animation:read-progress linear both; animation-timeline:scroll(); }
  [data-scene-var="--tissue-p"][data-scene-css] { animation:tissue-lift linear both; animation-timeline:view(); animation-range:entry 0% cover 60%; }
}
.read-line { position:fixed; top:var(--nav-h); left:0; width:100%; height:1px; background:var(--accent); transform-origin:left; transform:scaleX(var(--read-p, 0)); z-index:var(--z-folio); pointer-events:none; }
.tissue { transform:translateY(calc(var(--tissue-p, 0) * -100%)); will-change:transform; }

/* Lamp return (DESIGN.md §4 b): a fixed paper layer revealed by a growing circle from 78% 14%.
   The layer exists only while the chapter-ground controller says the lamp is in progress
   (html.is-lamp, 0 < --lamp-p < 1): at rest it is visibility:hidden — a zero-size mask alone is not
   a reliable way to hide a fixed layer on a GPU compositor (Chrome painted it as a full sheet of
   paper). The hard edge is a clip-path circle (stays on the compositor); the same-sized soft mask
   feathers 8vmax inside it. While the ground is still night the layer sits above the nav and folio,
   so their bone text is simply covered by the paper as it grows; once --melt crosses .5 the ground
   flips to paper, the text turns ink, and the layer drops beneath them. */
.lamp-layer {
  position:fixed; inset:0; z-index:calc(var(--z-nav) + 1); pointer-events:none; background:var(--white);
  visibility:hidden; clip-path:circle(0 at 78% 14%);
}
html.is-lamp .lamp-layer {
  visibility:visible;
  clip-path:circle(calc(var(--lamp-p, 0) * 220vmax) at 78% 14%);
  mask-image:radial-gradient(circle, black 0 calc(100% - 8vmax), transparent 100%);
  -webkit-mask-image:radial-gradient(circle, black 0 calc(100% - 8vmax), transparent 100%);
  mask-repeat:no-repeat; -webkit-mask-repeat:no-repeat; mask-position:78% 14%; -webkit-mask-position:78% 14%;
  mask-size:calc(var(--lamp-p, 0) * 440vmax) calc(var(--lamp-p, 0) * 440vmax);
  -webkit-mask-size:calc(var(--lamp-p, 0) * 440vmax) calc(var(--lamp-p, 0) * 440vmax);
}
html.is-lamp[data-ground="light"] .lamp-layer { z-index:1; }

/* Word illumination: the scene controller toggles .is-lit per word; colour is the only thing that moves. */
.lit { color:var(--fg-3); }
.lit .w { transition:color 400ms var(--ease-out); }
.lit .w.is-lit { color:var(--fg); }

/* Three-hours style step panels */
.steps__panel { opacity:0; transition:opacity 600ms var(--ease-out); }
.steps__panel.is-active { opacity:1; clip-path:inset(0 0 0 0); animation:unroll 900ms var(--ease-out) both; }
@keyframes unroll { from { clip-path:inset(0 0 100% 0); } to { clip-path:inset(0 0 0 0); } }
.steps__label { color:var(--fg-3); transition:color 400ms var(--ease-out); }
.steps__label.is-active { color:var(--fg); }

/* ---- Dialogs: index sheet, drawers ----------------------------------------------------------------- */
dialog.sheet, dialog.index-sheet { opacity:0; transition:opacity var(--d-fade) var(--ease-out), overlay var(--d-fade) allow-discrete, display var(--d-fade) allow-discrete; }
dialog.sheet[open], dialog.index-sheet[open] { opacity:1; }
@starting-style { dialog.sheet[open], dialog.index-sheet[open] { opacity:0; } }
dialog.drawer {
  position:fixed; inset:0 0 0 auto; width:min(480px, 100vw); height:100dvh; max-height:100dvh; margin:0;
  background:var(--bg-2); color:var(--fg); padding:0;
  transform:translateX(100%); transition:transform 600ms var(--ease-slow), overlay 600ms allow-discrete, display 600ms allow-discrete;
}
dialog.drawer[open] { transform:translateX(0); }
@starting-style { dialog.drawer[open] { transform:translateX(100%); } }
dialog.drawer::backdrop { background:var(--scrim); opacity:0; transition:opacity 600ms var(--ease-slow); }
dialog.drawer[open]::backdrop { opacity:1; }
@starting-style { dialog.drawer[open]::backdrop { opacity:0; } }

/* ---- Video plates ----------------------------------------------------------------------------------- */
.plate__media { opacity:0; transition:opacity var(--d-fade) var(--ease-out); }
.plate__media.is-playing, .plate__media.is-ready { opacity:1; }
.plate.is-fallback .plate__media { opacity:0; }
.plate.is-fallback .plate__poster { opacity:.85; }

/* ---- Reduced motion / Still ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  [data-reveal="lines"].is-grouped .ln__in { transform:none; }
  [data-reveal="lines"].is-grouped .accent { filter:none; opacity:1; }
  [data-reveal="lines"], [data-reveal="rise"], [data-reveal="fade"], [data-reveal="line"] { opacity:0; transform:none; }
  [data-reveal].is-revealed { opacity:1; transform:none; transition:opacity 200ms var(--ease-out) !important; transition-delay:0s !important; }
  [data-reveal="plate"] .plate > img, [data-reveal="plate"] .plate > video, [data-reveal="plate"] .plate__media, [data-reveal="plate"] .plate__poster { transform:none; }
  [data-reveal="plate"] .plate::after, .plate[data-reveal="plate"]::after { transition:opacity 200ms var(--ease-out) !important; transform:none !important; opacity:1; }
  [data-reveal="plate"].is-revealed .plate::after, .plate[data-reveal="plate"].is-revealed::after { opacity:0; }
  .parallax, [data-scene-css] { animation:none !important; }
  .cover-plate { --bleed:0; }
  .lit .w { color:var(--fg); }
  .steps__panel { animation:none; }
  .tissue { transform:none; }
  .plate__media { opacity:0 !important; }           /* videos become posters over the mesh */
  .plate__poster { opacity:1 !important; }
}
html[data-still] [data-reveal="lines"].is-grouped .ln__in { transform:none; }
html[data-still] [data-reveal="lines"].is-grouped .accent { filter:none; opacity:1; }
html[data-still] [data-reveal="lines"], html[data-still] [data-reveal="rise"], html[data-still] [data-reveal="fade"], html[data-still] [data-reveal="line"] { opacity:0; transform:none; }
html[data-still] [data-reveal].is-revealed { opacity:1; transform:none; transition:opacity 200ms var(--ease-out) !important; transition-delay:0s !important; }
html[data-still] [data-reveal="plate"] .plate > img, html[data-still] [data-reveal="plate"] .plate > video, html[data-still] [data-reveal="plate"] .plate__media, html[data-still] [data-reveal="plate"] .plate__poster { transform:none; }
html[data-still] [data-reveal="plate"] .plate::after, html[data-still] .plate[data-reveal="plate"]::after { transition:opacity 200ms var(--ease-out) !important; transform:none !important; opacity:1; }
html[data-still] [data-reveal="plate"].is-revealed .plate::after, html[data-still] .plate[data-reveal="plate"].is-revealed::after { opacity:0; }
html[data-still] .parallax, html[data-still] [data-scene-css] { animation:none !important; }
html[data-still] .cover-plate { --bleed:0; }
html[data-still] .lit .w { color:var(--fg); }
html[data-still] .steps__panel { animation:none; }
html[data-still] .tissue { transform:none; }
html[data-still] .plate__media { opacity:0 !important; }
html[data-still] .plate__poster { opacity:1 !important; }

/* ---- No JS: nothing stays hidden, whatever the motion setting --------------------------------------
   Placed last on purpose: the reduced-motion and Still blocks above hide every primitive at opacity 0
   until the reveal controller adds .is-revealed, which never happens without JS. */
html.no-js [data-reveal], html.no-js [data-reveal="lines"].is-grouped .ln__in, html.no-js [data-reveal="lines"] .accent {
  opacity:1 !important; transform:none !important; filter:none !important;
}
