/* ===== Tilt phone — a 3D device frame that leans toward the cursor =====
   Wraps the Yumoji prototype on the case page. Reference: marco.fyi.

   The prototype stays in a SAME-ORIGIN iframe rather than being inlined.
   Inlining would have moved 4.1 MB of prototype into the case document
   itself — render-blocking, and impossible to defer. Same origin means the
   parent can reach into the frame and listen for pointer moves there, which
   is the one thing the iframe would otherwise cost us (see tilt-phone.js).

   Geometry:
   • --screen-w is the ONLY size knob. Everything else follows.
   • aspect-ratio sits on .tilt-phone__viewport (the screen), NOT on the
     shell — the bezel padding would otherwise eat into the ratio and crop
     the prototype.
   • The prototype's own screen is a fixed 390x800 (its avatar camera scale
     is derived from that 800), so the iframe keeps those pixels and is
     SCALED to the viewport instead of being resized. --tp-scale is written
     by the JS; CSS cannot divide a container width by a fixed length. */

.tilt-stage {
  --rx: 0deg;
  --ry: 0deg;
  --lift: 0;
  /* The only size knob, clamped three ways:
       360px            — native-ish ceiling; bigger only softens the render
       height-driven    — the whole phone has to fit a laptop window, not just
                          a tall desktop one. 0.4875 is 390/800, so this reads
                          "how wide may the screen be for its height to fit".
                          160px covers the sticky topbar plus breathing room.
       width-driven     — 72px = 22 bezel + two 16px margins + a little for
                          the corners the tilt projects outward */
  --screen-w: min(360px, calc((100vh - 160px) * 0.4875), calc(100vw - 72px));
  --bezel: 11px;

  perspective: 1400px;
  perspective-origin: 50% 50%;
  /* Deliberate breathing room: the cursor has to be inside the tilt field
     before it reaches the screen, or the effect never gets a chance to read. */
  padding: 6vh 8vw;
  display: flex;
  justify-content: center;
}

/* Shell and screen corners are halved from the 60/48 they started at. They
   have to move TOGETHER: the screen's radius is the shell's less the bezel, so
   changing one alone either leaves a crescent of shell inside the corner or
   lets the screen square off against it. */
.tilt-phone {
  position: relative;
  display: inline-block;
  padding: var(--bezel);
  border-radius: 30px;
  background: #1a1c24;
  /* THREE layers, and only the first is elevation:
       --shadow-lg   the cast shadow, the site's top elevation step (--elev-3),
                     the same one the About modal and a lifted blog card use.
                     It was a hand-written `0 40px 90px rgba(0,0,0,.45)` — the
                     last hardcoded box-shadow left in the project, and the one
                     place the phone ignored the tinted, theme-aware ramp
                     everything else on the page is drawn with.
       inset hairline + 2px ring
                     NOT elevation. These draw the device's rim, so they stay
                     literal: they belong to the object, not to how high above
                     the page it floats, and they must not flip with the theme.
     `var()` expanding to a two-shadow list inside a longer list is fine —
     substitution happens before the value is parsed, so this reads as four
     shadows in the end. */
  box-shadow:
    var(--shadow-lg),
    0 0 0 1px rgba(255, 255, 255, .06) inset,
    0 0 0 2px #2b2f3b;
  transform-style: preserve-3d;
  /* --rz is the RESTING angle, set by whoever places the phone (the Yumoji
     hero asks for 4.29deg, straight off the mock's placeholder). It is
     composed with the live tilt rather than replacing it, so the mock's
     pose survives and the cursor still moves the phone from there. */
  transform:
    rotateX(var(--rx))
    rotateY(var(--ry))
    rotateZ(var(--rz, 0deg))
    translateZ(calc(var(--lift) * 40px));
  will-change: transform;
}

/* the sheen that travels with the tilt */
.tilt-phone::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 30px;    /* matches .tilt-phone, or the sheen corners overhang */
  pointer-events: none;   /* load-bearing: without it the sheen eats every click */
  z-index: 4;
  background: linear-gradient(105deg, transparent 42%, rgba(255, 255, 255, .06) 50%, transparent 58%);
  background-size: 300% 100%;
  background-position: calc(50% - var(--ry) * 4) 0;
}

.tilt-phone__viewport {
  position: relative;
  width: var(--screen-w);
  aspect-ratio: 390 / 800;   /* measured off the prototype, not the iPhone spec sheet */
  border-radius: 24px;
  overflow: hidden;
  background: #fff;
  transform: translateZ(1px);   /* or the screen sinks into the bezel when tilted */
  contain: paint;
}

.tilt-phone__frame {
  position: absolute;
  top: 0;
  left: 0;
  width: 390px;
  height: 800px;
  border: 0;
  transform: scale(var(--tp-scale, 1));
  transform-origin: top left;
}

/* --- narrow screens ---
   --screen-w was a flat 360px, which does not fit a phone: 360 + 2x11 bezel
   + the stage's 8vw field came to 442px of layout inside a 375px viewport,
   and the page scrolled sideways by 8px. (8, not 67, because the tilt
   projects the box — getBoundingClientRect reports the projected size, so
   the raw number understated the problem. The layout overflow was real.)

   The width clamp now lives in --screen-w itself; this only narrows the tilt
   field so there is still somewhere to aim from on a tablet. */
@media (max-width: 640px) {
  .tilt-stage { padding: 4vh 16px; }
}

/* No tilt where there is no cursor. The phone still renders, just flat. */
@media (pointer: coarse) {
  .tilt-stage { --rx: 0deg; --ry: 0deg; --lift: 0; padding: 3vh 8px; }
  .tilt-phone { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .tilt-phone { transform: none; }
  .tilt-phone::after { background-position: 50% 0; }
}
