/* The sunrise wash across the building photo.

   Every colour and every duration in here arrives from src/lib/light-wash.ts
   as a custom property on elements that module creates. There are deliberately
   no hex values and no hard-coded seconds below: the palette and the motion
   budget are asserted by light-wash.test.ts against horizon-tokens.ts and
   horizon-field.ts, and a second copy here would drift out from under those
   assertions without anyone noticing.

   Nothing in this file applies until the script has run. `.light-wash` is
   created by mountLightWash(), and the photo transform is scoped to
   [data-wash-ready], which the same function sets. A page where the script
   never ran shows the plain, uncropped photo, which is the finished design. */

/* The wash itself. Wider than its wrapper by more than the travel, so no edge
   can enter the frame at either end of the sweep. The wrapper is already
   position: relative and overflow: hidden. */
.light-wash {
  position: absolute;
  inset-block: 0;
  inset-inline: calc(-1.25 * var(--wash-travel));
  z-index: 1;
  pointer-events: none;
  background-image: var(--wash-gradient);

  /* soft-light, not a paint: it lifts and warms what is underneath and leaves
     the brick and the window frames readable.

     0.48 rather than something stronger, chosen by screenshotting 0.48 / 0.60
     / 0.72 side by side against the plain photo. Past about 0.6 the sky stops
     being sky and the window reveals lose their edge. The photo starts
     reading as a treatment rather than as the building. */
  mix-blend-mode: soft-light;
  opacity: 0.48;

  /* Parked off to one side until the section is on screen. */
  transform: translate3d(calc(-1 * var(--wash-travel)), 0, 0);
  will-change: transform;
}

/* The sweep runs only while the section is in the viewport. The attribute is
   toggled by the IntersectionObserver in light-wash.ts, so a photo scrolled
   past is not still animating somewhere below the fold.

   linear is load-bearing, not a style choice: washSpeedPxPerSecond() is travel
   over duration, which is the true speed only if the sweep is unaccelerated.
   An eased sweep would peak above the motion budget the hero established. */
[data-wash-visible] .light-wash {
  animation-name: light-wash-sweep;
  animation-duration: var(--wash-period);
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}

@keyframes light-wash-sweep {
  from {
    transform: translate3d(calc(-1 * var(--wash-travel)), 0, 0);
  }
  to {
    transform: translate3d(var(--wash-travel), 0, 0);
  }
}

/* The parallax. --wash-parallax is written per frame by light-wash.ts and is
   already rate-limited to the motion budget there; this rule only applies it.

   The scale is what makes the translate safe against the wrapper's clip: the
   photo is grown by --wash-scale so half the parallax travel still has photo
   behind it at whichever edge is moving away. */
[data-light-wash][data-wash-ready] img {
  transform: translate3d(0, var(--wash-parallax, 0px), 0) scale(var(--wash-scale, 1));
  transform-origin: center;
  will-change: transform;
}

/* Nothing. Not a slower wash, not a shorter parallax. The plain photo.

   Belt and braces: mountLightWash() already declines to attach under this
   preference, so in practice none of these elements exist. They are written
   anyway because a cached script and a fresh stylesheet can meet, and the
   audience is 65+. */
@media (prefers-reduced-motion: reduce) {
  .light-wash {
    display: none;
    animation: none;
  }

  [data-light-wash][data-wash-ready] img {
    transform: none;
    will-change: auto;
  }
}
