/* ============================================================
   Site 1.1, the world (scroll-scrubbed flight)
   The copy rail (layout, entrance/exit) lives in copy.css. The old
   morph layer (#morph / .m-ghost / .m-main) was removed: the
   element and its JS are gone from this build, the client rejected
   the horizontal line. No replacement here; that is a different
   builder's call.
   ============================================================ */

/* ---------- the flight stage ---------- */

#world{ position:relative; }

.stage{
  position:sticky; top:0;
  height:100svh; width:100%;
  overflow:hidden;
  background:var(--void);
}

/* Every leg is stacked; only the active one is visible. Opacity crossfade
   at the seams hides any residual mismatch between rendered endpoints. */
.leg{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:cover;
  opacity:0;
  /* Exempt from the 400ms floor: this is the seam crossfade between adjacent
     legs, not an interaction. Slower and you see two clips at once mid scrub. */
  transition:opacity .18s linear;
  will-change:opacity;
}
.leg.is-live{ opacity:1; z-index:2; }

/* The outgoing leg stays FULLY OPAQUE underneath while the incoming one fades
   in above it. Cross-fading both was the one frame flash: two layers at 50%
   alpha cover 75% of the frame, not 100%, so a quarter of the dark stage
   showed through at the midpoint of every seam. Nothing is ever semi
   transparent over the void now, so there is no dip to see. */
.leg.is-prev{ opacity:1; z-index:1; transition:none; }

/* Poster still shows until the clip paints its first frame (iOS blank fix) */
.leg-poster{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:cover;
  opacity:0;
  transition:opacity .4s var(--ease);
}
.leg-poster.is-live{ opacity:1; }

/* NO full frame wash.

   An earlier version laid a warm scrim across the whole frame to keep the
   copy legible over dark footage. The footage is bright now, and the client's
   verdict on that wash was blunt: glare, throughout the scroll, get rid of it.
   They were right. Washing the entire picture to protect one column of text
   is a tax paid by every pixel to solve a problem in about a fifth of them.

   The copy carries its own plate instead, in copy.css, in the same glass
   language as the masthead capsules. The film is now left alone. */

/* scroll spacer, its height defines how long the flight lasts */
.flight-track{ position:relative; }

.scroll-hint{
  position:absolute; z-index:6;
  left:var(--gut); bottom:1.6rem;
  display:flex; align-items:center; gap:.8rem;
  font:500 var(--t-xs)/1 var(--mono);
  letter-spacing:.24em; text-transform:uppercase;
  color:rgba(255,253,250,.82);
  text-shadow:0 1px 3px rgba(4,10,20,.6);
}
.scroll-hint i{
  display:block; width:44px; height:1px; background:rgba(255,253,250,.45);
  position:relative; overflow:hidden;
}
.scroll-hint i::after{
  content:''; position:absolute; inset:0; width:40%;
  background:var(--amber);
  animation:hintRun 2.4s var(--ease-io) infinite;
}
@keyframes hintRun{ 0%{ transform:translateX(-100%);} 100%{ transform:translateX(250%);} }

/* ================================================================
   Film grain.
   An overlay, never baked into the encode. Flat digital gradients
   band without it, especially across a bright sky, and grain is the
   cheapest "shot on film" signal there is. Deliberately faint: it
   should read as texture, never as noise.

   feTurbulence generates it, so there is no image to download and
   nothing to go stale. It shifts on a step timer because static
   grain reads as dirt on the lens rather than film.
   ================================================================ */
.grain{
  position:absolute; inset:-60px;      /* overscan so the shift never bares an edge */
  z-index:2;                            /* over the film, under the copy at 6 */
  pointer-events:none;
  opacity:.19;
  mix-blend-mode:overlay;
  background-repeat:repeat;
  background-size:170px 170px;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='170' height='170'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='170' height='170' filter='url(%23n)'/></svg>");
  animation:grain-drift 1.1s steps(1) infinite;
}
@keyframes grain-drift{
  0%   { transform:translate3d(0,0,0); }
  20%  { transform:translate3d(-9px,5px,0); }
  40%  { transform:translate3d(6px,-8px,0); }
  60%  { transform:translate3d(-5px,-4px,0); }
  80%  { transform:translate3d(8px,7px,0); }
  100% { transform:translate3d(0,0,0); }
}
/* Cheaper on small screens, where the blend costs most and shows least. */
@media (max-width:700px){ .grain{ opacity:.13; background-size:130px 130px; } }
@media (prefers-reduced-motion:reduce){ .grain{ animation:none; } }


/* ============================================================
   The veil.

   One soft field across the whole frame, sitting on the stage rather than on
   any scene. Its predecessor hung off .scene::before and the scenes are
   animated with clip-path, which clipped it into a hard edged rectangle: the
   grey box the client rightly objected to.

   Anchored to the frame, so there is no box to see. Weighted to the bottom
   left, where the copy sits, and gone entirely by the middle of the picture,
   so the film is never dimmed where anyone is actually looking at it.
   ============================================================ */
.stage-veil{
  position:absolute; inset:0;
  z-index:3;                 /* over the film and the grain, under the copy at 6 */
  pointer-events:none;
  /* Two directional gradients, each spanning the WHOLE frame. That is the
     point: a gradient that runs edge to edge has no boundary to see, so it can
     be as strong as legibility needs without ever reading as a panel. The
     corner-anchored radial that came first was invisible enough to look clean
     and left the headline at 1.86:1 over sunlit foliage, which is unreadable. */
  background:
    linear-gradient(to right,
      rgba(6,14,26,.74) 0%,
      rgba(6,14,26,.60) 16%,
      rgba(6,14,26,.38) 34%,
      rgba(6,14,26,.16) 50%,
      rgba(6,14,26,.04) 62%,
      rgba(6,14,26,0)   72%),
    linear-gradient(to top,
      rgba(6,14,26,.52) 0%,
      rgba(6,14,26,.34) 18%,
      rgba(6,14,26,.16) 36%,
      rgba(6,14,26,.04) 52%,
      rgba(6,14,26,0)   66%);
}

/* Narrower screens put the copy across the full width, so the horizontal fall
   is stretched and the vertical one carries more of the load. */
@media (max-width:820px){
  .stage-veil{
    background:
      linear-gradient(to right,
        rgba(6,14,26,.70) 0%,
        rgba(6,14,26,.58) 30%,
        rgba(6,14,26,.40) 58%,
        rgba(6,14,26,.24) 82%,
        rgba(6,14,26,.16) 100%),
      linear-gradient(to top,
        rgba(6,14,26,.60) 0%,
        rgba(6,14,26,.42) 20%,
        rgba(6,14,26,.20) 40%,
        rgba(6,14,26,.05) 58%,
        rgba(6,14,26,0)   72%);
  }
}
}
