/**
 * Page transitions — the ink wash, carried between inner pages.
 *
 * The cover already owns this action: press an entrance and the blue is washed
 * away by white ink blooming from the point you pressed. It was the only
 * transition on the site — every other navigation was a hard page load. This
 * makes the same action the grammar for moving between inner pages, so the
 * device reads as a system rather than a one-off on the front page.
 *
 * Mechanism: the cross-document View Transitions API. The browser holds the
 * page you are leaving on screen until the page you are going to is ready, so
 * the wash costs no navigation time — nothing waits on an animation, and there
 * is no blank moment. Browsers without support navigate exactly as before;
 * nothing here is required for a page to work.
 *
 * The reveal is a mask, not a clip-path circle. A geometric circle was tried
 * on the cover and rejected: a circle is a curtain, it does not dissolve. The
 * mask is the same baked bitmap the cover uses (img/ink-bloom.webp) — wet
 * edge, torn rim, paper mottle — and it carries a real alpha channel, so the
 * middle of the wash is a soft gradient rather than a hard boundary.
 *
 * The front page is deliberately left out. It has its own wash, tuned to
 * 1500ms, and two washes on one press would fight. page-transition.css is
 * therefore not enqueued there.
 */

@view-transition {
  navigation: auto;
}

/*
 * Take over both snapshots. The default is a cross-fade, which would put the
 * new page everywhere at once and make the mask pointless.
 *
 * The old snapshot must stay opaque: it is what hides the live document
 * underneath the overlay. Where the mask is transparent you see this, where it
 * is opaque you see the new page — that difference *is* the wash. Fading the
 * old one out would let the live new document show through everywhere.
 */
::view-transition-old(root) {
  animation: none;
  opacity: 1;
  mix-blend-mode: normal;
}

/*
 * Aiming the wash, and the trap in it.
 *
 * The obvious way to open a mask from a point is `mask-position: <x%> <y%>`,
 * and it is wrong. A percentage position aligns *that point of the mask* with
 * the same point of the box — it does not put the mask's centre there. With a
 * mask far larger than the viewport the two are nowhere near each other: at
 * `mask-size: 340vmax` on a 1440px screen the bloom's centre landed at x=1574,
 * entirely off the right edge, so the whole wash opened from outside the page
 * and every frame read as a double exposure.
 *
 * `calc(50% + <offset>)` does what was wanted. The `50%` resolves against
 * `(box - mask)`, so the mask's left edge is `0.5(box - mask) + offset`, and
 * its centre is `0.5(box - mask) + offset + 0.5(mask)` — which simplifies to
 * `0.5(box) + offset`. The mask size cancels out, so the bloom stays pinned to
 * the press point at every scale, which is exactly what a growing mask needs.
 *
 * The offset is supplied by page-transition.js as `(ratio - 0.5) × 100vw/vh`.
 * `vw`/`vh` rather than `%` because a percentage there would be read against
 * `(box - mask)` again and reintroduce the drift.
 */
::view-transition-new(root) {
  mix-blend-mode: normal;
  mask-image: url("img/ink-bloom.webp");
  mask-repeat: no-repeat;
  mask-position: calc(50% + var(--ink-dx, 0vw)) calc(50% + var(--ink-dy, 0vh));
  /* Static fallback only — the animation below overrides it in every frame.
     It exists so that a browser which somehow drops the keyframes still shows
     the new page rather than a 1400px square of it. 404vmax is the worst case
     worked out in the note on the keyframes. */
  mask-size: 404vmax 404vmax;
  animation: uniqorn-ink-wash 900ms cubic-bezier(.3, 0, .22, 1) both;
}

/*
 * How big the wash has to get.
 *
 * `ink-bloom.webp` is 1400×1400 and its alpha is flat 255 out to 0.45 of its
 * half-width, holds at 250 or better out to 0.70, and falls to nothing by
 * 1.05. So the mask is not a square of ink: it is a *disc* of ink of radius
 * 0.35 × mask-size, sitting in a square of wet rim and transparency. Measured
 * off the bitmap, not assumed.
 *
 * That disc has to have covered the window by the time the animation ends,
 * because the mask is lifted along with the overlay. Whatever is still under
 * the rim at that moment snaps to the new page in a single frame, which is the
 * failure mode of a mask that is too small. The disc is centred on the press,
 * so the far corner is the last thing to arrive, and how far away it is
 * depends on where the press landed and on the shape of the window.
 * page-transition.js measures it and writes `--ink-r`.
 *
 * The fallback above is the worst case: a press in one corner of a square
 * window, where the far corner is the whole diagonal away — `sqrt(2) / 0.35`,
 * or 404vmax. The original version used a flat `340vmax`, and it was not a
 * silly guess: 340 covers that press on a 1440×900 window with 17% to spare.
 * The trouble with one number is that it has to be the *worst* number. Every
 * ordinary press then finishes early — this one at 85% of the travel — and
 * because the curve below decelerates at the end, the leftover is a long, slow
 * stretch with nothing left to reveal. Sizing it to the press removes it
 * instead of hiding it behind a bigger constant.
 *
 * `vmax` and not `%`: a percentage size would stretch the square bloom into an
 * ellipse on a wide viewport.
 */
@keyframes uniqorn-ink-wash {
  from { mask-size: 0vmax 0vmax; }
  to   { mask-size: var(--ink-r, 404vmax) var(--ink-r, 404vmax); }
}

/*
 * The easing, and the second thing the measurements caught.
 *
 * The first version used `cubic-bezier(.16, 1, .3, 1)` — easeOutExpo, the
 * reflex choice for "make it feel responsive". On a *growth* animation it is
 * the wrong reflex: frozen at the 25% mark it had already reached 83% of the
 * final size, so the wash snapped open in the first 150ms and then sat still.
 * A wash is supposed to arrive.
 *
 * This is the cover's own curve (entrance.css, `.is-leaving .white-transition`),
 * so the two washes are one gesture at two scales. It starts gently — a small
 * bloom exactly where you pressed, which is what makes the press point read —
 * carries its speed through the middle, and settles.
 *
 * 900ms, against the cover's 1500ms. The cover is crossed once and can afford
 * to be slow; inner pages are walked, and the fifth visit should not feel
 * heavier than the first. Same curve, shorter clock.
 */

/*
 * Reduced motion: the pages still change, they just do not animate. Dropping
 * to 1ms keeps the swap itself instant and skips the wash entirely.
 */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: 1ms;
  }
}
