/* ===================================================================
   THE FOREST — five depth layers, the path spine, mist, grain
   Only transform and opacity are ever animated: both are
   compositor-only, so no layout or paint happens per frame.
   =================================================================== */

.forest {
  position: fixed;
  inset: -8vh -6vw;          /* bleed, so parallax never exposes an edge */
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}

/* Layer 0 — the sky seen through the canopy. Warm gold low on the
   horizon at dusk 0, cooling and dropping away as --dusk climbs. */
.forest__sky {
  position: absolute; inset: 0;
  background:
    radial-gradient(120% 80% at 72% calc(38% + var(--dusk) * 26%),
      color-mix(in oklab, var(--sun-core), var(--foxfire) calc(var(--dusk) * 78%)) 0%,
      color-mix(in oklab, var(--amber), transparent calc(35% + var(--dusk) * 45%)) 22%,
      transparent 62%),
    linear-gradient(180deg,
      color-mix(in oklab, var(--canopy), var(--hollow) calc(var(--dusk) * 90%)) 0%,
      var(--ground) 58%,
      var(--hollow) 100%);
}

/* Layer 1 — volumetric shafts (WebGL2). Sits behind the trunks so the
   trunk layer occludes it and the beams break into vertical bands. */
/* The shader paints the sky too, so this is an opaque background. It used
   to be mix-blend-mode: screen over the CSS gradient below, which forced
   a full-viewport blending group AND a gradient repaint every frame. */
.forest__rays {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
}

/* Only the no-WebGL fallback needs the CSS sky */
html.has-gl .forest__sky { display: none; }

/* No WebGL2: a static layered-gradient ray composite instead.
   Handsome, zero JS, and nobody sees a blank rectangle. */
html.no-gl .forest__rays {
  background:
    linear-gradient(101deg, transparent 30%, color-mix(in oklab, var(--sun-core), transparent 78%) 36%, transparent 42%),
    linear-gradient(97deg,  transparent 44%, color-mix(in oklab, var(--sun), transparent 82%) 51%, transparent 57%),
    linear-gradient(104deg, transparent 58%, color-mix(in oklab, var(--amber), transparent 84%) 65%, transparent 71%),
    radial-gradient(60% 50% at 72% 34%, color-mix(in oklab, var(--sun-core), transparent 72%), transparent 70%);
  opacity: calc(.9 - var(--dusk) * .55);
}

/* Layers 2–4 — silhouettes, generated in scene.js */
.layer {
  position: absolute;
  left: 0; right: 0;
  will-change: transform;
  contain: paint;
}
.layer svg { width: 100%; height: 100%; display: block; }

/* Tall enough that the bounded parallax travel in scene.js can never
   drag a layer's bottom edge into frame. */
.layer--canopy { top: -12%; height: 78%;  opacity: .95; }
.layer--far    { top: -6%;  height: 155%; opacity: .82; }
.layer--trunks { top: -12%; height: 175%; opacity: 1; }

/* Atmospheric perspective: distance is not just smaller, it is bluer,
   hazier and lower in contrast, because you are looking through air. */
.layer--canopy svg { filter: blur(6px) saturate(.5) brightness(.55); }
.layer--far svg    { filter: blur(.6px) saturate(.72) brightness(.72); }
.layer--trunks svg { filter: saturate(.95); }

/* Radiation fog pooling in the hollows */
.forest__mist {
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 55%;
  background: linear-gradient(0deg,
    color-mix(in oklab, var(--foxfire), transparent calc(88% + var(--dusk) * 6%)) 0%,
    color-mix(in oklab, var(--bark-silver), transparent 94%) 30%,
    transparent 100%);
  opacity: calc(.35 + var(--dusk) * .65);
  will-change: transform;
}

/* ===================================================================
   THE PATH — one trail, drawn exactly as far as you have walked
   =================================================================== */

.spine {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  z-index: 1;
  pointer-events: none;
  overflow: visible;
}
.spine path { fill: none; stroke-linecap: round; }

.spine__shadow {
  stroke: var(--hollow);
  stroke-width: 10;
  opacity: .55;
}
.spine__trail {
  stroke: var(--amber);
  stroke-width: 2;
  opacity: .5;
  stroke-dasharray: 4 9;
  /* Deliberately NOT --live. This element is as tall as the document, so
     tying its filter to --dusk re-rendered the whole thing every frame. */
  filter: drop-shadow(0 0 6px color-mix(in oklab, var(--amber), transparent 55%));
}

/* ===================================================================
   FOREGROUND FRONDS — near-black, blurred, and moving FASTER than the
   content. This single layer is what puts the reader inside the space
   instead of in front of a picture of it.
   =================================================================== */

.fronds {
  position: fixed;
  inset: 0;
  z-index: 6;
  pointer-events: none;
  overflow: hidden;
}
/* These sit in front of the reader, so they stay in the outer margin of
   the frame. A foreground silhouette across a headline is atmosphere
   bought with legibility, which is a bad trade. */
.fronds .frond {
  position: absolute;
  width: clamp(160px, 21vw, 400px);
  height: clamp(260px, 46vh, 640px);
  filter: blur(7px) brightness(.15);
  will-change: transform;
  opacity: .9;
}
.fronds .frond svg { height: 100%; }
.fronds .frond--tl { top: -20%;  left: -13%;  transform-origin: top left; rotate: 16deg; }
.fronds .frond--tr { top: -22%;  right: -12%; transform-origin: top right; scale: -1 1; rotate: 12deg; }
.fronds .frond--bl { bottom: -26%; left: -12%; transform-origin: bottom left; rotate: -150deg; }
.fronds .frond--br { bottom: -24%; right: -11%; transform-origin: bottom right; scale: -1 1; rotate: -156deg; }

/* Highest overdraw, lowest payoff on a small screen. Dropped on mobile. */
@media (max-width: 780px) {
  .fronds .frond--bl, .fronds .frond--br { display: none; }
  .fronds .frond { filter: blur(4px) brightness(.18); }
}

/* ===================================================================
   THE AIR — pollen becoming fireflies. Sits above content at low
   alpha, because dust hangs in front of what you are looking at.
   All contrast pairs have 9:1+ headroom, so this costs no legibility.
   =================================================================== */

.air {
  position: fixed;
  inset: 0;
  z-index: 7;
  width: 100%; height: 100%;
  pointer-events: none;
}

/* Airborne dust, giving the whole frame volume */
.grain {
  position: fixed;
  inset: -50%;
  z-index: 8;
  pointer-events: none;
  opacity: .16;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grain-drift 5s steps(6) infinite;
}
@keyframes grain-drift {
  0%   { transform: translate3d(0,0,0); }
  20%  { transform: translate3d(-3%, 2%, 0); }
  40%  { transform: translate3d(2%, -3%, 0); }
  60%  { transform: translate3d(-2%, -2%, 0); }
  80%  { transform: translate3d(3%, 1%, 0); }
  100% { transform: translate3d(0,0,0); }
}
@media (prefers-reduced-motion: reduce) { .grain { animation: none; } }

/* ===================================================================
   BOTANICAL PLATE DIVIDERS — hand-built beech branches that draw
   themselves on as you arrive. Section breaks become specimens.
   =================================================================== */

.divider {
  width: min(100%, 720px);
  margin: 0 auto clamp(2.5rem, 6vw, 5rem);
  opacity: .8;
}
.divider svg { width: 100%; height: auto; overflow: visible; }
.divider .ink {
  fill: none;
  stroke: var(--bark-silver);
  stroke-width: 1.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.divider .leaf {
  fill: color-mix(in oklab, var(--moss), transparent 40%);
  stroke: var(--bark-silver);
  stroke-width: .9;
}
.divider .vein { fill: none; stroke: var(--bark-silver); stroke-width: .5; opacity: .55; }

/* draw-on */
.divider [data-draw] {
  stroke-dasharray: var(--len);
  stroke-dashoffset: var(--len);
  transition: stroke-dashoffset 1600ms var(--ease-settle);
}
.divider.is-found [data-draw] { stroke-dashoffset: 0; }
.divider .leaf, .divider .vein {
  opacity: 0;
  transition: opacity 700ms ease var(--leaf-delay, 0ms);
}
.divider.is-found .leaf { opacity: 1; }
.divider.is-found .vein { opacity: .55; }
