/* The 3D iPhone: body, light, scenes and pop-ups.

   app.js `renderOtfIphone3d` writes the markup and scripts/phone3d.js turns
   the hero and tour phones. It is a Pro in titanium, built in CSS 3D as a
   closed shell: front glass, back glass, four flat sides between the
   corners, and each rounded corner a quarter cylinder of flat facets. Every
   face hides its back, so the shell draws right in any order (Safari sorts
   3D layers without splitting them) and the band stays solid edge on.
   Proportions follow the real phone (71.5 x 149.6 x 8.25 mm) and the
   captures' 1206 x 2622 screen.

   One key light, up and to the right in front of the phone, shades it.
   `--lx/--ly/--lz` is that light in the phone's own axes: CSS works it out
   from the phone's angle (`--p3d-rx`, `--p3d-ry`), and the script writes it
   while it turns a phone by hand. A face darkens as it turns from the light,
   the glare slides over the glass against the turn, and the contact shadow
   narrows as the phone turns edge on.

   Only transform and opacity change while anything moves. Without the
   script, or under reduced motion, every phone rests at its finished angle
   with its pop-ups out. */

@property --p3d-rx { syntax: "<angle>"; inherits: true; initial-value: 0deg; }
@property --p3d-ry { syntax: "<angle>"; inherits: true; initial-value: 0deg; }
/* Extra pitch and yaw for the light and shadow while a phone turns in. */
@property --p3d-tip { syntax: "<angle>"; inherits: true; initial-value: 0deg; }
@property --p3d-turn { syntax: "<angle>"; inherits: true; initial-value: 0deg; }
/* 1 at a phone's own angle, 0 facing you (the screen gallery's hover). */
@property --p3d-face { syntax: "<number>"; inherits: true; initial-value: 1; }

.p3d {
    --w: var(--otf-phone-w, 280px);
    --h: calc(var(--w) * 2.0965);
    --d: calc(var(--w) * 0.108);
    --r: calc(var(--w) * 0.168);
    --rim: calc(var(--w) * 0.0085);
    --bezel: calc(var(--w) * 0.0245);
    --sr: calc(var(--r) - var(--rim) - var(--bezel));
    --nub: max(1.5px, calc(var(--w) * 0.0075));
    /* A corner facet spans a quarter turn over the facet count (app.js sets
       --arc-t to tan(45deg / facets)), plus a pixel of overlap. */
    --arc-w: calc(2 * (var(--r) + 0.4px) * var(--arc-t, 0.4142) + 1px);
    --antenna-w: max(1px, calc(var(--w) * 0.007));
    /* Desert titanium. */
    --ti-hi: #f1e5d4;
    --ti: #c2ad96;
    --ti-mid: #a08b76;
    --ti-lo: #6f5e4e;
    --ti-dk: #40352b;
    --antenna: rgba(78, 66, 55, 0.62);
    --back-hi: #d8c6ae;
    --back: #c6b199;
    --back-lo: #b19b83;
    /* The band from its back edge to its front: shadow at the back, the
       polished chamfer catching the light at the front. */
    --band: var(--ti-dk), var(--ti-lo) 9%, var(--ti-mid) 24%, var(--ti) 52%, var(--ti-hi) 82%, var(--ti) 93%, var(--ti-lo);
    /* Lit from the front until the angle is known. */
    --lx: 0.35;
    --ly: -0.35;
    --lz: 0.87;
    --yc: 1;
    --ys: 0;
    /* A damped spring (damping ratio 0.86, so it settles without a bounce). */
    --p3d-spring: cubic-bezier(0.2, 0.9, 0.3, 1);
    --p3d-enter: 1600ms;
    position: relative;
    flex: none;
    width: var(--w);
    height: var(--h);
    perspective: calc(var(--w) * 6.5);
    perspective-origin: 50% 44%;
}

/* The light (0.35, -0.35, 0.87) turned back through the pitch, then the
   yaw, into the phone's axes; the yaw's cosine and sine size the shadow. */
@supports (width: calc(1px * cos(0deg))) {
    .p3d {
        --p3d-pitch: calc(var(--p3d-rx) + var(--p3d-tip));
        --p3d-yaw: calc(var(--p3d-ry) + var(--p3d-turn));
        --p3d-lz1: calc(0.35 * sin(var(--p3d-pitch)) + 0.87 * cos(var(--p3d-pitch)));
        --lx: calc(0.35 * cos(var(--p3d-yaw)) - var(--p3d-lz1) * sin(var(--p3d-yaw)));
        --ly: calc(0.87 * sin(var(--p3d-pitch)) - 0.35 * cos(var(--p3d-pitch)));
        --lz: calc(0.35 * sin(var(--p3d-yaw)) + var(--p3d-lz1) * cos(var(--p3d-yaw)));
        --yc: cos(var(--p3d-yaw));
        --ys: sin(var(--p3d-yaw));
    }
}

@supports (transition-timing-function: linear(0, 1)) {
    .p3d {
        --p3d-spring: linear(0, 0.0008 0.6%, 0.0068 1.9%, 0.0233 3.6%, 0.0541 5.7%, 0.101 8.1%, 0.1636 10.9%,
            0.2397 13.9%, 0.3258 17.2%, 0.4175 20.8%, 0.5103 24.6%, 0.6001 28.7%, 0.6833 33%, 0.7574 37.5%,
            0.8209 42.2%, 0.8733 47.1%, 0.9148 52.3%, 0.9463 57.6%, 0.9692 63.1%, 0.9849 68.8%, 0.995 74.7%,
            1.0009 80.8%, 1.0039 87%, 1.0049 93.4%, 1);
    }
}

/* Other finishes, for the fan. */
.p3d.is-black {
    --ti-hi: #9a948d;
    --ti: #5f5a54;
    --ti-mid: #4b4743;
    --ti-lo: #302d2a;
    --ti-dk: #1b1a18;
    --antenna: rgba(14, 13, 12, 0.6);
    --back-hi: #55524e;
    --back: #3f3c39;
    --back-lo: #2e2c2a;
}

.p3d.is-natural {
    --ti-hi: #f0eeea;
    --ti: #bdb9b2;
    --ti-mid: #9e9a93;
    --ti-lo: #6d6a65;
    --ti-dk: #42403c;
    --antenna: rgba(70, 68, 64, 0.55);
    --back-hi: #d6d3cd;
    --back: #c2beb7;
    --back-lo: #aaa69f;
}

.p3d-rotor,
.p3d-body,
.p3d-frame {
    position: absolute;
    inset: 0;
    transform-style: preserve-3d;
}

/* One list of functions everywhere, so a turn in interpolates angle by
   angle instead of through a matrix (a half turn has no shortest path). */
.p3d-body {
    transform: translate3d(0, 0, 0) rotateX(var(--p3d-rx)) rotateY(var(--p3d-ry)) scale(1);
}

.p3d.is-moving .p3d-rotor { will-change: transform; }

/* A face's shade: --f is how squarely it faces the light (1 head on, below
   0 turned away). */
.p3d-shade,
.p3d-arc::after,
.p3d-key::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: #120d09;
    opacity: clamp(0.02, calc(0.5 - 0.62 * var(--f, 0)), 0.72);
    pointer-events: none;
}

/* ── The band ───────────────────────────────────────────────────────────── */
/* A corner facet stands on the band's line, 0.4px out, facing away from
   the corner's centre. Each is a pixel taller than the band, so the front
   and back glass overlap it and no seam opens at an angle. */
.p3d-arc {
    --f: calc(var(--nx) * var(--lx) + var(--ny) * var(--ly));
    position: absolute;
    left: calc(50% - var(--arc-w) / 2);
    top: calc(50% - var(--d) / 2 - 0.5px);
    width: var(--arc-w);
    height: calc(var(--d) + 1px);
    background: linear-gradient(0deg, var(--band));
    backface-visibility: hidden;
    transform:
        translate3d(calc(var(--qx) * (var(--w) / 2 - var(--r))), calc(var(--qy) * (var(--h) / 2 - var(--r))), 0)
        rotateZ(var(--a)) translate3d(0, calc(var(--r) + 0.4px), 0) rotateX(-90deg);
}

/* The flat sides, for the straight runs between the corners. */
.p3d-side {
    position: absolute;
    backface-visibility: hidden;
}

/* The button faces stand proud of the long sides, in the same 3D space. */
.p3d-side.is-left,
.p3d-side.is-right {
    top: var(--r);
    left: calc(50% - var(--d) / 2 - 0.5px);
    width: calc(var(--d) + 1px);
    height: calc(var(--h) - 2 * var(--r));
    transform-style: preserve-3d;
}

.p3d-side.is-top,
.p3d-side.is-bottom {
    top: calc(50% - var(--d) / 2 - 0.5px);
    left: var(--r);
    width: calc(var(--w) - 2 * var(--r));
    height: calc(var(--d) + 1px);
}

/* Each side's gradient runs from the back edge to the front edge. */
.p3d-side.is-left {
    --f: calc(-1 * var(--lx));
    background: linear-gradient(90deg, var(--band));
    transform: rotateY(-90deg) translateZ(calc(var(--w) / 2 + 0.4px));
}

.p3d-side.is-right {
    --f: var(--lx);
    background: linear-gradient(270deg, var(--band));
    transform: rotateY(90deg) translateZ(calc(var(--w) / 2 + 0.4px));
}

.p3d-side.is-top {
    --f: calc(-1 * var(--ly));
    background: linear-gradient(180deg, var(--band));
    transform: rotateX(90deg) translateZ(calc(var(--h) / 2 + 0.4px));
}

/* The bottom: two screws beside the port. */
.p3d-side.is-bottom {
    --f: var(--ly);
    --screw: calc(var(--w) * 0.083);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: calc(var(--w) * 0.036);
    background:
        radial-gradient(circle at calc(50% - var(--screw)) 50%, #4b4037 0 calc(var(--w) * 0.0055), rgba(75, 64, 55, 0.4) calc(var(--w) * 0.0068), transparent calc(var(--w) * 0.008)),
        radial-gradient(circle at calc(50% + var(--screw)) 50%, #4b4037 0 calc(var(--w) * 0.0055), rgba(75, 64, 55, 0.4) calc(var(--w) * 0.0068), transparent calc(var(--w) * 0.008)),
        linear-gradient(0deg, var(--band));
    transform: rotateX(-90deg) translateZ(calc(var(--h) / 2 + 0.4px));
}

/* Antenna bands: a matte strip across the band near each end of a side. */
.p3d-side::before {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
}

.p3d-side.is-left::before,
.p3d-side.is-right::before {
    background:
        linear-gradient(var(--antenna), var(--antenna)) 0 3% / 100% var(--antenna-w) no-repeat,
        linear-gradient(var(--antenna), var(--antenna)) 0 97% / 100% var(--antenna-w) no-repeat;
}

.p3d-side.is-top::before,
.p3d-side.is-bottom::before {
    background:
        linear-gradient(90deg, var(--antenna), var(--antenna)) 7% 0 / var(--antenna-w) 100% no-repeat,
        linear-gradient(90deg, var(--antenna), var(--antenna)) 93% 0 / var(--antenna-w) 100% no-repeat;
}

.p3d-key {
    position: absolute;
    left: 30%;
    width: 40%;
    border-radius: 999px;
    background: linear-gradient(90deg, var(--ti-lo), var(--ti-hi) 42%, var(--ti) 70%, var(--ti-lo));
    box-shadow: 0 0 0 0.5px rgba(40, 32, 26, 0.5);
    backface-visibility: hidden;
    transform: translateZ(var(--nub));
}

.p3d-key.is-action { top: calc(var(--h) * 0.175 - var(--r)); height: calc(var(--h) * 0.046); }
.p3d-key.is-vol-up { top: calc(var(--h) * 0.25 - var(--r)); height: calc(var(--h) * 0.075); }
.p3d-key.is-vol-down { top: calc(var(--h) * 0.34 - var(--r)); height: calc(var(--h) * 0.075); }
.p3d-key.is-side { top: calc(var(--h) * 0.27 - var(--r)); height: calc(var(--h) * 0.12); }

/* Camera Control: sapphire set flush in a titanium surround. */
.p3d-key.is-camera {
    top: calc(var(--h) * 0.58 - var(--r));
    left: 24%;
    width: 52%;
    height: calc(var(--h) * 0.07);
    border-radius: calc(var(--d) * 0.12);
    background:
        linear-gradient(90deg, transparent 30%, rgba(255, 255, 255, 0.14) 46%, transparent 60%),
        linear-gradient(180deg, #4b4038, #231d19 45%, #3a312a);
    box-shadow: 0 0 0 1px var(--ti-hi), 0 0 0 1.5px rgba(40, 32, 26, 0.5);
}

/* What the buttons show from the front: a sliver past the band's line on
   each side, outside the shell, so it never cuts through a face. */
.p3d-keyline {
    position: absolute;
    top: 0;
    width: var(--nub);
    height: 100%;
    background-repeat: no-repeat;
    transform: translateZ(0);
}

.p3d-keyline.is-left {
    right: calc(100% + 0.4px);
    background-image:
        linear-gradient(90deg, var(--ti-lo), var(--ti)),
        linear-gradient(90deg, var(--ti-lo), var(--ti)),
        linear-gradient(90deg, var(--ti-lo), var(--ti));
    background-size:
        100% calc(var(--h) * 0.046),
        100% calc(var(--h) * 0.075),
        100% calc(var(--h) * 0.075);
    background-position:
        0 calc(var(--h) * 0.175),
        0 calc(var(--h) * 0.25),
        0 calc(var(--h) * 0.34);
}

.p3d-keyline.is-right {
    left: calc(100% + 0.4px);
    background-image:
        linear-gradient(270deg, var(--ti-lo), var(--ti)),
        linear-gradient(270deg, #2a2420, #4d4238);
    background-size:
        100% calc(var(--h) * 0.12),
        70% calc(var(--h) * 0.07);
    background-position:
        0 calc(var(--h) * 0.27),
        0 calc(var(--h) * 0.58);
}

/* USB-C with its tongue, the speaker and microphone holes either side,
   and the microphone on the top edge. */
.p3d-port {
    position: relative;
    flex: none;
    width: calc(var(--w) * 0.115);
    height: 34%;
    border-radius: 999px;
    background: linear-gradient(#0e0b09, #2c251f);
    box-shadow: inset 0 0 0 max(0.5px, calc(var(--w) * 0.002)) rgba(0, 0, 0, 0.55), 0 0 0 max(0.5px, calc(var(--w) * 0.0018)) rgba(255, 245, 230, 0.18);
}

.p3d-port::after {
    content: "";
    position: absolute;
    top: 40%;
    left: 20%;
    right: 20%;
    height: 20%;
    border-radius: 999px;
    background: #4a3f36;
}

.p3d-grille {
    flex: none;
    width: calc(var(--w) * 0.156);
    height: max(1.5px, calc(var(--w) * 0.0135));
    background: radial-gradient(circle closest-side, #120e0b 0 58%, rgba(18, 14, 11, 0.45) 76%, transparent 86%) 0 50% / calc(var(--w) * 0.026) 100% repeat-x;
}

.p3d-mic {
    position: absolute;
    top: 50%;
    left: 62%;
    width: max(1.5px, calc(var(--w) * 0.011));
    aspect-ratio: 1;
    border-radius: 50%;
    background: radial-gradient(circle closest-side, #120e0b 0 60%, rgba(18, 14, 11, 0.4) 85%, transparent);
    transform: translate(-50%, -50%);
}

/* ── Front: titanium rim, black glass, the screen ──────────────────────── */
.p3d-front,
.p3d-back {
    position: absolute;
    inset: 0;
    padding: var(--rim);
    border-radius: var(--r);
    backface-visibility: hidden;
}

.p3d-front {
    background: linear-gradient(145deg, var(--ti-hi), var(--ti) 22%, var(--ti-lo) 56%, var(--ti) 84%, var(--ti-hi));
    transform: translateZ(calc(var(--d) / 2));
}

/* The black border is even all round: the screen's corners are the glass's
   less the border, so the curves stay concentric. A fine light line marks
   the glass's edge and a darker one the gap to the frame. */
.p3d-glass {
    position: relative;
    height: 100%;
    padding: var(--bezel);
    overflow: hidden;
    border-radius: calc(var(--r) - var(--rim));
    background: #050505;
    box-shadow:
        inset 0 0 0 max(0.5px, calc(var(--w) * 0.0022)) rgba(255, 255, 255, 0.1),
        0 0 0 max(0.5px, calc(var(--w) * 0.0018)) rgba(0, 0, 0, 0.55);
}

.p3d-screen {
    position: relative;
    height: 100%;
    overflow: hidden;
    border-radius: var(--sr);
    background: #15110f;
}

.p3d-screen > img,
.p3d-shot,
.p3d-shot > img {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* The earpiece: a fine slot where the glass meets the frame. */
.p3d-glass::before {
    content: "";
    position: absolute;
    z-index: 1;
    top: calc(var(--bezel) * 0.32);
    left: 50%;
    width: calc(var(--w) * 0.17);
    height: max(1px, calc(var(--w) * 0.0055));
    margin-left: calc(var(--w) * -0.085);
    border-radius: 999px;
    background: rgba(58, 54, 50, 0.85);
    pointer-events: none;
}

.p3d-island {
    position: absolute;
    z-index: 1;
    top: calc(var(--bezel) + var(--w) * 0.03);
    left: 50%;
    width: calc(var(--w) * 0.305);
    height: calc(var(--w) * 0.088);
    margin-left: calc(var(--w) * -0.1525);
    border-radius: 999px;
    background: #000;
}

/* The front camera, with a glint. */
.p3d-island::after {
    content: "";
    position: absolute;
    top: 50%;
    right: 17%;
    width: 22%;
    aspect-ratio: 1;
    border-radius: 50%;
    background:
        radial-gradient(circle at 34% 30%, rgba(190, 210, 255, 0.55) 0 8%, transparent 16%),
        radial-gradient(circle at 50% 50%, #263042 0%, #0b0e13 52%, #000 70%);
    transform: translateY(-50%);
}

/* A fixed soft glare at the top, plus a band of light that slides over the
   glass against the turn and brightens as the phone turns. It has its own
   layer, so moving it never repaints the screen. No blend modes: inside 3D
   Chrome blends them with the page behind the phone. */
.p3d-glass::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: linear-gradient(162deg, rgba(255, 255, 255, 0.09), transparent 26%);
    pointer-events: none;
}

.p3d-sheen {
    position: absolute;
    top: -20%;
    left: 0;
    width: 100%;
    height: 140%;
    background: linear-gradient(100deg, transparent 22%, rgba(255, 255, 255, 0.05) 36%, rgba(255, 255, 255, 0.14) 48%, rgba(255, 255, 255, 0.045) 60%, transparent 74%);
    opacity: min(1, calc(0.45 + 1.8 * max(var(--lx) - 0.35, 0.35 - var(--lx))));
    pointer-events: none;
    backface-visibility: hidden;
    will-change: transform;
    transform: translate3d(calc((var(--lx) - 0.35) * 130%), calc((var(--ly) + 0.35) * 57%), 0);
}

/* ── Back: frosted glass and the camera plateau ────────────────────────── */
.p3d-back {
    background: linear-gradient(215deg, var(--ti-hi), var(--ti) 22%, var(--ti-lo) 56%, var(--ti) 84%, var(--ti-hi));
    transform: translateZ(calc(var(--d) / -2)) rotateY(180deg);
}

.p3d-back-glass {
    position: relative;
    display: block;
    height: 100%;
    overflow: hidden;
    border-radius: calc(var(--r) - var(--rim));
    background:
        radial-gradient(90% 55% at 22% 6%, rgba(255, 255, 255, 0.2), transparent 70%),
        linear-gradient(168deg, var(--back-hi) 0%, var(--back) 44%, var(--back-lo) 100%);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22);
}

.p3d-plateau {
    position: absolute;
    top: calc(var(--w) * 0.032);
    left: calc(var(--w) * 0.032);
    width: calc(var(--w) * 0.47);
    height: calc(var(--w) * 0.47);
    border-radius: calc(var(--w) * 0.125);
    background:
        radial-gradient(120% 90% at 18% 10%, rgba(255, 255, 255, 0.4), transparent 55%),
        linear-gradient(150deg, var(--back-hi), var(--back) 55%, var(--back-lo));
    box-shadow:
        inset 0 0 0 1px rgba(255, 255, 255, 0.45),
        inset 0 calc(var(--w) * -0.006) calc(var(--w) * 0.012) rgba(60, 44, 30, 0.25),
        0 calc(var(--w) * 0.008) calc(var(--w) * 0.02) rgba(60, 44, 30, 0.35);
}

/* A lens, outside in: a polished titanium ring (its light runs round it),
   a black step, the glass with its coating's colour, the aperture rings,
   and two glints. */
.p3d-lens {
    position: absolute;
    width: 44%;
    aspect-ratio: 1;
    border-radius: 50%;
    background:
        radial-gradient(circle at 35% 31%, rgba(255, 255, 255, 0.9) 0 2.2%, rgba(255, 255, 255, 0.25) 3.6%, transparent 5.5%),
        radial-gradient(circle at 62% 66%, rgba(255, 255, 255, 0.35) 0 1.4%, transparent 3.2%),
        radial-gradient(circle at 42% 40%, rgba(150, 120, 255, 0.28) 0 10%, rgba(80, 200, 170, 0.12) 18%, transparent 30%),
        radial-gradient(circle closest-side, #04060a 0 14%, #1b2232 19%, #07090e 25%, #1a2130 33%, #090b10 40% 55%, #000 61%, #1d1915 64%, transparent 66%),
        radial-gradient(circle closest-side, transparent 0 74%, rgba(0, 0, 0, 0.35) 76%, transparent 80%),
        conic-gradient(from 210deg, var(--ti-hi), var(--ti-lo) 18%, var(--ti) 34%, var(--ti-hi) 50%, var(--ti-lo) 68%, var(--ti) 84%, var(--ti-hi));
    box-shadow:
        0 0 0 max(0.5px, calc(var(--w) * 0.0018)) rgba(40, 30, 20, 0.35),
        0 calc(var(--w) * 0.005) calc(var(--w) * 0.012) rgba(30, 20, 12, 0.45);
}

.p3d-lens.is-1 { top: 5%; left: 5%; }
.p3d-lens.is-2 { top: 51%; left: 5%; }
.p3d-lens.is-3 { top: 28%; left: 51%; }

.p3d-flash,
.p3d-lidar,
.p3d-back-mic {
    position: absolute;
    border-radius: 50%;
}

.p3d-flash {
    top: 8%;
    right: 11%;
    width: 13%;
    aspect-ratio: 1;
    background:
        repeating-radial-gradient(circle, rgba(205, 183, 150, 0.35) 0 6%, transparent 6% 14%),
        radial-gradient(circle closest-side, #fffbef 0 35%, #f0dfb9 52%, #cdb796 72%, var(--ti-lo) 100%);
}

.p3d-lidar {
    bottom: 8%;
    right: 11%;
    width: 13%;
    aspect-ratio: 1;
    background:
        radial-gradient(circle at 38% 34%, rgba(255, 255, 255, 0.4) 0 6%, transparent 14%),
        radial-gradient(circle closest-side, #3b2f3f 0 30%, #120e14 55%, #2d2520 80%, var(--ti-lo) 100%);
}

.p3d-back-mic {
    top: 76%;
    right: 34%;
    width: 3.5%;
    aspect-ratio: 1;
    background: #3a2f26;
}

.p3d-back-sheen {
    position: absolute;
    top: -20%;
    bottom: -20%;
    left: -70%;
    width: 240%;
    background: linear-gradient(80deg, transparent 38%, rgba(255, 255, 255, 0.16) 48%, transparent 58%);
    opacity: 0;
    pointer-events: none;
    backface-visibility: hidden;
}

/* ── Contact shadow: it narrows to the band as the phone turns edge on ─── */
.p3d-shadow {
    position: absolute;
    top: calc(100% - var(--w) * 0.07);
    left: 50%;
    width: calc(var(--w) * 0.92);
    height: calc(var(--w) * 0.17);
    margin-left: calc(var(--w) * -0.46);
    border-radius: 50%;
    background: radial-gradient(closest-side, rgba(0, 0, 0, 0.62), rgba(0, 0, 0, 0.3) 48%, transparent);
    pointer-events: none;
    transform: translateX(calc(var(--ys) * 17%)) scaleX(calc(0.06 + 0.88 * max(var(--yc), -1 * var(--yc)) + 0.16 * max(var(--ys), -1 * var(--ys))));
}

/* ── Hero: Sprout's sample chat on the phone ───────────────────────────── */
.p3d.is-hero {
    --otf-phone-w: clamp(256px, 24vw, 312px);
    --p3d-rx: 3deg;
    --p3d-ry: -12deg;
}

/* The chat keeps its flat-phone class for its own rules; the 3D frame
   replaces the flat one's rim, bezel and buttons. */
.p3d .otf-phone {
    width: auto;
    height: 100%;
    aspect-ratio: auto;
    padding: 0;
    border: 0;
    border-radius: inherit;
    background: none;
    box-shadow: none;
}

.p3d .otf-phone::before,
.p3d .otf-phone::after { content: none; }

.p3d .otf-phone-screen { border-radius: var(--sr); }

/* One turn on load, from its back to facing you. The body's angles
   interpolate one by one because both ends use its own list of functions;
   the light and the shadow follow the same turn. */
@media (prefers-reduced-motion: no-preference) {
    .p3d.is-hero:not(.is-entered) {
        animation: p3dLightIn var(--p3d-enter) var(--p3d-spring) var(--p3d-enter-delay, 120ms) backwards;
    }

    .p3d.is-hero:not(.is-entered) .p3d-body {
        animation: p3dTurnIn var(--p3d-enter) var(--p3d-spring) var(--p3d-enter-delay, 120ms) backwards;
    }

    .p3d.is-hero:not(.is-entered) .p3d-back-sheen {
        animation: p3dBackSheen calc(var(--p3d-enter) * 0.5) ease-out var(--p3d-enter-delay, 120ms) backwards;
    }
}

@keyframes p3dTurnIn {
    from { transform: translate3d(0, 5%, 0) rotateX(9deg) rotateY(168deg) scale(0.93); }
}

@keyframes p3dLightIn {
    from { --p3d-tip: 6deg; --p3d-turn: 180deg; }
}

@keyframes p3dBackSheen {
    from { transform: translate3d(-30%, 0, 0); opacity: 0.9; }
    to { transform: translate3d(40%, 0, 0); opacity: 0; }
}

/* ── Tour ───────────────────────────────────────────────────────────────── */
/* Every step keeps a slot the phone's size, so the copy sits where it does
   whichever phone is drawn. */
.otf-tour-phone {
    display: grid;
    place-items: center;
    width: var(--otf-phone-w);
    height: calc(var(--otf-phone-w) * 2.0965);
}

/* Pinned, one phone turns between the steps; unpinned (short windows,
   reduced motion), each step has its own at a fixed angle, pop-ups out. */
.otf-tour-device-row {
    display: none;
    pointer-events: none;
}

.otf-tour-track.is-live .otf-tour-device-row { display: grid; }

/* Unpinned, the steps scroll by, so a short window need not shrink them. */
@media (min-width: 901px) {
    .otf-tour-track:not(.is-live) { --otf-phone-w: clamp(230px, 21vw, 300px); }
}
.otf-tour-track.is-live .p3d.is-static { display: none; }

/* A phone that never turns paints its button faces flush, under the
   side's own shade. */
.p3d.is-static .p3d-side { transform-style: flat; }
.p3d.is-static .p3d-key::after { content: none; }

.p3d-shot {
    opacity: 0;
    transition: opacity 460ms ease;
}

.p3d-shot.is-on { opacity: 1; }

.p3d.is-moving .p3d-shot { will-change: opacity; }

/* ── The fan: three phones that open out in depth as it rises ──────────── */
/* --spread (0 to 1) comes from the section's scroll progress. The outer
   two start tucked behind the middle one, then slide out, step back and
   turn their screens toward it. */
.otf-fan-stage .p3d { transform-origin: 50% 90%; }

.otf-fan-stage .p3d.is-left {
    --p3d-rx: 3deg;
    --p3d-ry: calc(6deg + var(--spread) * 20deg);
    transform: translateX(calc(100% - var(--spread) * 84%)) scale(calc(0.93 - var(--spread) * 0.05));
}

.otf-fan-stage .p3d.is-center {
    z-index: 1;
    --p3d-rx: calc(2deg + var(--spread) * 3deg);
    --p3d-ry: calc(var(--spread) * -4deg);
    transform: translateY(calc(var(--spread) * -22px));
}

.otf-fan-stage .p3d.is-right {
    --p3d-rx: 3deg;
    --p3d-ry: calc(-6deg - var(--spread) * 20deg);
    transform: translateX(calc(-100% + var(--spread) * 84%)) scale(calc(0.93 - var(--spread) * 0.05));
}

/* ── For hosts: the pass turns slowly as it scrolls into view ──────────── */
.otf-hosts-pass-phone .p3d {
    --p3d-rx: calc(6deg - var(--p, 1) * 3deg);
    --p3d-ry: calc(30deg - var(--p, 1) * 44deg);
}

/* ── The screen gallery (Press) ─────────────────────────────────────────── */
/* Neighbours lean toward each other, a little more while they rise into
   view; hovered or focused, a phone turns to face you. */
.otf-screen-phone {
    display: grid;
    place-items: center;
}

.otf-screen-phone .p3d {
    --p3d-rx: calc(3deg * var(--p3d-face));
    --p3d-ry: calc(var(--lean, 10deg) * (1 + (1 - var(--p, 1)) * 1.6) * var(--p3d-face));
    transition: --p3d-face 700ms var(--p3d-spring), translate 420ms cubic-bezier(0.22, 1, 0.36, 1);
}

.otf-screen:is(:hover, :focus-within) .otf-screen-phone .p3d {
    --p3d-face: 0;
    translate: 0 -6px;
}

/* ── Demo: turns in once, then rests square so the spots sit on it ────── */
@media (prefers-reduced-motion: no-preference) {
    .p3d.is-demo {
        animation: p3dDemoLight 1300ms var(--p3d-spring) 180ms backwards;
    }

    .p3d.is-demo .p3d-body {
        animation: p3dDemoIn 1300ms var(--p3d-spring) 180ms backwards;
    }
}

@keyframes p3dDemoIn {
    from { transform: translate3d(0, 3%, 0) rotateX(7deg) rotateY(-34deg) scale(0.96); }
}

@keyframes p3dDemoLight {
    from { --p3d-tip: 7deg; --p3d-turn: -34deg; }
}

/* ── The Dynamic Island's phone ────────────────────────────────────────── */
/* The section's device keeps its padding, so its content box (what every
   cqw inside is a share of) is still the screen; the 3D phone fills the
   device edge to edge over that padding. The rim is the padding and the
   Lock Screen's own black border is the bezel. Pinned, the scene turns it
   a little as the states go by (--otf-island-turn); otherwise it rests. */
.otf-island-device > .p3d {
    --r: calc(var(--w) * 0.175);
    --rim: calc(var(--w) * 0.014);
    --bezel: 0px;
    --h: calc(var(--w) * 2.1413);
    --p3d-rx: 2deg;
    --p3d-ry: var(--otf-island-turn, -9deg);
    margin: calc(var(--w) * -0.014);
}

.otf-island-device .p3d-screen { background: #050505; }

@media (prefers-reduced-motion: reduce) {
    .otf-screen-phone .p3d { transition: none; }
}

/* ── Pop-ups: lifted off the glass, in front of it ─────────────────────── */
.p3d-pop {
    --ax: -50%;
    --ay: -50%;
    --lift: calc(var(--d) / 2 + var(--w) * var(--z, 0.14));
    position: absolute;
    left: var(--x, 50%);
    top: var(--y, 50%);
    box-sizing: border-box;
    color: #1c1814;
    font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
    font-size: clamp(9px, calc(var(--w) * 0.041), 12.5px);
    line-height: 1.25;
    text-align: left;
    pointer-events: none;
    transform: translate3d(var(--ax), var(--ay), var(--lift));
}

/* In the pinned tour they rise out of the glass one after another when
   their step arrives, and sink back when it goes. */
.p3d.is-tour-device .p3d-pop {
    transition:
        transform 900ms var(--p3d-spring) calc(260ms + var(--i, 0) * 140ms),
        opacity 320ms ease calc(260ms + var(--i, 0) * 140ms),
        visibility 0s;
}

.p3d.is-tour-device .p3d-pop:not(.is-on) {
    opacity: 0;
    visibility: hidden;
    transform: translate3d(var(--ax), calc(var(--ay) + 14%), calc(var(--d) / 2 + 1px)) scale(0.84);
    transition:
        transform 280ms cubic-bezier(0.4, 0, 1, 1),
        opacity 200ms ease,
        visibility 0s linear 280ms;
}

.p3d-pop-text {
    display: grid;
    gap: 0.14em;
    min-width: 0;
}

.p3d-pop-text small {
    display: flex;
    justify-content: space-between;
    gap: 0.6em;
    color: rgba(28, 24, 20, 0.58);
    font-size: 0.8em;
    font-weight: 650;
    letter-spacing: 0.01em;
}

.p3d-pop-text small em { font-style: normal; font-weight: 500; }
.p3d-pop-text strong { color: #1c1814; font-weight: 700; }
.p3d-pop-text span { color: rgba(28, 24, 20, 0.78); text-wrap: pretty; }

.p3d-pop-icon,
.p3d-pop-badge {
    display: grid;
    place-items: center;
    flex: none;
}

.p3d-pop-badge {
    width: 2.3em;
    height: 2.3em;
    border-radius: 50%;
    background: #8c1515;
    color: #fffaf0;
    font-weight: 800;
}

.p3d-pop-items {
    display: flex;
    gap: 0.45em;
}

.p3d-pop-items > span { white-space: nowrap; }

.p3d-pop-action {
    white-space: nowrap;
    font-weight: 750;
}

/* Cards share a surface: solid ivory and a soft shadow on the glass. */
:is(.p3d-pop.is-banner, .p3d-pop.is-card, .p3d-pop.is-arrival, .p3d-pop.is-sprout, .p3d-pop.is-comment) {
    background: #fbf8f2;
    box-shadow:
        0 0.1em 0.25em rgba(24, 16, 10, 0.14),
        0 1.3em 2.4em -0.7em rgba(24, 16, 10, 0.5);
}

/* A notification banner, as iOS draws one. */
.p3d-pop.is-banner {
    display: grid;
    grid-template-columns: 2.6em minmax(0, 1fr);
    column-gap: 0.75em;
    align-items: center;
    width: 20.5em;
    padding: 0.75em 0.9em;
    border-radius: 1.45em;
    background: rgba(250, 247, 241, 0.98);
}

.p3d-pop.is-banner .p3d-pop-icon img {
    display: block;
    width: 2.6em;
    height: 2.6em;
    border-radius: 0.62em;
}

/* Filter chips, the first one picked. */
.p3d-pop.is-chips .p3d-pop-items > span {
    padding: 0.5em 0.95em;
    border-radius: 999px;
    background: #fbf8f2;
    font-weight: 650;
    box-shadow: 0 0.1em 0.25em rgba(24, 16, 10, 0.14), 0 0.9em 1.8em -0.6em rgba(24, 16, 10, 0.5);
}

.p3d-pop.is-chips .p3d-pop-items > .is-picked {
    background: #8c1515;
    color: #fffaf0;
}

.p3d-pop.is-chips .p3d-pop-items > .is-picked::before {
    content: "\f00c";
    margin-right: 0.45em;
    font: var(--fa-font-solid, normal 900 1em/1 "Font Awesome 6 Free");
    font-size: 0.82em;
}

/* An event pin lifting off the map, its shadow left on the glass. */
.p3d-pop.is-pin { --ay: -100%; }

.p3d-pop.is-pin .p3d-pop-icon {
    width: 2.7em;
    height: 2.7em;
    border: 0.17em solid #fff;
    border-radius: 50% 50% 50% 0.2em;
    background: linear-gradient(135deg, #e0464c, #a3141c);
    box-shadow: 0 1em 1.6em -0.5em rgba(60, 10, 10, 0.6);
    color: #fff;
    rotate: -45deg;
}

.p3d-pop.is-pin .p3d-pop-icon i { rotate: 45deg; font-size: 1em; }

.p3d-pop.is-pin-shadow {
    --ay: -50%;
    width: 2.8em;
    height: 0.9em;
    border-radius: 50%;
    background: radial-gradient(closest-side, rgba(20, 10, 4, 0.45), transparent);
}

/* Walking directions with Go. Blue is the app's Go colour. */
.p3d-pop.is-card {
    display: grid;
    grid-template-columns: 2.3em minmax(0, 1fr) auto;
    gap: 0.7em;
    align-items: center;
    width: 18.5em;
    padding: 0.7em 0.75em;
    border-radius: 1.3em;
}

.p3d-pop.is-card .p3d-pop-icon {
    width: 2.3em;
    height: 2.3em;
    border-radius: 50%;
    background: #e5edfc;
    color: #2463d6;
}

.p3d-pop.is-card .p3d-pop-action {
    padding: 0.45em 1em;
    border-radius: 999px;
    background: #2463d6;
    color: #fff;
}

/* The ride as a Live Activity: the Dynamic Island widens around the camera. */
.p3d-pop.is-island {
    --ax: -50%;
    --ay: 0%;
    --lift: calc(var(--d) / 2 + 0.6px);
    top: calc(var(--rim) + var(--bezel) + var(--w) * 0.03);
    left: 50%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: calc(var(--w) * 0.46);
    height: calc(var(--w) * 0.088);
    padding: 0 calc(var(--w) * 0.03);
    border-radius: 999px;
    background: #000;
    color: #fffaf0;
    font-size: max(7.5px, calc(var(--w) * 0.04));
    clip-path: inset(0 round 999px);
}

.p3d-pop.is-island .p3d-pop-icon { color: #e3585d; }

.p3d.is-tour-device .p3d-pop.is-island {
    transition: clip-path 760ms var(--p3d-spring) 200ms;
}

.p3d.is-tour-device .p3d-pop.is-island:not(.is-on) {
    opacity: 1;
    visibility: visible;
    transform: translate3d(var(--ax), var(--ay), var(--lift));
    clip-path: inset(0 16.8% round 999px);
    transition: clip-path 300ms ease;
}

.p3d.is-tour-device .p3d-pop.is-island > * { transition: opacity 260ms ease 480ms; }
.p3d.is-tour-device .p3d-pop.is-island:not(.is-on) > * { opacity: 0; transition: opacity 120ms ease; }

/* The next bus. */
.p3d-pop.is-arrival {
    display: grid;
    grid-template-columns: 2.3em minmax(0, 1fr) auto;
    gap: 0.7em;
    align-items: center;
    width: 16em;
    padding: 0.7em 0.95em 0.7em 0.7em;
    border-radius: 1.3em;
}

.p3d-pop.is-arrival .p3d-pop-action {
    color: #1c1814;
    font-size: 1.45em;
    font-weight: 800;
    letter-spacing: -0.02em;
}

/* Your question to Sprout, and Sprout's pick. */
.p3d-pop.is-bubble {
    width: max-content;
    max-width: 15em;
    padding: 0.65em 0.95em;
    border-radius: 1.25em 1.25em 0.35em 1.25em;
    background: #8c1515;
    color: #fffaf0;
    box-shadow: 0 1.1em 2.2em -0.7em rgba(60, 10, 10, 0.55);
}

.p3d-pop.is-bubble .p3d-pop-text strong {
    color: inherit;
    font-weight: 600;
}

.p3d-pop.is-sprout {
    display: grid;
    grid-template-columns: 2.4em minmax(0, 1fr);
    gap: 0.3em 0.7em;
    width: 17.5em;
    padding: 0.8em 0.9em;
    border-radius: 1.3em;
}

.p3d-pop.is-sprout .p3d-pop-icon {
    width: 2.4em;
    height: 2.4em;
    border-radius: 50%;
    background: #e2eee5;
}

.p3d-pop.is-sprout .sprout-mascot { --sprout-size: 1.9em !important; }
.p3d-pop.is-sprout .p3d-pop-text small { color: #214f38; font-weight: 800; }

.p3d-pop.is-sprout .p3d-pop-action {
    grid-column: 2;
    justify-self: start;
    margin-top: 0.3em;
    padding: 0.42em 1em;
    border-radius: 999px;
    background: #214f38;
    color: #fffaf0;
}

/* The board's tabs, lifted off the top of the screen. */
.p3d-pop.is-tabs {
    padding: 0.3em;
    border-radius: 999px;
    background: rgba(28, 24, 20, 0.95);
    box-shadow: 0 1.1em 2.2em -0.7em rgba(24, 16, 10, 0.6);
}

.p3d-pop.is-tabs .p3d-pop-items { gap: 0.15em; }

.p3d-pop.is-tabs .p3d-pop-items > span {
    padding: 0.5em 1.05em;
    border-radius: 999px;
    color: rgba(255, 250, 240, 0.74);
    font-weight: 650;
}

.p3d-pop.is-tabs .p3d-pop-items > .is-picked {
    background: #fffaf0;
    color: #1c1814;
}

/* A reply on a post. */
.p3d-pop.is-comment {
    display: grid;
    grid-template-columns: 2.1em minmax(0, 1fr);
    gap: 0.65em;
    align-items: start;
    width: 14em;
    padding: 0.75em 0.85em;
    border-radius: 1.3em 1.3em 1.3em 0.35em;
}

.p3d-pop.is-comment .p3d-pop-badge {
    width: 2.1em;
    height: 2.1em;
    background: #dfc79f;
    color: #3a2a18;
}

/* The pass, as it sits in Apple Wallet. */
.p3d-pop.is-wallet {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 0.5em 0.7em;
    align-items: end;
    width: 15.5em;
    padding: 0.9em;
    border-radius: 1em;
    background: linear-gradient(160deg, #2b6048, #173f2e);
    color: #fffaf0;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12), 0 1.4em 2.6em -0.8em rgba(0, 0, 0, 0.65);
    rotate: -5deg;
}

.p3d-pop.is-wallet .p3d-pop-text { grid-column: 1; grid-row: 1; }
.p3d-pop.is-wallet .p3d-pop-text small { color: rgba(255, 250, 240, 0.7); font-size: 0.7em; letter-spacing: 0.08em; text-transform: uppercase; }
.p3d-pop.is-wallet .p3d-pop-text strong { color: #fffaf0; font-size: 1.06em; line-height: 1.15; }
.p3d-pop.is-wallet .p3d-pop-text span { color: rgba(255, 250, 240, 0.8); font-size: 0.86em; }

.p3d-pop.is-wallet .p3d-pop-icon {
    grid-column: 2;
    grid-row: 1;
    width: 3.1em;
    height: 3.1em;
    border-radius: 0.45em;
    background: #fffaf0;
    color: #173f2e;
    font-size: 1em;
}

.p3d-pop.is-wallet .p3d-pop-icon i { font-size: 2.3em; }

.p3d-pop.is-wallet .p3d-pop-action {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    gap: 0.45em;
    padding-top: 0.6em;
    border-top: 1px solid rgba(255, 250, 240, 0.18);
    font-size: 0.84em;
}

@media (prefers-reduced-motion: reduce) {
    .p3d-shot,
    .p3d-pop,
    .p3d-pop > * { transition: none !important; }
}
