/*
 * The shared anatomy every collage uses, and the cursor.
 *
 * Hero, Feature and the interior-page pair all draw a photograph the same way:
 * a frame that holds the position and clips, a zoom box that carries the
 * scroll-driven push-in, the image itself carrying the entrance scale, and a
 * curtain that wipes up off it. In the Next app this was written out three
 * times in three CSS modules; here it is one place, because the three ports
 * would otherwise drift apart.
 */

/* Holds the position. The clipping is its child's job — see below. */
.btd-frame {
  position: relative;
  min-width: 0;
  will-change: transform;
}

/*
 * The clip box. Separate from the frame so the curtain, which is the frame's
 * other child, sits outside it: the frame's edges land on fractional device
 * pixels, and two sibling layers clipped by one fractional rect do not round it
 * the same way — the scaled image rounds outward and paints a row the curtain
 * rounds inward and misses. Unclipped, the curtain simply covers the frame and
 * a pixel beyond it, so there is nothing left to leak. `clip-path` alongside
 * `overflow` for the image's own edge, which is rasterised with the element
 * rather than applied as a layer rect.
 */
.btd-frame__clip {
  position: absolute;
  inset: 0;
  overflow: hidden;
  clip-path: inset(0);
}

/* Scroll-driven push-in. Separate box from the image so this scale and the
   entrance scale compose instead of overwriting each other. `will-change`
   promotes it to its own compositor layer — without it the browser
   re-rasterises the photo every frame instead of re-compositing a layer. */
.btd-frame__zoom {
  position: absolute;
  inset: 0;
  display: block;
  will-change: transform;
}

.btd-frame__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/*
 * Wipes up off the image on entry. Collapsed by default, so if the reveal
 * script never runs the image is simply visible — every "from" state in this
 * theme is set from JS for exactly that reason.
 */
.btd-frame__curtain {
  position: absolute;
  /* Overshoots the frame by a pixel, and is outside the clip box so the
     overshoot survives — that margin is what covers the row the image can
     round outward into. It paints the ground onto the ground, so it costs
     nothing to be wrong about. */
  inset: -1px;
  transform: scaleY(0);
  transform-origin: top;
  background: var(--ground);
  /* Same easing as the page ground: `--ground` itself jumps, so without this a
     curtain snaps to the new colour while the background is still blending and
     shows up as a bright rectangle. */
  transition: background-color 700ms ease;
}

/*
 * The dot that trails the pointer and swells over links.
 *
 * Drawn at its largest size and scaled down at rest, never up: a promoted
 * layer is rasterised once at its natural size, so scaling past that stretches
 * the bitmap and the edges go blocky.
 */
.btd-cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 100;
  width: var(--cursor-size, 12px);
  height: var(--cursor-size, 12px);
  border-radius: 50%;
  background: var(--lime);
  opacity: 0;
  /* Never swallow a click, and never sit between the pointer and a link. */
  pointer-events: none;
  will-change: transform;
}

/*
 * No pointer to follow — and no cursor for anyone who asked for less motion.
 * The original gated on `(pointer: fine)` alone, so a reduced-motion visitor
 * still got a dot on a 0.22s eased tween while every other behaviour on the
 * site stood still.
 */
@media (pointer: coarse), (prefers-reduced-motion: reduce) {
  .btd-cursor {
    display: none;
  }
}

/*
 * The entrance overlay. Present in the server HTML so it paints with the first
 * frame — anything that waits for JS shows a flash of hero first, which is
 * exactly what a preloader exists to prevent.
 */
.btd-intro {
  position: fixed;
  inset: 0;
  /* svh, not vh: on mobile `vh` is the *largest* viewport, so the overlay would
     run under the URL bar and the curtain would start its lift off-screen. */
  height: 100svh;
  /* Above the cursor, which is the ceiling at 100. */
  z-index: 200;
  display: grid;
  place-items: center;
  background: var(--lime);
  /* Nothing underneath is meant to be reachable while this is up; the scroll
     lock in base.css handles the page, this handles the pointer. */
  pointer-events: none;
  will-change: transform;
}

.btd-intro__rays {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/*
 * The mark is a mask, not an image: the PNG supplies only alpha and the colour
 * comes from underneath. Flat — the rays carry the movement, so the logo just
 * arrives.
 */
.btd-intro__mark {
  position: relative;
  width: min(33.6vw, 480px);
  /* The mask's own crop ratio (14866 × 6338). Without this the box collapses,
     since a masked div has no intrinsic size. */
  aspect-ratio: 2.346;
  background: var(--green);
  /* Hidden from the first paint rather than by script — setting this from JS
     leaves the logo solid on screen until the runtime loads. */
  opacity: 0;
  will-change: opacity;
}

/*
 * Handled here rather than in script so it never paints at all: a JS-side
 * check runs one frame too late.
 */
@media (prefers-reduced-motion: reduce) {
  .btd-intro {
    display: none;
  }
}

/*
 * The entrance reveal's starting state, held from the first paint until
 * reveal.js takes over.
 *
 * Keyed off the flag the blocking script in inc/assets.php stamps on <html>,
 * and only when motion is allowed — the same query reveal.js gates itself on.
 * No JS or reduced motion leaves the flag unset and the page in its natural,
 * fully visible state, which is the guarantee the whole motion system is
 * written around.
 *
 * These are only the *from* values. GSAP writes inline styles, which outrank a
 * stylesheet, so it takes the elements over as soon as it runs.
 *
 * Why this exists at all: reveal.js is deferred, so on a cold load the browser
 * painted the finished page and then yanked it back to animate. The front page
 * never showed it — the intro overlay covers exactly that window — but every
 * interior page sat with its photographs uncovered until the curtains dropped
 * on them. The three numbers come from btd_motion_config() as custom
 * properties rather than being restated here, so there is still one source.
 */
html[data-reveal-pending] [data-curtain] {
  transform: scaleY(1);
}

html[data-reveal-pending] [data-reveal-img] {
  transform: scale(var(--reveal-scale));
}

html[data-reveal-pending] [data-reveal-line] {
  opacity: 0;
  transform: translateY(var(--reveal-line-shift));
}

html[data-reveal-pending] [data-reveal-fade] {
  opacity: 0;
  transform: translateY(var(--reveal-fade-shift));
}

html[data-reveal-pending] [data-rule] {
  transform: scaleX(0);
}

/* The buttons start as just their arrow. The same handover parts.css makes
   under [data-cta-ready], brought forward to the first paint. */
html[data-reveal-pending] [data-cta-spacer] {
  display: block;
  width: 0;
}

html[data-reveal-pending] [data-cta-label] {
  position: absolute;
  top: 50%;
  left: 0;
  transform: translateY(-50%);
}

html[data-reveal-pending] [data-cta-char] {
  opacity: 0;
}
