/* =============================================================================================
   THE SWIPE PHONE — four stacked photographs of the app, one of which moves.

   ⚠️ THESE ARE PHOTOGRAPHS, NOT A RENDER, AND THAT IS THE RIGHT CALL. An earlier version rebuilt the
   app's UI in CSS from measured component values. It was accurate and still lost: the app draws
   gradient tile fills, colored glows, insight capsules and six kinds of generated hero graphic that a
   copy only approximates. A crop matches exactly, because it IS the app.

   ⭐ AND IT IS NOW ONE ARTBOARD RATHER THAN THREE CROPS. Every layer is exported at 664×1348 from
   the same canvas, so there are no offsets to keep in sync — `inset: 0` on all of them is the whole
   layout. The old build sliced the screen into chrome-top (107pt) / cards (515pt) / chrome-bottom
   (230pt) and had to add back to 852 exactly; that arithmetic is gone.

   Layer order, bottom to top:
       .swipe-base     the screen, bottom action row included — IN FLOW, so it sets the height
       .swipe-state    the same screen with one button lit; cross-faded over the base
       .swipe-card     the capsule + its glow — the only thing that moves
       .swipe-frame    the device

   ⭐ MEASURED APP VALUES, kept because they were expensive to establish and are the reference if a
   live render is ever needed again (all from components/MasonryCapsule.jsx, CapsuleCard.jsx,
   app/capsules.jsx, theme/tokens.js):
     · dashboard  GUTTER 16 · COLUMN_GAP 13 · column width 174 · tile 230 tall, radius 20 (FIXED —
                  the stagger comes from the square goal dial, not from varying tile heights)
     · tile title 15/22/600/-0.4 · type chip radius 8, padding 4/10, text 10.5/13/600/-0.4
     · goal dial  square, column-width; ring 98 with a 7 stroke; arc #0A84FF → #32D9A8; fraction 30/36/700
     · card       radius 22 · padding 16/18/14 · title 20/27/600 · bullets 15/21/600/-0.4
     · hero pill  radius 13, hairline border, 91 design units tall, k = pillWidth / 324
     · strokes    NEVER a plain border — a solid tint plus two corner-highlight gradients on one rect
                  (tile: rx 19.5, solid @0.25, k = w/176 · card: rx 21.5, solid @0.30, kx = w/363)
   ============================================================================================= */

/* The host is a plain block: the base image inside it establishes both dimensions, and the wrap's
   own `width` (`.phone-wrap--shot`, 303.19px) is what sizes the whole thing. No aspect-ratio and no
   fixed height — the artboard's proportions are already correct. */
/* ⚠️ `overflow: hidden` IS LOAD-BEARING, and it is not the thing that hides the thrown card. The
   frame does that: it is opaque from the screen's edge out to the artboard's, so a capsule on its way
   out is behind the bezel long before it reaches this boundary. What the clip stops is the rest of
   the flight — the throw travels to 125% of the card's width, well past the artboard, and with no
   clip the capsule would sail out over the page beside the phone. */
#swipeLoop { position: relative; display: block; line-height: 0; overflow: hidden; }

.swipe-layer { display: block; width: 100%; height: auto; }
.swipe-abs, .swipe-card { position: absolute; inset: 0; }

/* ⭐ THE CLIP IS THE SCREEN, NOT THE ARTBOARD — and this is what stopped the capsule bleeding out
   beside the phone. The frame's bezel is thinner than it looks: the screen opening runs x 42..619 of
   664 and y 47..1301 of 1348 (measured off the frame PNG's alpha), so there is only ~42px of artboard
   bezel to hide behind. A capsule dragged to the commit point travels 199px and lands 44px past the
   opening — visible in the transparent strip outside the frame, over the page.

   ⚠️ THE CLIP GOES ON THIS WRAPPER, NEVER ON `.swipe-card`. `clip-path` on a transformed element is
   applied in that element's own coordinate space, so it would travel with the card and clip nothing.
   This box does not move, so its clip stays put and the card slides out of view underneath it.

   The radii resolve against the reference box (the full artboard), so the frame's ~73px corner is
   73/664 horizontally and 73/1348 vertically. */
.swipe-clip {
  position: absolute; inset: 0;
  clip-path: inset(3.487% 6.627% 3.412% 6.325% round 10.99% / 5.42%);
}

.swipe-card {
  will-change: transform, opacity;
  /* The app rotates the card about its own centre (`rotateZ` on the card view). Not a thumb-shaped
     pivot near the bottom — that was a flourish the app does not have. */
  transform-origin: 50% 50%;
}

.swipe-capsule { position: absolute; inset: 0; }

/* ⭐ THE GLOW IS A BAKED IMAGE, NOT A TINTED MASK — and it is baked because the mask did not work.
   The app tints one white PNG at runtime (`tintColor` on an Animated.Image, components/CardGlow.jsx)
   and the web equivalent is a mask filled with a solid color. Two spellings of it rendered nothing,
   which is the trouble with masking: a masked element is invisible whenever ANY link in the chain
   fails, and it fails silently, so a broken mask and a correctly-hidden layer look identical.

   `scripts/bake-swipe-glows.js` now writes one PNG per action per mode straight from
   theme/tokens.js `capSwipeGlow`, so this is an `<img>` with an opacity and nothing that can fail
   quietly. Six files, ~110 KB each, and only one is ever fetched.

   ⚠️ NOT `filter: hue-rotate` either — white has no hue to rotate, so it can never reach a given hex.

   The opacity is `capSwipeGlowOpacity` (0.3, both modes) and the falloff lives in the PNG's alpha,
   exactly as it does in the app. */
.swipe-glow {
  position: absolute; inset: 0;
  width: 100%; height: auto;
  opacity: 0;
  pointer-events: none;
}

.swipe-state { opacity: 0; }

/* The device is on top of everything, so it must never eat a click aimed at the section. */
.swipe-frame { pointer-events: none; }
