/* ================================================================
   COMPONENT: FLYER — the shared element that carries you into a case

   WHAT IT IS
   When you click a project card, the card's image does not disappear and
   get replaced by a new page. The same image is lifted out of the grid
   onto a fixed layer, rides the document swap, and settles into the case
   cover's media plate. One object, two documents.

   WHY IT READS AS CALM
   It is the ONLY thing that moves. Everything else — the whole outgoing
   page, the whole incoming page — merely dissolves through the paper
   veil (`curtain.css`) behind it. An earlier version stacked a hard teal
   wipe underneath this flight and put a 160ms dead stop in the middle of
   it; two loud gestures fighting, with a stutter between them. That is
   what made the navigation feel rigid. Now: no panel, no pause, one
   unhurried move on one curve (`--ease-soft`).

   THE SEQUENCE
     depart  card media rect + src -> sessionStorage; the flyer is pinned
             at that exact rect and the real card media is hidden beneath
             it. The page dissolves to blank paper around it — everything
             goes, the thing you chose stays.
     swap    the arriving page rebuilds the flyer at the SAME stored rect
             while still covered, so the handoff frame is identical.
     arrive  the veil dissolves away and the flyer travels to the plate's
             live-measured rect at the same time, on the same curve. Then
             the real plate takes over and the flyer is destroyed.

   Nothing about the image changes during the flight — no tint arrives, none
   lifts. It carries the same top crease as both endpoints so the mark never
   blinks, and the picture is true colour throughout.

   The two halves of the arrival are simultaneous and share one easing, so
   it reads as a single settle rather than a sequence of events.
   ================================================================ */

.pt-flyer {
  position: fixed;
  top: 0;
  left: 0;
  /* One layer above the veil, so the image stays crisp while everything
     else dissolves through it. */
  z-index: calc(var(--z-curtain) + 1);
  border-radius: var(--radius-card);
  corner-shape: squircle;
  overflow: hidden;
  /* Must match .project-card__media and .case-cover__media exactly — this
     element crosses the navigation, so any difference reads as a colour pop
     mid-flight rather than as one object moving. */
  background-color: var(--paper-tint-3);
  pointer-events: none;
  /* Top-left origin: the flight is `translate(x, y) scale(sx, sy)`, so the
     origin has to be the corner being positioned or the scale would drag
     the box away from the translate. */
  transform-origin: 0 0;
  /* TRANSFORM ONLY. Never animate this element's width/height — that is a
     layout animation, it runs on the main thread, and the main thread on an
     arriving case page is saturated with GSAP/ScrollTrigger/p5/Lenis boot.
     It stuttered. Transform is composited off-thread and does not care. */
  will-change: transform;
}

/* Sized by JS to exactly cover the frame, centred with margins so the
   transform slot stays free for the counter-scale that keeps the picture
   undistorted while the frame's aspect ratio changes under it. */
/* NO `will-change` HERE — the director sets it inline, and only on the flights
   that actually counter-scale the image. A permanently promoted child inside a
   parent that clips with `overflow: hidden` + `border-radius` +
   `corner-shape: squircle` is what made the corner disappear for the duration
   of the flight on mobile and then reappear: the rounded clip is not applied
   to the child's own composited layer, so it degrades to a rectangle until the
   layers are folded back down. On a near-uniform flight (mobile) the image now
   never transforms at all, so it is never promoted and the clip never breaks. */
.pt-flyer__img {
  display: block;
  position: absolute;
  left: 50%;
  top: 50%;
  max-width: none;
  transform-origin: center center;
}

/* `.pt-flyer::before` is FREE. It carried the card's teal grade, then briefly
   the media crease; it now carries neither.

   The crease was pulled back to `.project-card--full` + `.case-cover__media`
   only (see `layout.css` → "The media crease"), so most flights start from a
   card that has none. Giving the flyer one unconditionally would invent a
   mark mid-flight. The plate FILL still has to match all three exactly — that
   is a large area and a mismatch pops — but a 6px tapered edge mark in motion
   under a dissolving veil does not need to.

   This pseudo-element used to be the card's teal grade, travelling with the
   image and lifting on `.is-landing` to "reveal the true picture". That grade
   was removed from the card itself (see `DESIGN.md → Color → Rules`), so
   keeping it here would have made the flight the only place on the site that
   tints the work — a colour pop appearing and dissolving over 620ms with no
   endpoint to justify it. `.is-landing` is still set by the head director on
   all five pages and is left in place as a hook; it simply has no rule now. */

/* The real card media is hidden while its flyer stands in, so the image
   is never on screen twice. */
.project-card.pt-source .project-card__media {
  opacity: 0;
}

/* No flyer under reduced motion — the navigation is a plain, instant
   document swap, and a lone image sliding across the screen would be the
   loudest thing on the page. */
@media (prefers-reduced-motion: reduce) {
  .pt-flyer { display: none; }
  .project-card.pt-source .project-card__media { opacity: 1; }
}
