/* Cinematic scroll locks freeze the root scroller (overflow:hidden). Reserving
   the scrollbar gutter keeps the viewport width IDENTICAL while the classic
   scrollbar blinks out, so locking/unlocking can never reflow the layout. */
        html { scrollbar-gutter: stable; }
        /* App-grade touch feel: no grey flash rectangle on taps (Apple/DJI ship this) */
        html { -webkit-tap-highlight-color: transparent; }
        /* ── Finishing kit: the details that make a dark page feel NATIVE ── */
        /* Native UI (form controls, scrollbars, autofill) renders in dark */
        :root { color-scheme: dark; }
        /* Text selection carries the brand blue instead of default OS blue-gray */
        ::selection { background: rgba(41,151,255,.32); color: #fff; }
        /* Keyboard focus: one confident ring, only for keyboard users —
           pointer clicks stay clean (Apple's exact pattern) */
        :focus { outline: none; }
        :focus-visible { outline: 2px solid rgba(110,175,255,.85); outline-offset: 3px; border-radius: 4px; }
        /* Desktop scrollbar: slim graphite, invisible track (WebKit + Firefox) */
        @media (hover: hover) and (pointer: fine) {
            html { scrollbar-color: rgba(255,255,255,.22) transparent; scrollbar-width: thin; }
            ::-webkit-scrollbar { width: 9px; height: 9px; }
            ::-webkit-scrollbar-track { background: transparent; }
            ::-webkit-scrollbar-thumb { background: rgba(255,255,255,.16); border-radius: 8px; border: 2px solid transparent; background-clip: content-box; }
            ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,.3); border: 2px solid transparent; background-clip: content-box; }
        }
        @keyframes cmosSweep {
            0%   { transform: translateX(-420px); }
            55%  { transform: translateX(420px); }
            100% { transform: translateX(420px); }
        }
        @keyframes cmosArcSpin { to { transform: rotate(360deg); } }
        @keyframes cmosGlowPulse { 0%,100% { opacity:.7; } 50% { opacity:1; } }
        /* Cinematic video showcase */
        .sensor-cinema { position:relative; width:100%; max-width:520px; margin:0 auto; border-radius:16px; overflow:hidden; aspect-ratio:16/10; background:#080808; }
        .sensor-cinema video { width:100%; height:100%; object-fit:cover; display:block; }
        .sensor-cinema-grain { position:absolute; inset:0; background:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.04'/%3E%3C/svg%3E"); pointer-events:none; z-index:2; opacity:.15; }
        .sensor-cinema-vignette { position:absolute; inset:0; background:radial-gradient(ellipse at center,transparent 50%,rgba(0,0,0,.6) 100%); pointer-events:none; z-index:3; }
        .sensor-cinema-bars { position:absolute; left:0; right:0; height:8%; background:#000; pointer-events:none; z-index:4; }
        .sensor-cinema-bars.top { top:0; }
        .sensor-cinema-bars.bot { bottom:0; }
        .sensor-cinema-hud { position:absolute; inset:0; pointer-events:none; z-index:5; }
        .sensor-hud-focus { position:absolute; top:50%; left:50%; width:56px; height:56px; transform:translate(-50%,-50%); border:1.5px solid rgba(255,200,0,.5); border-radius:2px; opacity:0; animation:sensorFocus 4s ease-in-out infinite; }
        @keyframes sensorFocus { 0%,100% { opacity:0; transform:translate(-50%,-50%) scale(1.3); } 15% { opacity:1; transform:translate(-50%,-50%) scale(1); } 30% { opacity:1; transform:translate(-50%,-50%) scale(1); } 45% { opacity:0; transform:translate(-50%,-50%) scale(.95); } }
        .sensor-hud-tag { position:absolute; font-size:9px; font-weight:500; letter-spacing:.04em; font-family:'SF Mono',SFMono-Regular,Menlo,monospace; }
        .sensor-hud-rec { color:rgba(255,59,48,.7); top:14%; left:6%; display:flex; align-items:center; gap:4px; }
        .sensor-hud-rec::before { content:''; width:6px; height:6px; border-radius:50%; background:#ff3b30; animation:aesPulse 2s ease-in-out infinite; }
        .sensor-hud-iso { color:rgba(255,255,255,.3); bottom:14%; left:6%; }
        .sensor-hud-fps { color:rgba(255,255,255,.3); bottom:14%; right:6%; }
        .sensor-hud-wb { color:rgba(255,200,0,.4); top:14%; right:6%; }
        /* 4K120fps Value Section */
        .fps-value { margin-top: clamp(40px, 6vw, 60px); }
        .fps-hero { text-align: center; margin-bottom: clamp(32px, 5vh, 48px); }
        .fps-hero h3 { font-size: clamp(36px, 5vw, 56px); font-weight: 600; color: #f5f5f7; margin-bottom: 12px; letter-spacing: -0.04em; }
        .fps-hero p { font-size: 21px; color: #86868b; }
        @keyframes scanLine { 0% { top: 0; opacity: 0; } 8% { opacity: 1; } 92% { opacity: 1; } 100% { top: 100%; opacity: 0; } }
        @keyframes badgePulse { 0%,100% { opacity: 1; } 50% { opacity: .5; } }
        #lidarCanvas { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1; transform: translateZ(0); will-change: transform; }
        #lidarOverlay { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 2; transform: translateZ(0); will-change: transform; }
        /* ── end LiDAR section ── */

        /* ═══════════════════════════════════════════════════════════════════
           SENSOR STACK — one macro, one travelling light.

           This was two Cycles macros of the same casting stacked down the
           page, one per window, and it read as a spec sheet for the reason
           any such pair does: the reader is shown the same object twice and
           left to find the difference. There is one render now, pinned, and
           a spotlight walks from the ranging window down to the imaging one
           while the copy changes under it — so the comparison happens in one
           frame instead of across a scroll.

           The cover glass is real geometry with a solved highlight, so the
           page adds only shadow and a bloom; the earlier CSS gradients faking
           a coating are gone, and with them the moulded-plastic look.
           ═══════════════════════════════════════════════════════════════════ */
        .optics-section { position: relative; background: #000; padding: clamp(56px, 9vh, 120px) 0 0; }
        .optics-intro { max-width: 900px; margin: 0 auto clamp(28px, 5vh, 64px); padding: 0 clamp(20px, 5vw, 40px); text-align: center; }
        .optics-eyebrow { display: inline-flex; align-items: center; gap: 10px; font-family: var(--font-mono); font-size: 11px; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: rgba(255,255,255,.45); margin-bottom: 16px; }
        .optics-title { font-size: clamp(28px, 4.4vw, 56px); font-weight: 700; letter-spacing: -.035em; line-height: 1.1; color: #f5f5f7; margin: 0 0 16px; text-wrap: balance; }
        .optics-sub { font-size: clamp(14px, 1.15vw, 17px); line-height: 1.65; color: #96969c; margin: 0 auto; max-width: 56ch; text-wrap: pretty; }

        /* Scroll room for the two beats plus the settle at either end. The pin
           is what the reader actually sees; the stage's height is the budget
           the light has to make the walk in. */
        /* Four holds and two openings need more runway than two beats did. */
        .optics-stage { position: relative; height: 380vh; }
        .optics-pin { position: sticky; top: 0; height: 100svh; overflow: hidden; background: #000; }

        /* The plate keeps the render's own aspect so the window coordinates
           below are plate fractions and land on glass at any viewport; max()
           is what guarantees it still covers a frame taller than 1800/1038. */
        .opt-plate {
            position: absolute; left: 50%; top: 50%;
            width: max(100%, calc(100svh * 1800 / 1040));
            aspect-ratio: 1800 / 1040;
            transform: translate(-50%, -50%) scale(var(--pz, 1.02)) translate(var(--px, 0%), var(--py, 0%));
            will-change: transform;
        }
        /* The film. A canvas rather than an img because the frame changes on
           every scroll tick and swapping a src is a decode away from a flash;
           drawing an already-decoded frame is not. Its intrinsic size is the
           frame's, so it lays out exactly as the still it replaced and the
           window fractions above still land on glass. */
        .opt-film { display: block; width: 100%; height: 100%; background: #000; }
        /* The court, grown out of the ranging window rather than shown next to
           it. --cx/--cy are that window's centre in the pin's own coordinates,
           written per frame by the JS: the plate is transformed and wider than
           the pin, so the window's plate fraction is not where it is on screen.
           The mask is the whole effect — the cloud is only visible where the
           circle has opened, so it appears to come out of the glass. */
        .opt-cloud {
            position: absolute; inset: 0; z-index: 1;
            opacity: var(--cloud, 0);
            /* Slid so the court's own centre lands on the window, then opened
               from there. Without this the aperture cut a hole in whatever part
               of the court render happened to be behind it — which on a phone,
               where the plate sits across the top as a band and the window ends
               up around a third of the way down, was the empty sky above the
               court. The canvas measured full of content and the hole showed
               black. Downscaled at the start and native at full open, never up,
               so the canvas is only ever sampled sharper than it rendered. */
            transform: translate(calc(var(--cx, 40%) - 50%), calc(var(--cy, 32%) - 50%))
                       scale(calc(0.86 + 0.14 * var(--cloud, 0)));
            transform-origin: 50% 50%;
            /* An aperture the shape of the window it comes out of, opened by the
               scrub. Both radii and the centre are written by the JS from the
               window's measured rectangle — see the note there on why they are
               not percentages of this box.
               It began as `circle` with a percentage radius, which is not a
               thing CSS allows, so the mask was invalid, silently ignored, and
               the court filled the frame. That is precisely the cut this
               section exists to avoid: the machine vanished and it read as a
               jump to another scene instead of a look through a window. */
            -webkit-mask-image: radial-gradient(ellipse
                calc(var(--crx, 8%) * var(--cloud, 0)) calc(var(--cry, 8%) * var(--cloud, 0))
                at 50% 50%,
                #000 64%, rgba(0,0,0,.38) 86%, transparent 100%);
            mask-image: radial-gradient(ellipse
                calc(var(--crx, 8%) * var(--cloud, 0)) calc(var(--cry, 8%) * var(--cloud, 0))
                at 50% 50%,
                #000 64%, rgba(0,0,0,.38) 86%, transparent 100%);
            pointer-events: none; contain: layout paint;
        }
        /* The casting steps back a little while either window is open, so what
           is inside is what the eye lands on — but only a little, because the
           whole point is that you can still see the part it comes out of. */
        .optics-stage .opt-plate { transition: filter .5s ease; }
        .optics-stage.beat-cloud .opt-plate,
        .optics-stage.die-live .opt-plate { filter: brightness(.62); }

        /* ── The die behind the imaging window. Positioned as the glass rather
           than masked out of a full-bleed layer: it is vector, so it rescales
           for nothing and can simply be the window. --dx/--dy/--dw/--dh are
           that window's measured rectangle, written per frame alongside the
           court's, so it stays on the glass while the camera moves. ── */
        .opt-die {
            position: absolute;
            left: var(--dx, 43%); top: var(--dy, 62%);
            width: var(--dw, 30%); height: var(--dh, 34%);
            z-index: 2; pointer-events: none;
            opacity: var(--die, 0);
            transform: translate(-50%, -50%) scale(calc(0.90 + 0.10 * var(--die, 0)));
            /* Only the very edge, because these edges are the glass's edges and
               a hard stop there is correct — the array does fill the window. */
            -webkit-mask-image: radial-gradient(ellipse 78% 78% at 50% 50%,
                #000 82%, transparent 100%);
            mask-image: radial-gradient(ellipse 78% 78% at 50% 50%,
                #000 82%, transparent 100%);
        }
        .opt-die .die-svg {
            display: block; position: absolute; inset: 0;
            width: 100%; height: 100%;
        }
        .optics-stage:not(.die-live) .opt-die { visibility: hidden; }
        /* The frame it resolves into. Sits under the array and is uncovered by
           it rather than faded in over it, so the array reads as becoming the
           picture instead of the picture reading as a second thing on top. */
        .opt-die .die-shot {
            position: absolute; inset: 0; width: 100%; height: 100%;
            object-fit: cover; display: block; border-radius: 6px;
            background: #05070b;
        }
        .opt-die .die-face { opacity: calc(1 - var(--shot, 0)); }
        .opt-die .die-mark {
            opacity: var(--die, 0);
            transform: scale(calc(0.84 + 0.16 * var(--die, 0)));
            transform-box: view-box; transform-origin: 600px 410px;
        }
        .opt-die .die-etch { opacity: calc(var(--die, 0) * var(--die, 0)); }
        /* The rim survives the dissolve — a sensor's edge is also a frame's
           edge, and losing it makes the footage look pasted over the section
           rather than delivered by the part in front of it. */
        .opt-die .die-svg > rect, .opt-die .die-svg > path {
            opacity: calc(1 - 0.55 * var(--shot, 0));
        }
        /* Grown past the glass, the vignette holding the eye on one window has
           nothing left to hold, and the casting it was darkening is gone. */
        .optics-stage.die-full .opt-focus,
        .optics-stage.die-full .opt-beam { opacity: 0 !important; }
        .optics-stage.die-full .opt-plate { filter: brightness(.2); }
        /* The scrim has to work much harder here than anywhere else in the
           section. Everywhere else the copy sits over a near-black casting; at
           the end it sits over a sunlit court, and the gradient tuned for the
           first case left orange type on bright green trees. Scaled with --shot
           so it arrives with the picture and not before it. */
        .optics-stage .opt-scrim { transition: none; }
        .optics-stage.die-full .opt-scrim::after {
            content: ''; position: absolute; inset: 0;
            opacity: var(--shot, 0);
            background: radial-gradient(84% 130% at 100% 50%,
                rgba(0,0,0,.95) 0%, rgba(0,0,0,.80) 30%,
                rgba(0,0,0,.34) 58%, rgba(0,0,0,.06) 76%, transparent 88%);
        }
        /* The loops only run while the layer is open — two orbiting arcs and a
           travelling sweep against a hidden element is work for nobody. */
        .optics-stage.die-live .opt-die .die-sweep {
            animation: dieSweep 7.5s cubic-bezier(.45,.05,.35,.95) infinite;
        }
        @keyframes dieSweep {
            0%   { transform: translateX(-430px); }
            58%  { transform: translateX(430px); }
            100% { transform: translateX(430px); }
        }

        /* ── The aura: the old chapter's light system, radiating out of the
           imaging window and over the casting instead of being trapped inside
           the glass. Full-bleed and slid so its centre lands on the window, the
           same way the court is — see the note on .opt-cloud's transform.
           Screen-blended, because these are lights. ── */
        .opt-aura {
            position: absolute; inset: 0; z-index: 2;
            pointer-events: none; mix-blend-mode: screen;
            /* Squared so it comes up late and quietly: the rings arriving at the
               same rate as the array made the window look like it was switching
               on rather than being looked into. Then out again as the die grows
               past the window — the rings radiate from a window, and once the
               array is bigger than the casting there is nothing to radiate over. */
            opacity: calc(var(--die, 0) * var(--die, 0) * 0.92 * (1 - var(--grow, 0)));
            transform: translate(calc(var(--ax, 43%) - 50%), calc(var(--ay, 62%) - 50%))
                       scale(calc(0.82 + 0.18 * var(--die, 0)));
            transform-origin: 50% 50%;
        }
        /* Sized off the shorter edge so the rings stay circles on any viewport,
           and generously, because most of the system is meant to run off frame. */
        .opt-aura svg {
            display: block; position: absolute; left: 50%; top: 50%;
            width: 190vmin; height: 190vmin; transform: translate(-50%, -50%);
        }
        .optics-stage:not(.die-live) .opt-aura { visibility: hidden; }
        .optics-stage.die-live .opt-aura .aura-arc {
            transform-box: view-box; transform-origin: 600px 600px;
            animation: auraArc 40s linear infinite;
        }
        .optics-stage.die-live .opt-aura .aura-arc-rev {
            animation-direction: reverse; animation-duration: 58s;
        }
        @keyframes auraArc { to { transform: rotate(360deg); } }
        .optics-stage.die-live .opt-aura .aura-halo {
            animation: auraHaloPulse 7s ease-in-out infinite;
        }
        @keyframes auraHaloPulse { 0%, 100% { opacity: .68; } 50% { opacity: 1; } }
        /* Hidden outright at zero so a live WebGL canvas is not being composited
           through a mask for the nine tenths of the section it plays no part in.
           Gated on the court being worth drawing rather than on the copy beat —
           the two do not share edges, and the mismatch showed up as a pop. */
        .optics-stage:not(.cloud-live) .opt-cloud { visibility: hidden; }

        /* ── Guidance the way DJI does it: sink everything except the window
           being discussed into shadow. --fx/--fy are the window's centre as a
           fraction of the plate, measured out of the render camera by
           shoot_optics.py, so a reframe cannot leave the light on bare metal.
           publish_optics.py prints these. ── */
        .optics-stage { --fx: 40.2%; --fy: 32.0%; --fw: 20.1%; --fh: 25.0%; --dim: 0; }
        /* Same measurement, same source: where each window sits and which way it
           looks. Both used to be static, because the picture was. Now the camera
           moves, so --bx/--by/--ba are written per frame from the manifest
           publish_dolly.py ships beside the frames — a beam pinned to one
           coordinate slides off its window the moment the push starts.
           --bl is not measured and cannot be: the tight end of this move is a
           macro 139mm off the casting, and it projects anything a beam would
           actually cross to several times the frame width, so the length is
           chosen to land inside it. The ranging fan gets the shorter of the two
           only because it leaves steeply and would otherwise spend most of
           itself above the top edge. */
        .opt-beam.is-lidar { --bl: 28%; }
        .opt-beam.is-cam   { --bl: 32%; }
        /* Tight and hard. A gentle vignette reads as a gradient someone left on
           the image; the point is that the reader cannot look anywhere except
           the window being described, so the spot is barely wider than the
           glass and everything past it goes most of the way to black. */
        .opt-focus {
            position: absolute; inset: -1px; pointer-events: none; z-index: 2;
            background: radial-gradient(34% 42% at var(--fx) var(--fy),
                rgba(0,0,0,0) 0%, rgba(0,0,0,0) 38%,
                rgba(0,0,0,.34) 58%, rgba(0,0,0,.78) 82%, rgba(0,0,0,.92) 100%);
            opacity: var(--dim);
        }
        /* Window-shaped, and only there to clip the ranging sweep to the glass.
           It used to carry a box-shadow bloom as well, which on a phone — where
           the plate is zoomed and the surround is dark — showed up as a plainly
           rectangular smear across the casting. The glass has a real solved
           highlight in the render; it does not need a painted one. */
        .opt-halo {
            position: absolute; left: var(--fx); top: var(--fy);
            width: var(--fw); height: var(--fh); z-index: 3;
            transform: translate(-50%, -50%);
            border-radius: 14%;
            pointer-events: none; overflow: hidden;
            opacity: var(--dim);
        }
        /* Ranging sweep. It belongs to the emitter window, so it fades out
           once the light has walked on to the imaging one. */
        .opt-scan {
            position: absolute; left: 0; right: 0; top: 0; height: 26%;
            background: linear-gradient(180deg, transparent, rgba(190,225,255,.46), transparent);
            opacity: 0;
        }
        .optics-pin.is-live .opt-scan { animation: optScan 2.8s cubic-bezier(.55,0,.45,1) infinite; }
        .optics-stage:not(.beat-lidar) .opt-scan { animation: none; opacity: 0; }
        @keyframes optScan {
            0%   { transform: translateY(-40%); opacity: 0; }
            25%  { opacity: .9; }
            75%  { opacity: .55; }
            100% { transform: translateY(300%); opacity: 0; }
        }
        /* The imaging window's counterpart on the glass: it is collecting light
           rather than sending any, so it breathes instead of sweeping. */
        .opt-iris {
            position: absolute; inset: 8%; border-radius: 12%; opacity: 0;
            background: radial-gradient(60% 60% at 50% 50%,
                rgba(255,206,150,.50) 0%, rgba(255,166,96,.20) 52%, transparent 100%);
        }
        .optics-stage.beat-cam .optics-pin.is-live .opt-iris {
            animation: optIris 3.4s ease-in-out infinite;
        }
        @keyframes optIris {
            0%, 100% { opacity: .40; transform: scale(.92); }
            50%      { opacity: 1;   transform: scale(1.05); }
        }

        /* ── The beams. What a sensor is doing is invisible, and a spotlight on
           the glass only says "look here" — so each window throws its own work
           out into the frame.
           --ba is the direction the window is genuinely pointing, projected
           through the render camera by shoot_optics.py, which is the whole
           reason this reads as light leaving a part rather than a gradient
           lying on top of one. --bl is that same projection's length, clamped
           only so a window raking away from the lens still gets a beam long
           enough to see.
           The box is square in plate-width units, so rotate() cannot shear the
           wedge, and it hinges on its own left edge — which is the glass. ── */
        .opt-beam {
            position: absolute; left: var(--bx); top: var(--by);
            width: clamp(17%, var(--bl), 44%); aspect-ratio: 1;
            z-index: 3; pointer-events: none; mix-blend-mode: screen;
            transform-origin: 0 50%;
            transform: translateY(-50%) rotate(var(--ba));
            opacity: 0; transition: opacity .55s ease;
            /* So the softening below can be a fraction of the beam rather than
               of the window. The plate is 210% wide on a phone and about 108%
               on a laptop, so a viewport-relative blur is twice as soft on one
               as the other and the raster stops resolving on whichever loses. */
            container-type: inline-size;
        }
        /* Both wedges are clipped out of that square: apex on the glass, mouth
           at the far edge, --spread being the half-angle as a fraction of the
           throw. Ranging is the tighter of the two because it is a beam; the
           imaging window is a lens and sees most of a half-court.
           The blur is what stops it being a paper triangle. A clip-path has
           perfectly straight sides and light does not, and at these sizes the
           two edges are the whole read: unblurred, the wedge looks like a
           shape sitting on the photograph, and a few pixels of softness is the
           difference between that and light in air. Every gradient below also
           starts at nothing on the glass itself, so the beam appears to leave
           the window rather than to be painted across it. */
        .opt-fan {
            position: absolute; inset: 0;
            clip-path: polygon(0 50%, 100% calc(50% - var(--spread)),
                                       100% calc(50% + var(--spread)));
            filter: blur(0.42vw);
            filter: blur(1.6cqw);
        }
        .opt-fan::before, .opt-fan::after { content: ''; position: absolute; inset: 0; }
        /* Range planes marching away from the emitter, and the depth grid they
           are marching through — 240 x 240 at 240 fps is a raster, so it is
           drawn as one rather than as a glow. */
        .opt-beam.is-lidar { --spread: 26%; }
        .opt-beam.is-lidar .opt-fan {
            background: linear-gradient(90deg, transparent 0%,
                rgba(150,205,255,.46) 15%, rgba(118,176,252,.17) 52%, transparent 86%);
        }
        .opt-beam.is-lidar .opt-fan::before {
            background: repeating-linear-gradient(90deg,
                rgba(176,220,255,.44) 0 0.45%, transparent 0.45% 7.4%);
            -webkit-mask-image: linear-gradient(90deg, transparent 6%, #000 22%,
                                               #000 58%, transparent 88%);
            mask-image: linear-gradient(90deg, transparent 6%, #000 22%,
                                        #000 58%, transparent 88%);
        }
        .opt-beam.is-lidar .opt-fan::after {
            background: linear-gradient(90deg, transparent 41%,
                rgba(206,238,255,.85) 50%, transparent 59%);
            opacity: 0;
        }
        /* The imaging window is the same idea run backwards: a wider cone, warm
           because it is the same amber the guides use, and the light travels
           inward to the glass because that is the direction it is going.
           It also runs out sooner. This one leaves almost level and the copy
           lives in the half of the frame it is heading into, so it has to have
           finished before it gets there — a cone under the specifications is
           the point at which guidance turns into decoration. */
        .opt-beam.is-cam { --spread: 38%; }
        .opt-beam.is-cam .opt-fan {
            background: linear-gradient(90deg, transparent 0%,
                rgba(255,186,122,.32) 17%, rgba(255,150,80,.11) 52%, transparent 80%);
        }
        .opt-beam.is-cam .opt-fan::before {
            background: radial-gradient(46% 88% at 4% 50%,
                rgba(255,214,164,.26) 0%, transparent 72%);
        }
        .opt-beam.is-cam .opt-fan::after {
            background: linear-gradient(90deg, transparent 38%,
                rgba(255,222,178,.58) 50%, transparent 62%);
            opacity: 0;
        }
        /* Only the beat being narrated is lit, and only while the pin is on
           screen — these are the one thing in the section that costs frames
           when nobody is looking at it. --dim brings them up with the
           spotlight so the beam never arrives before the shadow does. */
        .optics-stage.beat-lidar .optics-pin.is-live .opt-beam.is-lidar,
        .optics-stage.beat-cloud .optics-pin.is-live .opt-beam.is-lidar,
        .optics-stage.beat-cam .optics-pin.is-live .opt-beam.is-cam {
            /* --bvis is the JS saying this window is still on the plate and
               still turned towards the lens. The push takes both windows
               through off-frame and through raked-away-to-nothing, and a wedge
               hinged on glass that has left the picture is just a wedge. */
            opacity: calc(var(--dim, 1) * var(--bvis, 1));
        }
        .optics-stage.beat-lidar .optics-pin.is-live .opt-beam.is-lidar .opt-fan::after {
            animation: optRange 2.6s cubic-bezier(.4,0,.5,1) infinite;
        }
        .optics-stage.beat-cam .optics-pin.is-live .opt-beam.is-cam .opt-fan::after {
            animation: optGather 3.0s cubic-bezier(.4,0,.5,1) infinite;
        }
        @keyframes optRange {
            0%   { transform: translateX(-100%); opacity: 0; }
            22%  { opacity: 1; }
            100% { transform: translateX(100%); opacity: 0; }
        }
        @keyframes optGather {
            0%   { transform: translateX(100%); opacity: 0; }
            26%  { opacity: .9; }
            100% { transform: translateX(-100%); opacity: 0; }
        }
        @media (prefers-reduced-motion: reduce) {
            .optics-pin.is-live .opt-scan { animation: none; opacity: .3; }
            .optics-stage.beat-cam .optics-pin.is-live .opt-iris { animation: none; opacity: .7; }
            .optics-pin.is-live .opt-beam .opt-fan::after { animation: none; opacity: .5; }
        }
        /* The render parks the head in the left of the frame, so the copy gets
           the right — and this seats it without darkening the head. */
        .opt-scrim {
            position: absolute; inset: 0; pointer-events: none; z-index: 3;
            background: radial-gradient(90% 120% at 100% 50%,
                rgba(0,0,0,.82) 0%, rgba(0,0,0,.46) 42%, transparent 72%);
        }

        /* ── Copy: both beats live in the same slot and cross-fade, so the eye
           never has to travel to find the new text. ── */
        .opt-deck {
            position: absolute; top: 50%; right: clamp(24px, 7vw, 130px);
            transform: translateY(-50%);
            width: min(38%, 420px); z-index: 4;
        }
        .opt-copy {
            grid-area: 1 / 1;
            opacity: 0; transform: translateY(14px);
            transition: opacity .55s ease, transform .7s cubic-bezier(.22,.61,.36,1);
            pointer-events: none;
        }
        .opt-deck { display: grid; }
        .opt-copy.is-on { opacity: 1; transform: none; }
        .opt-kicker { display: flex; align-items: center; gap: 10px; font-family: var(--font-mono); font-size: 11px; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: rgba(255,255,255,.58); margin-bottom: 14px; }
        .opt-num { color: #ff8a3d; }
        .opt-h {
            font-size: clamp(21px, 2.2vw, 32px); font-weight: 600; letter-spacing: -.02em;
            line-height: 1.2; margin: 0 0 18px;
            background: linear-gradient(135deg, #ffb340 0%, #ff7a3d 100%);
            -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
        }
        .opt-specs { list-style: none; margin: 0; padding: 0; }
        .opt-specs li { font-size: clamp(12px, 1.05vw, 15px); line-height: 1.85; color: rgba(255,255,255,.68); letter-spacing: .01em; }

        /* Which of the two the light is on, so the pin does not feel like a
           stall with no way of knowing how much is left. */
        .opt-rail { position: absolute; right: clamp(24px, 7vw, 130px); bottom: clamp(28px, 6vh, 64px); display: flex; gap: 8px; z-index: 4; }
        .opt-rail i { width: 22px; height: 2px; background: rgba(255,255,255,.2); transition: background .4s ease; }
        .optics-stage.beat-lidar .opt-rail i:nth-child(1),
        .optics-stage.beat-cloud .opt-rail i:nth-child(2),
        .optics-stage.beat-cam   .opt-rail i:nth-child(3),
        .optics-stage.beat-frame .opt-rail i:nth-child(4) { background: #ff8a3d; }

        @media (max-width: 900px) {
            .opt-deck { width: min(46%, 340px); right: clamp(18px, 4vw, 40px); }
        }
        @media (max-width: 768px) {
            /* Portrait has no column to spare, so the head takes the upper two
               thirds and the copy takes the bottom third.

               The plate is a band here rather than a full-bleed cover. Covering
               a 100svh portrait frame with a 1.73:1 render means blowing it up
               nearly four times, and at that magnification a phone sees about a
               quarter of the plate's width: two enormous windows and no way to
               tell they are mounted on anything. Letting the band end where the
               copy starts trades the bleed for showing half the casting, which
               is the trade worth making — there is nothing above or below it but
               the black the section is already sitting on. */
            /* Raised with the desktop runway for the same reason — two openings
               now play out in the back half — but kept shorter, because a phone
               screen of scroll covers less ground than a desktop one. */
            .optics-stage { height: 320vh; }
            .opt-plate {
                width: 210%; top: clamp(20px, 4vh, 44px);
                transform: translate(-50%, 0) scale(var(--pz, 1.02)) translate(var(--px, 5%), var(--py, 0%));
            }
            .opt-scrim {
                background: linear-gradient(0deg, rgba(0,0,0,.94) 0%, rgba(0,0,0,.72) 26%, transparent 52%);
            }
            .opt-deck {
                top: auto; bottom: clamp(56px, 12vh, 104px); right: 0; left: 0;
                transform: none; width: auto; padding: 0 22px;
            }
            .opt-h { font-size: 21px; margin-bottom: 12px; }
            .opt-specs li { font-size: 13px; line-height: 1.8; }
            .opt-rail { right: 22px; bottom: clamp(24px, 5vh, 40px); }
        }

        /* ═══════════════════════════════════════════════════════════════════
           WHERE IT LIVES — three photographic scenes, one full-bleed beat each.
           The plate carries the frame; the copy sits in the corner the plate
           left empty, over a scrim that follows the composition.
           ═══════════════════════════════════════════════════════════════════ */
        .scene-section { position: relative; background: #000; padding: 0; }
        /* The chapter title is a card on the plate now, not a header above
           it. The padding above went with it: there is nothing left in this
           section but the pin, so the picture meets the black section over
           it directly, which is the join the film chapters already make.
           .scene-scrim fades the top edge, so the meeting is soft. */
        .scene-intro {
            position: absolute; inset: 0; z-index: 5;
            display: grid; align-content: center; justify-items: center;
            padding: 0 clamp(20px, 5vw, 40px);
            text-align: center; pointer-events: none;
        }
        .scene-intro > * { max-width: 880px; }
        /* The contrast the words need, inside the block that fades rather
           than on the pin: a scrim on the pin would go on dimming the
           photograph long after the type it was bought for had left.
           An ellipse rather than a band, for the reason .scene-scrim gives
           further down -- the machine stands in the bottom half of this
           frame, and a full-width wash spends its exposure. */
        .scene-intro::before {
            content: ''; position: absolute; inset: 0; z-index: -1;
            background: radial-gradient(56% 36% at 50% 50%,
                rgba(0,0,0,.68) 0%, rgba(0,0,0,.42) 46%, transparent 76%);
        }
        /* Both carry their own shadow now. On black they did not need one;
           on a sunlit concrete plate the eyebrow at .45 disappeared into
           the wall behind it. */
        .scene-eyebrow { font-family: var(--font-mono); font-size: 11px; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: rgba(255,255,255,.66); margin-bottom: 14px; text-shadow: 0 1px 12px rgba(0,0,0,.75); }
        /* pretty AFTER balance, not instead of it: the value is dropped whole
           where it is not supported, which leaves the balanced pair behind
           rather than no rule at all. balance alone put "up." on a line by
           itself at 390px -- fine in a left-aligned header, conspicuous in
           the middle of a centred card. */
        .scene-title { font-size: clamp(28px, 4.4vw, 56px); font-weight: 700; letter-spacing: -.035em; line-height: 1.1; color: #f5f5f7; margin: 0; text-wrap: balance; text-wrap: pretty; text-shadow: 0 2px 28px rgba(0,0,0,.62); }
        /* CJK offers a break between any two characters, and pretty cannot
           help with it because every candidate line is equally legal:
           Japanese broke 選ぶのは、きみの時間だ / け。 and stranded two
           glyphs. keep-all leaves the punctuation as the only place the
           line may end, so it breaks at the comma. Same treatment
           .dawn-headline gets, for the same reason. Latin locales are
           unaffected -- keep-all does not touch breaking at spaces. */
        html[lang^="zh"] .scene-title,
        html[lang="ja"] .scene-title,
        html[lang="ko"] .scene-title { word-break: keep-all; }

        /* One pin, three layers. The stage is scroll room only. */
        /* 200vh, down from 380. Three plates needed most of three screens to
           arrive one at a time; one plate needs exactly as much runway as its
           push-in, and anything past that is a reader scrolling through a
           picture that has stopped moving. */
        .scene-stage { position: relative; height: 200vh; }
        .scene-pin { position: sticky; top: 0; height: 100svh; overflow: hidden; background: #000; }
        .scene-layer { position: absolute; inset: 0; }
        .scene-layer picture { display: block; width: 100%; height: 100%; }
        /* The scale pivots on the machine rather than the frame centre, so the
           push-in expands the world around a product that holds still.

           object-position is the whole art of surviving cover, and it needs a
           ladder rather than a value. The frame holds the rim a fifth of the way
           down and the machine's feet at seven eighths — about seventy percent
           of the height apart, because the rim is six times the machine's height
           above the floor it stands on. A window at 16:9 can show 84% of the
           master and fits both. At 2:1 it can show 64% and cannot, so something
           has to be given up, and it is never the machine: the wider the window,
           the harder this biases toward the floor, and the hoop loses its rim to
           the top edge. A photograph being cropped is normal; a product cut off
           at the ankles is not. tests/sweep_viewports.py does the arithmetic for
           every window shape rather than leaving it to a guess, and it was a
           guess that had the feet 56px off the bottom of a 1920x820 window. */
        .scene-bg {
            width: 100%; height: 100%; object-fit: cover; object-position: 50% 72%;
            transform: scale(var(--sz, 1.02));
            transform-origin: 75% 79%;
            will-change: transform;
        }
        /* ===== A PHOTOGRAPH THAT IS STILL WHEN THE READER IS =====
           Both of the page's older full-bleed photographs already move, and both
           only move while the page is being scrolled: this one is scrubbed by
           --sz across its pin, and the teardown hero is a scroll parallax. A
           reader who has stopped to read the sentence written across a
           photograph -- which is what these two are for -- is looking at a
           frozen picture, and next to the film chapters and the drifting bento
           stills, frozen is what it reads as.
           So they get the same slow drift those have. It goes on the parent in
           both cases and that is the whole trick: GSAP owns the transform on the
           child (--sz here, an inline transform on .td-hero-bg), and a keyframe
           on the same element would be overwritten every frame. On the parent
           the two compose -- scroll still drives the push, this only breathes
           under it.
           Gentler than the chapter drift, 1.03 to 1.07 rather than 1.05 to 1.10,
           because these two have no inset to spare: the child's own scale is
           what keeps the edges covered, and it starts at 1.02. */
        @keyframes hostedDrift {
            0%   { transform: scale(1.03) translate3d(-0.6%, -0.4%, 0); }
            50%  { transform: scale(1.07) translate3d(0.6%,  0.5%, 0); }
            100% { transform: scale(1.03) translate3d(-0.6%, -0.4%, 0); }
        }
        .scene-layer,
        .td-hero-bg-wrap {
            animation: hostedDrift 46s ease-in-out infinite;
            will-change: transform;
        }
        .td-hero-bg-wrap { animation-delay: -17s; }
        /* The still gets its own copy of that drift, and the paragraph above
           already explains why it has to: on the parent the two transforms
           compose. .stage-still has no parent to hang this on and it is centred
           by a transform of its own, so the shared keyframe -- which sets
           `transform` outright -- was throwing the centring away on every frame
           and laying the image out from its own top-left at the middle of the
           frame. It hung out of the bottom-right corner: 52% of it outside the
           frame on a phone, 45% on a desktop. That is what the gated poster's
           "the machine floats" actually was. It was not floating, it was
           falling out of the corner, and no amount of ground under it would
           have helped.
           So the centring is carried through every keyframe here. It costs a
           duplicated animation and leaves the two wrappers on the original.
           The reduced-motion escape is repeated for the same reason: this
           selector is three classes and the one below is one, so the shared
           `animation: none` never reached the still. */
        @keyframes hostedDriftStill {
            0%   { transform: translate(-50%, -52%) scale(1.03) translate3d(-0.6%, -0.4%, 0); }
            50%  { transform: translate(-50%, -52%) scale(1.07) translate3d(0.6%,  0.5%, 0); }
            100% { transform: translate(-50%, -52%) scale(1.03) translate3d(-0.6%, -0.4%, 0); }
        }
        .stage-pin.is-gated .stage-still {
            animation: hostedDriftStill 46s ease-in-out -31s infinite;
            will-change: transform;
        }
        @media (prefers-reduced-motion: reduce) {
            .stage-pin.is-gated .stage-still { animation: none; }
        }
        :root[data-rb-tier="low"] .scene-layer,
        :root[data-rb-tier="low"] .td-hero-bg-wrap,
        :root[data-rb-tier="low"] .stage-still { animation: none; }
        @media (prefers-reduced-motion: reduce) {
            .scene-layer, .td-hero-bg-wrap, .stage-still { animation: none; }
        }
        /* Only as far down as the machine's own margin allows, not as far as
           it can go: every percent held back is another percent of hoop above
           it, and at these shapes the difference is whether the net is in
           frame or only the stanchion. */
        @media (min-aspect-ratio: 19/10) { .scene-bg { object-position: 50% 76%; } }
        @media (min-aspect-ratio: 5/2)   { .scene-bg { object-position: 50% 85%; } }
        /* The light changes before the place does.
           A straight opacity dissolve between two photographs is the one thing
           that always reads as software: both frames are simultaneously half
           there and the eye is given nothing to follow. So a warm bloom is lit
           through the machine at the moment of the change, peaks while the two
           plates are level, and is gone by the time the new one settles — the
           new world arrives out of a flare instead of soaking through the old
           one. Screen rather than a plain overlay, so it adds light to the
           highlights and leaves the blacks where they are.
           Centred where the machine stands, which is also where the push-in
           pivots, so every bit of motion in the section shares one focal point. */
        /* ── Grain, over the plate. See the note on the element for why it is
           not baked into it.
           feTurbulence rather than a shipped tile: a 256px patch of noise is
           incompressible and costs about 100KB as a PNG, while this costs the
           length of the string. stitchTiles is what makes the repeat seamless.
           Overlay, so it modulates what is under it instead of veiling it —
           grain on a photograph is a property of the emulsion, not a layer of
           fog on top — and stopped at the frame edges by the pin's own overflow.
           Scaled at 168px on a 1x screen so the clumps land a little over a
           pixel, which is film rather than sensor gain. ── */
        .scene-grain {
            position: absolute; inset: -8px; pointer-events: none; z-index: 2;
            mix-blend-mode: overlay; opacity: .40;
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='168' height='168'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='168' height='168' filter='url(%23g)'/%3E%3C/svg%3E");
            background-size: 168px 168px;
            /* A slow walk, so it is film running rather than dirt on the
               screen. Four steps, not a smooth pan: grain jumps per frame, it
               does not slide. */
            animation: sceneGrain 1.1s steps(1, end) infinite;
        }
        @keyframes sceneGrain {
            0%   { background-position:   0px   0px; }
            25%  { background-position: -47px  29px; }
            50%  { background-position:  31px -53px; }
            75%  { background-position: -23px -19px; }
            100% { background-position:   0px   0px; }
        }
        /* Denser on a high-density screen: at 2x the same tile is half the
           physical size, so the clumps drop under a device pixel and the whole
           field averages itself away into a flat haze. */
        @media (min-resolution: 1.5dppx) {
            .scene-grain { background-size: 116px 116px; opacity: .34; }
        }
        @media (prefers-reduced-motion: reduce) {
            .scene-grain { animation: none; }
        }
        /* The machine stands right of centre in the plate, so the copy has one
           home — the left. Anchored on its own corner rather than
           run across the frame: a full-width bottom band is the obvious way to
           seat white text on a photograph, and it was costing the machine
           40-60% of its exposure, since it stands in the bottom half too. An
           ellipse in the corner buys the text the same contrast and has fallen
           off to nothing before it reaches the machine. */
        .scene-scrim {
            position: absolute; inset: 0; pointer-events: none; z-index: 3;
            background:
                radial-gradient(112% 92% at 0% 100%,
                    rgba(0,0,0,.9) 0%, rgba(0,0,0,.54) 34%, transparent 68%),
                linear-gradient(0deg, rgba(0,0,0,.6) 0%, transparent 12%),
                linear-gradient(180deg, rgba(0,0,0,.52) 0%, transparent 22%);
        }
        /* All three sets of words share one slot and cross-fade in place. None
           of them is on during a plate change: the words clear out before the
           light turns and the next set arrives after it has settled, so the
           picture gets a beat entirely to itself. Swapping the copy in step
           with the plates is what made the sequence feel like three slides. */
        .scene-deck {
            position: absolute; left: clamp(20px, 6vw, 110px); bottom: clamp(48px, 11vh, 130px);
            width: min(46ch, 620px); display: grid; z-index: 4;
        }
        .scene-copy {
            grid-area: 1 / 1; pointer-events: none;
            opacity: 0; transform: translateY(22px) scale(.986);
            transition: opacity .5s ease, transform .8s cubic-bezier(.22,.61,.36,1);
        }
        .scene-copy.is-on { opacity: 1; transform: none; }
        .scene-chip {
            display: inline-flex; align-items: center; gap: 8px;
            font-family: var(--font-mono); font-size: 10px; letter-spacing: .18em; text-transform: uppercase;
            color: rgba(255,255,255,.72);
            padding: 6px 12px; border-radius: 999px;
            border: 1px solid rgba(255,255,255,.16);
            background: rgba(0,0,0,.4);
            backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
            margin-bottom: 18px;
        }
        .scene-chip-dot { width: 5px; height: 5px; border-radius: 50%; background: #ff8a3d; box-shadow: 0 0 8px rgba(255,138,61,.8); }
        /* The type carries its own contrast so the scrim does not have to
           carry it for them. Sunset concrete is the bright plate and the one
           that used to force the scrim up until it swallowed the machine. */
        .scene-copy h3, .scene-copy p { text-shadow: 0 1px 3px rgba(0,0,0,.5), 0 2px 26px rgba(0,0,0,.6); }
        .scene-copy h3 { font-size: clamp(24px, 3.6vw, 46px); font-weight: 700; letter-spacing: -.03em; line-height: 1.14; color: #fff; margin: 0 0 14px; text-wrap: balance; }
        .scene-copy p { font-size: clamp(14px, 1.15vw, 17px); line-height: 1.66; color: rgba(255,255,255,.82); margin: 0; max-width: 40ch; text-wrap: balance; }
        /* ── Portrait-ish windows, which is not the same question as narrow ones.
           The switch is the aspect ratio, because that is what decides how much
           of a 3:2 crop a cover fit can show: below about 1:1 the visible band
           of the landscape master no longer reaches the column the machine
           stands in, and it gets sliced down the middle. A width breakpoint
           misses that entirely — a tablet held upright is 834px wide and was
           losing half the product.

           So this and the portrait <source> in the template share one condition,
           and it has to stay shared: the two crops put the machine in different
           places, and every number below is which crop is on screen. ── */
        @media (max-width: 768px), (max-aspect-ratio: 1/1) {
            /* The copy moves to the top. A portrait frame puts the machine
               across the bottom third with nothing above it but sky, stands or
               roof, so anchoring the text down there would bury the product
               behind it — which is the one thing these beats exist to avoid.
               The scrim flips with it. */
            .scene-deck {
                left: 20px; right: 20px; top: clamp(80px, 13vh, 124px); bottom: auto;
                width: auto;
            }
            .scene-copy p { max-width: none; }
            .scene-scrim {
                background:
                    linear-gradient(180deg, rgba(0,0,0,.88) 0%, rgba(0,0,0,.54) 36%, transparent 64%),
                    linear-gradient(0deg, rgba(0,0,0,.5) 0%, transparent 24%);
            }
            /* The portrait cut centres the machine horizontally, so only the
               vertical bias carries over — and it still has to, for the window
               shapes between this breakpoint and the master's own 9:16, which
               crop height rather than width. On a phone there is no height crop
               at all and this does nothing. */
            .scene-bg { object-position: 50% 74%; transform-origin: 50% 79%; }
            /* And the bloom has to follow it there: the portrait crop puts the
               machine on the centre line, where the landscape frame has it at
               three quarters, so the shared position would light empty floor. */
        }
        /* ========== DJI-STYLE BENTO GRID ========== */
        .bento-section {
            background: #000;
            padding: clamp(40px, 6vh, 80px) 0 clamp(60px, 8vh, 100px);
            overflow: hidden;
            /* No content-visibility, on any viewport. It was culled on desktop
               for the render saving, and the saving cost two entire screens of
               black page.

               This section stands in at contain-intrinsic-size until it first
               renders, and 800px is not what a nine-video bento grid measures:
               the real box is 2313px at 1512 wide, so the document grows 1379px
               the moment a reader reaches it. Everything below moves down by
               that much — including the SoC and 5kg cabin chapters, which are
               GSAP pins whose scroll ranges were computed at load and are not
               recomputed by a section quietly resizing.

               Measured (tests/_pin_drift.py): both pins end up 1378px above the
               pin-spacer that implements them. A trigger past its end releases
               its pin by translating the section down one pin distance, so at
               y=9000 the SoC board sits 411px below the fold while the cabin's
               trigger has not started — nothing is on screen, and the viewport
               reads 1.44 mean luma from y=9000 to 9750 and again from 11500 to
               12000. Forcing this one property to visible takes the drift to 0
               and both bands back to ~25 (tests/_bugs/pin-drift.txt).

               The two comments further down this file already worked out that
               culling near a pin is a trap; this is the same trap reached from
               above rather than from inside. */
            content-visibility: visible;
            contain-intrinsic-size: auto;
        }
        @media (max-width: 768px) {
            /* Reduce backdrop-filter on touch devices — cuts composite layer cost on scroll */
            .bento-card .card-icon,
            .bento-card[data-story-key]::before,
            .motor-card::before {
                -webkit-backdrop-filter: none;
                backdrop-filter: none;
            }
        }
        .bento-header { text-align: center; padding: 0 clamp(16px, 4vw, 24px); margin-bottom: clamp(30px, 5vh, 44px); }
        .bento-header h2 { font-size: clamp(44px, 8vw, 84px); font-weight: 600; letter-spacing: -.045em; margin-bottom: 0; line-height: 1.04; }
        .bento-header p { max-width: 760px; margin: 18px auto 0; font-size: clamp(16px, 2vw, 19px); color: rgba(255,255,255,.56); line-height: 1.6; letter-spacing: -0.01em; }
        /* ===== COLOR DISCIPLINE (v2) =====
           UI chrome (buttons / borders / focus rings / callouts) stays strictly ice-blue
           monochrome. TYPE is where color lives: headline keywords carry rich Apple-grade
           gradients — like the iPhone-page gradient words on a black stage. */
        .bento-header h2 .highlight { 
            color: #30d158; 
            background: linear-gradient(135deg, #30d158 0%, #63e6a5 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }
        .txt-grad-green  { background: linear-gradient(135deg, #30d158 0%, #63e6a5 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
        .txt-grad-blue   { background: linear-gradient(135deg, #2997ff 0%, #64d2ff 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
        .txt-grad-orange { background: linear-gradient(135deg, #ffb340 0%, #ff7a3d 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
        /* Purple accent lifted into the same luminance band as the blue pair
           (#2997ff→#64d2ff): the old #bf5af2→#8e5bf7 bottomed out so dark that
           highlighted words read DIMMER than the surrounding white — the
           emphasis inverted. Still unmistakably violet, now it leads the line. */
        .txt-grad-purple { background: linear-gradient(135deg, #d3b4ff 0%, #a06dff 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
        .txt-grad-warm   { background: linear-gradient(135deg, #ffb340 0%, #ff5f6d 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
        .txt-em { color:rgba(255,255,255,.95); font-weight:inherit; }
        /* Apple/DJI Bento Grid - tight, immersive */
        .bento-grid { 
            display: grid; 
            grid-template-columns: repeat(3, 1fr); 
            /* 360, having been 320 and then briefly 440.
               440 was set to fix a wall showing three frozen cards: eight cards
               carried video, the renderer decodes four streams at once (see the
               MAX_ARMED note in index.app.js, measured on both codecs by
               tests/_codec_cap.py), and taller rows put fewer of them on screen
               together. That worked, and then the real fix landed underneath
               it -- four of those cards gave up their players for film stills,
               which cost no decoder at all. With only four video cards left on
               the wall, the cap cannot be reached however many cards are on
               screen, and height is no longer buying anything against it.
               So height goes back to buying what it should, which is how much
               of the wall a reader sees at once. At 360 a 950px viewport holds
               about two and a half rows instead of two: a denser, more varied
               mosaic, which is the point of a bento and was being traded away
               to work around a constraint that no longer applies.
               Not back to 320. 389x360 at 2x asks for 720 rows and the 720 rung
               has exactly that, so this is the smallest row height that still
               matches a rung without upscaling. */
            grid-template-rows: repeat(2, minmax(360px, auto)); 
            /* The grid holds twelve cards, and only the first two rows were
               ever given a size. Everything past them landed in implicit rows
               at `auto` — and a bento card's children are all absolutely
               positioned, so `auto` resolved to the border and nothing else.
               The four privacy cards were rendering 2px tall on desktop: on the
               page, an empty hairline where four cards should be. The motor
               cards in the same rows only survived on their own min-height. */
            /* minmax, not a fixed height. 360 is a floor for density; it
               cannot also be a ceiling, because the four motor cards carry real
               flow content and eight locales of it. Measured at a flat 360 they
               ran past their own bottom edge by 63 to 232px with overflow:hidden
               cutting the last line off -- worst on the 14ms card, whose final
               line ends in the number the card is named after. Tuning the type
               fixes it for the language it was tuned in; German and French copy
               is longer than Chinese, so the next locale clips instead. A floor
               plus auto cannot clip in any of them. */
            grid-auto-rows: minmax(360px, auto);
            gap: 16px; 
            max-width: 1200px; 
            margin: 0 auto; 
            padding: 0; 
        }
        .bento-card { 
            position: relative; 
            border-radius: 20px; 
            overflow: hidden; 
            background: #131316; 
            cursor: pointer; 
            transition: box-shadow .5s cubic-bezier(.2,0,0,1), z-index 0s;
            border: 1px solid rgba(255,255,255,.05);
        }
        .bento-card:hover { 
            z-index: 2;
            box-shadow: 0 30px 80px rgba(0,0,0,0.6);
        }
        .bento-card.wide { grid-column: span 2; }
        .bento-card.tall { grid-row: span 2; }
        /* Card Content - Apple style: clean, breathable */
        .bento-card .card-bg { 
            position: absolute; 
            inset: 0; 
            transition: transform .8s cubic-bezier(.2,0,0,1); 
        }
        .bento-card:hover .card-bg { transform: scale(1.03); }
        .bento-card video { transition: opacity .6s ease, filter .6s ease, transform .8s cubic-bezier(.2,0,0,1); }
        /* Only the push-in on hover now. These used to step the clip down to
           0.84, which was consistent while it rested at 0.84 and became backwards
           the moment it rested at 1.0 — pointing at a card would have dimmed it. */
        .bento-card:hover video { transform: scale(1.04); }
        /* ===== STILLS THAT ARE NOT STILL =====
           Four cards here hand their decoder back for good, because
           tests/_codec_cap.py measured the ceiling at four moving streams on
           both AV1 and H.264 -- it is the renderer, not the encode -- and eight
           cards were asking. What they get instead of a clip is a frame of the
           very same shot, and this drift.
           A transform animation is the whole point: the compositor runs it on
           the GPU without decoding anything, so these four cards move at no
           cost against the cap. The wall reads as alive across its full height
           while only four decoders are in use, which is the thing the grid could
           not do when all eight were competing -- then, half the wall was frozen
           and which half depended on where the reader had stopped scrolling.
           Slow and small on purpose. 34 seconds and a 6% travel is below the
           speed at which the eye tracks it as animation; it should read as the
           picture breathing, not as a slideshow panning. The scale never goes
           under 1.06 so no edge can slide into frame. */
        @keyframes bentoDrift {
            0%   { transform: scale(1.06) translate3d(-1.1%, -0.7%, 0); }
            50%  { transform: scale(1.12) translate3d(1.1%,  0.8%, 0); }
            100% { transform: scale(1.06) translate3d(-1.1%, -0.7%, 0); }
        }
        .bento-still {
            animation: bentoDrift 34s ease-in-out infinite;
            will-change: transform;
        }
        /* A real loop, not a drift. picture fills the card; the img inside
           already has the cover styles. No Ken Burns — the frames are the
           motion, and a transform on top would crop the barrel. */
        .bento-loop {
            position: absolute; inset: 0; z-index: 0;
            display: block; width: 100%; height: 100%;
            pointer-events: none;
        }
        .bento-loop img {
            position: absolute; inset: 0;
            width: 100%; height: 100%;
            object-fit: cover;
        }
        /* Negative delays start each card mid-cycle, so four cards on one screen
           are not breathing in unison -- which is what gives a set of looping
           backgrounds away as a set. */
        .bento-grid > *:nth-child(even) .bento-still { animation-delay: -11s; }
        .bento-grid > *:nth-child(3n)   .bento-still { animation-delay: -21s; }
        .bento-card:hover .bento-still { animation-play-state: paused; }
        /* The same two audiences the plates opt out for: a reader who has asked
           the OS for less motion, and a device the runtime has tiered down. */
        :root[data-rb-tier="low"] .bento-still { animation: none; }
        @media (prefers-reduced-motion: reduce) {
            .bento-still { animation: none; }
        }
        /* ===== SCRIM, NOT DIMMING =====
           The clips used to be held at 0.56-0.79 effective strength so that white
           type would read on top of them, and on desktop that was the only thing
           providing contrast: .card-content had no background above 768px, while
           the mobile block below has had a proper gradient all along.
           Pulling a whole frame down to make its bottom corner legible is the
           wrong trade on any page, and especially here — these clips are how the
           film reaches the visitors who never open the dialog, so 56% strength
           was delivering half of the only footage most people see.
           So the contrast is bought where the words are. This gradient is the
           mobile one, promoted to every viewport: opaque under the copy, gone by
           the time it reaches the middle of the card, which leaves the picture
           itself untouched. */
        .bento-card .card-content {
            background: linear-gradient(to top, rgba(0,0,0,.90) 0%, rgba(0,0,0,.62) 46%, rgba(0,0,0,.22) 78%, transparent 100%);
        }
        /* Type that is not in a bottom strip gets a shadow rather than a plate.
           The stat numerals sit in the corner opposite the copy, and the motor
           cards lay their text out as a flex row beside an icon — a gradient on
           either would read as a panel dropped onto the shot rather than as the
           shot being legible. */
        .bento-stat,
        .motor-card .card-num,
        .motor-card .card-copy,
        .motor-card .card-sub {
            text-shadow: 0 1px 3px rgba(0,0,0,.6), 0 2px 22px rgba(0,0,0,.8);
        }
        /* A shadow alone was not enough on the motor cards. Their footage is the
           brightest on the page — a gym floor and a lit ball — and the sub-line
           under the numeral is 11-13px, so it disappeared once the clip came up
           to full strength.
           A radial falloff rather than a strip, because these cards lay their
           text out as a flex row beside an icon and alternate which side that is
           (:nth-child(even) reverses the row). A gradient keyed to an edge would
           be on the wrong edge half the time; one centred on the text block
           travels with it and reads as the light falling off rather than as a
           plate laid over the shot. Inset negatively so it is already faded to
           nothing before it reaches the card's own rounding. */
        .motor-card .card-text::before {
            content: '';
            position: absolute;
            inset: -45% -34%;
            background: radial-gradient(60% 56% at 50% 50%, rgba(0,0,0,.80) 0%, rgba(0,0,0,.52) 46%, transparent 78%);
            pointer-events: none;
        }
        /* card-major (#bentoBalls) is the narrow one carrying the brightest shot
           on the page — a lit ball filling the frame — behind the smallest
           sub-line. Because the falloff is sized as a percentage of the text
           block, the same percentages buy far less absolute cover on a narrow
           card than on a wide one, so this one is mixed stronger.
           card-minor (#bentoMulti) deliberately does not get this: its plate is
           the near-black tracking view, so the resting falloff is already more
           than its thin cyan linework needs. */
        .motor-card.card-major .card-text::before {
            background: radial-gradient(66% 60% at 50% 50%, rgba(0,0,0,.86) 0%, rgba(0,0,0,.6) 48%, transparent 80%);
        }
        /* Multi is a photo card now, not a motor-card with a picture stuffed
           behind 84px type and a three-person icon. The plate is the squad;
           the numeral sits in the quiet corner; the copy sits on a bottom
           scrim. The SVG is gone from the markup. */
        #bentoMulti {
            flex-direction: column !important;
            justify-content: flex-end;
            align-items: stretch;
            padding: 22px 22px 20px !important;
            gap: 0 !important;
        }
        #bentoMulti .card-num {
            position: absolute;
            top: 22px; left: 22px;
            font-size: clamp(28px, 3.2vw, 36px) !important;
            margin: 0;
            letter-spacing: -0.04em;
        }
        #bentoMulti .card-copy {
            font-size: clamp(20px, 2.1vw, 24px) !important;
            margin-top: 0 !important;
            max-width: 100%;
        }
        #bentoMulti .card-sub {
            font-size: 13px !important;
            margin-top: 6px !important;
            max-width: 100%;
            color: rgba(255,255,255,.62);
        }
        #bentoMulti .card-text {
            width: 100%;
            z-index: 2;
            padding-right: 36px;
        }
        #bentoMulti .card-text::before {
            inset: -70% -16% -18%;
            background: linear-gradient(to top, rgba(0,0,0,.78) 0%, rgba(0,0,0,.40) 42%, transparent 78%);
        }
        .bento-card .card-content { 
            position: absolute; 
            bottom: 0; 
            left: 0; 
            right: 0; 
            padding: 34px 28px 26px; 
            z-index: 5; 
            /* Deeper scrim — copy must win against bright video frames */
            background: linear-gradient(to top, rgba(0,0,0,.90) 0%, rgba(0,0,0,.62) 40%, rgba(0,0,0,.22) 72%, transparent 100%); 
        }
        .bento-card .card-title { 
            font-size: 26px; 
            font-weight: 700; 
            margin-bottom: 8px; 
            line-height: 1.15; 
            letter-spacing: -0.02em;
            text-shadow: 0 1px 12px rgba(0,0,0,.45);
        }
        .bento-card .card-title .em { 
            color: #30d158;
            display: inline;
        }
        /* Copy is content, not an easter egg — visible once revealed, brightens on hover.
           (Previously hover-only, which buried every description on desktop.) */
        .bento-card .card-desc { 
            font-size: 15px; 
            color: rgba(255,255,255,0.66); 
            line-height: 1.55; 
            max-width: 360px; 
            font-weight: 400;
            letter-spacing: -0.01em;
            text-shadow: 0 1px 10px rgba(0,0,0,.5);
            transition: color .4s ease;
        }
        .bento-card:hover .card-desc { color: rgba(255,255,255,0.88); }
        /* Mission index — mono corner marker on every card */
        .card-index {
            position: absolute;
            bottom: 14px; right: 16px;
            z-index: 6;
            font-family: var(--font-mono);
            font-size: 10px;
            letter-spacing: .16em;
            color: rgba(255,255,255,.32);
            pointer-events: none;
        }
        .card-index::before {
            content: '';
            display: inline-block;
            width: 14px; height: 1px;
            background: rgba(255,255,255,.22);
            margin-right: 7px;
            vertical-align: middle;
        }
        @media (max-width: 768px) { .card-index { display: none; } }
        .bento-card .card-icon { 
            position: absolute; 
            top: 22px; 
            right: 22px; 
            width: 44px; 
            height: 44px; 
            border-radius: 14px; 
            background: rgba(255,255,255,.06); 
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            display: flex; 
            align-items: center; 
            justify-content: center; 
            z-index: 3; 
            border: 1px solid rgba(255,255,255,0.06);
            transition: background .3s ease, border-color .3s ease;
        }
        .bento-card:hover .card-icon {
            background: rgba(255,255,255,.1);
            border-color: rgba(255,255,255,0.1);
        }
        .bento-card .card-icon svg { width: 22px; height: 22px; stroke: rgba(255,255,255,0.7); fill: none; stroke-width: 1.5; }
        .bento-card .card-expand { 
            position: absolute; 
            bottom: 24px; 
            right: 24px; 
            width: 32px; 
            height: 32px; 
            border-radius: 50%; 
            background: rgba(255,255,255,.08); 
            backdrop-filter: blur(10px);
            display: flex; 
            align-items: center; 
            justify-content: center; 
            opacity: 0; 
            transition: opacity .3s ease, transform .3s ease; 
            z-index: 6; 
        }
        .bento-card:hover .card-expand { opacity: 1; }
        .bento-card::after { content:''; position:absolute; inset:0; border-radius:16px; border:1px solid transparent; transition: border-color .4s ease; pointer-events:none; z-index:7; }
        .bento-card:hover::after { border-color: rgba(255,255,255,.08); }
        .bento-card .card-expand svg { width: 14px; height: 14px; stroke: rgba(255,255,255,0.8); fill: none; stroke-width: 2; }
        .bento-card[data-story-key],
        .motor-card[data-story-key] { cursor:pointer; }
        .bento-card[data-story-key]::before,
        .motor-card[data-story-key]::before {
            content:'+';
            position:absolute;
            top:18px;
            right:18px;
            width:38px;
            height:38px;
            border-radius:999px;
            background:rgba(255,255,255,.08);
            border:1px solid rgba(255,255,255,.08);
            color:rgba(255,255,255,.86);
            display:flex;
            align-items:center;
            justify-content:center;
            font-size:22px;
            font-weight:300;
            line-height:1;
            z-index:8;
            pointer-events:none;
            backdrop-filter:blur(12px);
            -webkit-backdrop-filter:blur(12px);
            transition:transform .35s ease, background .35s ease, border-color .35s ease, box-shadow .35s ease;
        }
        .bento-card[data-story-key]:hover::before,
        .motor-card[data-story-key]:hover::before {
            transform:rotate(90deg) scale(1.05);
            background:rgba(255,255,255,.14);
            border-color:rgba(255,255,255,.16);
            box-shadow:0 12px 24px rgba(0,0,0,.24);
        }
        /* ── Mobile Bento: staggered masonry feel ───────────────────────────── */
        @media (max-width: 768px) {
            .bento-grid {
                grid-template-columns: 1fr 1fr;
                grid-template-rows: auto;
                /* Explicitly back to 320. The desktop rule raised grid-auto-rows
                   to 440 so that fewer video cards share a viewport, and only
                   grid-template-rows was being reset here -- so the taller rows
                   leaked onto phones, where the grid is two columns of about
                   181px and a 440px row makes every card 0.41 wide for its
                   height. The reason for 440 does not apply here anyway: a phone
                   fits one or two cards on screen regardless, so the decoder cap
                   was never the binding constraint at this width. */
                grid-auto-rows: minmax(320px, auto);
                gap: 10px;
                padding: 0 clamp(10px, 2.5vw, 14px);
            }
            /* 1. Wide: full-width hero row */
            .bento-card.wide {
                grid-column: 1 / -1;
                min-height: 180px;
            }
            /* 2. Tall: left column, 2-row span — right column stays open for the two smalls */
            .bento-card.tall {
                grid-column: 1;
                grid-row: span 2;
                min-height: 220px;
            }
            /* 3 & 4. Arc + Range: right column, one row each, sit beside
               tall's two rows. These were :nth-child(4) and (5), which is
               range and the setup motor card -- and since the second is not
               a .bento-card, that half of the rule matched nothing and arc
               was placed by auto-flow rather than by the rule naming it. */
            .bento-grid > .bento-card:nth-child(3),
            .bento-grid > .bento-card:nth-child(4) {
                grid-column: 2;
                grid-row: span 1;
            }
            /* 5. Setup motor: full-width */
            .bento-grid > .motor-card.card-wide {
                grid-column: 1 / -1;
                min-height: 160px;
            }
            /* 6 & 7. Rapid + Multi: side-by-side two columns */
            .bento-grid > .motor-card.card-major {
                grid-column: 1;
            }
            .bento-grid > .motor-card.card-minor {
                grid-column: 2;
            }
            /* 8–10. Privacy cards: 2-col auto flow, and the last of them
               spans the row — see .bento-card.card-full above. */

            /* Common overrides for all bento cards on mobile — cards used to
               hide every description (display:none) and read as empty tiles;
               now each keeps a two-line synopsis and a real title size. */
            .bento-card { min-height: 152px; }
            .bento-card.wide { min-height: 200px; }
            .bento-card.tall { min-height: 314px; }
            .bento-card .card-desc {
                display: -webkit-box;
                -webkit-line-clamp: 2;
                -webkit-box-orient: vertical;
                overflow: hidden;
                transform: translateY(0); opacity: 1;
                font-size: 12px; line-height: 1.45;
                color: rgba(255,255,255,.58);
                margin-top: 3px;
            }
            .bento-card .card-title { font-size: 15px; margin-bottom: 0; line-height: 1.25; }
            .bento-card .card-content { padding: 12px 13px 13px; background: linear-gradient(to top, rgba(0,0,0,.94) 0%, rgba(0,0,0,.55) 55%, transparent 100%); }
            .bento-card .card-icon { width: 28px; height: 28px; top: 10px; right: 10px; border-radius: 7px; }
            .bento-card .card-icon svg { width: 14px; height: 14px; }
            .bento-card .card-expand { display: none; }
            .bento-card[data-story-key]::before,
            .motor-card[data-story-key]::before { top: 10px; right: 10px; width: 28px; height: 28px; font-size: 16px; }

            /* Stat overlays — compact on mobile */
            .bento-stat { transform: scale(0.78); transform-origin: top left; }
            /* Arc + Range cards park their ghost numerals top-right where the
               story "+" chip lives — drop the numerals below the chip.
               (Arc is the grid's 3rd element child, Range the 4th.) */
            .bento-grid > .bento-card:nth-child(3) .bento-stat,
            .bento-grid > .bento-card:nth-child(4) .bento-stat {
                top: 44px !important;
                transform-origin: top right;
            }
            .bento-stat[style*="right"] { transform-origin: top right; }
            .bento-stat-num { font-size: 30px !important; }

            /* Motor cards mobile.
               Column, not row. A row puts the icon beside the copy, which is
               fine on a full-width card and impossible on this grid: the wall
               is two columns of about 181px on a 390pt phone, and once the
               48px icon and the padding are taken out the text is left with
               roughly 85px to wrap in. Measured, the four motor cards were
               running 62 to 87px past their own bottom edge with overflow
               hidden cutting the last line off. Stacked, the copy gets the
               full 149px and the same words fit inside 320.
               .card-wide keeps the row: it spans both columns, so it has the
               width the argument above says it needs. */
            .motor-card { min-height: 150px !important; padding: 18px 16px !important; gap: 10px !important; flex-direction: column !important; }
            .motor-card.card-wide { flex-direction: row !important; }
            .motor-card .card-svg { margin-bottom: 0 !important; }
            .motor-card .card-svg svg { width: 40px !important; height: 40px !important; }
            .motor-card.card-wide { padding: 22px 16px !important; }
            .motor-card .card-num { font-size: clamp(28px, 9vw, 44px) !important; }
            .motor-card.card-wide .card-num { font-size: clamp(32px, 10vw, 52px) !important; }
            .motor-card .card-copy { font-size: 14px !important; margin-top: 4px !important; }
            .motor-card .card-sub { font-size: 11px !important; margin-top: 2px !important; }
            .motor-card .card-svg svg { width: 48px !important; height: 48px !important; }
            .motor-card.card-wide .card-svg svg { width: 64px !important; height: 64px !important; }
            .bento-ring-progress { stroke-width: 2; }

            .bento-header h2 { font-size: clamp(28px, 7vw, 40px); }
            .bento-header p { font-size: 13px; margin-top: 10px; }
        }

        @media (max-width: 480px) {
            .bento-grid { gap: 10px; padding: 0 clamp(8px, 2vw, 12px); }
            .bento-card { min-height: 142px; border-radius: 14px; }
            .bento-card.tall { min-height: 294px; }
            .bento-card.wide { min-height: 184px; }
            .bento-card .card-title { font-size: 14px; }
            .bento-card .card-desc { font-size: 11.5px; }
            .bento-card .card-content { padding: 10px 11px 11px; }
            .bento-stat { transform: scale(0.68); }
            .bento-stat-num { font-size: 26px !important; }
            .bento-ring-progress { stroke-width: 1.5; }
            .motor-card { padding: 14px 12px !important; min-height: 130px !important; }
            .motor-card .card-num { font-size: clamp(24px, 8vw, 36px) !important; }
            .motor-card.card-wide .card-num { font-size: clamp(28px, 9vw, 44px) !important; }
            .motor-card .card-copy { font-size: 12px !important; }
            .motor-card .card-sub { font-size: 10px !important; }
            .motor-card .card-svg svg { width: 40px !important; height: 40px !important; }
            .motor-card.card-wide .card-svg svg { width: 52px !important; height: 52px !important; }
            .bento-header h2 { font-size: clamp(24px, 7vw, 36px); }
        }
        /* The tall card keeps its two rows here too. Held to one, the grid was
           thirteen cell-widths over two columns, which cannot tile: the row
           holding the range card was left with an empty cell beside it because
           the setup card behind it needs both columns and could not fit in one.
           Spanning restores an even count, and the card is a portrait clip, so
           two rows of a 406px column is the shape its footage already is. */
        @media (min-width: 769px) and (max-width: 1024px) { .bento-grid { grid-template-columns: repeat(2, 1fr); } .bento-card.tall { min-height: 360px; } .bento-grid > .motor-card.card-wide { grid-column: span 2; } .bento-card .card-desc { transform: translateY(0); opacity: 0.7; } }
        /* Motor cards inside bento grid */
        .bento-grid > .motor-card { border-radius: 20px; }
        .bento-grid > .motor-card.card-wide { grid-column: span 2; }
        /* 1 / -1 rather than span 3: this grid is three columns on a
           desktop, two on a tablet and two on a phone, and the card has to
           fill the row in all three. span 3 overflows the narrow ones. */
        .bento-grid > .bento-card.card-full { grid-column: 1 / -1; }
        @media (hover: none) and (min-width: 769px) { .bento-card .card-desc { transform: translateY(0); opacity: 0.8; } }
        .privacy-stat-overlay { position:absolute; top:24px; left:24px; z-index:5; }
        .privacy-stat-big { font-size:clamp(40px,10vw,72px); font-weight:600; color:#f5f5f7; letter-spacing:-0.04em; line-height:1; text-shadow: 0 1px 3px rgba(0,0,0,.6), 0 2px 22px rgba(0,0,0,.8); }
        .privacy-stat-unit { font-size:clamp(14px,3vw,24px); color:rgba(255,255,255,.5); margin-left:4px; text-shadow: 0 1px 3px rgba(0,0,0,.6), 0 2px 18px rgba(0,0,0,.75); }
        @media (max-width: 768px) { .privacy-stat-overlay { top:14px; left:14px; } .privacy-stat-big { font-size:clamp(28px,8vw,48px); } .privacy-stat-unit { font-size:clamp(12px,2.5vw,18px); } }
        @media (max-width: 480px) { .privacy-stat-big { font-size:clamp(24px,7vw,36px); } }
        /* Card Animations */
        .bento-card .anim-container { position: absolute; inset: 0; pointer-events: none; z-index: 1; }
        .bento-card .pulse-ring { display: none; }
        /* Scan Line Animation */
        .bento-card .scan-line { display: none; }
        /* Arc Animation */
        .bento-card .arc-path { display: none; }
        /* Meter Animation */
        .bento-card .meter-bar { display: none; }
        /* Bento Animation SVGs */
        .bento-anim-svg { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -55%); width: 80%; height: 60%; opacity: 0.9; }
        .bento-card.tall .bento-anim-svg { width: 60%; height: 70%; transform: translate(-50%, -50%); }
        .skeleton-anim .arm-l { transform-origin: 50px 45px; animation: armSwing 2s ease-in-out infinite; }
        .skeleton-anim .arm-r { transform-origin: 50px 45px; animation: armSwing 2s ease-in-out infinite reverse; }
        @keyframes armSwing { 0%, 100% { transform: rotate(-10deg); } 50% { transform: rotate(15deg); } }
        /* MOTOR CARDS (now inside bento grid) */
        .motor-card { background:rgba(255,255,255,.02); border:none; border-radius:28px; padding:clamp(40px,5vw,64px) clamp(24px,4vw,48px); position:relative; overflow:hidden; transition:box-shadow .5s, background .3s; display:flex; align-items:center; gap:clamp(24px,4vw,48px); min-height:280px; }
        .motor-card::before { content:''; position:absolute; inset:0; opacity:0; transition:opacity .5s; pointer-events:none; }
        .motor-card video { transition: opacity .6s ease, filter .6s ease, transform .8s cubic-bezier(.2,0,0,1); }
        /* Push-in only, for the reason given on .bento-card:hover video. */
        .motor-card:hover video { transform: scale(1.04); }
        .motor-card:hover { background:rgba(255,255,255,.03); box-shadow:0 20px 60px rgba(0,0,0,.3); }
        .motor-card:hover::before { opacity:1; }
        /* All cards are full-width now */
        .motor-card.card-wide { padding:clamp(48px,6vw,72px) clamp(28px,5vw,56px); }
        .motor-card.card-major, .motor-card.card-minor { flex-direction:row; }
        /* Alternating layout */
        .motor-card:nth-child(even) { flex-direction:row-reverse; }
        /* Visual side */
        .motor-card .card-svg { flex-shrink:0; margin-bottom:0; position:relative; z-index:2; }
        /* Content side */
        .motor-card .card-text { flex:1; position:relative; z-index:2; }
        /* Heartbeat line for 14ms card */
        /* Number counter animation */
        .motor-card .card-num { font-size:clamp(72px,14vw,140px); font-weight:900; letter-spacing:-.06em; line-height:1; margin-bottom:4px; display:inline-flex; align-items:baseline; }
        .motor-card.card-wide .card-num { font-size:clamp(96px,18vw,180px); }
        .motor-card .card-unit { font-size:clamp(14px,2.2vw,22px); font-weight:500; margin-left:10px; line-height:1; vertical-align:baseline; opacity:.7; white-space:nowrap; }
        .motor-card .card-copy { font-size:clamp(20px,3.5vw,32px); font-weight:700; color:#f5f5f7; margin-top:20px; letter-spacing:-.02em; line-height:1.15; text-wrap:balance; }
        .motor-card.card-wide .card-copy { font-size:clamp(24px,4vw,40px); }
        .motor-card .card-sub { font-size:clamp(14px,1.6vw,15px); color:#6e6e73; margin-top:10px; line-height:1.55; max-width:420px; }
        .motor-card .card-svg { margin-bottom:20px; }
        /* ===== MOTOR CARDS, SIZED FOR THE BOX THEY ARE ACTUALLY IN =====
           Everything above this is scaled for a card 440px tall or more: up to
           64px of padding at each end, a 140-180px numeral, a 32-40px headline.
           The grid rows are 360 now, and measured across three viewports all
           four motor cards were running past their own bottom edge -- 127px on
           the setup card, 121 on the ball rate, 232 on the squad card, 63 on
           safety -- with overflow:hidden quietly cutting the last line of copy
           off. The 14ms card is where it shows worst, because its last line
           ends in the number the card is named after.
           That is a fault I introduced: the row height came down from 440 to
           360 to get more of the wall on screen, and I did not check that the
           contents still fit. This is the other half of that change.
           Scoped to .bento-grid > so the motor-card styling is untouched
           anywhere else, and left above the mobile blocks, which carry their
           own !important sizes and are not affected. */
        .bento-grid > .motor-card {
            padding: clamp(18px, 1.9vw, 24px) clamp(18px, 2vw, 26px);
            gap: 12px;
        }
        .bento-grid > .motor-card .card-svg { margin-bottom: 10px; }
        .bento-grid > .motor-card .card-svg svg { width: 76px; height: 76px; }
        .bento-grid > .motor-card .card-num,
        .bento-grid > .motor-card.card-wide .card-num { font-size: clamp(52px, 7vw, 84px); }
        .bento-grid > .motor-card .card-copy,
        .bento-grid > .motor-card.card-wide .card-copy {
            font-size: clamp(18px, 1.9vw, 24px); margin-top: 10px;
        }
        .bento-grid > .motor-card .card-sub { font-size: 13px; margin-top: 7px; line-height: 1.5; }
        /* The setup card keeps two columns, so it has width the others do not
           and can carry the larger numeral it was designed around. */
        .bento-grid > .motor-card.card-wide .card-num { font-size: clamp(64px, 9vw, 112px); }
        /* Pulse icon animation */
        .motor-card .card-svg svg { transition:transform .4s cubic-bezier(.4,0,.2,1); }
        .motor-card:hover .card-svg svg { transform:scale(1.15) rotate(-5deg); }
        @media (max-width:768px) {
            .motor-card { flex-direction:column !important; padding:28px 20px; gap:16px; min-height:auto; }
            .motor-card.card-wide .card-num { font-size:clamp(56px,16vw,96px); }
            .motor-card .card-num { font-size:clamp(44px,14vw,80px); }
            .motor-card.card-wide { padding:32px 24px; }
            .motor-card .card-copy { font-size:clamp(17px,3vw,24px); margin-top:12px; }
            .motor-card .card-sub { font-size:13px; margin-top:6px; }
            .motor-card .card-svg svg { width:80px; height:80px; }
            .motor-card.card-wide .card-svg svg { width:96px; height:96px; }
            .setup-steps { gap:8px; margin-top:12px; }
            .setup-step { font-size:11px; padding:4px 8px; }
        }
        /* ===== TEARDOWN HERO — full-screen parallax reveal
           v2 (desktop): title moved from centered-overlay-on-image (obscured court composition)
           to top-centered (Apple product-hero pattern), so the wide court shot below reads as a
           complete, uncropped image. Mobile keeps its own centered-overlay layout. */
        .td-hero-section {
            position:relative;
            height:100vh;
            min-height:620px;
            display:flex;
            flex-direction:column;
            align-items:center;
            justify-content:center;
            padding-top:0;
            overflow:hidden;
        }
        /* Background image wrap — overflow:hidden so it can scale within the frame.
           Inset -3%/-1% allows scale:1.02 without showing edges. Raw colors, no filter. */
        .td-hero-bg-wrap {
            position:absolute;
            inset:-3% -1%;
            z-index:0;
            will-change:transform;
        }
        .td-hero-bg {
            width:100%;
            height:100%;
            object-fit:cover;
            object-position:center 58%;
            display:block;
            transform-origin:center center;
            transform:scale(1.02);
            will-change:transform;
        }
        /* Vignette: bottom fade for the section handoff + a WHISPER of radial
           scrim behind the headline zone. The sunlit court runs bright sand →
           deep shadow directly behind the white type; 22% of center darkening
           makes every word effortless without dulling the photograph. */
        .td-hero-vignette {
            position:absolute;
            inset:0;
            z-index:1;
            /* The centre scrim was tuned against the stock plate, which was a
               dark blue court; the frame under it is now our own poolside
               concrete and roughly twice as bright. Measured on a screenshot of
               the band the headline occupies, the old .22/.10 left it at 3.39:1
               against white — over the 3:1 large-text bar and under it for the
               eyebrow, which is 12px. .34/.16 buys back the margin without
               closing down the picture, which is the whole reason the frame was
               swapped for a real one. */
            background:
                radial-gradient(58% 46% at 50% 46%, rgba(0,0,0,.34) 0%, rgba(0,0,0,.16) 55%, transparent 78%),
                linear-gradient(to bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 62%, rgba(6,6,8,.55) 86%, #060608 100%);
            pointer-events:none;
        }
        /* Text content — no longer absolutely centered; participates in section's flex column
           flow so it sits at the top per the new padding-top spec. */
        .td-hero-content {
            position:relative;
            z-index:5;
            text-align:center;
            padding:0 clamp(20px,5vw,48px);
            pointer-events:none;
            width:100%;
            max-width:1100px;
        }
        .td-hero-eyebrow {
            font-size:clamp(13px,1.4vw,17px);
            font-weight:600;
            color:#ff8c5a;
            letter-spacing:.22em;
            text-transform:uppercase;
            margin-bottom:20px;
            will-change:transform,opacity;
        }
        .td-hero-clip {
            overflow:hidden;
            will-change:clip-path;
        }
        .td-hero-headline {
            font-size:clamp(56px,9.5vw,124px);
            font-weight:700;
            color:#f5f5f7;
            text-shadow:0 1px 2px rgba(0,0,0,.55);
            position:relative;
            z-index:5;
            letter-spacing:-.045em;
            line-height:1.03;
            /* No language's first sentence fits one line at 124px, so this
               block is three lines wide in all eight of them. Left to fill
               greedily the Chinese broke 973/372/842 -- eight glyphs, then the
               three that did not fit, alone under 124px type. CJK permits a
               break between any two glyphs, which is what makes the remainder
               so short. Balanced, the same three lines come out even.
               balance is declared second so it wins where both exist: pretty
               evens out the LAST line and the short line here is the middle
               one, so pretty is the fallback, not the goal. */
            text-wrap:pretty;
            text-wrap:balance;
            will-change:transform,filter,clip-path;
        }
        @media (max-width:768px) {
            /* Mobile teardown hero: keep centered-overlay layout (image + text stacked at mid).
               Revert the desktop flex-start/padding-top so nothing shifts upward on small screens. */
            .td-hero-section { height:100svh; min-height:620px; justify-content:center; padding-top:0; }
            .td-hero-bg-wrap { inset:-8% -5%; }
            .td-hero-bg { object-position:center 50%; }
            /* Both layers, on purpose. This rule used to declare the fade
               alone, and because `background` is a shorthand that silently
               dropped the base rule's centre radial -- so the phone had no
               scrim behind the headline at all. Harmless while the plate was a
               graded aerial; not harmless now it is the film's opening, which
               is white machinery lit from the front. Measured behind the type
               with the copy hidden, the phone sat at p90 190 against white.
               .48/.26 and a wider ellipse than the desktop's .34/.16: the crop
               here is the brightest part of the frame and the headline wraps to
               three lines, so there is more of it to cover. */
            .td-hero-vignette {
                background:
                    radial-gradient(88% 42% at 50% 50%, rgba(0,0,0,.48) 0%, rgba(0,0,0,.26) 58%, transparent 82%),
                    linear-gradient(to bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 55%, rgba(6,6,8,.6) 85%, #060608 100%);
            }
            .td-hero-headline { font-size:clamp(38px,9.5vw,60px); line-height:1.05; letter-spacing:-0.04em; }
            .td-hero-eyebrow { font-size:12px; letter-spacing:.22em; margin-bottom:14px; }
            .td-hero-content { padding:0 18px; }
        }
        /* ===== PRODUCT TEARDOWN ===== */
        .teardown-section {
            background:#060608;
            padding:0 0 clamp(48px,8vh,72px);
            position:relative;
            z-index:1;
            overflow:visible;
        }
        @media (max-width:768px) {
            .teardown-section { padding:0 0 clamp(28px, 4.5vh, 40px); }
        }
        /* Radial warm-glow — Apple hero studio-light feel, centered on text */
        .teardown-section::before {
            content:'';
            position:absolute;
            top:-10%;
            left:50%;
            transform:translateX(-50%);
            width:min(900px,130vw);
            height:min(600px,80vh);
            background:radial-gradient(ellipse at 50% 30%,
                rgba(255,110,35,.14) 0%,
                rgba(255,70,20,.06) 25%,
                transparent 60%
            );
            pointer-events:none;
            z-index:0;
        }
        /* Subtle cool-fill light from below — depth */
        .teardown-section::after {
            content:'';
            position:absolute;
            bottom:-5%;
            left:50%;
            transform:translateX(-50%);
            width:min(1200px,160vw);
            height:min(400px,60vh);
            background:radial-gradient(ellipse at 50% 80%,
                rgba(0,80,200,.07) 0%,
                transparent 65%
            );
            pointer-events:none;
            z-index:0;
        }
        /* Film-grain noise overlay */
        .teardown-grain {
            position:absolute;
            inset:0;
            pointer-events:none;
            z-index:1;
            opacity:.035;
            background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
            background-size:256px 256px;
        }
        /* teardown-wrap: structural wrapper only; sizing lives on the sticky row below. */
        .teardown-wrap { max-width: 100%; }
        /* v3 desktop grid: 1.3fr / 0.7fr (was 1.05 / 0.95) — product column gets ~65%, copy
           column ~35%, then copy is pulled back with negative margin so its left edge bleeds
           over the product image for Apple-style depth overlap. */
        .teardown-sticky-row { max-width:1500px; margin:0 auto; padding:0 clamp(16px,4vw,48px); display:grid; grid-template-columns:1.3fr .7fr; gap:clamp(24px,3vw,40px); align-items:start; }
        /* 左栏：sticky + 固定视口高度 + flex 居中（不用 transform，避免 sticky 在部分内核失效/错位） */
        .teardown-visual {
            position: sticky;
            top: var(--nav-height, 48px);
            align-self: start;
            /* Lower z than features so the right copy column can overlap on top */
            z-index: 1;
            box-sizing: border-box;
            /* svh (stable small viewport): always fits under the nav even with the
               URL bar expanded, and the sticky stage + its WebGL canvas never
               resize mid-scroll when the bar collapses (dvh did, one reflow +
               GL viewport resize per toolbar transition). */
            height: calc(100vh - var(--nav-height, 48px));
            height: calc(100svh - var(--nav-height, 48px));
            max-height: calc(100svh - var(--nav-height, 48px));
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            padding: 0 clamp(4px, 1.2vw, 16px);
            /* v3: shift image right + scale up so it reads as a "hero" product shot, not a
               column-trapped thumbnail. Origin at center-right to keep right edge anchored. */
            transform: translateX(5%) scale(1.1);
            transform-origin: center right;
        }
        .teardown-img-wrap { position: relative; width: 100%; max-width: 100%; }
        .teardown-img { width:100%; height:auto; max-height:88vh; object-fit:contain; transition:transform .5s cubic-bezier(.4,0,.2,1),filter .4s ease, opacity .5s ease; transform-origin:center center; will-change:transform; }
        /* ── Exploded-view GLB stage: overlays the flat render and takes over
           once the model is parsed; the webp remains the loading/fallback
           frame (reduced-motion, load failure, no WebGL). ── */
        .teardown-canvas {
            position: absolute; inset: 0; width: 100%; height: 100%;
            display: block; opacity: 0; pointer-events: none;
            transition: opacity .6s ease;
        }
        .teardown-img-wrap.is-3d .teardown-canvas { opacity: 1; }
        .teardown-img-wrap.is-3d .teardown-img { opacity: 0; }
        .teardown-img.dimmed { filter:none; }
        .td-annotation { position:absolute; opacity:0; transition:opacity .35s ease; pointer-events:none; z-index:6; }
        .td-annotation.active { opacity:1; }
        .td-dot { width:12px; height:12px; border-radius:50%; position:relative; }
        .td-dot::after { content:''; position:absolute; inset:-6px; border:2px solid currentColor; border-radius:50%; animation:tdPulse 2s ease-out infinite; opacity:.5; }
        @keyframes tdPulse { 0%{transform:scale(1);opacity:.5} 100%{transform:scale(2.5);opacity:0} }
        .td-line { width:1px; height:34px; margin-left:5px; }
        .td-label { font-size:11px; font-weight:600; letter-spacing:.09em; text-transform:uppercase; white-space:nowrap; padding:6px 12px; border-radius:6px; border:1px solid; background:rgba(0,0,0,.8); backdrop-filter:blur(8px); }
        .td-connector { position:absolute; top:4px; left:14px; height:1px; width:0; pointer-events:none; transition:width .6s cubic-bezier(.4,0,.2,1) .1s; }
        .td-annotation.active .td-connector { width:clamp(80px,20vw,220px); }
        /* v3: copy column bleeds left over the enlarged product image. Higher z-index keeps text on top. */
        .teardown-features { padding:14vh 0 50vh; position:relative; z-index:3; margin-left:-8%; }
        .td-feature {
            min-height:78vh;
            display:flex;
            flex-direction:column;
            justify-content:center;
            padding:40px 32px;
            opacity:.35;
            transition:opacity .4s ease;
            margin-bottom: 24px;
        }
        .td-feature.active { opacity:1; }
        .td-badge { font-size:12px; font-weight:600; letter-spacing:.11em; text-transform:uppercase; margin-bottom:20px; display:inline-flex; align-items:center; gap:8px; }
        /* Rebalanced hierarchy: the 68px/w800 headlines out-shouted the machine
           itself — the copy column is the CAPTION here, the hardware is the
           star. One step down in size and weight hands the frame back. */
        .td-feature h3 { font-size:clamp(30px,4.2vw,52px); font-weight:700; color:#f5f5f7; letter-spacing:-.03em; line-height:1.1; margin-bottom:18px; }
        .td-feature p { font-size:clamp(16px,1.4vw,19px); color:#b0b0b5; line-height:1.65; max-width:460px; text-wrap:pretty; }
        .td-spec-row { display:flex; align-items:baseline; gap:12px; margin-top:26px; padding-top:26px; border-top:1px solid rgba(255,255,255,.06); }
        .td-spec-val { font-size:44px; font-weight:700; letter-spacing:-.03em; }
        .td-spec-label { font-size:15px; color:#a1a1a6; }
        /* ── Sensor readout — the LiDAR chapter's instrument panel. Desktop parks
           it under the stage's lower-left quadrant, phones tuck it into the
           stage corner. Values are driven live by the scan sweep. ── */
        .td-scan-hud {
            position: absolute; left: clamp(8px, 3vw, 40px); bottom: clamp(14px, 5vh, 56px);
            z-index: 7; pointer-events: none;
            font-family: var(--font-mono, 'SF Mono','Monaco','Consolas',monospace);
            display: grid; gap: 5px;
            padding: 12px 14px 11px;
            border-radius: 10px;
            border: 1px solid rgba(120,180,255,.16);
            background: linear-gradient(180deg, rgba(6,12,22,.72), rgba(4,8,16,.58));
            backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
            opacity: 0; transform: translateY(8px);
            transition: opacity .5s ease, transform .6s cubic-bezier(.22,.61,.36,1);
        }
        .td-scan-hud.is-in { opacity: 1; transform: translateY(0); }
        .tsh-row { display: flex; align-items: baseline; gap: 7px; font-size: 10px; letter-spacing: .1em; }
        .tsh-k { color: rgba(190,212,240,.45); min-width: 40px; }
        .tsh-v { color: #dceaff; font-size: 13px; letter-spacing: .02em; font-variant-numeric: tabular-nums; min-width: 62px; text-align: right; }
        .tsh-u { color: rgba(190,212,240,.5); }
        /* sweep bar — mirrors the beam fan travelling across the cone */
        .td-scan-hud .tsh-bar { position: relative; height: 2px; margin-top: 3px; border-radius: 2px; background: rgba(120,180,255,.14); overflow: hidden; }
        .td-scan-hud .tsh-bar i { position: absolute; inset: 0 auto 0 0; width: 34%; border-radius: 2px; background: linear-gradient(90deg, transparent, #2997ff, transparent); animation: tshSweep 2.6s cubic-bezier(.45,0,.55,1) infinite; }
        @keyframes tshSweep { 0% { transform: translateX(-110%); } 100% { transform: translateX(320%); } }
        @media (prefers-reduced-motion: reduce) {
            .td-scan-hud .tsh-bar i { animation: none; opacity: .5; }
        }
        /* Stage HUD + copy scrim are phone-only compositional aids. */
        .td-hud, .td-stage-scrim { display: none; }
        @media (max-width:1100px) {
            /* Mid widths: back off the overlap and the scale-up so layout stays clean. */
            .teardown-sticky-row { grid-template-columns: 1.05fr .95fr; gap:36px; }
            .teardown-visual { transform: none; }
            .teardown-features { margin-left: 0; }
            .td-feature {
                min-height:70vh; padding:36px 24px;
                margin-bottom: 0;
            }
            .td-feature h3 { font-size:clamp(26px,4.6vw,42px); }
            .td-feature p { font-size:16px; }
        }
        /* Chapter close-up stage — mobile only (display gated below) */
        .td-mobile-stage { display: none; }
        @media (max-width:768px) {
            /* ── Mobile teardown v3: same side-by-side grammar as desktop.
               v2 stacked the stage on top of the copy, which broke the one
               thing a teardown diagram is FOR — part and caption sharing an
               optical line. The machine and its words now sit shoulder to
               shoulder again.

               A literal 46/54 grid would cap the hardware at ~180px, so the
               stage stays FULL-BLEED and sticky behind everything; the copy
               column rides the right half over a scrim, and the 3D camera is
               shifted (view offset, see index.app.js) so the machine composes
               into the left half. The hardware keeps ~70% of the screen and
               only its far edge fades under the text — the same deliberate
               bleed desktop gets from `.teardown-features { margin-left:-8% }`. */
            .teardown-sticky-row {
                display: grid;
                grid-template-columns: 1fr;
                padding: 0;
                gap: 0;
            }
            /* Both children share one cell: the stage pins across the whole
               column of chapters, exactly like the desktop two-column row. */
            .teardown-visual, .teardown-features { grid-area: 1 / 1; }
            .teardown-visual {
                /* Stage box geometry — the render sits in a squarer box centred
                   in the sticky column; the HUD hangs off its bottom edge so it
                   reads as a caption for the hardware, not a viewport overlay. */
                --td-stage-h: min(47svh, 400px);
                --td-stage-floor: calc((100svh - var(--nav-height, 48px) - var(--td-stage-h)) / 2);
                position: sticky;
                top: var(--nav-height, 48px);
                height: calc(100svh - var(--nav-height, 48px));
                max-height: calc(100svh - var(--nav-height, 48px));
                min-height: 460px;
                width: 100%;
                z-index: 1;
                padding: 0;
                display: block;
                transform: none;
                /* stage floor: deep space + a warm halo under the hardware,
                   pulled left to sit behind the machine, not behind the copy */
                background:
                    radial-gradient(76% 46% at 32% 46%, rgba(255,130,60,.11) 0%, rgba(255,110,35,.035) 46%, transparent 74%),
                    linear-gradient(180deg, #000 0%, #050607 100%);
            }
            /* Copy-side scrim — the machine dissolves into black under the
               text instead of colliding with it. */
            .td-stage-scrim {
                display: block;
                position: absolute; inset: 0;
                z-index: 4; pointer-events: none;
                background:
                    linear-gradient(90deg, transparent 22%, rgba(0,0,0,.38) 36%, rgba(0,0,0,.76) 49%, rgba(0,0,0,.93) 64%),
                    linear-gradient(180deg, rgba(0,0,0,.55) 0%, transparent 14%, transparent 84%, rgba(0,0,0,.6) 100%);
            }
            /* pre-3D: chapter-cinema images carry the stage; once the machine
               GLB lands (#tdVisual.td-3d) the SAME interactive stage as
               desktop takes over — canvas + projected hardware dots.
               The wrap must keep a LAYOUT BOX pre-3D (opacity, not display:
               none) — the three.js lazy loader watches #teardownCanvas with
               an IntersectionObserver, and display:none never intersects. */
            /* The stage box is squarer than the full-height column so the
               machine doesn't end up marooned in a tall strip of dead air —
               it stays vertically centred against the copy beside it. */
            .teardown-img-wrap {
                display: block;
                position: absolute;
                left: 0; right: 0; top: 50%;
                transform: translateY(-50%);
                height: var(--td-stage-h);
                opacity: 0; pointer-events: none; z-index: 0;
            }
            .teardown-img { display: none; }
            #tdVisual.td-3d .teardown-img-wrap { opacity: 1; z-index: 2; }
            #tdVisual.td-3d .td-mobile-stage { display: none; }
            .td-mobile-stage { display: block; position: absolute; inset: 0; z-index: 1; }
            /* Fallback close-ups compose into the left half to match the 3D
               stage's shifted framing. */
            .tdm-img {
                position: absolute;
                left: 1%; width: 60%;
                top: 50%; height: min(48svh, 400px);
                object-fit: contain;
                opacity: 0;
                transform: translateY(-50%) scale(1.06);
                transition: opacity .5s ease, transform 1.1s cubic-bezier(.22,.61,.36,1);
                filter: drop-shadow(0 22px 40px rgba(0,0,0,.6));
                pointer-events: none;
            }
            .tdm-img.is-on { opacity: 1; transform: translateY(-50%) scale(1); }
            /* Locator dots ride the real hardware; the labels are too wide for
               a phone column, so the stage chip names the part instead. */
            .td-annotation { display: none; }
            #tdVisual.td-3d .td-annotation { display: block; }
            .td-annotation .td-label,
            .td-annotation .td-connector,
            .td-annotation .td-line { display: none; }
            .td-dot { width: 9px; height: 9px; }
            /* ── Stage HUD: one instrument cluster in the bottom-left corner —
               readout, chapter index, part name, reading upward. Sits outside
               .td-mobile-stage so it stays up after the GLB takes over. ── */
            .td-hud { display: block; position: absolute; inset: 0; z-index: 6; pointer-events: none; }
            .tdm-chip {
                position: absolute; left: 12px; bottom: calc(var(--td-stage-floor) - 4px);
                font-family: var(--font-mono);
                font-size: 9.5px; letter-spacing: .12em; text-transform: uppercase;
                color: #d5e3f5;
                padding: 6px 10px; border-radius: 8px;
                background: rgba(8,10,14,.72);
                border: 1px solid rgba(190,212,240,.2);
                backdrop-filter: blur(8px);
                -webkit-backdrop-filter: blur(8px);
                opacity: 0; transform: translateY(6px);
                transition: opacity .4s ease, transform .4s cubic-bezier(.22,.61,.36,1);
                max-width: 52%;
                white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
            }
            .tdm-chip::before {
                content: '';
                display: inline-block;
                width: 5px; height: 5px; border-radius: 50%;
                background: #2997ff;
                box-shadow: 0 0 8px rgba(41,151,255,.6);
                margin-right: 6px;
                vertical-align: 1px;
            }
            .tdm-chip.is-in { opacity: 1; transform: translateY(0); }
            .tdm-count {
                position: absolute; left: 14px; bottom: calc(var(--td-stage-floor) + 30px);
                font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .18em;
                color: rgba(255,255,255,.38);
                opacity: 0; transition: opacity .4s ease;
            }
            .tdm-count.is-in { opacity: 1; }
            .tdm-count b { color: rgba(255,255,255,.85); font-weight: 500; }
            .td-scan-hud { left: 12px; bottom: calc(var(--td-stage-floor) + 56px); padding: 9px 11px 8px; gap: 4px; max-width: 54%; }
            .tsh-row { font-size: 8.5px; gap: 5px; }
            .tsh-k { min-width: 30px; }
            .tsh-v { font-size: 11px; min-width: 50px; }
            /* ── Copy column: right half, riding over the stage. One chapter
               per screen so the pairing stays 1:1 with the pinned hardware. ── */
            .teardown-features {
                position: relative;
                z-index: 3;
                margin-left: 47%;
                width: 53%;
                padding: 0 15px 0 0;
                pointer-events: none;
            }
            .td-feature {
                position: relative;
                min-height: calc(100svh - var(--nav-height, 48px));
                padding: 0;
                margin-bottom: 0;
                justify-content: center;
                pointer-events: auto;
            }
            /* Balanced wrapping matters more here than anywhere else on the
               page: a ~190px column turns any long clause into a one-character
               orphan line, and the headline is the loudest thing in it. */
            .td-feature h3 { font-size: clamp(19px, 5.4vw, 24px); line-height: 1.18; margin-bottom: 10px; text-wrap: balance; }
            .td-feature p { font-size: 13px; line-height: 1.66; max-width: none; color: #c2c2c8; }
            .td-badge { font-size: 9px; letter-spacing: .1em; margin-bottom: 10px; gap: 6px; }
            .td-badge span:first-child { width: 14px !important; }
            .td-spec-row { margin-top: 16px; padding-top: 14px; gap: 8px; flex-wrap: wrap; }
            .td-spec-val { font-size: 26px; }
            .td-spec-label { font-size: 10.5px; line-height: 1.45; }
        }
        /* Extra-narrow phones: buy the copy a little more room, trim type */
        @media (max-width:380px) {
            .teardown-features { margin-left: 44%; width: 56%; padding-right: 12px; }
            .td-feature h3 { font-size: 19px; }
            .td-feature p { font-size: 12.5px; line-height: 1.6; }
            .td-spec-val { font-size: 23px; }
            .td-spec-label { font-size: 10px; }
        }
        /* Landscape phones: the 460px stage floor is taller than the viewport
           itself, which strands the sticky column — drop it and let the copy
           take an even half. */
        @media (max-width:768px) and (orientation:landscape) and (max-height:520px) {
            .teardown-visual { min-height: 0; }
            .teardown-features { margin-left: 50%; width: 50%; }
        }
        /* GRAB HERO - One hand pickup */
        .grab-hero {
            display: flex;
            align-items: center;
            justify-content: center;
            gap: clamp(30px, 5vw, 60px);
            margin-bottom: clamp(48px, 8vw, 80px);
            padding: clamp(24px, 4vw, 40px);
        }
        .grab-visual {
            flex-shrink: 0;
        }
        .hand-grab-svg {
            opacity: 0.9;
        }
        .grab-content {
            text-align: left;
        }
        .grab-stat {
            display: flex;
            align-items: baseline;
            margin-bottom: 16px;
        }
        .grab-num {
            font-size: clamp(120px, 25vw, 220px);
            font-weight: 900;
            letter-spacing: -0.06em;
            line-height: 0.8;
            color: #f5f5f7;
        }
        .grab-unit {
            font-size: clamp(40px, 8vw, 72px);
            font-weight: 700;
            color: #0071e3;
            margin-left: 8px;
        }
        .grab-headline {
            font-size: clamp(32px, 6vw, 52px);
            font-weight: 800;
            letter-spacing: -0.02em;
            margin-bottom: 12px;
            background: linear-gradient(180deg, #fff 30%, rgba(255,255,255,.65) 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }
        .grab-sub {
            font-size: clamp(16px, 2.5vw, 20px);
            color: #86868b;
            margin-bottom: 20px;
            line-height: 1.5;
        }
        .grab-compare {
            display: flex;
            gap: 24px;
            font-size: 14px;
        }
        .grab-compare span:first-child { color: #0071e3; font-weight: 600; }
        .compare-vs { color: #6e6e73; }
        @media (max-width: 768px) { .grab-hero { flex-direction: column; text-align: center; } .grab-content { text-align: center; } .grab-compare { justify-content: center; } }
        /* SETUP HERO - 30sec */
        .setup-hero {
            text-align: center;
            padding: clamp(28px, 4vw, 40px);
            margin-bottom: clamp(40px, 6vw, 60px);
            background: linear-gradient(180deg, rgba(255,255,255,.03) 0%, transparent 100%);
            border-radius: 24px;
        }
        .setup-stat {
            display: inline-flex;
            align-items: baseline;
            margin-bottom: 12px;
        }
        .setup-num {
            font-size: clamp(80px, 16vw, 140px);
            font-weight: 300;
            letter-spacing: -0.04em;
            background: linear-gradient(180deg, #ffffff 0%, rgba(255,255,255,.44) 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }
        .setup-unit {
            font-size: clamp(24px, 4vw, 40px);
            font-weight: 500;
            color: rgba(255,255,255,.5);
            margin-left: 8px;
        }
        .setup-desc {
            font-size: clamp(15px, 2vw, 18px);
            color: #86868b;
        }
        @media (max-width: 768px) {
            .grab-hero { flex-direction: column; text-align: center; gap: 30px; }
            .grab-content { text-align: center; }
            .grab-compare { justify-content: center; }
        }
        /* RANGE HERO - 15m Secondary */
        .range-hero { text-align: center; margin-bottom: clamp(40px, 6vw, 60px); padding: clamp(40px, 6vh, 60px) 0; }
        .range-number { margin-bottom: 20px; }
        .range-num {
            font-size: clamp(72px, 14vw, 120px);
            font-weight: 300;
            letter-spacing: -0.05em;
            line-height: 0.9;
            background: linear-gradient(180deg, #ffffff 0%, rgba(255,255,255,.44) 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }
        .range-unit {
            font-size: clamp(32px, 5vw, 48px);
            font-weight: 500;
            color: rgba(255,255,255,.5);
            vertical-align: super;
            margin-left: -4px;
        }
        .range-hero h3 {
            font-size: clamp(24px, 4vw, 36px);
            font-weight: 600;
            color: #f5f5f7;
            margin-bottom: 12px;
        }
        .range-hook {
            font-size: clamp(16px, 2vw, 18px);
            color: #86868b;
            max-width: 550px;
            margin: 0 auto;
            line-height: 1.5;
        }
        @media (max-width: 768px) {
            .portable-duo { flex-direction: column; gap: 24px; }
            .duo-card { padding: 36px 48px; width: 100%; max-width: 300px; }
        }
        /* Pass Features - DJI Spec Cards */
        .pass-features { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(8px, 1.5vw, 12px); margin-bottom: clamp(40px, 6vw, 60px); }
        .pass-feature { 
            transition: opacity 0.4s;
        }
        .pass-feature:hover::before { opacity: 1; }
        .pass-feature:hover { border-color: rgba(255,255,255,.16); transform: translateY(-4px); }
        .feature-icon { width: 48px; height: 48px; margin: 0 auto 20px; }
        .feature-icon svg { width: 100%; height: 100%; }
        .feature-data { display: flex; align-items: baseline; justify-content: center; gap: 4px; margin-bottom: 12px; }
        .feature-value { font-size: clamp(36px, 5vw, 48px); font-weight: 800; color: #f5f5f7; letter-spacing: -0.04em; }
        .feature-unit { font-size: 16px; color: #86868b; font-weight: 500; }
        .pass-feature p { font-size: 15px; color: rgba(255,255,255,0.5); line-height: 1.5; font-weight: 400; }
        @media (max-width: 640px) { .pass-features { grid-template-columns: 1fr; max-width: 320px; margin-left: auto; margin-right: auto; } }
        /* Mamba CTA */
        .mamba-cta {
            text-align: center;
            padding: clamp(28px, 4vw, 40px) clamp(16px, 3vw, 20px);
            margin-bottom: clamp(40px, 6vw, 60px);
        }
        .mamba-cta p {
            font-size: clamp(22px, 4vw, 32px);
            font-weight: 600;
            color: #f5f5f7;
            letter-spacing: -0.02em;
            font-style: italic;
        }
        /* Nike-Style Quotes */
        .nike-quote {
            text-align: center;
            padding: clamp(36px, 6vh, 50px) clamp(16px, 4vw, 24px);
            background: linear-gradient(180deg, rgba(0,113,227,0.02) 0%, transparent 100%);
        }
        .nike-quote p {
            font-size: clamp(22px, 4vw, 34px);
            font-weight: 600;
            background: linear-gradient(180deg, #ffffff 0%, rgba(255,255,255,.44) 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
            letter-spacing: -0.02em;
            font-style: italic;
            max-width: 650px;
            margin: 0 auto;
            line-height: 1.35;
        }
        .final-quote {
            text-align: center;
            padding: clamp(60px, 10vh, 100px) clamp(16px, 4vw, 24px) clamp(60px, 8vh, 80px);
            background: #000;
            position: relative;
            overflow: hidden;
        }
        .final-quote::before {
            content: '';
            position: absolute;
            width: 600px;
            height: 600px;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: radial-gradient(circle, rgba(41,151,255,0.05), transparent 60%);
            pointer-events: none;
        }
        .final-quote p {
            font-size: clamp(28px, 5vw, 48px);
            font-weight: 600;
            background: linear-gradient(180deg, #ffffff 0%, rgba(255,255,255,.44) 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
            letter-spacing: -0.03em;
            position: relative;
        }
        /* ═══════════════════════════════════════════════════════════════════════
           AI SOC — hybrid heterogeneous compute board. Perception → thinking:
           a dark PCB stage, traces converging on the ARCX N1 package, six silicon
           dies that ignite in sequence, mission stats on the left.
           ═══════════════════════════════════════════════════════════════════════ */
        .soc-section { background: #000; padding: clamp(70px, 11vh, 120px) clamp(16px, 4vw, 24px); position: relative; overflow: hidden; }
        /* Pinned boot mode: the section holds the viewport while the chip boots —
           scroll drives the sequence, and the page only releases once it's done.
           Sizes tighten so header + board always clear the fixed nav. */
        .soc-section.soc-pinned {
            display: flex; flex-direction: column; justify-content: center;
            min-height: 100vh; min-height: 100lvh;
            padding-top: calc(var(--nav-height, 48px) + 10px); padding-bottom: 14px;
        }
        .soc-section.soc-pinned .soc-header { margin-bottom: clamp(18px, 3vh, 34px); }
        .soc-section.soc-pinned .soc-title { font-size: clamp(30px, 4.4vw, 50px); }
        .soc-section.soc-pinned .soc-sub { margin-top: 10px; font-size: clamp(13px, 1.5vw, 15.5px); }
        .soc-section.soc-pinned .soc-board { min-height: clamp(380px, 52vh, 560px); }
        /* short viewports: compress the pinned boot harder so the lock still fits */
        @media (max-height: 840px) {
            .soc-section.soc-pinned .soc-title { font-size: clamp(26px, 3.6vw, 40px); }
            .soc-section.soc-pinned .soc-sub { margin-top: 6px; font-size: 13px; }
            .soc-section.soc-pinned .soc-header { margin-bottom: 14px; }
            .soc-section.soc-pinned .soc-board { min-height: 0; padding-top: 22px; padding-bottom: 22px; }
            .soc-section.soc-pinned .soc-die { min-height: 94px; padding: 11px 12px 10px; }
            .soc-section.soc-pinned .soc-die-grid { gap: 9px; padding: 12px; }
            .soc-section.soc-pinned .soc-die .die-role { font-size: clamp(13px, 1.3vw, 16px); }
        }
        .soc-container { max-width: 1200px; margin: 0 auto; width: 100%; }
        .soc-header { text-align: center; max-width: 840px; margin: 0 auto clamp(34px, 5vh, 54px); }
        .soc-eyebrow {
            display: inline-flex; align-items: center;
            font-family: var(--font-mono); font-size: 11px; font-weight: 500;
            letter-spacing: .16em; text-transform: uppercase;
            color: rgba(167,139,250,.85); margin-bottom: 16px;
        }
        /* Readability pass: the old fill faded to 64%-alpha violet by the
           baseline — the LOWER HALF of every glyph washed into the black
           stage and the headline read blurry. Match the silver recipe the
           hero/CMOS titles use (white → 78% white), slightly heavier weight
           so the compressed pinned sizes stay crisp. */
        .soc-title {
            font-size: clamp(34px, 5.6vw, 62px); font-weight: 650; letter-spacing: -.04em; line-height: 1.1;
            background: linear-gradient(180deg, #ffffff 30%, rgba(255,255,255,.78) 100%);
            -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
        }
        .soc-sub { margin-top: 14px; font-size: clamp(14px, 1.7vw, 17px); color: rgba(255,255,255,.62); line-height: 1.65; max-width: 640px; margin-left: auto; margin-right: auto; }
        .soc-board {
            position: relative; border-radius: 24px;
            padding: clamp(26px, 4.2vw, 48px) clamp(18px, 3.4vw, 44px);
            /* Real PCB macro photograph under a dark grade + violet cast —
               the板 is a place now, not a gradient (die-shot language). */
            background:
                radial-gradient(64% 86% at 60% 46%, rgba(124,102,255,.10) 0%, rgba(104,84,230,.04) 46%, transparent 70%),
                linear-gradient(180deg, rgba(3,3,8,.42) 0%, rgba(3,3,8,.18) 34%, rgba(3,3,9,.55) 100%),
                url('/static/product/soc/soc-board-bg.webp') center 42% / cover no-repeat,
                #04040a;
            border: 1px solid rgba(255,255,255,.06);
            overflow: hidden;
            display: flex; align-items: center; justify-content: center;
            gap: clamp(24px, 3.6vw, 56px);
            min-height: clamp(440px, 58vh, 640px);
        }
        /* edge vignette keeps the photo from fighting the copy above/below */
        .soc-board::before {
            content: ''; position: absolute; inset: 0; pointer-events: none;
            background: radial-gradient(115% 115% at 50% 46%, transparent 52%, rgba(2,2,6,.66) 100%);
        }
        .soc-traces { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
        .soc-trace {
            fill: none; stroke: rgba(178,172,214,.17); stroke-width: 1.2;
            stroke-dasharray: 700; stroke-dashoffset: 700;
            transition: stroke-dashoffset 1.7s cubic-bezier(.4,0,.2,1);
        }
        .soc-board.is-live .soc-trace { stroke-dashoffset: 0; }
        .soc-trace-pads circle { fill: rgba(202,194,246,.42); opacity: 0; transition: opacity .6s ease 1.1s; }
        .soc-board.is-live .soc-trace-pads circle { opacity: 1; }
        /* energy pulses riding the traces once the chip is working */
        .soc-pulse {
            fill: none; stroke: rgba(226,220,255,.92); stroke-width: 1.7;
            stroke-linecap: round;
            stroke-dasharray: 0.05 0.95; stroke-dashoffset: 1;
            opacity: 0;
            filter: drop-shadow(0 0 4px rgba(176,158,255,.9));
        }
        .soc-board.is-alive .soc-pulse { opacity: 1; animation: socPulseRun 3.6s linear infinite; }
        @keyframes socPulseRun { from { stroke-dashoffset: 1; } to { stroke-dashoffset: -1; } }
        .soc-stats { position: relative; z-index: 2; display: flex; flex-direction: column; gap: clamp(20px, 3.4vh, 36px); min-width: 176px; }
        .soc-stat {
            padding-left: 15px;
            border-left: 1px solid rgba(167,139,250,.38);
            border-image: linear-gradient(180deg, rgba(167,139,250,.7), rgba(167,139,250,.08)) 1;
        }
        .soc-stat .ss-val { display: block; font-family: var(--font-display); font-size: clamp(28px, 3.3vw, 44px); color: #f2f4ff; line-height: 1; font-variant-numeric: tabular-nums; text-shadow: 0 0 30px rgba(174,158,255,.3); }
        .soc-stat .ss-val b { font-weight: 400; }
        .soc-stat .ss-val i { font-style: normal; font-family: var(--font-mono); font-size: .36em; margin-left: 6px; color: rgba(205,214,230,.6); letter-spacing: .14em; }
        .soc-stat .ss-label { display: block; margin-top: 8px; font-size: 13px; color: rgba(255,255,255,.55); max-width: 22ch; line-height: 1.5; }
        /* ── ARCX N1 package — the hero of the board: machined-metal bezel over a
           violet-graphite substrate, gold pad rails top and bottom ── */
        .soc-package {
            position: relative; z-index: 2;
            width: min(720px, 100%);
            border-radius: 14px; padding: 0;
            border: 1px solid transparent;
            background:
                linear-gradient(180deg, #171a24 0%, #0b0d15 58%, #0d0f1a 100%) padding-box,
                linear-gradient(165deg, rgba(255,255,255,.30) 0%, rgba(150,158,196,.10) 26%, rgba(255,255,255,.03) 52%, rgba(168,158,230,.20) 100%) border-box;
            backdrop-filter: none;
            -webkit-backdrop-filter: none;
            box-shadow:
                0 30px 70px rgba(0,0,0,.55),
                0 2px 0 rgba(0,0,0,.5),
                inset 0 1px 0 rgba(255,255,255,.08),
                inset 0 -1px 0 rgba(0,0,0,.6);
            transition: box-shadow 1.1s ease;
        }
        .soc-board.is-live .soc-package {
            box-shadow:
                0 34px 84px rgba(0,0,0,.6),
                0 0 120px rgba(138,116,255,.20),
                0 2px 0 rgba(0,0,0,.5),
                inset 0 1px 0 rgba(255,255,255,.1),
                inset 0 -1px 0 rgba(0,0,0,.6);
        }
        /* substrate pin pads along the package edges — fine-pitch gold fingers */
        .soc-package::before, .soc-package::after {
            content: ''; position: absolute; left: 22px; right: 22px; height: 5px;
            background: repeating-linear-gradient(90deg,
                rgba(224,198,128,.62) 0 5px, rgba(150,126,70,.34) 5px 6px, transparent 6px 12px);
            opacity: .5; pointer-events: none;
        }
        .soc-package::before { top: -5px; border-radius: 2px 2px 0 0; }
        .soc-package::after { bottom: -5px; border-radius: 0 0 2px 2px; }
        /* scanline sweep across the exposed dies while the chip works */
        .soc-scan {
            position: absolute; left: 0; right: 0; top: 0; height: 26%;
            background: linear-gradient(180deg, transparent, rgba(206,198,240,.10) 45%, rgba(246,242,255,.17) 50%, rgba(206,198,240,.10) 55%, transparent);
            opacity: 0; pointer-events: none; z-index: 3;
        }
        .soc-board.is-alive .soc-scan { animation: socScan 5.8s cubic-bezier(.45,.05,.55,.95) infinite; }
        @keyframes socScan {
            0% { transform: translateY(-60%); opacity: 0; }
            10% { opacity: 1; }
            52% { transform: translateY(330%); opacity: .85; }
            62%, 100% { transform: translateY(350%); opacity: 0; }
        }
        .soc-pkg-head {
            display: flex; justify-content: space-between; align-items: center; gap: 12px;
            padding: 12px 18px;
            border-bottom: 1px solid rgba(255,255,255,.07);
            background: linear-gradient(180deg, rgba(255,255,255,.045) 0%, rgba(255,255,255,.012) 100%);
            border-radius: 13px 13px 0 0;
        }
        .soc-pkg-name { font-family: var(--font-display); font-size: 14px; letter-spacing: .14em; color: #fff; white-space: nowrap; text-shadow: 0 0 16px rgba(178,160,255,.35); }
        .soc-leds { display: inline-flex; gap: 5px; margin-left: 10px; }
        .soc-leds i { width: 5px; height: 5px; border-radius: 50%; background: rgba(120,255,170,.22); box-shadow: inset 0 0 1px rgba(0,0,0,.6); }
        .soc-board.is-alive .soc-leds i { animation: socLed 2.1s ease-in-out infinite; }
        .soc-board.is-alive .soc-leds i:nth-child(2) { animation-delay: .7s; }
        .soc-board.is-alive .soc-leds i:nth-child(3) { animation-delay: 1.3s; }
        @keyframes socLed { 0%, 100% { background: rgba(120,255,170,.22); } 50% { background: rgba(140,255,185,.95); box-shadow: 0 0 8px rgba(120,255,170,.7); } }
        .soc-pkg-node { font-family: var(--font-mono); font-size: 10px; letter-spacing: .12em; color: rgba(255,255,255,.42); text-align: right; }
        .soc-die-grid {
            position: relative;
            display: grid; gap: 11px; padding: 16px;
            grid-template-columns: repeat(4, 1fr);
            grid-template-areas:
                "npu npu isp isp"
                "npu npu dtof dtof"
                "foc cpu cpu sec";
            /* interposer substrate between dies: micro solder-bump dot lattice */
            background:
                radial-gradient(circle at 1px 1px, rgba(196,188,240,.10) 1px, transparent 1.5px) 0 0 / 9px 9px,
                linear-gradient(180deg, rgba(10,11,18,.7), rgba(6,7,12,.7));
            border-radius: 0 0 13px 13px;
        }
        /* per-die identity hue — a full spread around the color wheel, warm and
           cool alternating in the grid so no two neighbours read as one family:
           violet=AI vision, AMBER=imaging, cyan=depth, red-orange=motors,
           silver=scheduling, green=privacy. Border, tint, glow key off --dh.
           NOTE: double-class selectors — they MUST outrank the `.soc-die` base
           rule below, whose fallback --dh silently painted every die violet. */
        .die-npu { grid-area: npu; }
        .die-isp { grid-area: isp; }
        .die-dtof { grid-area: dtof; }
        .die-foc { grid-area: foc; }
        .die-cpu { grid-area: cpu; }
        .die-sec { grid-area: sec; }
        .soc-die.die-npu { --dh: 167,139,250; --die-pos: 12% 10%; --die-zoom: 210%; }
        .soc-die.die-isp { --dh: 255,193,74; --die-pos: 88% 8%; --die-zoom: 265%; }
        .soc-die.die-dtof { --dh: 45,212,255; --die-pos: 10% 86%; --die-zoom: 255%; }
        .soc-die.die-foc { --dh: 255,111,74; --die-pos: 90% 88%; --die-zoom: 305%; }
        .soc-die.die-cpu { --dh: 214,222,240; --die-pos: 50% 48%; --die-zoom: 235%; }
        .soc-die.die-sec { --dh: 74,222,128; --die-pos: 32% 66%; --die-zoom: 320%; }
        .soc-die {
            --dh: 130,112,255;
            position: relative; border-radius: 8px;
            padding: 14px 14px 13px; min-height: 112px;
            display: flex; flex-direction: column; justify-content: flex-end; gap: 4px;
            /* A REAL silicon die-shot photograph carries each tile (violet
               micrograph, unique crop per die so no two read as clones); a
               bottom scrim guards the copy and the identity color survives in
               a faint tint, the border and the glow. */
            background:
                linear-gradient(180deg, rgba(4,5,10,.30) 0%, transparent 30%, transparent 46%, rgba(3,4,8,.82) 92%),
                linear-gradient(150deg, rgba(var(--dh), .20) 0%, rgba(var(--dh), .07) 46%, transparent 80%),
                url('/static/product/soc/soc-die-shot.webp') var(--die-pos, 50% 50%) / var(--die-zoom, 240%) no-repeat,
                linear-gradient(165deg, #10131a 0%, #0a0c11 100%);
            border: 1px solid rgba(var(--dh), .4);
            overflow: hidden;
            /* unlit dies stay dim but keep their hue — the palette reads even pre-boot */
            opacity: .36; filter: saturate(.85) brightness(.6); transform: translateY(8px);
            transition: opacity .7s ease, filter .9s ease, transform .7s cubic-bezier(.22,.61,.36,1), box-shadow .9s ease, border-color .9s ease;
        }
        /* top-metal routing shimmer over the photograph — barely-there, keeps
           the surface "live" instead of a flat JPEG */
        .soc-die::before {
            content: ''; position: absolute; inset: 0;
            background:
                repeating-linear-gradient(45deg, rgba(var(--dh), .04) 0 2px, transparent 2px 9px),
                repeating-linear-gradient(90deg, rgba(255,255,255,.015) 0 1px, transparent 1px 4px);
            opacity: .8;
        }
        /* hue bloom breathing out of the die once lit */
        .soc-die::after {
            content: ''; position: absolute; inset: 0;
            background:
                radial-gradient(95% 75% at 30% 18%, rgba(var(--dh), .46), transparent 64%),
                radial-gradient(70% 55% at 78% 92%, rgba(var(--dh), .18), transparent 70%);
            opacity: 0; transition: opacity .9s ease;
        }
        /* one-shot specular sweep on ignition */
        .soc-die .die-shine {
            position: absolute; top: -20%; bottom: -20%; width: 34%;
            background: linear-gradient(100deg, transparent, rgba(255,255,255,.22) 48%, rgba(255,255,255,.05) 55%, transparent);
            transform: translateX(-220%) skewX(-14deg);
            pointer-events: none;
        }
        .soc-die.is-lit .die-shine { animation: dieShine .95s cubic-bezier(.3,.1,.25,1) .12s forwards; }
        @keyframes dieShine { to { transform: translateX(420%) skewX(-14deg); } }
        /* In-die hierarchy: codename (small, top) → ROLE in identity color (the
           headline — what this silicon actually does) → spec detail (dim). */
        .soc-die .die-head {
            position: relative; display: flex; align-items: center; gap: 8px;
            margin-bottom: auto; padding-bottom: 10px;
            /* tier line in the die's own hue: codename row / role / spec read
               as three distinct strata (in-die structure, not a page seam) */
            border-bottom: 1px solid rgba(var(--dh), .16);
        }
        .soc-die .die-ico { width: 22px; height: 22px; flex: none; color: rgb(var(--dh)); filter: drop-shadow(0 0 7px rgba(var(--dh), .6)); }
        .soc-die b {
            position: relative; font-family: var(--font-mono); font-weight: 500;
            font-size: 10.5px; letter-spacing: .12em; color: rgba(240,243,255,.74);
            text-transform: uppercase; white-space: nowrap;
        }
        .soc-die .die-role {
            position: relative; font-style: normal;
            font-family: var(--font-display); font-weight: 400;
            font-size: clamp(16px, 1.6vw, 20px); letter-spacing: .02em;
            color: rgb(var(--dh)); line-height: 1.28;
            margin-top: 7px;
        }
        .soc-die .die-spec { position: relative; font-family: var(--font-mono); font-size: 11px; letter-spacing: .03em; color: rgba(235,240,255,.64); text-transform: uppercase; line-height: 1.5; }
        .soc-die.is-lit {
            opacity: 1; filter: none; transform: none;
            border-color: rgba(var(--dh), .62);
            box-shadow:
                0 0 40px rgba(var(--dh), .3),
                0 10px 30px rgba(0,0,0,.4),
                inset 0 0 28px rgba(var(--dh), .14),
                inset 0 1px 0 rgba(255,255,255,.16);
        }
        .soc-die.is-lit::after { opacity: 1; }
        .soc-die.is-lit .die-role { text-shadow: 0 0 18px rgba(var(--dh), .6); }
        /* ── Boot spotlight: modules come online ONE BY ONE. While the board is
           booting, already-lit dies settle back and the newest module holds the
           stage in its own identity color. ── */
        .soc-board.is-booting .soc-die.is-lit:not(.is-focus) {
            opacity: .78; filter: brightness(.86) saturate(.9);
        }
        .soc-die.is-focus {
            z-index: 2;
            transform: scale(1.05);
            border-color: rgba(var(--dh), .95);
            filter: brightness(1.1);
            box-shadow:
                0 0 0 1px rgba(var(--dh), .7),
                0 0 64px rgba(var(--dh), .5),
                0 16px 44px rgba(0,0,0,.5),
                inset 0 0 34px rgba(var(--dh), .22),
                inset 0 1px 0 rgba(255,255,255,.22);
        }
        .soc-die.is-focus .die-role { text-shadow: 0 0 26px rgba(var(--dh), .95); }
        .soc-die.is-focus .die-ico { filter: drop-shadow(0 0 12px rgba(var(--dh), .95)); }
        .die-npu.is-lit { box-shadow: 0 0 52px rgba(var(--dh), .42), inset 0 0 32px rgba(var(--dh), .18), inset 0 1px 0 rgba(255,255,255,.16); }
        /* working state: staggered thermal breathing, like live silicon under load */
        .soc-board.is-alive .soc-die.is-lit { animation: dieBreath 4.8s ease-in-out infinite; }
        .soc-board.is-alive .die-isp.is-lit { animation-delay: .7s; }
        .soc-board.is-alive .die-dtof.is-lit { animation-delay: 1.5s; }
        .soc-board.is-alive .die-cpu.is-lit { animation-delay: 2.2s; }
        .soc-board.is-alive .die-foc.is-lit { animation-delay: 2.9s; }
        .soc-board.is-alive .die-sec.is-lit { animation-delay: 3.6s; }
        @keyframes dieBreath {
            0%, 100% { filter: brightness(1) saturate(1); }
            50% { filter: brightness(1.17) saturate(1.12); }
        }
        @media (max-width: 900px) {
            .soc-board { flex-direction: column; gap: 26px; padding: 24px 14px 28px; min-height: 0; }
            .soc-stats { flex-direction: row; width: 100%; justify-content: space-between; gap: 10px; min-width: 0; }
            .soc-stat { padding-left: 10px; }
            .soc-stat .ss-label { font-size: 10.5px; max-width: 14ch; }
            .soc-die-grid {
                gap: 9px; padding: 12px;
                grid-template-columns: repeat(2, 1fr);
                grid-template-areas:
                    "npu npu"
                    "isp dtof"
                    "foc cpu"
                    "sec sec";
            }
            /* the NPU is the hero die on phones — give the marquee module its stage */
            .soc-die { min-height: 100px; padding: 12px 12px 11px; }
            .soc-die.die-npu { min-height: 122px; }
            .soc-die.die-npu .die-role { font-size: clamp(17px, 4.6vw, 21px); }
            .soc-die .die-head { padding-bottom: 9px; }
            /* ── Portrait parity: the LOCKED pinned boot must fit a phone screen.
               The stacked layout used to measure right at (or past) the viewport
               budget, so canPin failed and phones silently fell back to the
               free-scrolling legacy boot — a different chapter from desktop.
               This compression brings the whole stage to ~620px, so the same
               dock → lock → module-by-module ignition plays on iPhone. ── */
            .soc-section.soc-pinned { padding-top: calc(var(--nav-height, 48px) + 8px); padding-bottom: 10px; }
            .soc-section.soc-pinned .soc-eyebrow { margin-bottom: 8px; }
            .soc-section.soc-pinned .soc-header { margin-bottom: 12px; }
            .soc-section.soc-pinned .soc-title { font-size: clamp(24px, 6.6vw, 32px); }
            .soc-section.soc-pinned .soc-sub { margin-top: 6px; font-size: 11.5px; line-height: 1.45; }
            .soc-section.soc-pinned .soc-board { gap: 14px; padding: 14px 10px 16px; }
            .soc-section.soc-pinned .soc-pkg-head { padding: 8px 12px; }
            .soc-section.soc-pinned .soc-pkg-name { font-size: 12px; }
            .soc-section.soc-pinned .soc-pkg-node { font-size: 8.5px; }
            .soc-section.soc-pinned .soc-die-grid { gap: 7px; padding: 10px; }
            .soc-section.soc-pinned .soc-die { min-height: 58px; padding: 9px 10px 8px; gap: 2px; }
            .soc-section.soc-pinned .soc-die.die-npu { min-height: 78px; }
            .soc-section.soc-pinned .soc-die .die-head { padding-bottom: 5px; gap: 6px; }
            .soc-section.soc-pinned .soc-die .die-ico { width: 15px; height: 15px; }
            .soc-section.soc-pinned .soc-die b { font-size: 8.5px; letter-spacing: .1em; }
            .soc-section.soc-pinned .soc-die .die-role { font-size: 13px; }
            .soc-section.soc-pinned .soc-die.die-npu .die-role { font-size: clamp(14px, 4vw, 17px); }
            .soc-section.soc-pinned .soc-die .die-spec { font-size: 9px; line-height: 1.35; }
            .soc-section.soc-pinned .soc-stats { gap: 8px; }
            .soc-section.soc-pinned .soc-stat { padding-left: 8px; }
            .soc-section.soc-pinned .soc-stat .ss-val { font-size: clamp(17px, 5vw, 24px); }
            .soc-section.soc-pinned .soc-stat .ss-label { margin-top: 5px; font-size: 9.5px; line-height: 1.4; }
        }
        /* ===== 5KG — THE WEIGHT MOMENT (cabin flythrough animation) ===== */
        /* 100lvh, NOT dvh: this section pins, and ScrollTrigger locks its box size
           at refresh (URL bar expanded → small). When the bar collapsed mid-pin,
           the dvh-sized cabin stopped covering the phone and the next chapter's
           court photo crept in under the story. lvh is the stable large viewport:
           always covers, never resizes mid-pin. */
        .weight-section { position: relative; min-height: 100vh; min-height: 100lvh; display: flex; flex-direction: column; justify-content: center; isolation: isolate; background: #000; touch-action: pan-y; overflow-x: clip; }
        .weight-section::before { content:''; position:absolute; inset:0; background: radial-gradient(circle at 50% 50%, #161a24 0%, #06080a 70%, #000 100%); pointer-events:none; z-index:0; opacity:0; transition: opacity 1.2s ease; }
        .weight-section::after { display:none; }
        .weight-section.in-view::before { opacity:1; }

        .weight-moment { text-align: center; padding: 8vh clamp(8px,2vw,20px) 1vh; position: relative; max-width:1500px; margin:0 auto; height:100vh; height:100lvh; display:flex; flex-direction:column; align-items:center; justify-content:center; }
        .weight-moment::after { display:none; }
        .weight-moment.cabin-phase::after { opacity:1; }
        /* ── Phase 0: Airplane Silhouette ── */
        .airplane-outline-wrap { position:absolute; top:50%; left:50%; width:clamp(420px,88vw,1200px); z-index:10; opacity:0; pointer-events:none; overflow:visible; will-change:transform,opacity; contain:layout paint; backface-visibility:hidden; }
        .airplane-outline-svg { display:block; width:100%; height:auto; shape-rendering:geometricPrecision; }
        .airplane-outline-svg.is-drawn .plane-detail { opacity:1; }
        /* Draw timings sized to the FILM: the blueprint must be fully inked
           BEFORE the zoom-through dissolves it (mobile zoom starts ~1.05s in;
           the old 2.4s draw never finished — the plane read as a dim smudge). */
        .airplane-outline-svg .plane-body { stroke-dasharray:2400; stroke-dashoffset:2400; transition: stroke-dashoffset 1.0s cubic-bezier(.4,0,.2,1) .05s; }
        .airplane-outline-svg .plane-wing { stroke-dasharray:900; stroke-dashoffset:900; transition: stroke-dashoffset .85s cubic-bezier(.4,0,.2,1) .1s; }
        .airplane-outline-svg .plane-tail { stroke-dasharray:500; stroke-dashoffset:500; transition: stroke-dashoffset .7s cubic-bezier(.4,0,.2,1) .18s; }
        .airplane-outline-svg .plane-engine ellipse { stroke-dasharray:200; stroke-dashoffset:200; transition: stroke-dashoffset .6s cubic-bezier(.4,0,.2,1) .26s; }
        .airplane-outline-svg .plane-engine line { opacity:0; transition: opacity .5s ease .5s; }
        .airplane-outline-svg .plane-detail { opacity:0; transition: opacity .6s ease .42s; }
        .airplane-outline-svg.is-drawn .plane-body { stroke-dashoffset:0; }
        .airplane-outline-svg.is-drawn .plane-wing { stroke-dashoffset:0; }
        .airplane-outline-svg.is-drawn .plane-tail { stroke-dashoffset:0; }
        .airplane-outline-svg.is-drawn .plane-engine ellipse { stroke-dashoffset:0; }
        .airplane-outline-svg.is-drawn .plane-engine line { opacity:1; }
        .airplane-outline-svg.is-drawn .plane-detail { opacity:1; }
        .airplane-label { display: none; }
        /* ── Speed Lines — radial streaks during zoom push (removed for performance) ── */
        /* ── Cabin Cross-Section Container — height budget shares the viewport with the
           weight band below, so the porthole no longer has to host the numbers. ── */
        /* Cabin sized to leave a clear band of viewport BELOW it for the weight
           verdict — the balance never overlaps the porthole or the wing. */
        .cabin-cross { position:relative; width:min(84vw, 39vh, 520px); margin:0 auto; opacity:0; z-index:1; will-change:transform,opacity; backface-visibility:hidden; contain:layout style; }
        .cabin-cross-inner { position:relative; width:100%; overflow:visible; border-radius: 22px; backface-visibility:hidden; background: #1a1c23; background-image: radial-gradient(ellipse 120% 100% at 50% 0%, rgba(255,255,255,0.06) 0%, transparent 40%), linear-gradient(180deg, rgba(255,255,255,0.03) 0%, transparent 15%, rgba(0,0,0,0.5) 100%); box-shadow: inset 0 1px 1px rgba(255,255,255,0.1), inset 0 0 40px rgba(0,0,0,0.6), 0 40px 100px rgba(0,0,0,0.9); padding-bottom: 0; border: 1px solid rgba(255,255,255,0.04); will-change:transform,opacity; }
        /* ── Overhead Bin — sits ABOVE the porthole window ── */
        .bin-shelf-wrap { position:relative; width:120%; left:-10%; z-index:3; opacity:0; margin-top:-10px; margin-bottom:-2px; perspective:800px; transform-style:preserve-3d; will-change:transform; }
        .bin-shelf { display:block; width:100%; height:auto; transform:rotateX(8deg); transform-origin:bottom center; transition:transform 1.2s cubic-bezier(.22,1,.36,1); will-change:transform; backface-visibility:hidden; }
        .bin-shelf-wrap.is-open .bin-shelf { transform:rotateX(0deg); }
        /* LED strip — static on once bin opens; no infinite pulse (removes GPU thrash) */
        .bin-shelf-wrap .bin-led-glow { position:absolute; bottom:-1px; left:2%; right:2%; height:4px; background:linear-gradient(90deg, transparent, rgba(255,240,200,.4) 20%, rgba(255,255,255,.8) 50%, rgba(255,240,200,.4) 80%, transparent); border-radius:2px; opacity:0; z-index:5; pointer-events:none; transition:opacity .6s ease; box-shadow: 0 -4px 12px rgba(255,240,200,0.3); }
        .bin-shelf-wrap.is-open .bin-led-glow { opacity:1; }
        /* Internal glow when suitcase docks */
        @keyframes binInternalGlow { 0% { opacity:0; } 40% { opacity:1; } 100% { opacity:.8; } }
        .bin-shelf-wrap .bin-internal-light { position:absolute; inset:5% 10% 5%; background:radial-gradient(ellipse 60% 70% at 50% 55%, rgba(255,200,140,.14) 0%, rgba(255,180,100,.06) 35%, transparent 65%); opacity:0; pointer-events:none; z-index:1; }
        .bin-shelf-wrap.is-docked .bin-internal-light { animation:binInternalGlow 1.2s ease forwards; }
        /* Shimmer sweep on bin */
        @keyframes binShimmer { 0% { transform:translateX(-120%) skewX(-20deg); } 100% { transform:translateX(220%) skewX(-20deg); } }
        .bin-shelf-wrap .bin-shimmer { position:absolute; inset:0; overflow:hidden; pointer-events:none; z-index:6; opacity:0; }
        .bin-shelf-wrap .bin-shimmer::after { content:''; position:absolute; top:0; left:0; width:40%; height:100%; background:linear-gradient(90deg, transparent, rgba(255,255,255,.04) 40%, rgba(255,255,255,.08) 50%, rgba(255,255,255,.04) 60%, transparent); transform:translateX(-120%) skewX(-20deg); }
        .bin-shelf-wrap.is-docked .bin-shimmer { opacity:1; }
        .bin-shelf-wrap.is-docked .bin-shimmer::after { animation:binShimmer 1.5s cubic-bezier(.4,0,.2,1) .3s forwards; }
        /* ── Bin desktop expansion — spans all 5 portholes ──
           Scoped to mouse+pointer-fine devices only. On Android tablets (pointer:coarse) the
           4x width + negative left creates a massive overflow (up to 4096px wide) that combined
           with parent's 3D context causes composite-layer thrash → flicker during GSAP fade-in. */
        @media (hover: hover) and (pointer: fine) and (min-width: 901px) {
            .bin-shelf-wrap { width:400%; left:-150%; }
        }
        /* ── Touch-device (Android tablet) flicker fix for bin-shelf ──
           Root cause: .bin-shelf-wrap has perspective/preserve-3d/will-change, its SVG child
           has rotateX(8deg) transition + backface-visibility, and a sibling runs infinite
           ledPulse while GSAP simultaneously does translate3d on the wrap. On mid-tier Mali/
           Adreno GPUs this combination causes composite-layer re-ordering → black frames =
           "bin-shelf flickers mid-layout". Fix: drop 3D context on touch devices, establish
           isolated composite layer, freeze infinite animations. Desktop visual untouched. */
        @media (hover: none) and (pointer: coarse) {
            .bin-shelf-wrap {
                perspective: none;
                transform-style: flat;
                will-change: auto;
                isolation: isolate;
            }
            .bin-shelf,
            .bin-shelf-wrap.is-open .bin-shelf {
                transform: none;
                transition: none;
                will-change: auto;
                backface-visibility: visible;
            }
        }
        /* ── Adjacent Portholes for Immersion ── */
        .porthole-side { position:absolute; width:65%; aspect-ratio:5/8; top:50%; transform:translateY(-50%); border-radius:26% / 20%; z-index:0; opacity:0; pointer-events:none; transition:opacity 1.2s ease; overflow:hidden; }
        .porthole-side::before { content:''; position:absolute; inset:0; background:linear-gradient(180deg, #020610 0%, #071428 8%, #0e2448 20%, #163c6a 35%, #1f5288 48%, #2d70a8 58%, #4090c4 68%, #58aed8 78%, #78c4e4 88%, #98d8f0 96%, #b0e4f4 100%); border-radius:inherit; }
        .porthole-side::after { content:''; position:absolute; inset:0; border-radius:inherit; box-shadow:inset 0 0 30px rgba(0,0,0,.5), inset 0 0 80px rgba(0,0,0,.2), inset 0 0 0 1px rgba(255,255,255,.1); }
        .porthole-left { right: 92%; }
        .porthole-right { left: 92%; }
        .porthole-left-far { right: 166%; }
        .porthole-right-far { left: 166%; }
        .porthole-side.is-visible { opacity:0.85; filter: blur(2px); }
        .porthole-left-far.is-visible, .porthole-right-far.is-visible { opacity:0.6; filter: blur(5px); }
        @media (max-width: 900px) { .porthole-side { display:none; } }
        /* ── Porthole Window — middle of cabin cross-section ── */
        /* Inner content caps use svh (small viewport, stable): the porthole always
           fits even with the URL bar expanded, and never reflows mid-pin. */
        .porthole-viewport { position:relative; width:65%; margin:0 auto; aspect-ratio:5/8; z-index:1; overflow:hidden; border-radius:26% / 20%; max-height:calc(100svh - 330px); background:radial-gradient(120% 90% at 50% -5%, rgba(170,214,255,.28) 0%, rgba(71,122,184,.22) 40%, rgba(10,19,38,.42) 100%); box-shadow: inset 0 0 0 3px rgba(160,195,230,.12), inset 0 0 0 6px rgba(80,120,170,.06), inset 0 0 40px rgba(0,0,0,.45), inset 0 0 80px rgba(0,0,0,.18), 0 0 0 1px rgba(140,180,220,.08), 0 2px 12px rgba(0,0,0,.4); transform:translateZ(0); contain:layout style; }
        .porthole-frame-svg { position:absolute; inset:0; width:100%; height:100%; z-index:10; pointer-events:none; opacity:0; transition:opacity .5s ease; shape-rendering:geometricPrecision; }
        .porthole-frame-svg.is-drawn { opacity:1; }
        .porthole-frame-svg .ph-frame { stroke-dasharray:2400; stroke-dashoffset:2400; transition: stroke-dashoffset 2s cubic-bezier(.4,0,.2,1); }
        .porthole-frame-svg .ph-inner { stroke-dasharray:2200; stroke-dashoffset:2200; transition: stroke-dashoffset 1.6s cubic-bezier(.4,0,.2,1) .2s; }
        .porthole-frame-svg .ph-reflect { opacity:0; transition: opacity .6s ease 1s; }
        .porthole-frame-svg .ph-rivet { opacity:0; transition: opacity .4s ease 1.2s; }
        .porthole-frame-svg .ph-detail { opacity:0; transition: opacity .5s ease 1.4s; }
        .porthole-frame-svg.is-drawn .ph-frame { stroke-dashoffset:0; }
        .porthole-frame-svg.is-drawn .ph-detail { opacity:1; }
        .porthole-frame-svg.is-drawn .ph-inner { stroke-dashoffset:0; }
        .porthole-frame-svg.is-drawn .ph-reflect { opacity:1; }
        .porthole-frame-svg.is-drawn .ph-rivet { opacity:1; }
        /* ── Porthole content — clipped to window shape ── */
        .porthole-content { position:absolute; inset:0; border-radius:inherit; overflow:hidden; z-index:1; }

        /* ── Sky system: multi-layer atmosphere ── */
        .porthole-sky { position:absolute; inset:0; z-index:0; pointer-events:none; opacity:0; transition:opacity .35s ease; background:linear-gradient(180deg, #020610 0%, #071428 8%, #0e2448 20%, #163c6a 35%, #1f5288 48%, #2d70a8 58%, #4090c4 68%, #58aed8 78%, #78c4e4 88%, #98d8f0 96%, #b0e4f4 100%); }
        .porthole-sky.is-visible { opacity:1; }
        .porthole-sky::after { content:''; position:absolute; bottom:18%; left:0; right:0; height:2px; background:linear-gradient(90deg, transparent, rgba(255,200,120,.08), rgba(255,180,100,.14), rgba(255,200,120,.08), transparent); }

        /* ── Sun atmosphere — multi-layer glow (Rayleigh scattering) ── */
        .porthole-sun {
            position:absolute; z-index:1; pointer-events:none;
            opacity:0; transition:opacity .4s ease;
            border-radius:50%;
        }
        .porthole-sun.is-visible { opacity:1; }
        /* Core sun disc */
        .porthole-sun-disc {
            position:absolute; width:60px; height:60px;
            top:38%; right:18%;
            background:radial-gradient(circle, #fff8e0 0%, rgba(255,248,180,.92) 25%, rgba(255,230,140,.7) 50%, rgba(255,200,80,.3) 70%, transparent 100%);
            border-radius:50%; filter:blur(2px);
        }
        /* Inner corona */
        .porthole-sun-corona {
            position:absolute; width:140px; height:140px;
            top:calc(38% - 40px); right:calc(18% - 40px);
            background:radial-gradient(circle, rgba(255,240,160,.18) 0%, rgba(255,220,120,.1) 40%, rgba(255,190,80,.04) 65%, transparent 100%);
            border-radius:50%; filter:blur(18px);
        }
        /* Outer halo */
        .porthole-sun-halo {
            position:absolute; width:280px; height:280px;
            top:calc(38% - 110px); right:calc(18% - 110px);
            background:radial-gradient(circle, rgba(255,220,160,.06) 0%, rgba(255,200,140,.03) 50%, transparent 70%);
            border-radius:50%; filter:blur(40px);
        }
        /* Sun lens flare streaks */
        .porthole-sun-flare {
            position:absolute; top:38%; right:18%;
            width:3px; height:80px;
            background:linear-gradient(180deg, rgba(255,240,180,.3) 0%, rgba(255,220,140,.1) 50%, transparent 100%);
            transform:translateX(-50%) rotate(15deg);
            filter:blur(1px); border-radius:2px;
        }
        .porthole-sun-flare-2 {
            position:absolute; top:38%; right:18%;
            width:2px; height:50px;
            background:linear-gradient(180deg, rgba(255,230,160,.2) 0%, transparent 100%);
            transform:translateX(-50%) rotate(-20deg);
            filter:blur(0.8px); border-radius:2px;
        }
        .porthole-sun.is-visible .porthole-sun-disc { animation:sunPulse 6s ease-in-out infinite; }
        @keyframes sunPulse { 0%,100%{opacity:.85;transform:scale(1);} 50%{opacity:1;transform:scale(1.06);} }

        /* ── Horizon / mountain silhouette ── */
        .porthole-horizon {
            position:absolute; bottom:0; left:0; right:0; z-index:2;
            height:28%; pointer-events:none;
            opacity:0; transition:opacity .5s ease .2s;
        }
        .porthole-horizon.is-visible { opacity:1; }
        .porthole-horizon svg { display:block; width:100%; height:100%; }
        .ph-horizon-fill { fill:rgba(10,20,40,.7); }

        /* ── Clouds — multi-layer volumetric clouds with warm sun glow ── */
        .porthole-clouds { position:absolute; inset:0; overflow:hidden; z-index:3; pointer-events:none; opacity:0; transition:opacity .3s ease; }
        .porthole-clouds.is-visible { opacity:1; }
        .cloud-layer { position:absolute; inset:0; transition:transform .12s ease-out; will-change:transform; }
        .cloud { position:absolute; }

        /* Far layer — wispy cirrus, sun-warmed */
        .cloud-far {
            background:
                radial-gradient(ellipse 130% 60% at 28% 48%, rgba(255,255,255,.028) 0%, transparent 68%),
                radial-gradient(ellipse 90% 50% at 62% 40%, rgba(255,255,255,.022) 0%, transparent 62%),
                radial-gradient(ellipse 110% 45% at 82% 52%, rgba(255,250,235,.018) 0%, transparent 58%);
            box-shadow:
                0 6px 18px rgba(255,230,180,.04),
                0 2px 8px rgba(255,220,160,.02);
        }

        /* Mid layer — cumulus with warm underlighting */
        .cloud-mid {
            background:
                radial-gradient(ellipse 150% 75% at 22% 58%, rgba(255,255,255,.048) 0%, transparent 58%),
                radial-gradient(ellipse 100% 58% at 55% 42%, rgba(255,255,255,.038) 0%, transparent 52%),
                radial-gradient(ellipse 120% 52% at 74% 55%, rgba(255,250,240,.03) 0%, transparent 48%);
            box-shadow:
                0 8px 24px rgba(255,228,170,.06),
                0 3px 10px rgba(255,210,140,.03);
        }

        /* Near layer — large cumulus, strong warm glow from below */
        .cloud-near {
            background:
                radial-gradient(ellipse 170% 85% at 24% 52%, rgba(255,255,255,.065) 0%, transparent 52%),
                radial-gradient(ellipse 105% 62% at 52% 38%, rgba(255,255,255,.055) 0%, transparent 48%),
                radial-gradient(ellipse 135% 72% at 70% 48%, rgba(255,252,245,.045) 0%, transparent 50%);
            box-shadow:
                0 12px 36px rgba(255,225,165,.1),
                0 4px 14px rgba(255,205,130,.05),
                inset 0 -8px 20px rgba(255,200,130,.06);
        }

        /* Warm underside glow on all clouds (simulating sunlight from below) */
        .cloud::after {
            content:''; position:absolute; bottom:-4px; left:-10%; right:-10%; height:40%;
            background:linear-gradient(180deg, rgba(255,210,140,.12) 0%, rgba(255,195,120,.06) 40%, transparent 100%);
            filter:blur(8px); border-radius:50%;
        }

        @keyframes cloudDriftL { 0%{transform:translateX(-160%)} 100%{transform:translateX(280%)} }
        @keyframes cloudDriftR { 0%{transform:translateX(280%)} 100%{transform:translateX(-160%)} }
        .cloud-f1 { width:300px; height:48px; top:5%; animation:cloudDriftL 42s linear infinite; }
        .cloud-f2 { width:240px; height:38px; top:18%; animation:cloudDriftR 48s linear infinite 8s; }
        .cloud-f3 { width:260px; height:42px; top:50%; animation:cloudDriftL 44s linear infinite 20s; }
        .cloud-f4 { width:190px; height:34px; top:68%; animation:cloudDriftR 38s linear infinite 14s; }
        .cloud-m1 { width:360px; height:64px; top:10%; animation:cloudDriftR 26s linear infinite 3s; }
        .cloud-m2 { width:280px; height:56px; top:40%; animation:cloudDriftL 22s linear infinite 11s; }
        .cloud-m3 { width:220px; height:46px; top:72%; animation:cloudDriftR 28s linear infinite 7s; }
        .cloud-n1 { width:420px; height:82px; top:20%; animation:cloudDriftL 15s linear infinite 1s; }
        .cloud-n2 { width:340px; height:72px; top:56%; animation:cloudDriftR 17s linear infinite 8s; }
        .cloud-n3 { width:370px; height:76px; top:83%; animation:cloudDriftL 14s linear infinite 4s; }

        /* ── Touch-device GPU relief — fixes flicker on Android tablets ──
           10 infinite-animating clouds + per-cloud ::after filter:blur(8px) + nested
           will-change/backdrop-filter inside .cabin-cross create composite-layer thrash on
           mid-tier Mali/Adreno GPUs (observed: overhead bin area flickers on Android tablets).
           On (pointer:coarse) devices we:
             1. Hide the far/mid cloud layers (keep only 3 near clouds for the core parallax)
             2. Drop the blurred cloud undersides (biggest per-frame repaint cost)
             3. Promote cloud-layer + cabin-cross-inner to their own composite layers (translateZ)
             4. Disable the .12s micro-transition on .cloud-layer so JS parallax can't fight
                the CSS infinite animation on the children. */
        @media (hover: none) and (pointer: coarse) {
            .cloud-f1, .cloud-f2, .cloud-f3, .cloud-f4,
            .cloud-m1, .cloud-m2, .cloud-m3 { display: none; }
            .cloud::after { filter: none; opacity: .55; }
            .cloud-layer { transition: none; transform: translateZ(0); }
            .cabin-cross-inner { transform: translateZ(0); }
            .porthole-clouds { transform: translateZ(0); }
        }

        /* ── Porthole depth — enhanced vignette + glass glare ── */
        .porthole-depth { position:absolute; inset:0; border-radius:inherit; z-index:8; pointer-events:none; opacity:0; transition:opacity .4s ease; }
        .porthole-depth.is-visible { opacity:1; }
        /* Multi-layer glass shadow depth */
        .porthole-depth::before {
            content:''; position:absolute; inset:0; border-radius:inherit;
            box-shadow:
                inset 0 0 50px rgba(0,0,0,.48),
                inset 0 0 100px rgba(0,0,0,.18),
                inset 0 24px 40px rgba(0,0,0,.12),
                inset 0 -16px 30px rgba(0,0,0,.06);
            background:linear-gradient(180deg,
                rgba(20,40,80,.04) 0%,
                transparent 22%,
                transparent 80%,
                rgba(18,45,110,.06) 100%);
        }
        /* Glass glare — top arc reflection */
        .porthole-depth::after {
            content:''; position:absolute; top:0; left:0; right:0;
            height:42%;
            background:linear-gradient(180deg,
                rgba(255,255,255,.055) 0%,
                rgba(255,255,255,.028) 30%,
                rgba(255,255,255,.008) 60%,
                transparent 100%);
            border-radius:inherit;
            clip-path:ellipse(70% 100% at 50% 0%);
        }

        /* ── Boeing 787 electrochromic dimmer control (bottom of window) ── */
        .porthole-dimmer {
            position:absolute; bottom:5%; left:50%; transform:translateX(-50%);
            z-index:12; pointer-events:none; opacity:0; transition:opacity .6s ease;
            display:flex; flex-direction:column; align-items:center; gap:4px;
        }
        .porthole-dimmer.is-visible { opacity:1; }
        .ph-dimmer-track {
            width:32px; height:3px;
            background:rgba(255,255,255,.12); border-radius:2px;
            position:relative; overflow:hidden;
        }
        .ph-dimmer-fill {
            height:100%; width:30%;
            background:rgba(100,180,255,.5); border-radius:2px;
            transition:width .3s ease;
        }
        .ph-dimmer-btn {
            width:6px; height:6px; border-radius:50%;
            background:rgba(255,255,255,.3);
            box-shadow:0 0 4px rgba(100,180,255,.4);
        }
        .ph-dimmer-label {
            font-family:'SF Mono','JetBrains Mono',monospace;
            font-size:5px; color:rgba(100,180,255,.5);
            letter-spacing:.08em; text-transform:uppercase;
        }

        /* ── Flight info HUD — elegant transparent style ── */
        .porthole-hud { display:flex; flex-direction:column; gap:clamp(2px,.5vh,5px); z-index:11; opacity:0; transition:opacity .6s ease; pointer-events:none; }
        .porthole-hud.is-visible { opacity:1; }
        .ph-hud-item {
            font-family:'SF Mono','JetBrains Mono','Fira Code',monospace;
            font-size:clamp(7px,.9vw,11px); font-weight:500;
            color:rgba(100,200,255,.55);
            letter-spacing:.06em;
            text-shadow:0 0 8px rgba(100,200,255,.3), 0 1px 4px rgba(0,0,0,.8);
            display:flex; align-items:center; gap:5px;
        }
        .ph-hud-dot {
            width:4px; height:4px; border-radius:50%;
            background:rgba(48,209,88,.7); box-shadow:0 0 6px rgba(48,209,88,.5);
            animation:hudBlink 2.5s ease-in-out infinite;
        }
        @keyframes hudBlink { 0%,100%{opacity:1;} 50%{opacity:.3;} }
        .ph-hud-alt { animation:hudFlicker 8s ease-in-out infinite; }
        @keyframes hudFlicker { 0%,92%,100%{opacity:1;} 93%{opacity:.6;} 94%{opacity:1;} 96%{opacity:.7;} 97%{opacity:1;} }
        .ph-hud-spd { color:rgba(100,200,255,.45); }
        .ph-hud-dest { color:rgba(255,210,140,.4); font-size:clamp(6px,.8vw,10px); letter-spacing:.1em; }
        /* ── Porthole text overlay — headline inside the window ── */
        .porthole-text { position:relative; z-index:6; padding:0; background:none; opacity:0; pointer-events:none; text-align:center; margin-top:8px; margin-bottom:4px; }
        .porthole-text .ph-headline { display:block; max-width:14ch; margin:0 auto; font-size:clamp(20px,3vw,30px); font-weight:900; color:#fff; letter-spacing:-0.04em; line-height:1.04; text-shadow:0 0 16px rgba(255,255,255,.18), 0 3px 12px rgba(0,0,0,.55); }
        .porthole-text .ph-sub { display:block; font-size:clamp(9px,1.1vw,13px); font-weight:400; color:rgba(255,255,255,.8); letter-spacing:0.01em; line-height:1.45; margin-top:3px; text-shadow:0 0 8px rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.9); }
        /* ── Porthole overlay — headline + tagline only (numbers moved to the band) ── */
        .porthole-compare { position:absolute; inset:0; z-index:5; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:clamp(6px,1.2vh,12px); padding:5%; opacity:0; pointer-events:none; }
        .porthole-compare.is-visible { opacity:1; }
        .wc-tagline { font-size:clamp(11px,1.5vw,14px); color:rgba(255,255,255,.72); letter-spacing:.04em; text-align:center; opacity:0; font-weight:500; text-shadow:0 1px 8px rgba(0,0,0,.6); }
        /* (The balance-scale verdict band's styles were retired with the act —
           the chapter now ends on the stowed cabin inside one continuous scrub.) */
        /* ── Cabin wall panel lines below porthole ── */
        .cabin-wall-below { display:none; }
        /* ── Folded product — absolute layer, rides UP into the overhead bin ── */
        .weight-hero { position:absolute; top:0; left:50%; z-index:4; opacity:0; pointer-events:none; will-change:transform,opacity; }
        .weight-hero.is-inside-bin { z-index:2; }
        .weight-case { position: relative; display: inline-block; z-index: 1; }
        .weight-case .case-img { display:block; width:100%; height:auto; filter:drop-shadow(0 18px 34px rgba(0,0,0,.6)) drop-shadow(0 2px 6px rgba(0,0,0,.5)); }
        .weight-case.is-docked .case-img { filter:drop-shadow(0 10px 22px rgba(0,0,0,.55)) brightness(1.1); transition:filter .8s ease; }
        /* Luggage-tag style spec sticker, stamps on when the unit docks */
        .case-tag { position:absolute; right:-6%; bottom:4%; display:flex; flex-direction:column; align-items:center; gap:1px; padding:7px 12px 6px; border-radius:9px; background:linear-gradient(160deg, #ffb054, #ff7a2d); color:#1a0d02; box-shadow:0 8px 22px rgba(255,120,40,.35), 0 2px 6px rgba(0,0,0,.4); transform:scale(0) rotate(14deg); opacity:0; }
        .case-tag.is-stamped { animation:tagStamp .6s cubic-bezier(.2,1.4,.35,1) forwards; }
        @keyframes tagStamp { 0% { transform:scale(0) rotate(14deg); opacity:0; } 60% { transform:scale(1.12) rotate(-7deg); opacity:1; } 100% { transform:scale(1) rotate(-5deg); opacity:1; } }
        .case-tag-num { font-size:clamp(17px,2.2vw,24px); font-weight:800; letter-spacing:-.03em; line-height:1; }
        .case-tag-num i { font-style:normal; font-size:.55em; font-weight:700; margin-left:1px; }
        .case-tag-label { font-size:clamp(7px,.85vw,9px); font-weight:700; letter-spacing:.1em; text-transform:uppercase; opacity:.82; white-space:nowrap; }
        .bin-shelf-wrap.is-docked #binSlot { fill: rgba(255,150,60,.07); stroke: rgba(255,150,60,.32); transition: fill .6s ease, stroke .6s ease; }
        /* ── Night flight — cabin dims once the bin is secured; the LED strip and
           reading-light pool carry the scene ── */
        .weight-moment .cabin-cross-inner { transition: filter 1.4s ease; }
        .weight-moment.is-night .cabin-cross-inner { filter: brightness(.76) saturate(.9); }
        .cabin-ambient.is-dim { opacity: .38 !important; transition: opacity 1.6s ease !important; }
        #binDoor { pointer-events: none; }
        /* ═══ WINDOW-SEAT IMMERSION PACK ═══════════════════════════════════════ */
        /* The wing outside — bottom of the porthole, drifting with the near clouds */
        .porthole-wing {
            position: absolute; left: -8%; right: -10%; bottom: -3%;
            height: 58%;
            z-index: 4; pointer-events: none;
            opacity: 0; transition: opacity 1.2s ease .4s;
            filter: drop-shadow(0 -6px 22px rgba(0,0,0,.35));
        }
        .porthole-wing.is-visible { opacity: 1; }
        .porthole-wing svg { width: 100%; height: 100%; display: block; animation: phWingFlex 7s ease-in-out infinite; transform-origin: 10% 90%; }
        @keyframes phWingFlex { 0%, 100% { transform: rotate(0deg) translateY(0); } 50% { transform: rotate(.35deg) translateY(-2px); } }
        /* red navigation light: steady glow; white anti-collision strobe: double-flash */
        .porthole-wing .ph-nav { fill: #ff4545; filter: drop-shadow(0 0 4px rgba(255,70,70,.9)); animation: phNavPulse 2.6s ease-in-out infinite; }
        @keyframes phNavPulse { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }
        .porthole-wing .ph-strobe { fill: #fff; opacity: 0; animation: phStrobe 2.2s linear infinite; filter: drop-shadow(0 0 10px rgba(255,255,255,.95)); }
        @keyframes phStrobe {
            0%, 5.4%, 100% { opacity: 0; }
            2% { opacity: 1; }
            3.4% { opacity: 0; }
            4.6% { opacity: .95; }
        }
        /* Golden hour wash — warms the whole view, ramps in via class */
        .porthole-golden {
            position: absolute; inset: 0; z-index: 5; pointer-events: none;
            background:
                linear-gradient(200deg, rgba(255,166,88,0) 30%, rgba(255,146,68,.14) 62%, rgba(255,116,52,.24) 100%),
                radial-gradient(70% 50% at 24% 62%, rgba(255,178,96,.22), transparent 70%);
            opacity: 0; transition: opacity 2.4s ease;
        }
        .porthole-golden.is-on { opacity: 1; }
        /* Starfield — appears as the cabin goes to night */
        .porthole-night {
            position: absolute; inset: 0; z-index: 1; pointer-events: none;
            background-image:
                radial-gradient(1.2px 1.2px at 18% 12%, rgba(255,255,255,.8), transparent 60%),
                radial-gradient(1px 1px at 64% 8%, rgba(255,255,255,.6), transparent 60%),
                radial-gradient(1.4px 1.4px at 82% 22%, rgba(255,255,255,.75), transparent 60%),
                radial-gradient(1px 1px at 36% 26%, rgba(255,255,255,.55), transparent 60%),
                radial-gradient(1.1px 1.1px at 52% 16%, rgba(255,255,255,.65), transparent 60%),
                radial-gradient(1px 1px at 12% 34%, rgba(255,255,255,.5), transparent 60%),
                radial-gradient(1.3px 1.3px at 74% 38%, rgba(255,255,255,.6), transparent 60%);
            opacity: 0; transition: opacity 3s ease .6s;
        }
        .porthole-night.is-on { opacity: .9; }
        /* Window light shaft spilling onto the cabin wall below the porthole */
        .cabin-lightshaft {
            position: absolute;
            left: 50%; top: 60%;
            width: 48%; height: 38%;
            transform: translateX(-50%) perspective(420px) rotateX(38deg);
            transform-origin: 50% 0%;
            background: linear-gradient(180deg, rgba(190,215,255,.13) 0%, rgba(170,200,250,.05) 55%, transparent 100%);
            -webkit-mask-image: radial-gradient(85% 100% at 50% 0%, #000 30%, transparent 100%);
            mask-image: radial-gradient(85% 100% at 50% 0%, #000 30%, transparent 100%);
            opacity: 0; transition: opacity 1.6s ease .3s;
            pointer-events: none; z-index: 0;
        }
        .cabin-lightshaft.is-visible { opacity: 1; }
        .weight-moment.is-night .cabin-lightshaft { opacity: .35; }
        /* dust motes drifting through the shaft */
        .cabin-lightshaft .mote {
            position: absolute; width: 2px; height: 2px; border-radius: 50%;
            background: rgba(225,238,255,.85);
            filter: blur(.4px);
            opacity: 0;
            animation: moteDrift 7.5s linear infinite;
        }
        @keyframes moteDrift {
            0% { transform: translate(0, 0); opacity: 0; }
            12% { opacity: .7; }
            55% { opacity: .5; }
            100% { transform: translate(26px, 46px); opacity: 0; }
        }
        /* Cruise vibration — sub-pixel engine hum on the whole cross-section, plus a
           soft turbulence bump every ~11s. Runs only once the cabin phase is active. */
        .weight-moment.cabin-phase .cabin-cross-inner {
            animation: cruiseHum 1.6s ease-in-out infinite, cruiseBump 11s ease-in-out infinite;
        }
        @keyframes cruiseHum {
            0%, 100% { transform: translateY(0); }
            50% { transform: translateY(.6px); }
        }
        @keyframes cruiseBump {
            0%, 88.5%, 92.5%, 100% { margin-top: 0; }
            89.5% { margin-top: -2.2px; }
            91% { margin-top: 1.4px; }
        }
        @media (max-width: 768px) {
            .cabin-lightshaft { display: none; }
            .porthole-wing { height: 50%; }
        }
        @keyframes caseFloat { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }
        .weight-hero.is-sliding .weight-case { will-change: transform; }
        .weight-hero.is-sliding { will-change:transform,opacity; }
        .weight-case.is-floating { animation: caseFloat 4s ease-in-out infinite; }
        /* Screen shake on slam */
        @keyframes screenShake { 0% { transform:translate(0,0); } 10% { transform:translate(-3px,2px); } 20% { transform:translate(4px,-2px); } 30% { transform:translate(-2px,3px); } 40% { transform:translate(3px,-1px); } 50% { transform:translate(-1px,2px); } 60% { transform:translate(2px,-1px); } 70% { transform:translate(-1px,1px); } 80% { transform:translate(1px,0); } 100% { transform:translate(0,0); } }
        .weight-moment.is-shaking { animation: screenShake .45s cubic-bezier(.36,.07,.19,.97); }
        /* Cabin light sweep */
        @keyframes cabinLightSweep { 0% { transform:translateX(-100%) skewX(-15deg); } 100% { transform:translateX(200%) skewX(-15deg); } }
        .cabin-cross-inner::after { content:''; position:absolute; inset:0; z-index:20; pointer-events:none; background:linear-gradient(90deg, transparent 0%, rgba(255,255,255,.03) 45%, rgba(255,255,255,.06) 50%, rgba(255,255,255,.03) 55%, transparent 100%); transform:translateX(-100%) skewX(-15deg); opacity:0; }
        .cabin-cross-inner.is-sweeping::after { opacity:1; animation:cabinLightSweep 1.2s cubic-bezier(.4,0,.2,1) forwards; }
        /* ── Cabin ambient glow ── */
        .cabin-ambient { position:absolute; inset:0; pointer-events:none; z-index:0; overflow:hidden; opacity:0; transition:opacity 1.5s ease; background: radial-gradient(ellipse at 50% 40%, rgba(45, 55, 75, 0.4) 0%, rgba(20, 25, 35, 0.8) 60%, #0a0c10 100%); }
        .cabin-ambient.is-visible { opacity:1; }
        .cabin-ambient::before { content:''; position:absolute; top:0; left:50%; width:clamp(300px,30vw,500px); height:70%; transform:translateX(-50%); background:linear-gradient(180deg, rgba(255,230,180,.06) 0%, rgba(255,220,160,.02) 40%, transparent 100%); clip-path:polygon(40% 0%, 60% 0%, 78% 100%, 22% 100%); }
        .cabin-ambient::after { content:''; position:absolute; top:20%; left:50%; width:clamp(400px,70vw,900px); height:clamp(400px,70vw,900px); transform:translate(-50%,-20%); border-radius:50%; background:radial-gradient(circle, rgba(255,180,80,.05) 0%, rgba(255,149,0,.02) 30%, transparent 65%); }
        /* ── Scanline effect ── */
        .weight-scanline { display:none; }
        /* ── Teardown Grid Layout — annotations flanking the cabin ── */
        .weight-teardown { position:relative; display:grid; grid-template-columns:minmax(0,1fr) auto minmax(0,1fr); align-items:center; gap:0 clamp(24px,4vw,64px); max-width:1440px; margin:0 auto; width:100%; transform:translateY(0); }
        .weight-td-svg { position:absolute; inset:0; width:100%; height:100%; pointer-events:none; z-index:3; overflow:visible; }
        .weight-side { display:flex; flex-direction:column; gap:24px; z-index:5; }
        .weight-side-left { align-items:flex-end; justify-content:center; text-align:right; }
        .weight-side-right { align-items:flex-start; justify-content:center; text-align:left; }
        .weight-side-spec { display:none; }
        .weight-side-left .weight-side-spec { align-items:flex-end; text-align:right; }
        .weight-side-right .weight-side-spec { align-items:flex-start; text-align:left; }
        .weight-side-spec span { font-size:14px; font-weight:600; color:rgba(255,255,255,.3); letter-spacing:.07em; text-transform:uppercase; }
        .weight-center { position:relative; justify-self: center; }
        .anno-line { stroke:rgba(255,255,255,.08); stroke-width:1; fill:none; stroke-dasharray:600; stroke-dashoffset:600; transition:stroke .5s, stroke-width .5s; }
        .anno-line.is-drawn { stroke-dashoffset:0; transition:stroke-dashoffset 1.2s cubic-bezier(.25,.46,.45,.94), stroke .5s, stroke-width .5s; }
        .anno-line.is-active { stroke:rgba(255,255,255,.2); }
        .anno-dot { fill:rgba(255,255,255,.4); transition:fill .5s, r .5s; }
        .anno-dot.is-active { fill:#fff; filter:drop-shadow(0 0 6px rgba(255,255,255,.4)); }
        .anno-card { display:none; }
        .anno-card svg { width:15px; height:15px; flex-shrink:0; opacity:.35; transition:opacity .4s; }
        .anno-card::before { content:''; display:inline-block; width:6px; height:6px; border-radius:50%; background:rgba(255,255,255,.15); flex-shrink:0; transition:background .4s, box-shadow .4s; }
        .weight-side-left .anno-card:hover, .weight-side-left .anno-card.is-active { color:rgba(255,255,255,.9); transform:translateX(-4px); }
        .weight-side-right .anno-card:hover, .weight-side-right .anno-card.is-active { color:rgba(255,255,255,.9); transform:translateX(4px); }
        .anno-card:hover::before, .anno-card.is-active::before { background:#fff; box-shadow:0 0 8px rgba(255,255,255,.3); }
        .anno-card:hover svg, .anno-card.is-active svg { opacity:.7; }
        .anno-pulse { fill:none; stroke:rgba(255,255,255,.1); stroke-width:.5; opacity:0; }
        @keyframes annoPulse { 0% { r:3px; opacity:.6; } 100% { r:14px; opacity:0; } }
        .anno-pulse.is-active { animation:annoPulse 2.5s ease-out infinite; }
        .anno-light { fill:#fff; opacity:0; filter:drop-shadow(0 0 3px rgba(255,255,255,.6)); }
        @keyframes travelLight { 0% { offset-distance:0%; opacity:0; } 5% { opacity:1; } 95% { opacity:1; } 100% { offset-distance:100%; opacity:0; } }
        .anno-light.is-traveling { animation:travelLight 0.7s cubic-bezier(.4,0,.2,1) forwards; }
        /* ── Cabin interior atmosphere details ── */
        .cabin-reading-light { position:absolute; top:0; left:50%; width:clamp(60px,12vw,120px); height:60%; transform:translateX(-50%); pointer-events:none; z-index:2; opacity:0; }
        .cabin-reading-light.is-on { opacity:1; transition:opacity 1.5s ease; }
        /* Triangular light cone from ceiling reading light */
        .cabin-reading-light::before {
            content:''; position:absolute; top:0; left:50%; width:120%; height:100%;
            transform:translateX(-50%);
            background:linear-gradient(180deg,
                rgba(255,240,200,.06) 0%,
                rgba(255,235,180,.04) 30%,
                rgba(255,225,160,.02) 60%,
                transparent 100%);
            clip-path:polygon(35% 0%, 65% 0%, 85% 100%, 15% 100%);
            filter:blur(8px);
        }
        /* Sharp inner beam */
        .cabin-reading-light::after {
            content:''; position:absolute; top:0; left:50%; width:30%; height:70%;
            transform:translateX(-50%);
            background:linear-gradient(180deg,
                rgba(255,245,210,.04) 0%,
                rgba(255,240,195,.02) 50%,
                transparent 100%);
            clip-path:polygon(40% 0%, 60% 0%, 80% 100%, 20% 100%);
        }
        /* Seat headrest silhouettes on both sides */
        .cabin-seat-silhouette { position:absolute; z-index:2; opacity:0; transition:opacity 1s ease; pointer-events:none; }
        .cabin-seat-silhouette.is-visible { opacity:1; }
        .cabin-seat-left { left:0; top:42%; }
        .cabin-seat-right { right:0; top:42%; }
        .seat-headrest {
            width:clamp(18px,3.5vw,32px); height:clamp(22px,4vw,38px);
            border-radius:50% 50% 40% 40%;
            background:radial-gradient(ellipse at 50% 40%, rgba(255,255,255,.055) 0%, rgba(200,210,230,.03) 60%, transparent 100%);
            box-shadow:0 2px 12px rgba(0,0,0,.4);
        }
        .seat-shoulder {
            width:clamp(14px,2.8vw,26px); height:clamp(30px,5vw,44px);
            border-radius:4px 4px 0 0;
            background:linear-gradient(180deg, rgba(255,255,255,.03) 0%, rgba(255,255,255,.015) 100%);
            margin-top:2px;
        }
        .cabin-wall-detail { position:absolute; top:38%; z-index:3; opacity:0; pointer-events:none; display:flex; flex-direction:column; align-items:center; gap:clamp(8px,1.5vw,16px); }
        .cabin-wall-left { left:clamp(2px,1.5vw,12px); }
        .cabin-wall-right { right:clamp(2px,1.5vw,12px); }
        .cabin-wall-detail.is-visible { opacity:1; transition:opacity 1.2s ease; }
        .cabin-air-vent { width:clamp(16px,3vw,28px); height:clamp(16px,3vw,28px); border-radius:50%; border:1px solid rgba(255,255,255,.06); background:radial-gradient(circle, rgba(255,255,255,.02) 30%, transparent 70%); position:relative; }
        .cabin-air-vent::before { content:''; position:absolute; inset:3px; border-radius:50%; border:1px solid rgba(255,255,255,.04); }
        .cabin-air-vent::after { content:''; position:absolute; top:50%; left:50%; width:40%; height:1px; background:rgba(255,255,255,.06); transform:translate(-50%,-50%); }
        .cabin-call-btn { width:clamp(10px,2vw,16px); height:clamp(10px,2vw,16px); border-radius:3px; border:1px solid rgba(255,255,255,.05); background:rgba(255,180,60,.03); position:relative; }
        .cabin-call-btn::after { content:''; position:absolute; top:50%; left:50%; width:4px; height:4px; border-radius:50%; background:rgba(255,180,60,.12); transform:translate(-50%,-50%); }
        .cabin-light-btn { width:clamp(10px,2vw,16px); height:clamp(10px,2vw,16px); border-radius:3px; border:1px solid rgba(255,255,255,.05); background:rgba(255,255,255,.02); position:relative; }
        .cabin-light-btn::after { content:''; position:absolute; top:50%; left:50%; width:4px; height:4px; border-radius:50%; background:rgba(255,255,255,.08); transform:translate(-50%,-50%); box-shadow:0 0 4px rgba(255,255,255,.06); }
        @media (max-width: 1024px) {
            .anno-card { font-size:14px; }
        }
        @media (max-width: 768px) {
            /* content-visibility REMOVED here: culling a PINNED section is a trap —
               its intrinsic-size estimate (900px) never matched the real 100lvh+
               box, so every materialize/cull flip shifted the whole document under
               the reader (the same scroll-jump the bento section documented).
               The WebGL/video work inside is already gated by IntersectionObservers. */
            .weight-teardown { transform:translateY(0); }
            .weight-section { padding:0 10px !important; }
            .weight-moment { min-height:100lvh; height:100lvh; justify-content:center; padding:8vh 10px calc(1vh + env(safe-area-inset-bottom)); }
            .airplane-outline-wrap { width:100%; top:49%; }
            .airplane-label { bottom:16%; font-size:11px; letter-spacing:.16em; }
            .weight-teardown { grid-template-columns:1fr; gap:14px; max-width:560px; }
            .weight-center { order:1; }
            .weight-side { width:min(560px,95vw); display:none; grid-template-columns:1fr 1fr; gap:10px; margin:0 auto; }
            .weight-side-left { order:2; align-items:stretch; }
            .weight-side-right { order:3; align-items:stretch; }
            .weight-side-spec { display:none; }
            .weight-td-svg { display:none; }
            /* ── Full-bleed cabin: on phones the fuselage IS the viewport. The
               old caps (86vw / 38vh) existed to leave room for the retired
               balance band and left the cabin floating as a small rounded
               card — zero immersion. Edge-to-edge walls, no card chrome. ── */
            .weight-moment { padding-left: 0; padding-right: 0; }
            .cabin-cross { width: 100%; max-width: none; }
            .cabin-cross-inner {
                padding-bottom: 0; overflow: visible;
                border-radius: 0; border-left: 0; border-right: 0;
                box-shadow: inset 0 1px 1px rgba(255,255,255,.08), inset 0 0 60px rgba(0,0,0,.55);
            }
            .porthole-viewport { width: 74%; max-height: 46svh; }
            .anno-card { display:none; }
            .cabin-wall-detail { gap:6px; }
            /* Headline + tagline centered in the porthole */
            .porthole-text { margin-top:0; margin-bottom:0; padding:0; background:none; }
            .porthole-text .ph-headline { font-size:clamp(17px,3.8vw,22px); text-align:center; max-width:14ch; }
            .porthole-text .ph-sub { display:none; }
            .porthole-compare {
                inset: auto;
                top: 50%; left: 50%;
                transform: translate(-50%, -50%);
                padding: 0; margin: 0;
                width: 90%;
                justify-content: center;
            }
            .wc-tagline { font-size: 10px; }
            .weight-chips-fallback { display:none !important; }
        }
        @media (max-width: 640px) {
            .weight-teardown { transform:translateY(0); }
            .weight-section { padding:0 !important; }
            .weight-moment { min-height:100lvh; height:100lvh; padding:5vh 0 calc(1vh + env(safe-area-inset-bottom)); }
            .cabin-ambient { filter:blur(15px); }
            .weight-side { display:none; }
            /* Full-bleed fuselage on small phones too — the window seat, not a
               postcard of one */
            .cabin-cross { width: 100%; max-width: none; }
            .cabin-cross-inner {
                padding-bottom:0; overflow:visible;
                border-radius: 0; border-left: 0; border-right: 0;
                box-shadow: inset 0 1px 1px rgba(255,255,255,.08), inset 0 0 60px rgba(0,0,0,.55);
            }
            .porthole-viewport { width: 72%; max-height: 44svh; }
            /* Compact luggage tag — at full size it buried the stowed machine */
            .case-tag { right:-3%; bottom:6%; padding:5px 9px 4px; border-radius:7px; }
            .case-tag-num { font-size:14px; }
            .case-tag-label { font-size:6.5px; letter-spacing:.08em; }
            .porthole-text { margin-top:0; margin-bottom:0; padding:0; }
            .porthole-text .ph-headline { font-size:clamp(16px,4.4vw,20px); max-width:14ch; }
            .porthole-compare {
                inset: auto;
                top: 50%; left: 50%;
                transform: translate(-50%, -50%);
                width: 94%;
                gap: 4px;
            }
            .wc-tagline { font-size: 9px; }
        }
        /* ── Landscape orientation — cabin uses vh-based width since viewport is wide but short ── */
        @media (orientation: landscape) and (max-height: 500px) {
            .cabin-cross { width: min(50vh, 80vw, 700px); }
            .porthole-viewport { max-height: calc(100svh - 140px); }
            .weight-moment { padding: 2vh 10px 1vh; }
            .airplane-outline-wrap { width: clamp(300px, 70vw, 900px); }
        }
        @media (orientation: landscape) and (min-height: 501px) and (max-height: 700px) {
            .cabin-cross { width: min(58vh, 82vw, 700px); }
            .porthole-viewport { max-height: calc(100svh - 180px); }
            .weight-moment { padding: 4vh 10px 1vh; }
        }
        @supports (-webkit-touch-callout: none) {
            /* (iOS dvh overrides removed — the lvh stage sizing above is what
               keeps the pinned cabin covering the screen; see .weight-section) */
            .porthole-viewport { transform: translateZ(0); }
        }
        /* Fallback chips for mobile */
        .weight-chips-fallback { display: none; gap: 8px; justify-content: center; flex-wrap: wrap; margin-top: 20px; position: relative; z-index: 1; }
        .weight-chips-fallback .weight-chip { display: inline-flex; align-items: center; gap: 7px; padding: 6px 14px; border-radius: 100px; background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.08); font-size: 11px; font-weight: 600; color: rgba(255,255,255,.45); letter-spacing: 0.03em; backdrop-filter: blur(8px); opacity: 0; transition: border-color .3s, background .3s; }
        .weight-chips-fallback .weight-chip svg { width: 13px; height: 13px; opacity: .5; }
        .weight-chips { display: none; }
        .weight-spec-bar { display: flex; align-items: center; justify-content: center; gap: 20px; margin-top: 18px; position: relative; z-index: 1; }
        .weight-spec-bar span { font-size: 13px; font-weight: 600; color: rgba(255,255,255,.4); letter-spacing: 0.08em; text-transform: uppercase; }
        .weight-spec-bar .spec-dot { width: 5px; height: 5px; border-radius: 50%; background: rgba(255,255,255,.2); }
        /* ===== PANEL MICRO-INTERACTIONS ===== */
        .motor-card { transition: transform .4s cubic-bezier(.22,1,.36,1), box-shadow .5s, background .3s; background: rgba(255,255,255,.025); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); }
        .motor-card:hover { transform: translateY(-4px); box-shadow: 0 24px 80px rgba(0,0,0,.4); }
        .motor-card:active { transform: translateY(-2px); transition-duration: .15s; }
        /* Corner pulse dot */
        .motor-card::after { content:''; position:absolute; top:20px; right:20px; width:6px; height:6px; border-radius:50%; opacity:0; transition: opacity .4s; }
        .motor-card:hover::after { opacity:1; animation: cornerPulse 2s ease-in-out infinite; }
        @keyframes cornerPulse { 0%,100% { transform:scale(1); opacity:.6; } 50% { transform:scale(1.5); opacity:1; } }
        /* Single-accent discipline: all corner dots share the ice-blue accent */
        .card-green::after { background:#2997ff; box-shadow:0 0 8px rgba(41,151,255,.4); }
        .motor-card.card-major::after { background:#2997ff; box-shadow:0 0 8px rgba(41,151,255,.4); }
        .motor-card.card-minor::after { background:#2997ff; box-shadow:0 0 8px rgba(41,151,255,.4); }
        /* Card-specific hover accent glow */
        .card-green:hover { background: rgba(41,151,255,.03); }
        .card-green .card-svg svg { transition: transform .5s cubic-bezier(.22,1,.36,1); }
        .card-green:hover .card-svg svg { transform: scale(1.15) rotate(-8deg); }
        .motor-card.card-major:hover { background: rgba(41,151,255,.03); }
        .motor-card.card-minor:hover { background: rgba(41,151,255,.03); }
        /* Gradient mesh backgrounds */
        .card-green::before { content:''; position:absolute; inset:0; border-radius:28px; background: radial-gradient(ellipse at 80% 20%, rgba(41,151,255,.06) 0%, transparent 50%); opacity:0; transition:opacity .5s; pointer-events:none; }
        .card-green:hover::before { opacity:1; }
        .motor-card.card-major::before { content:''; position:absolute; inset:0; border-radius:28px; background: radial-gradient(ellipse at 20% 80%, rgba(41,151,255,.06) 0%, transparent 50%); opacity:0; transition:opacity .5s; pointer-events:none; }
        .motor-card.card-major:hover::before { opacity:1; }
        .motor-card.card-minor::before { content:''; position:absolute; inset:0; border-radius:28px; background: radial-gradient(ellipse at 80% 30%, rgba(41,151,255,.06) 0%, transparent 50%); opacity:0; transition:opacity .5s; pointer-events:none; }
        .motor-card.card-minor:hover::before { opacity:1; }
        /* Clock hand — continuous loop */
        .bento-clock-hand { transform-origin: 24px 24px; }
        @keyframes clockSweep { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
        .bento-clock-hand.is-spinning { animation: clockSweep 3s linear infinite; }
        /* Progress ring */
        .bento-ring-progress { stroke-dasharray: 126; stroke-dashoffset: 126; transition: stroke-dashoffset 1.8s cubic-bezier(.4,0,.2,1); }
        .bento-ring-progress.is-filled { stroke-dashoffset: 0; }
        /* 3-step process icons — always partially visible */
        .setup-steps { display: flex; gap: 16px; margin-top: 16px; opacity: .25; transform: translateY(4px); transition: opacity .4s cubic-bezier(.22,1,.36,1), transform .4s cubic-bezier(.22,1,.36,1); flex-wrap: wrap; }
        .card-green:hover .setup-steps { opacity: 1; transform: translateY(0); }
        @media (hover: none) { .setup-steps { opacity: .6; transform: translateY(0); } }
        .setup-step { display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600; color: rgba(255,255,255,.42); letter-spacing: 0.08em; text-transform: uppercase; }
        .setup-step svg { width: 16px; height: 16px; }
        .setup-step-arrow { color: rgba(255,255,255,.1); }
        /* Balls — continuous fire loop with motion blur */
        @keyframes ballFire { 0% { cx: 8; opacity: 0; } 15% { opacity: .9; } 85% { opacity: .3; } 100% { cx: 56; opacity: 0; } }
        .ball-loop { animation: ballFire 1s ease-out infinite; filter: url(#ballBlur); }
        .ball-loop:nth-child(2) { animation-delay: .33s; }
        .ball-loop:nth-child(3) { animation-delay: .66s; }
        /* Hover: speed up + glow */
        .motor-card.card-major:hover .ball-loop { animation-duration: .5s; }
        @media (max-width: 900px) {
            .pass-features { grid-template-columns: 1fr; }
        }
        @media (max-width: 768px) {
            .feature-value { font-size: 36px; }
        }
        /* FEATURES BENTO - Apple Style Varied Sizes */
        .features-bento {
            background: #000;
            padding: clamp(60px, 10vh, 100px) clamp(16px, 4vw, 24px);
        }
        .bento-wrap {
            max-width: 1000px;
            margin: 0 auto;
            display: grid;
            grid-template-columns: 2fr 1fr;
            grid-template-rows: auto auto;
            gap: 20px;
        }
        .bento-item {
            background: rgba(28,28,30,0.8);
            border: 1px solid rgba(255,255,255,0.08);
            border-radius: 24px;
            padding: 32px;
            transition: border-color 0.3s ease, box-shadow 0.3s ease;
        }
        .bento-item:hover {
            border-color: rgba(255,255,255,0.15);
            transform: translateY(-4px);
        }
        /* Tall Card - AI */
        .bento-item.ai {
            grid-row: span 2;
            display: flex;
            flex-direction: column;
            justify-content: center;
            text-align: center;
            background: linear-gradient(180deg, rgba(255,255,255,.05) 0%, rgba(255,255,255,.01) 100%);
            border-color: rgba(255,255,255,0.12);
        }
        .bento-item.lock {
            grid-row: span 2;
            display: flex;
            flex-direction: column;
            justify-content: center;
            text-align: center;
            background: linear-gradient(180deg, rgba(255,255,255,.05) 0%, rgba(255,255,255,.01) 100%);
            border-color: rgba(255,255,255,0.12);
        }
        .ai-visual {
            margin-bottom: 16px;
        }
        .lock-visual { margin-bottom: 14px; }
        .neural-svg .nn-node {
            filter: drop-shadow(0 0 4px currentColor);
        }
        .neural-svg .nn-lines line {
            animation: pulse-line 2s ease-in-out infinite;
        }
        @keyframes pulse-line {
            0%, 100% { opacity: 0.3; }
            50% { opacity: 0.7; }
        }
        .ai-stat {
            display: flex;
            align-items: baseline;
            justify-content: center;
            margin-bottom: 16px;
        }
        .ai-num {
            font-size: 56px;
            font-weight: 600;
            color: #f5f5f7;
            letter-spacing: -0.04em;
        }
        .ai-unit {
            font-size: 20px;
            font-weight: 500;
            color: rgba(255,255,255,.5);
            margin-left: 4px;
        }
        .lock-stat {
            display: flex;
            align-items: baseline;
            justify-content: center;
            margin-bottom: 14px;
            gap: 8px;
        }
        .lock-num {
            font-size: 40px;
            font-weight: 600;
            color: #f5f5f7;
            letter-spacing: -0.02em;
        }
        .lock-unit {
            font-size: 20px;
            font-weight: 500;
            color: rgba(255,255,255,.5);
        }
        .bento-item.ai h4 {
            font-size: 18px;
            font-weight: 600;
            color: #f5f5f7;
            margin-bottom: 8px;
        }
        .bento-item.ai p {
            font-size: 14px;
            color: #86868b;
        }
        .bento-item.lock h4 {
            font-size: 18px;
            font-weight: 600;
            color: #f5f5f7;
            margin-bottom: 8px;
        }
        .bento-item.lock p {
            font-size: 14px;
            color: #9bb3bd;
        }
        .bento-item.track {
            background: linear-gradient(180deg, rgba(255,255,255,.04) 0%, rgba(0,0,0,0.02) 100%);
            border-color: rgba(255,255,255,0.12);
            text-align: center;
        }
        .track-visual { margin-bottom: 14px; text-align: center; }
        .bento-item.track h4 {
            font-size: 18px;
            font-weight: 600;
            color: #f5f5f7;
            margin-bottom: 8px;
        }
        .bento-item.track p {
            font-size: 14px;
            color: #86868b;
        }
        /* Video Card */
        .bento-item.video {
            text-align: center;
            background: linear-gradient(180deg, rgba(255,255,255,.05) 0%, rgba(255,255,255,.01) 100%);
            border-color: rgba(255,255,255,0.12);
        }
        .video-badge {
            font-size: 48px;
            font-weight: 600;
            color: #f5f5f7;
            margin-bottom: 12px;
        }
        .video-badge span {
            font-size: 24px;
            opacity: 0.7;
        }
        .bento-item.video h4 {
            font-size: 16px;
            font-weight: 600;
            color: #f5f5f7;
            margin-bottom: 6px;
        }
        .bento-item.video p {
            font-size: 14px;
            color: #86868b;
        }
        /* Safety Card */
        .bento-item.safety {
            text-align: center;
            background: linear-gradient(180deg, rgba(255,255,255,.05) 0%, rgba(255,255,255,.01) 100%);
            border-color: rgba(255,255,255,0.12);
        }
        .safety-stat {
            font-size: 48px;
            font-weight: 600;
            color: #f5f5f7;
            margin-bottom: 12px;
        }
        .safety-stat span {
            font-size: 24px;
            opacity: 0.7;
        }
        .bento-item.safety h4 {
            font-size: 16px;
            font-weight: 600;
            color: #f5f5f7;
            margin-bottom: 6px;
        }
        .bento-item.safety p {
            font-size: 14px;
            color: #86868b;
        }
        @media (max-width: 768px) {
            .bento-wrap { grid-template-columns: 1fr; }
            .bento-item.ai,
            .bento-item.lock { grid-row: span 1; }
        }
        /* ═══════════════════════════════════════════════════════════════════════
           PRODUCT STAGE — the chapter is a room, not a card.

           The widget this replaces was a 500px-tall well inside a 1200px
           column, with tabs above it and a "drag to rotate" label below. Every
           one of those said "here is a 3D viewer". None of them said "here is
           the machine". Full bleed, pinned, one camera move, and the copy
           floating over the room instead of stacked above it.
           ═══════════════════════════════════════════════════════════════════════ */
        .stage-section {
            position: relative;
            background: #000;
            padding: clamp(56px, 9vh, 104px) clamp(16px, 4vw, 40px)
                     clamp(44px, 7vh, 80px);
            /* One token for both tabs. They are two views of one surface and
               they are 60 lines apart, so the only reliable way to stop them
               drifting into a flash on tab switch is to leave nothing to keep
               in sync. Reasoned about on .stage-pin, where it shows. */
            /* Neutral, and the measurement is why. The page is rgb(0,0,0)
               on all four sides of this frame; the old hot stop was #12203a,
               which put the bottom of the panel at blue-minus-red +25 and a
               luma near 90 against that black — a bright blue rectangle stuck
               onto the page rather than a part of it. DJI's equivalent runs
               rgb(20,20,20) with a flatter, darker panel inside it and no
               colour anywhere except the product's own accents.
               Same geometry, same bottom anchor, greys instead of blues, and
               the top of the ramp is now nearer the page than the old midpoint
               was. */
            --stage-bg: radial-gradient(76% 58% at 50% 102%,
                #17171a 0%, #101012 30%, #08080a 60%, #000 82%);
        }
        /* ── Two tabs over one frame ──
           Small, quiet, and underlined on the active one: this is a view
           switcher, not a call to action, and the machine below it is the thing
           worth looking at. */
        /* Centred, and directly above the tabs so the three parts read as one
           stack. Sized off .scene-title rather than the film chapters': this is
           a section header on a black page, not type over a photograph. */
        .stage-header {
            max-width: 1180px;
            margin: 0 auto clamp(20px, 3vh, 34px);
            padding: 0 clamp(16px, 4vw, 40px);
            text-align: center;
        }
        .stage-eyebrow {
            font-family: var(--font-mono, ui-monospace, 'SF Mono', Menlo, Consolas, monospace);
            font-size: 11px; font-weight: 600; letter-spacing: .2em;
            text-transform: uppercase;
            color: rgba(255,255,255,.45);
            margin: 0 0 14px;
        }
        .stage-head {
            font-size: clamp(28px, 4.4vw, 56px);
            font-weight: 700; letter-spacing: -.035em; line-height: 1.1;
            color: #f5f5f7; margin: 0;
            text-wrap: pretty;
            text-wrap: balance;
        }
        :lang(zh-CN) .stage-head, :lang(zh-TW) .stage-head,
        :lang(ja) .stage-head, :lang(ko) .stage-head {
            letter-spacing: 0;
            word-break: keep-all;
        }
        /* The translations mark the accent with <em> and the markup's own
           fallback uses .txt-grad-warm, so without this the eight locales get
           italics where English gets the gradient — and a synthetic oblique on
           a CJK headline is the worse of the two. Scoped to this headline
           rather than fixed globally: the same mismatch exists on other
           sections and changing all of them is not this section's business. */
        .stage-head em {
            font-style: normal;
            background: linear-gradient(135deg, #ffb340 0%, #ff5f6d 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }
        .stage-tabs {
            display: flex; justify-content: center; gap: clamp(18px, 3vw, 34px);
            margin: 0 auto clamp(18px, 2.6vh, 28px);
        }
        .stage-tab {
            appearance: none; background: none; border: 0; cursor: pointer;
            padding: 6px 2px 9px;
            font-family: inherit; font-size: 13px; font-weight: 600;
            letter-spacing: .02em;
            color: rgba(255,255,255,.42);
            border-bottom: 1.5px solid transparent;
            transition: color .25s ease, border-color .25s ease;
        }
        .stage-tab:hover { color: rgba(255,255,255,.72); }
        .stage-tab.is-on { color: #f5f5f7; border-bottom-color: rgba(255,255,255,.85); }
        .stage-tab:focus-visible { outline: 2px solid rgba(140,180,235,.8); outline-offset: 3px; }
        /* The AR panel stands in the same box as the frame, so switching tabs
           does not move anything below it -- and on the same background, so
           switching them does not flash either. There is no 3D floor on this
           side, which is the case that decided how dark the shared token could
           go: everything holding this panel up is in the token. */
        .stage-ar {
            position: relative;
            max-width: 1180px; margin: 0 auto;
            height: min(94svh, 1040px); min-height: 560px;
            border-radius: clamp(14px, 1.6vw, 22px);
            border: 1px solid rgba(255,255,255,.06);
            background: var(--stage-bg, #000);
            display: grid; place-items: center;
            padding: clamp(20px, 4vw, 48px);
        }
        .stage-ar[hidden] { display: none; }
        .stage-ar-inner { text-align: center; max-width: 420px; }
        /* White on the dark panel, because a QR has to be read by a camera
           pointed at a screen and the panel behind it is nearly black. The
           padding is the quiet zone the format requires. */
        .stage-ar-qr {
            display: block; margin: 0 auto clamp(18px, 3vh, 26px);
            width: clamp(140px, 22vw, 190px); height: auto;
            padding: 12px; border-radius: 12px;
            background: #f5f5f7;
        }
        .stage-ar-hint {
            margin: 0; font-size: clamp(13px, 1.2vw, 15px); line-height: 1.6;
            color: rgba(255,255,255,.62);
        }
        .stage-ar-btn {
            display: none; align-items: center; justify-content: center; gap: 9px;
            margin: 0 auto clamp(14px, 2vh, 20px);
            min-height: 50px; padding: 0 26px;
            border-radius: 980px; text-decoration: none;
            font-size: 15px; font-weight: 600; color: #06080c;
            background: linear-gradient(180deg, #f7f9fc 0%, #d9e2ee 100%);
            box-shadow: 0 10px 30px rgba(0,0,0,.4);
        }
        .stage-ar-btn[hidden] { display: none; }
        .stage-ar.is-native .stage-ar-btn { display: inline-flex; }
        .stage-ar.is-native .stage-ar-qr { display: none; }
        .stage-ar-note {
            margin: clamp(12px, 2vh, 18px) 0 0;
            font-family: var(--font-mono, ui-monospace, 'SF Mono', Menlo, Consolas, monospace);
            font-size: 10px; letter-spacing: .16em; text-transform: uppercase;
            color: rgba(255,255,255,.32);
        }
        @media (max-width: 900px) {
            .stage-ar { height: auto; min-height: 320px; }
        }
        /* The frame is the whole point of the boxed layout: page on both sides of
           the canvas is what lets the canvas own every gesture inside it, because
           a finger or a wheel landing here cannot have been meant for the
           document. 1180 rather than full bleed for the same reason a product
           photograph has a border -- the machine reads as an object on a stand
           rather than as the background of the page. */
        .stage-pin {
            position: relative;
            max-width: 1180px;
            margin: 0 auto;
            /* Tall enough to stand a machine in AND to swing the camera up
               and down it. 74svh/720 was the first number and it was too short:
               as a pinned chapter this canvas was 1322 x 1246, and going bounded
               took 47% of the height off it. Yaw only turns the silhouette;
               elevation moves it up and down the frame, and that range runs 0.16
               to 2.30, so height is the axis the drag actually spends.
               Bounded still, which is the point of the box -- page on both sides
               is what lets the canvas take every gesture inside it. */
            height: min(94svh, 1040px);
            min-height: 420px;
            border-radius: clamp(14px, 1.6vw, 22px);
            border: 1px solid rgba(255,255,255,.06);
            overflow: hidden;
            /* Black at the perimeter, a pool of light under the machine.
               The studio sweep this replaces argued that pure black gave the
               machine nothing to stand in, and that a dark contact shadow on a
               black floor is invisible by definition. That was true when it was
               written and stopped being true when the floor and the contact
               shadow moved into the 3D scene: the surface is geometry now, the
               canvas draws it, and it owes this rule nothing. What the sweep
               was still doing was leaking fill through a floor that is only
               ever half opaque — taking it off RAISED the ground's contrast,
               darkest to brightest tenth going 27 → 34 at rest and 45 → 56
               from a low camera, because a uniform lift behind a translucent
               floor is fill light and fill is what flattens a shadow.
               What it cost was the frame. Its border sat 26 luma above a #000
               page, which is how a background became a widget on the day this
               stage stopped being full bleed; it is 3.5 now. The pool that is
               left is centred below the bottom edge rather than swept up the
               whole panel, because that is where a floor is, and because the
               still render — everyone who never taps, everyone without WebGL —
               has no canvas under it and on flat black is a cut-out on a void. */
            background: var(--stage-bg, #000);
        }
        .stage-pin canvas {
            position: absolute; inset: 0;
            width: 100%; height: 100%;
            display: block;
            z-index: 1;
            /* `none`, which the full-bleed version could not afford: it was
               the whole viewport, so the browser had to keep the vertical
               axis or a phone had no way out of the section. Inside a frame
               with page above and below it, a finger that lands here is
               unambiguously reaching for the machine. */
            touch-action: none;
            -webkit-user-select: none; user-select: none;
        }
        .stage-pin.is-live canvas { cursor: grab; }
        .stage-pin.is-live canvas:active { cursor: grabbing; }
        /* The still and the canvas occupy the same frame at the same scale, so
           the hand-off when the model finishes decoding is a cross-fade rather
           than a jump. */
        /* One length for both, because the floor below is placed off the
           still's footprint and a floor that does not track the image it is
           under is worse than none. */
        .stage-pin { --still-w: min(76vw, 560px); }
        .stage-still {
            position: absolute; left: 50%; top: 50%;
            transform: translate(-50%, -52%);
            width: var(--still-w); height: auto;
            opacity: .92;
            z-index: 1;
            transition: opacity .7s cubic-bezier(.4,0,.2,1);
            pointer-events: none;
        }
        .stage-pin.is-live .stage-still { opacity: 0; }
        /* A floor for the poster, and for the poster only.
           Lifting .stage-pool to do this would put back exactly the fill light
           the black background was chosen to remove, because .stage-pool sits
           behind the canvas at all times. This cannot: before the model lands
           there is no renderer and the CSS is the entire room, and .is-live
           takes it away on the same 0.7s curve the still itself leaves on. The
           live scene is out of reach by construction rather than by tuning.
           Two layers, because one will not do it. A dark ellipse on a black
           floor is not a shadow -- it needs lit ground underneath before it can
           read as anything -- so the lower gradient is the lit patch and the
           upper one is the contact punched into it. Both are placed off the
           still's own width and off the footprint measured out of its alpha
           channel (tests/_stage_poster_geom.py): the machine touches down 0.446
           of that width below the frame's centre, the middle of its two feet is
           0.104 to the left of the image's centre, and the 8 degrees is the
           wheel hanging forward of the base plate. Placed by eye instead, it
           twice ended up under the wrong half of a machine that is widest at
           the head unit and narrowest where it meets the ground.
           Brightness is not taste either: the poster cross-fades into the live
           scene, so the two floors are seen one after the other. This one is
           set so the strip of ground in front of the machine lands near the
           live scene's -- 65 against 85 -- which is close enough that the
           hand-off reads as the lights coming up rather than changing. */
        .stage-pin::after {
            content: '';
            position: absolute; left: 50%; top: 50%;
            width: calc(var(--still-w) * 1.85);
            height: calc(var(--still-w) * .68);
            transform: translate(calc(-50% - var(--still-w) * .104),
                                 calc(-50% + var(--still-w) * .446))
                       rotate(-8deg);
            z-index: 0;
            background:
                radial-gradient(ellipse 26% 19% at 50% 50%,
                    rgba(0,0,0,.95) 0%, rgba(0,0,0,.62) 46%, rgba(0,0,0,0) 78%),
                radial-gradient(ellipse 50% 50% at 50% 46%,
                    rgba(154,184,230,.98) 0%, rgba(104,134,190,.58) 40%,
                    rgba(54,72,112,.17) 68%, rgba(0,0,0,0) 86%);
            transition: opacity .7s cubic-bezier(.4,0,.2,1);
            pointer-events: none;
        }
        .stage-pin.is-live::after { opacity: 0; }
        /* The floor moved into the 3D scene, where it belongs. A gradient behind
           the canvas can never be stood on and can never be occluded, which is
           precisely why the machine read as a cut-out pasted onto a slide. What
           stays here is only the far ambience of the room — well below the
           subject, and faint enough that the real lit floor in front of it does
           all of the work. */
        .stage-pool {
            position: absolute; left: 50%; bottom: 0;
            width: min(130vw, 1500px); height: 44vh;
            transform: translateX(-50%);
            z-index: 0;
            background: radial-gradient(ellipse 68% 58% at 50% 100%,
                rgba(56,78,124,.17) 0%, rgba(30,44,76,.06) 48%, transparent 80%);
            pointer-events: none;
        }
        .stage-mark {
            position: absolute;
            /* An inset from the frame's own corner. It used to be pushed
               clear of the fixed nav, which no longer crosses it. */
            top: clamp(14px, 2.4vw, 26px);
            right: clamp(16px, 2.4vw, 30px);
            z-index: 3;
            font-family: var(--font-mono, ui-monospace, 'SF Mono', Menlo, Consolas, monospace);
            font-size: 10px; letter-spacing: .34em;
            color: rgba(255,255,255,.3);
            pointer-events: none;
        }
        /* DJI's left rail: a short invitation, then a vertical list of states
           with a white bar on the one that is on. The machine keeps the rest
           of the panel. pointer-events stay off the rail so a drag that
           starts over the copy still turns the model; only the buttons take
           a click. */
        .stage-aside {
            position: absolute;
            left: clamp(28px, 3.6vw, 56px);
            top: 50%;
            transform: translateY(-50%);
            z-index: 3;
            width: min(268px, 30%);
            pointer-events: none;
            opacity: 1;
            transition: opacity .5s ease;
        }
        .stage-aside::before {
            content: "";
            position: absolute;
            inset: -48px -80px -48px -40px;
            background: linear-gradient(90deg, rgba(0,0,0,.62) 0%,
                rgba(0,0,0,.28) 52%, transparent 100%);
            pointer-events: none;
            z-index: -1;
        }
        .stage-look {
            margin: 0 0 8px;
            font-size: clamp(22px, 2.2vw, 32px);
            font-weight: 700;
            letter-spacing: -.03em;
            line-height: 1.18;
            color: #f5f5f7;
        }
        :lang(zh-CN) .stage-look, :lang(zh-TW) .stage-look,
        :lang(ja) .stage-look, :lang(ko) .stage-look {
            letter-spacing: 0;
            font-weight: 600;
        }
        .stage-look-hint {
            margin: 0 0 28px;
            font-size: 13px;
            font-weight: 400;
            letter-spacing: .01em;
            color: rgba(255,255,255,.42);
        }
        .stage-states {
            display: flex;
            flex-direction: column;
            gap: 2px;
        }
        .stage-state {
            appearance: none;
            background: none;
            border: 0;
            border-left: 2px solid rgba(255,255,255,.14);
            padding: 9px 4px 9px 15px;
            margin: 0;
            text-align: left;
            cursor: pointer;
            pointer-events: auto;
            font: inherit;
            font-size: 15px;
            letter-spacing: -.01em;
            color: rgba(255,255,255,.40);
            transition: border-color .2s ease, color .2s ease;
        }
        :lang(zh-CN) .stage-state, :lang(zh-TW) .stage-state,
        :lang(ja) .stage-state, :lang(ko) .stage-state { letter-spacing: 0; }
        .stage-state:focus-visible { outline: 2px solid rgba(255,255,255,.7); outline-offset: 3px; }
        .stage-state:hover { color: rgba(255,255,255,.74); }
        .stage-state.is-on { border-left-color: #fff; color: #f5f5f7; }
        /* One line about the view that is showing. It replaces three blocks of
           the same copy stacked permanently on top of each other: only the
           claim you are looking at is on screen, which is what made DJI's
           panel read as one object rather than a page of bullet points. */
        .stage-caption {
            margin: 26px 0 0;
            max-width: 250px;
            transition: opacity .22s ease;
        }
        .stage-caption.is-swapping { opacity: 0; }
        .stage-cap-num {
            display: block;
            font-family: var(--font-mono, ui-monospace, 'SF Mono', Menlo, Consolas, monospace);
            font-size: 11px;
            letter-spacing: .18em;
            text-transform: uppercase;
            color: rgba(255,255,255,.44);
            margin-bottom: 7px;
        }
        .stage-cap-line {
            display: block;
            font-size: 14px;
            line-height: 1.35;
            color: rgba(255,255,255,.62);
            text-wrap: balance;
        }
        :lang(zh-CN) .stage-cap-line, :lang(zh-TW) .stage-cap-line,
        :lang(ja) .stage-cap-line, :lang(ko) .stage-cap-line { letter-spacing: 0; }
        .stage-zoom {
            position: absolute;
            right: clamp(14px, 3vw, 34px);
            top: 50%; transform: translateY(-50%);
            z-index: 4;
            display: flex; flex-direction: column; gap: 8px;
            opacity: 0; pointer-events: none;
            transition: opacity .5s ease;
        }
        .stage-pin.is-live .stage-zoom {
            opacity: 1; pointer-events: auto; transition-delay: .5s;
        }
        .stage-zoom-btn {
            width: 34px; height: 34px; padding: 0;
            display: inline-flex; align-items: center; justify-content: center;
            border: 1px solid rgba(255,255,255,.12);
            border-radius: 50%;
            background: rgba(12,18,28,.5);
            -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
            color: rgba(255,255,255,.66);
            cursor: pointer;
            transition: background .25s ease, color .25s ease,
                        border-color .25s ease, transform .18s ease;
        }
        .stage-zoom-btn:hover {
            background: rgba(22,32,48,.72); color: #fff;
            border-color: rgba(255,255,255,.22);
        }
        .stage-zoom-btn:active { transform: scale(.94); }
        .stage-zoom-btn svg { width: 15px; height: 15px; }
        /* ── The tap gate ──
           A full-screen WebGL chapter that arms itself on approach spends a
           renderer, a 2 MB model and a GPU allocation on everyone who merely
           scrolls past — including the memory-tight phones this page has to
           survive on. The resting frame is the same still the canvas
           cross-fades out of, so taking the invitation changes what the
           chapter DOES, not what it looks like. */
        .stage-gate {
            position: absolute; inset: 0; z-index: 4;
            display: flex; flex-direction: column;
            align-items: center; justify-content: flex-end;
            gap: 12px;
            padding-bottom: clamp(56px, 13vh, 132px);
            transition: opacity .5s ease, transform .5s cubic-bezier(.16,1,.3,1);
        }
        .stage-gate.is-leaving { opacity: 0; transform: scale(1.03); pointer-events: none; }
        .stage-gate-btn {
            display: inline-flex; align-items: center; gap: 9px;
            min-height: 46px; padding: 0 24px;
            border: none; border-radius: 980px; cursor: pointer;
            background: var(--blue-primary, #0071e3); color: #fff;
            font-family: inherit; font-size: 15px; font-weight: 500; letter-spacing: -.01em;
            box-shadow: 0 10px 30px rgba(0,113,227,.35);
            transition: background .25s ease, transform .25s cubic-bezier(.16,1,.3,1);
        }
        .stage-gate-btn:hover { background: #0077ed; transform: translateY(-1px); }
        .stage-gate-btn:active { transform: translateY(0) scale(.98); }
        .stage-gate-btn svg { width: 19px; height: 19px; flex: none; }
        /* Tapped, but the loader stack has not landed yet. The tap is already
           banked — this only has to say so. */
        .stage-gate-btn.is-loading { pointer-events: none; background: #0a5cb8; }
        .stage-gate-btn.is-loading svg { animation: stage-gate-spin 1.1s linear infinite; }
        @keyframes stage-gate-spin { to { transform: rotate(360deg); } }
        @media (prefers-reduced-motion: reduce) {
            .stage-gate-btn.is-loading svg { animation: none; opacity: .6; }
        }
        .stage-gate-note {
            font-family: var(--font-mono, ui-monospace, 'SF Mono', Menlo, Consolas, monospace);
            font-size: 10px; letter-spacing: .14em; text-transform: uppercase;
            color: rgba(255,255,255,.38);
        }
        /* Until the invitation is taken the frame is a poster. The left rail
           and the zoom pair have nothing to drive yet, and on a phone they
           would sit on top of the gate button. */
        .stage-pin.is-gated .stage-zoom,
        .stage-pin.is-gated .stage-aside { opacity: 0; }
        @media (max-width: 900px) {
            .stage-pin { --still-w: min(84vw, 420px); }
            .stage-aside {
                left: clamp(14px, 4vw, 22px);
                right: clamp(14px, 4vw, 22px);
                top: auto;
                bottom: clamp(14px, 3vw, 22px);
                transform: none;
                width: auto;
            }
            .stage-aside::before {
                inset: -28px -20px -20px -20px;
                background: linear-gradient(0deg, rgba(0,0,0,.72) 0%,
                    rgba(0,0,0,.28) 70%, transparent 100%);
            }
            .stage-look { font-size: 20px; }
            .stage-look-hint { margin-bottom: 14px; font-size: 12px; }
            .stage-states { flex-direction: row; gap: 10px; }
            .stage-state {
                flex: 1;
                border-left: 0;
                border-bottom: 2px solid rgba(255,255,255,.14);
                padding: 7px 2px 9px;
                font-size: 13px;
                text-align: center;
            }
            .stage-state.is-on { border-left-color: transparent; border-bottom-color: #fff; }
            .stage-caption { margin-top: 14px; max-width: none; }
            .stage-cap-num { font-size: 9px; letter-spacing: .12em; margin-bottom: 3px; }
            .stage-cap-line { font-size: 12px; line-height: 1.3; }
            /* The floor is sized off the still, and here the still is nearly as
               wide as the frame, so the desktop multiplier puts the pool's
               transparent stop outside the border and lights the frame's edge
               against the page -- 11.5 luma above it rather than 5.2. This
               lands that stop just inside the border instead. */
            .stage-pin::after {
                width: calc(var(--still-w) * 1.25);
                height: calc(var(--still-w) * .46);
            }
            .stage-pool { bottom: 22%; height: 16vh; }
            .stage-zoom-btn { width: 38px; height: 38px; }
        }
        /* The pool is a large soft gradient — cheap, but it is also the only
           thing here a weak GPU has to composite behind the canvas. */
        :root[data-rb-tier="low"] .stage-pool { opacity: .55; }
        /* SPECS STRIP - Compact Inline Row */
        .specs-strip {
            display: flex;
            justify-content: center;
            align-items: center;
            gap: clamp(20px, 4vw, 40px);
            padding: clamp(24px, 3vh, 32px) clamp(16px, 4vw, 24px);
            background: linear-gradient(180deg, rgba(255,255,255,0.02) 0%, #000 100%);
            border-top: 1px solid rgba(255,255,255,0.06);
            border-bottom: 1px solid rgba(255,255,255,0.06);
            flex-wrap: wrap;
            position: relative;
        }
        .specs-strip::before {
            content: '';
            position: absolute;
            inset: 0;
            background: radial-gradient(ellipse at 50% 50%, rgba(0,113,227,0.04), transparent 70%);
            pointer-events: none;
        }
        /* MODULAR TAG - Simple */
        .modular-tag {
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 10px;
            padding: clamp(28px, 4vh, 40px) clamp(16px, 4vw, 24px);
            background: #000;
        }
        .modular-tag .tag-icon { opacity: 0.8; }
        .modular-tag span:last-child {
            font-size: clamp(14px, 1.8vw, 16px);
            color: #86868b;
            font-weight: 500;
        }
        /* PRIVACY & SAFETY - DJI Bento Style */
        .privacy-section { background: linear-gradient(180deg, #000 0%, #020406 100%); padding: clamp(28px, 5vh, 44px) clamp(16px, 4vw, 24px) clamp(60px, 10vh, 100px); content-visibility: auto; contain-intrinsic-size: auto 600px; border-top: 0 !important; }
        .privacy-content { max-width: 1200px; margin: 0 auto; }
        .privacy-header { text-align: center; margin: 0 auto clamp(28px, 4vh, 36px); max-width: 760px; }
        .privacy-header h2 { font-size: clamp(36px, 5.5vw, 60px); font-weight: 600; margin-bottom: 12px; letter-spacing: -0.045em; background: linear-gradient(180deg, #fff 30%, rgba(255,255,255,.72) 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
        .privacy-header p { font-size: clamp(14px, 2vw, 17px); color: rgba(255,255,255,.56); max-width: 640px; margin: 0 auto; line-height: 1.6; }
        .privacy-bento {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: clamp(8px, 1.5vw, 12px);
        }
        .priv-card {
            background: rgba(255,255,255,.02);
            border: 1px solid rgba(255,255,255,.06);
            border-radius: 24px;
            padding: clamp(24px, 3vw, 32px);
            transition: transform 0.3s var(--ease-smooth), border-color 0.3s var(--ease-smooth), box-shadow 0.3s var(--ease-smooth);
            position: relative;
            overflow: hidden;
        }
        .priv-card:hover {
            background: rgba(255,255,255,.04);
            border-color: rgba(255,255,255,.15);
        }
        /* Large AI Card */
        .priv-card.large {
            grid-column: span 2;
            display: flex;
            flex-direction: row;
            align-items: center;
            gap: clamp(20px, 3vw, 32px);
            justify-content: space-between;
            background: linear-gradient(135deg, rgba(255,255,255,.05) 0%, rgba(255,255,255,.02) 100%);
            border-color: rgba(255,255,255,0.12);
        }
        .priv-card.large:hover { border-color: rgba(255,255,255,0.22); }
        .priv-visual { text-align: center; margin-bottom: 24px; }
        .priv-visual svg { animation: aiPulse 4s ease-in-out infinite; }
        .neural-visual svg { animation: none; }
        .neural-svg .nn-node { animation: nodeGlow 2s ease-in-out infinite; }
        .neural-svg .nn-node.n1 { animation-delay: 0s; }
        .neural-svg .nn-node.n2 { animation-delay: 0.1s; }
        .neural-svg .nn-node.n3 { animation-delay: 0.2s; }
        .neural-svg .nn-node.n4 { animation-delay: 0.3s; }
        .neural-svg .nn-node.n5 { animation-delay: 0.4s; }
        .neural-svg .nn-connections { animation: connPulse 1.5s ease-in-out infinite; }
        @keyframes nodeGlow {
            0%, 100% { filter: brightness(1); }
            50% { filter: brightness(1.4) drop-shadow(0 0 4px currentColor); }
        }
        @keyframes connPulse {
            0%, 100% { opacity: 0.3; }
            50% { opacity: 0.7; }
        }
        @keyframes aiPulse { 0%, 100% { transform: scale(1); opacity: 0.9; } 50% { transform: scale(1.05); opacity: 1; } }
        .priv-stat {
            font-size: 64px;
            font-weight: 600;
            letter-spacing: -0.03em;
            background: linear-gradient(180deg, #ffffff 0%, rgba(255,255,255,.44) 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
            display: block;
            margin-bottom: 8px;
        }
        .priv-stat .stat-sm { font-size: 28px; }
        .priv-content h4 { font-size: 20px; font-weight: 600; color: #f5f5f7; margin-bottom: 8px; }
        .priv-content p { font-size: 15px; color: #86868b; line-height: 1.5; }
        /* Standard Cards */
        .priv-icon { width: 56px; height: 56px; margin-bottom: 20px; }
        .priv-icon svg { width: 100%; height: 100%; }
        .priv-card h4 { font-size: 17px; font-weight: 600; color: #f5f5f7; margin-bottom: 10px; }
        .priv-card p { font-size: 14px; color: #6e6e73; line-height: 1.5; }
        /* Encrypt Card */
        .encrypt-card { background: linear-gradient(135deg, rgba(255,255,255,.05) 0%, rgba(255,255,255,.02) 100%); border-color: rgba(255,255,255,0.12); }
        .encrypt-card:hover { border-color: rgba(255,255,255,0.22); }
        /* Delete Card */
        .delete-card { background: linear-gradient(135deg, rgba(255,255,255,.05) 0%, rgba(255,255,255,.02) 100%); border-color: rgba(255,255,255,0.12); }
        .delete-card:hover { border-color: rgba(255,255,255,0.22); }
        /* Safety Stop Card */
        .safety-stop-card {
            background: linear-gradient(135deg, rgba(255,255,255,.05) 0%, rgba(255,255,255,.02) 100%);
            border-color: rgba(255,255,255,0.12);
            text-align: center;
        }
        .safety-stop-card:hover { border-color: rgba(255,255,255,0.22); }
        .safety-stop-card .priv-icon { margin-bottom: 16px; }
        .safety-stop-card .safety-icon svg { width: 60px; height: 60px; }
        .safety-stat-inline {
            font-size: 48px;
            font-weight: 600;
            color: #f5f5f7;
            margin-bottom: 12px;
        }
        .safety-stat-inline span {
            font-size: 24px;
            opacity: 0.8;
        }
        /* Wide Child Card */
        .priv-card.wide {
            grid-column: span 2;
            display: flex;
            align-items: center;
            gap: 24px;
            background: linear-gradient(135deg, rgba(255,255,255,.05) 0%, rgba(255,255,255,.02) 100%);
            border-color: rgba(255,255,255,0.12);
        }
        .priv-card.wide:hover { border-color: rgba(255,255,255,0.22); }
        .priv-card.wide .priv-icon { margin-bottom: 0; flex-shrink: 0; }
        .child-content h4 { margin-bottom: 6px; }
        @media (max-width: 768px) {
            .privacy-bento { grid-template-columns: 1fr; gap: 10px; }
            .priv-card.large, .priv-card.wide { grid-column: span 1; flex-direction: column; text-align: center; }
            .privacy-section { padding: 48px 16px; }
        }
        /* ===== FAQ ACCORDION ===== */
        /* NOTE: no content-visibility here — the 16-item accordion is ~3× the old
           500px placeholder, and the late expand shoved the CTA section around
           mid-scroll (read as a giant gap before the Stripe form). */
        .faq-section { padding: clamp(60px, 10vh, 100px) clamp(16px, 4vw, 24px); background: #000; position: relative; overflow: hidden; }
        @media (max-width:768px) { .faq-section { padding: clamp(32px, 5vh, 48px) 16px clamp(32px, 5vh, 48px); } }
        .faq-section > video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0.05; filter: brightness(0.4); z-index: 0; pointer-events: none; }
        .faq-container { max-width: 680px; margin: 0 auto; position: relative; z-index: 1; }
        .faq-header { text-align: center; margin-bottom: clamp(36px, 5vh, 52px); }
        .faq-eyebrow {
            display: inline-block; font-size: 11px; font-weight: 600;
            color: var(--blue-light); letter-spacing: .08em; text-transform: uppercase;
            margin-bottom: 16px;
        }
        .faq-header h2 { font-size: clamp(36px, 6vw, 56px); font-weight: 600; letter-spacing: -0.045em; background: linear-gradient(180deg, #fff 30%, rgba(255,255,255,.65) 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
        .faq-sub {
            margin: 14px auto 0; max-width: 42ch;
            font-size: 15px; line-height: 1.6; color: rgba(255,255,255,.45);
        }
        /* Items read as quiet glass rows: the open one lifts out of the list
           with its own surface + accent rail — the answer is a focused card,
           not just more lines of text. */
        .faq-item {
            border-bottom: 1px solid rgba(255,255,255,0.06);
            border-radius: 12px;
            margin: 0 -14px;
            padding: 0 14px;
            transition: background .35s ease, border-color .35s ease, box-shadow .35s ease;
        }
        .faq-item.open {
            background: linear-gradient(180deg, rgba(255,255,255,.035), rgba(255,255,255,.015));
            border-bottom-color: transparent;
            box-shadow: inset 2px 0 0 var(--blue-primary), 0 10px 30px rgba(0,0,0,.25);
        }
        .faq-q { width: 100%; background: none; border: none; color: var(--white); font-size: clamp(15px, 1.2vw, 17px); font-weight: 500; padding: 20px 0; text-align: left; cursor: pointer; display: flex; justify-content: space-between; align-items: center; gap: 16px; font-family: inherit; transition: color 0.2s; }
        .faq-q:hover { color: var(--blue-light); }
        .faq-q::after { content: '+'; font-size: 20px; font-weight: 300; color: var(--gray-500); transition: transform .35s cubic-bezier(.4,0,.2,1), color .2s ease; display: inline-block; flex: none; }
        .faq-item.open .faq-q::after { transform: rotate(45deg); color: var(--white); }
        .faq-a { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .42s cubic-bezier(.4,0,.2,1); }
        .faq-a > p { overflow: hidden; font-size: 15px; color: var(--gray-400); line-height: 1.65; margin: 0; max-width: 58ch; }
        .faq-item.open .faq-a { grid-template-rows: 1fr; }
        .faq-item.open .faq-a > p { padding-bottom: 20px; }
        /* ===== STORY HOOK — After Hero (Apple/DJI design language) ===== */
        .story-hook-section { padding: clamp(72px, 11vh, 108px) clamp(16px, 4vw, 24px); background: #000; }
        .story-hook-container { max-width: 880px; margin: 0 auto; text-align: center; }
        .story-hook-tag {
            display: inline-block;
            font-size: 11px; font-weight: 600;
            color: var(--blue-light);
            letter-spacing: 0.15em; text-transform: uppercase;
            margin-bottom: 20px;
        }
        .story-hook-headline {
            font-size: clamp(24px, 4vw, 36px); font-weight: 700;
            letter-spacing: -0.03em; line-height: 1.15;
            color: #fff;
            margin-bottom: clamp(36px, 5vh, 52px);
        }
        .story-hook-headline em {
            font-style: normal;
            background: linear-gradient(135deg, var(--blue-primary), var(--blue-light));
            -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
        }
        .story-hook-grid {
            display: grid; grid-template-columns: repeat(3, 1fr);
            gap: 1px; margin-bottom: 0;
            background: rgba(255,255,255,.06);
            border: 1px solid rgba(255,255,255,.06);
            border-radius: 16px; overflow: hidden;
        }
        .story-hook-item {
            background: var(--black-rich);
            padding: clamp(28px, 4vw, 44px) clamp(20px, 3vw, 32px);
            display: flex; flex-direction: column; align-items: center; gap: 6px;
            transition: background 0.3s ease;
        }
        .story-hook-item:hover { background: rgba(255,255,255,.02); }
        .story-hook-item .sh-num {
            font-size: clamp(38px, 6vw, 56px); font-weight: 900;
            letter-spacing: -0.04em; line-height: 1;
            background: linear-gradient(135deg, var(--blue-primary), var(--blue-light));
            -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
            margin-bottom: 4px;
        }
        .story-hook-item .sh-label { font-size: 13px; font-weight: 600; color: rgba(255,255,255,.55); letter-spacing: -0.01em; }
        .story-hook-item .sh-desc { font-size: 12px; color: rgba(255,255,255,.55); line-height: 1.5; margin-top: 4px; }
        .story-hook-quote {
            font-size: clamp(15px, 2vw, 17px); color: rgba(255,255,255,.6);
            margin-top: clamp(28px, 4vh, 40px);
            line-height: 1.6; max-width: 520px; margin-left: auto; margin-right: auto;
        }
        /* Latin keeps elegant italic; CJK falls back to upright (faux-italic looks bad) */
        html[lang="en"] .story-hook-quote,
        html[lang="de"] .story-hook-quote,
        html[lang="fr"] .story-hook-quote,
        html[lang="es"] .story-hook-quote { font-style: italic; }
        .story-hook-quote::before { content: '\201C'; }
        .story-hook-quote::after { content: '\201D'; }
        .story-hook-link {
            display: inline-block; margin-top: 16px;
            font-size: 13px; color: var(--blue-light); text-decoration: none; font-weight: 500;
            letter-spacing: 0.02em;
        }
        .story-hook-link:hover { text-decoration: underline; }
        @media (max-width: 640px) {
            .story-hook-grid { grid-template-columns: 1fr; border-radius: 12px; }
            .story-hook-item { padding: 24px 20px; }
        }
        /* ===== PROBLEM-SOLUTION HOOK (Apple/DJI design language) ===== */
        .problem-section { padding: clamp(72px, 11vh, 108px) clamp(16px, 4vw, 24px); background: var(--black-rich); }
        .problem-container { max-width: 880px; margin: 0 auto; }
        .problem-header { text-align: center; margin-bottom: clamp(40px, 5vh, 56px); }
        .problem-eyebrow {
            display: inline-block; font-size: 11px; font-weight: 600;
            color: var(--green-accent); letter-spacing: 0.15em; text-transform: uppercase;
            margin-bottom: 16px;
        }
        .problem-title {
            font-size: clamp(28px, 5vw, 44px); font-weight: 800;
            letter-spacing: -0.035em; line-height: 1.1;
            background: linear-gradient(180deg, #fff 30%, rgba(255,255,255,.6) 100%);
            -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
        }
        .problem-split {
            display: grid; grid-template-columns: 1fr 1fr;
            gap: 1px;
            background: rgba(255,255,255,.06);
            border: 1px solid rgba(255,255,255,.06);
            border-radius: 16px; overflow: hidden;
        }
        .problem-card {
            background: var(--black-rich);
            padding: clamp(28px, 4vw, 44px) clamp(24px, 3vw, 36px);
            transition: background 0.3s ease;
        }
        .problem-card:hover { background: rgba(255,255,255,.01); }
        .problem-card .pc-label {
            display: inline-block; font-size: 10px; font-weight: 700;
            letter-spacing: 0.12em; text-transform: uppercase;
            padding: 4px 10px; border-radius: 999px;
            margin-bottom: 20px;
        }
        .problem-before .pc-label {
            color: rgba(255,255,255,.3);
            background: rgba(255,255,255,.04);
            border: 1px solid rgba(255,255,255,.06);
        }
        .problem-after .pc-label {
            color: #7ab8ff;
            background: rgba(41,151,255,.08);
            border: 1px solid rgba(41,151,255,.2);
        }
        .problem-card h3 { font-size: 16px; font-weight: 700; letter-spacing: -0.01em; margin-bottom: 16px; }
        .problem-before h3 { color: rgba(255,255,255,.35); }
        .problem-after h3 { color: rgba(255,255,255,.8); }
        .problem-card p {
            font-size: 14px; line-height: 1.65; margin-bottom: 12px;
            color: rgba(255,255,255,.35);
        }
        .problem-card p:last-child { margin-bottom: 0; }
        .problem-after p { color: rgba(255,255,255,.5); }
        .problem-after .highlight { color: #f5f5f7; font-weight: 600; }
        @media (max-width: 640px) {
            .problem-split { grid-template-columns: 1fr; border-radius: 12px; }
        }
        /* ===== ROI COMPARISON TABLE (Apple/DJI design language) ===== */
        .roi-section { padding: clamp(72px, 11vh, 108px) clamp(16px, 4vw, 24px); background: #000; }
        .roi-container { max-width: 820px; margin: 0 auto; }
        .roi-header { text-align: center; margin-bottom: clamp(36px, 5vh, 52px); }
        .roi-eyebrow {
            display: inline-block; font-size: 11px; font-weight: 600;
            color: var(--blue-light); letter-spacing: 0.15em; text-transform: uppercase;
            margin-bottom: 12px;
        }
        .roi-header h2 {
            font-size: clamp(24px, 4vw, 38px); font-weight: 800;
            letter-spacing: -0.035em; line-height: 1.1;
            background: linear-gradient(180deg, #fff 30%, rgba(255,255,255,.55) 100%);
            -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
            margin-bottom: 10px;
        }
        .roi-header p { font-size: 15px; color: rgba(255,255,255,.4); line-height: 1.5; }
        .roi-table {
            border: 1px solid rgba(255,255,255,.06);
            border-radius: 12px; overflow: hidden;
        }
        .roi-row {
            display: grid;
            grid-template-columns: 2fr 1.4fr 1.4fr;
            border-bottom: 1px solid rgba(255,255,255,.04);
        }
        .roi-row:last-child { border-bottom: none; }
        .roi-header-row { background: rgba(255,255,255,.02); }
        .roi-cell {
            padding: 14px 20px; font-size: 13px;
            border-right: 1px solid rgba(255,255,255,.04);
            display: flex; align-items: center;
        }
        .roi-cell:last-child { border-right: none; }
        .roi-cell-label { color: rgba(255,255,255,.3); font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; }
        .roi-cell-text { color: rgba(255,255,255,.55); line-height: 1.5; }
        .roi-cell-cost { color: rgba(255,255,255,.25); text-decoration: line-through; font-size: 12px; }
        .roi-cell-save { color: #7ab8ff; font-weight: 600; font-size: 12px; }
        .roi-cta { text-align: center; margin-top: 32px; font-size: 14px; color: rgba(255,255,255,.4); }
        .roi-cta a { color: var(--blue-light); text-decoration: none; font-weight: 500; }
        .roi-cta a:hover { text-decoration: underline; }
        @media (max-width: 580px) {
            .roi-row { grid-template-columns: 1fr 1fr; }
            .roi-cell:nth-child(1) { display: none; }
            .roi-cell-label { display: flex; }
        }
        /* Touch: skip-render culling off for these tall sections — on iOS the
           intrinsic-size estimates undershoot the real stacked-mobile heights,
           and every materialize/cull flip shifted the document mid-scroll
           (same failure mode the bento section documented). Their render cost
           is trivial next to the scroll-jump they caused. */
        @media (max-width: 768px) {
            .preview-section, .privacy-section {
                content-visibility: visible;
                contain-intrinsic-size: auto;
            }
        }
        /* CTA - RESERVE */
        .cta-section { background: #000; padding: clamp(60px, 10vh, 100px) clamp(16px, 4vw, 24px) clamp(80px, 12vh, 120px); text-align: center; position: relative; overflow: hidden; }
        @media (max-width:768px) {
            .cta-section { padding: clamp(32px, 5vh, 48px) 16px clamp(40px, 7vh, 64px); }
        }
        /* 0.07 through a brightness(0.5) is 3.5% of the picture, which is not a
           background, it is a rounding error -- and it was carrying a 9.8 MB 4K
           rung to be 3.5% visible. Raised to a real presence and the 2160 rung
           dropped from the markup in the same change, because at this strength
           the resolution genuinely cannot be seen and 1080 is plenty.
           Not raised to full, unlike the chapters above: this section is a
           payment form. It has to feel like the poolside pack-up is behind the
           panel, not like a film is playing under a credit card field. The
           reserve card is opaque and carries its own surface, so the only type
           over the picture is the headline, which has a shadow. */
        .cta-section > video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0.38; filter: brightness(0.72); z-index: 0; pointer-events: none; }
        .cta-section::before { content: ''; position: absolute; inset: 0; background: radial-gradient(ellipse 60% 70% at 50% 40%, transparent 0%, rgba(0,0,0,0.7) 100%); z-index: 1; pointer-events: none; }
        .cta-section::after { content: ''; position: absolute; top: 30%; left: 50%; width: 400px; height: 400px; transform: translate(-50%, -50%); background: radial-gradient(circle, rgba(0,113,227,.08) 0%, transparent 70%); z-index: 0; pointer-events: none; opacity: 0.6; }
        .cta-coming { font-size: 12px; font-weight: 600; color: var(--orange-accent); letter-spacing: 0.12em; text-transform: uppercase; margin-bottom: 20px; position: relative; z-index: 2; }
        .cta-section h2 { font-size: clamp(40px, 7vw, 72px); font-weight: 600; margin-bottom: 12px; letter-spacing: -0.045em; position: relative; z-index: 2; color: #fff; background: linear-gradient(180deg, #fff 0%, rgba(255,255,255,.92) 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; text-shadow: 0 2px 24px rgba(0,0,0,.4); }
        .cta-sub { font-size: clamp(15px, 2vw, 17px); color: var(--gray-400); margin-bottom: 32px; max-width: 520px; margin-left: auto; margin-right: auto; line-height: 1.55; position: relative; z-index: 2; }
        /* ── Reserve card — Apple-grade unified design ──
           Container: wider (460 → 480), more breathing room, softer radius,
           layered glass with warm tint matching pricing-section's orange hero glow. */
        .preorder-form { max-width: 480px; margin: 0 auto; position: relative; z-index: 2; }
        .reserve-card {
            background:
                linear-gradient(180deg, rgba(255,255,255,.028) 0%, rgba(255,255,255,.012) 100%),
                rgba(10,10,12,.55);
            backdrop-filter: blur(28px) saturate(140%);
            -webkit-backdrop-filter: blur(28px) saturate(140%);
            border: 1px solid rgba(255,255,255,.07);
            border-radius: 28px;
            padding: clamp(32px, 5vh, 44px) clamp(24px, 4vw, 36px);
            box-shadow:
                0 1px 0 rgba(255,255,255,.06) inset,
                0 24px 72px rgba(0,0,0,.55),
                0 0 0 1px rgba(41,151,255,.04);
        }
        .form-row { margin-bottom: 16px; }
        /* Inputs: softer 16px radius, fuller 15px/18px padding, warm-amber focus ring matches Apple system feel */
        .form-input {
            width: 100%;
            padding: 15px 18px;
            font-size: 15px; font-family: inherit;
            background: rgba(255,255,255,.035);
            border: 1px solid rgba(255,255,255,.085);
            border-radius: 16px;
            color: #f5f5f7;
            transition: border-color .2s, box-shadow .2s, background .2s;
        }
        .form-input::placeholder { color: var(--gray-500); }
        .form-input:hover { background: rgba(255,255,255,.05); border-color: rgba(255,255,255,.12); }
        .form-input:focus {
            outline: none;
            border-color: rgba(41,151,255,.45);
            background: rgba(255,255,255,.045);
            box-shadow: 0 0 0 3px rgba(41,151,255,.12);
        }
        #card-element {
            padding: 15px 18px;
            background: rgba(255,255,255,.035);
            border: 1px solid rgba(255,255,255,.085);
            border-radius: 16px;
            min-height: 48px;
            transition: border-color .2s, box-shadow .2s, background .2s;
        }
        #card-element.StripeElement--focus {
            border-color: rgba(41,151,255,.45);
            background: rgba(255,255,255,.045);
            box-shadow: 0 0 0 3px rgba(41,151,255,.12);
        }
        #card-element.StripeElement--invalid { border-color: #ff453a; }
        #card-element.StripeElement--complete { border-color: #30d158; }
        #card-errors { color: #ff453a; font-size: 13px; margin-top: 8px; min-height: 20px; }
        /* Terms checkbox */
        .terms-row { margin-bottom: 8px; }
        .terms-label { display: flex; align-items: flex-start; gap: 10px; cursor: pointer; text-align: left; }
        .terms-label input { display: none; }
        .checkmark {
            width: 18px; height: 18px; flex-shrink: 0; margin-top: 2px;
            border: 1px solid rgba(255,255,255,0.12); border-radius: 6px;
            background: rgba(255,255,255,.04); position: relative; transition: background 0.2s, border-color 0.2s;
        }
        .terms-label input:checked + .checkmark {
            background: #0a84ff; border-color: #0a84ff;
        }
        .terms-label input:checked + .checkmark::after {
            content: ''; position: absolute; top: 3px; left: 6px;
            width: 4px; height: 8px; border: solid #fff;
            border-width: 0 2px 2px 0; transform: rotate(45deg);
        }
        .terms-text { font-size: 12px; color: #86868b; line-height: 1.4; }
        .terms-link { color: #0a84ff; text-decoration: none; }
        .terms-link:hover { text-decoration: underline; }
        /* Submit: ice-blue gradient — the single brand accent */
        .form-submit {
            width: 100%;
            padding: 17px 24px;
            font-size: 16px; font-weight: 600;
            background: linear-gradient(180deg, #47a9ff 0%, #0071e3 100%);
            color: #fff;
            border: 1px solid rgba(255,255,255,.08);
            border-radius: 980px;
            cursor: pointer;
            transition: transform .25s var(--ease-smooth), box-shadow .25s var(--ease-smooth), opacity .2s ease;
            margin-top: 22px;
            position: relative;
            box-shadow:
                0 1px 0 rgba(255,255,255,.25) inset,
                0 4px 16px rgba(41,151,255,.28),
                0 12px 40px rgba(0,113,227,.18);
            letter-spacing: -0.01em;
        }
        .form-submit:hover:not(:disabled) {
            transform: translateY(-1px) scale(1.005);
            box-shadow:
                0 1px 0 rgba(255,255,255,.35) inset,
                0 6px 22px rgba(41,151,255,.42),
                0 16px 48px rgba(0,113,227,.28);
            opacity: .96;
        }
        .form-submit:active:not(:disabled) { transform: scale(.98); }
        .form-submit:disabled { opacity: .45; cursor: not-allowed; filter: saturate(.6); transform: none; }
        .form-submit:focus-visible { outline: 2px solid rgba(41,151,255,.7); outline-offset: 3px; }
        .form-submit.loading { pointer-events: none; }
        .form-submit.loading::after { content: ''; display: inline-block; width: 16px; height: 16px; margin-left: 8px; border: 2px solid rgba(255,255,255,.3); border-top-color: #fff; border-radius: 50%; animation: btnSpin .6s linear infinite; vertical-align: middle; }
        .form-submit.success { background: #30d158; }
        @keyframes btnSpin { to { transform: rotate(360deg); } }
        .form-input.invalid { border-color: #ff453a; box-shadow: 0 0 0 3px rgba(255,69,58,.15); }
        .form-input.valid { border-color: #30d158; }
        .form-input:focus-visible { outline: none; border-color: #0a84ff; box-shadow: 0 0 0 3px rgba(10,132,255,.25); }
        .field-error { color: #ff453a; font-size: 12px; margin-top: 6px; min-height: 0; transition: opacity .2s, margin .2s; }
        .field-error:empty { margin: 0; }
        .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
        /* Unified bottom cluster: Stripe badge + fineprint grouped under a hairline separator for visual rhythm */
        .stripe-badge {
            display: flex; align-items: center; justify-content: center; gap: 8px;
            margin-top: 24px;
            padding-top: 20px;
            border-top: 1px solid rgba(255,255,255,.055);
            font-size: 11px; color: rgba(255,255,255,.35);
            letter-spacing: .02em;
        }
        .stripe-badge svg { height: 16px; opacity: .5; }
        /* Assurance row — the three buyer answers, scannable at the button */
        .assure-row {
            display: flex; justify-content: center; align-items: stretch;
            gap: 0;
            margin-top: 16px;
        }
        .assure-item {
            display: flex; align-items: center; gap: 7px;
            padding: 0 clamp(10px, 1.6vw, 18px);
            font-size: 11.5px; color: rgba(255,255,255,.55);
            letter-spacing: .01em; line-height: 1.35;
            white-space: nowrap;
        }
        .assure-item + .assure-item { border-left: 1px solid rgba(255,255,255,.1); }
        .assure-item svg { width: 15px; height: 15px; flex: none; color: rgba(120,180,255,.75); }
        .assure-item i { font-style: normal; }
        @media (max-width: 560px) {
            .assure-row { flex-direction: column; gap: 9px; align-items: center; }
            .assure-item + .assure-item { border-left: 0; }
        }
        .fineprint {
            margin-top: 12px;
            font-size: 11px;
            color: rgba(255,255,255,.32);
            text-align: center;
            line-height: 1.45;
        }
        .payment-status { max-width: 420px; margin: 16px auto 0; padding: 0; font-size: 14px; font-weight: 500; text-align: center; border-radius: 12px; overflow: hidden; max-height: 0; opacity: 0; transition: max-height .4s ease, opacity .4s ease, padding .4s ease; }
        .payment-status.show { max-height: 60px; opacity: 1; padding: 12px 20px; }
        .payment-status.success { background: rgba(48,209,88,.1); color: #30d158; border: 1px solid rgba(48,209,88,.2); }
        .payment-status.error { background: rgba(255,69,58,.1); color: #ff453a; border: 1px solid rgba(255,69,58,.2); }
        /* ── The form, when the pledge frame is what it drives ──
           The card is collected inside the frame, so the card field and the
           invitation code here have nothing to talk to. Hidden rather than
           removed from the markup: which checkout runs is a server setting,
           and the same document has to serve both. #card-errors stays visible
           either way — the terms notice is written into it. */
        .reserve-card.is-pledgebox #card-element,
        .reserve-card.is-pledgebox .coupon-row { display: none; }
        /* ── A last call, not a second form ──
           The pricing Reserve buttons collect the address now, so asking for it
           again three screens later is the same question twice on one page.
           What stays is what this card can say that the dialog cannot: which
           model is queued up, and the three reassurances at the button. The
           email field, the terms box and the card note go — the dialog owns
           them. In stripe mode none of this applies: the form IS the checkout,
           so the rules are scoped to the pledge class. */
        .reserve-card.is-pledgebox .form-row,
        .reserve-card.is-pledgebox .terms-row,
        .reserve-card.is-pledgebox .pay-next { display: none; }
        .reserve-card.is-pledgebox .tier-summary { margin-bottom: 18px; }
        /* The other half of hiding the card field: only shown when it IS
           hidden, because when the card field is present this sentence is
           false. */
        .pay-next { display: none; }
        .reserve-card.is-pledgebox .pay-next {
            display: flex; align-items: center; gap: 7px;
            margin: 0 0 12px;
            padding: 10px 12px;
            border: 1px solid rgba(255,255,255,.08);
            border-radius: 10px;
            background: rgba(255,255,255,.03);
            font-size: 12.5px; line-height: 1.35;
            color: rgba(255,255,255,.6);
        }
        .reserve-card.is-pledgebox .pay-next::before {
            content: "";
            flex: none; width: 13px; height: 13px;
            background: currentColor;
            opacity: .8;
            /* A padlock, inline so it costs no request and inherits the colour. */
            -webkit-mask: var(--lock-mask) center/contain no-repeat;
            mask: var(--lock-mask) center/contain no-repeat;
            --lock-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round'%3E%3Crect x='4' y='10.5' width='16' height='11' rx='2.5'/%3E%3Cpath d='M8 10.5V7a4 4 0 0 1 8 0v3.5'/%3E%3C/svg%3E");
        }
        /* ── Get the email even when there is no card ── */
        .sub-row {
            max-width: 420px;
            margin: 26px auto 0;
            padding: 20px 22px 22px;
            border: 1px solid rgba(255,255,255,.09);
            border-radius: 16px;
            background: rgba(255,255,255,.026);
            text-align: left;
        }
        .sub-title {
            margin: 0 0 4px;
            font-size: 15px; font-weight: 600; letter-spacing: -.01em;
            color: #f5f5f7;
        }
        .sub-sub {
            margin: 0 0 14px;
            font-size: 13px; line-height: 1.45;
            color: rgba(255,255,255,.5);
        }
        .sub-field { display: flex; gap: 8px; }
        .sub-field .form-input { flex: 1; margin: 0; min-width: 0; }
        .sub-btn {
            flex: none;
            padding: 0 18px; min-height: 46px;
            border: 0; border-radius: 12px; cursor: pointer;
            background: rgba(255,255,255,.1);
            color: #f5f5f7;
            font-family: inherit; font-size: 14px; font-weight: 600; letter-spacing: -.01em;
            white-space: nowrap;
            transition: background .22s ease, color .22s ease, transform .18s ease;
        }
        .sub-btn:hover { background: rgba(255,255,255,.17); }
        .sub-btn:active { transform: scale(.98); }
        .sub-btn:disabled { opacity: .55; cursor: default; }
        .sub-btn.is-done { background: rgba(48,209,88,.16); color: #30d158; }
        .sub-msg {
            margin: 10px 0 0; min-height: 17px;
            font-size: 12.5px; color: rgba(255,255,255,.5);
        }
        .sub-msg.is-err { color: #ff453a; }
        .sub-msg.is-ok { color: #30d158; }
        @media (max-width: 520px) {
            .sub-field { flex-direction: column; }
            .sub-btn { width: 100%; }
        }
        /* ── Reserve, at the card ── */
        .quick-modal {
            position: fixed; inset: 0; z-index: 3100;
            display: flex; align-items: center; justify-content: center;
            padding: clamp(14px, 4vw, 32px);
        }
        .quick-modal[hidden] { display: none; }
        .quick-panel {
            position: relative;
            width: min(400px, 100%);
            max-height: 92svh; overflow-y: auto;
            padding: 30px 26px 24px;
            border: 1px solid rgba(255,255,255,.11);
            border-radius: 20px;
            background: #0b0c0f;
            box-shadow: 0 40px 120px rgba(0,0,0,.72);
            animation: pledge-rise .32s cubic-bezier(.16,1,.3,1) both;
            text-align: left;
        }
        @media (prefers-reduced-motion: reduce) { .quick-panel { animation: none; } }
        .quick-x { position: absolute; top: 14px; right: 14px; }
        .quick-eyebrow {
            margin: 0 0 5px;
            font-family: var(--font-mono, ui-monospace, 'SF Mono', Menlo, Consolas, monospace);
            font-size: 9.5px; letter-spacing: .2em; text-transform: uppercase;
            color: rgba(255,255,255,.4);
        }
        .quick-name {
            margin: 0 0 2px;
            font-size: 21px; font-weight: 700; letter-spacing: -.025em;
            color: #f5f5f7;
        }
        .quick-price {
            margin: 0 0 20px;
            font-size: 14px; color: rgba(255,255,255,.5);
        }
        .quick-panel .form-input { margin: 0; }
        .quick-terms { margin: 14px 0 0; }
        .quick-next {
            display: flex; align-items: center; gap: 7px;
            margin: 14px 0 14px;
            padding: 10px 12px;
            border: 1px solid rgba(255,255,255,.08);
            border-radius: 10px;
            background: rgba(255,255,255,.03);
            font-size: 12.5px; line-height: 1.35;
            color: rgba(255,255,255,.6);
        }
        .quick-fine {
            margin: 12px 0 0;
            font-size: 11px; line-height: 1.5; text-align: center;
            color: rgba(255,255,255,.38);
        }
        /* ── The pledge frame ──
           A dialog rather than a page: the visitor keeps our chrome, our
           background and their place on the page, and closing costs one tap
           instead of a back navigation that reloads 30,000px of document. */
        .pledge-modal {
            position: fixed; inset: 0; z-index: 3000;
            display: flex; align-items: center; justify-content: center;
            padding: clamp(10px, 3vw, 32px);
        }
        .pledge-modal[hidden] { display: none; }
        .pledge-backdrop {
            position: absolute; inset: 0;
            background: rgba(0,0,0,.78);
            -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
            animation: pledge-fade .28s ease both;
        }
        .pledge-panel {
            position: relative;
            /* Sized to the framed page's own two-column breakpoint, not to what
               a dialog usually wants. At 560 it fell back to their stacked
               layout and the frame opened on a hero image with the card fields
               a scroll below — the visitor's next action off screen. Past
               ~1000 the columns split and the form is top-right, visible the
               moment the frame paints. */
            width: min(1060px, 100%);
            /* A definite height, not just a ceiling. The frame is the only
               thing here that wants to grow, and `height: 100%` inside a
               flex item whose own height is content-derived resolves against
               nothing — the frame collapsed to a sliver of the page. */
            height: min(92svh, 880px);
            display: flex; flex-direction: column;
            border: 1px solid rgba(255,255,255,.11);
            border-radius: clamp(14px, 2vw, 20px);
            background: #0b0c0f;
            box-shadow: 0 40px 120px rgba(0,0,0,.7);
            overflow: hidden;
            animation: pledge-rise .34s cubic-bezier(.16,1,.3,1) both;
        }
        @keyframes pledge-fade { from { opacity: 0; } to { opacity: 1; } }
        @keyframes pledge-rise {
            from { opacity: 0; transform: translateY(14px) scale(.985); }
            to   { opacity: 1; transform: none; }
        }
        @media (prefers-reduced-motion: reduce) {
            .pledge-backdrop, .pledge-panel { animation: none; }
        }
        .pledge-head {
            flex: none;
            display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
            padding: 18px 18px 14px 22px;
            border-bottom: 1px solid rgba(255,255,255,.07);
        }
        .pledge-title {
            margin: 0 0 3px;
            font-size: 16px; font-weight: 600; letter-spacing: -.015em;
            color: #f5f5f7;
        }
        .pledge-sub {
            margin: 0;
            font-size: 12.5px; line-height: 1.4;
            color: rgba(255,255,255,.46);
        }
        /* Only where the framed page stacks and puts its hero above the card
           fields. On the wide layout the form is already in view, and saying
           this there would be noise. */
        .pledge-hint { display: none; }
        @media (max-width: 760px) {
            .pledge-hint {
                display: block;
                margin: 5px 0 0;
                font-size: 12px; line-height: 1.35;
                color: rgba(255,190,120,.82);
            }
        }
        .pledge-close {
            flex: none;
            width: 34px; height: 34px; padding: 0;
            display: inline-flex; align-items: center; justify-content: center;
            border: 1px solid rgba(255,255,255,.12); border-radius: 50%;
            background: rgba(255,255,255,.04);
            color: rgba(255,255,255,.7);
            cursor: pointer;
            transition: background .2s ease, color .2s ease;
        }
        .pledge-close:hover { background: rgba(255,255,255,.12); color: #fff; }
        .pledge-close svg { width: 16px; height: 16px; }
        .pledge-frame-wrap {
            position: relative;
            flex: 1 1 auto;
            min-height: 520px;
            background: #0b0c0f;
        }
        /* On a phone the framed page stacks whatever we do, so the only useful
           thing left is room: full bleed, no radius, every pixel to the frame. */
        @media (max-width: 760px) {
            .pledge-modal { padding: 0; }
            .pledge-panel {
                width: 100%; max-width: none;
                height: 100svh; max-height: none;
                border: 0; border-radius: 0;
            }
            .pledge-head { padding: 14px 12px 12px 16px; }
            .pledge-frame-wrap { min-height: 0; }
        }
        .pledge-frame {
            position: absolute; inset: 0; z-index: 1;
            display: block; width: 100%; height: 100%;
            border: 0;
            opacity: 0;
            transition: opacity .3s ease;
        }
        .pledge-modal.is-ready .pledge-frame { opacity: 1; }
        .pledge-loading {
            position: absolute; inset: 0; z-index: 2;
            display: flex; align-items: center; justify-content: center;
            font-size: 13px; color: rgba(255,255,255,.45);
            pointer-events: none;
            transition: opacity .3s ease;
        }
        .pledge-modal.is-ready .pledge-loading { opacity: 0; }
        /* GSAP Animation Helpers */
        .line-wrap { display: block; overflow: hidden; }
        .line-inner { display: block; }
        /* GPU hint for animated cards — backface-visibility only (no preserve-3d overhead) */
        .bento-card {
            backface-visibility: hidden;
        }
        /* Magnetic button base styles */
        .btn, .nav-cta, .form-submit {
            position: relative;
        }
        /* Elements visible by default */
        .reveal { opacity: 1; transform: translateY(0); }
        .td-badge {
            text-transform: uppercase;
            letter-spacing: 0.12em;
            font-size: 12px;
            font-weight: 600;
        }
        /* Bento card — GSAP owns transform; CSS only handles box-shadow glow */
        /* Teardown feature — fade-slide on scroll */
        .td-feature {
            transition: opacity 0.6s ease, transform 0.6s var(--ease-out-expo);
        }
        /* Nav — DJI scroll-aware intensity */
        nav.scrolled {
            background: rgba(0,0,0,.92);
            border-bottom-color: rgba(255,255,255,0.04);
        }
        /* ── PRICING SECTION — Apple unified design: single card focus, consistent visual language ── */
        .pricing-section {
            position: relative; overflow: hidden;
            padding: clamp(72px, 10vh, 116px) max(24px, 6%) clamp(64px, 9vh, 96px);
            background: #000;
        }
        @media (max-width:768px) {
            .pricing-section { padding: clamp(40px, 6vh, 56px) 16px clamp(40px, 6vh, 56px); }
        }
        .pricing-section-hidden-marker {
            display: none;
        }
        .pricing-section::before {
            content: ''; position: absolute; inset: 0;
            background:
                radial-gradient(ellipse 55% 35% at 50% -5%, rgba(41,151,255,.06) 0%, transparent 65%),
                radial-gradient(ellipse 100% 60% at 50% 105%, rgba(255,255,255,.02) 0%, transparent 60%);
            pointer-events: none; z-index: 0;
        }
        .pricing-section::after {
            content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 1px;
            background: linear-gradient(90deg, transparent 10%, rgba(255,255,255,.05) 50%, transparent 90%);
        }

        /* Header — unified type hierarchy */
        .pricing-header {
            text-align: center; max-width: 780px;
            margin: 0 auto clamp(52px, 8vh, 76px);
            position: relative; z-index: 1;
        }
        .pricing-eyebrow {
            font-size: 11px; font-weight: 500;
            color: rgba(255,179,64,.75); letter-spacing: .18em; text-transform: uppercase;
            margin-bottom: 22px;
        }
        .pricing-headline {
            font-size: clamp(52px, 8vw, 88px);
            font-weight: 300; letter-spacing: -.04em; line-height: 1.02;
            color: #fff;
        }
        .pricing-headline .txt-grad-orange {
            background: linear-gradient(135deg, #ffb340, #ff7a3d);
            -webkit-background-clip: text; -webkit-text-fill-color: transparent;
            background-clip: text; font-weight: 600;
        }
        .pricing-sub {
            font-size: clamp(18px, 2vw, 22px);
            color: rgba(255,255,255,.45); margin-top: 24px;
            line-height: 1.6; max-width: 520px; margin-left: auto; margin-right: auto;
            font-weight: 400;
        }
        .pricing-sub .txt-em { color: rgba(255,255,255,.6); font-style: normal; }

        /* ─ CJK typography fixes — Latin display weights/letter-spacing don't translate to CJK ─
           Issues being fixed:
             • font-weight 300 looks anemic in Chinese/Japanese strokes (need 500)
             • letter-spacing -.04em squeezes CJK characters into each other
             • line-height 1.02 is too tight for the larger CJK character bodies
             • eyebrow .18em letter-spacing breaks "三款型号" into floating glyphs
             • sub .45 opacity makes Chinese strokes hard to read on dark BG */
        html[lang^="zh"] .pricing-eyebrow,
        html[lang="ja"] .pricing-eyebrow,
        html[lang="ko"] .pricing-eyebrow {
            letter-spacing: .06em;
            font-weight: 600;
        }
        html[lang^="zh"] .pricing-headline,
        html[lang="ja"] .pricing-headline,
        html[lang="ko"] .pricing-headline {
            font-size: clamp(40px, 6.5vw, 72px);
            font-weight: 500;
            letter-spacing: 0;
            line-height: 1.2;
        }
        html[lang^="zh"] .pricing-headline .txt-grad-orange,
        html[lang="ja"] .pricing-headline .txt-grad-orange,
        html[lang="ko"] .pricing-headline .txt-grad-orange {
            font-weight: 700;
        }
        html[lang^="zh"] .pricing-sub,
        html[lang="ja"] .pricing-sub,
        html[lang="ko"] .pricing-sub {
            font-size: clamp(15px, 1.55vw, 17px);
            color: rgba(255,255,255,.62);
            line-height: 1.7;
            letter-spacing: .01em;
            max-width: 580px;
        }

        html[lang^="zh"] .bento-header h2,
        html[lang="ja"] .bento-header h2,
        html[lang="ko"] .bento-header h2,
        html[lang^="zh"] .preview-header h2,
        html[lang="ja"] .preview-header h2,
        html[lang="ko"] .preview-header h2,
        html[lang^="zh"] .faq-header h2,
        html[lang="ja"] .faq-header h2,
        html[lang="ko"] .faq-header h2,
        html[lang^="zh"] .cta-section h2,
        html[lang="ja"] .cta-section h2,
        html[lang="ko"] .cta-section h2,
        html[lang^="zh"] .story-hook-headline,
        html[lang="ja"] .story-hook-headline,
        html[lang="ko"] .story-hook-headline {
            font-weight: 600;
            letter-spacing: 0;
            line-height: 1.2;
        }
        html[lang^="zh"] .bento-header h2,
        html[lang="ja"] .bento-header h2,
        html[lang="ko"] .bento-header h2 {
            font-size: clamp(38px, 7vw, 72px);
        }
        html[lang^="zh"] .preview-header h2,
        html[lang="ja"] .preview-header h2,
        html[lang="ko"] .preview-header h2 {
            font-size: clamp(34px, 6vw, 60px);
        }
        html[lang^="zh"] .faq-header h2,
        html[lang="ja"] .faq-header h2,
        html[lang="ko"] .faq-header h2,
        html[lang^="zh"] .cta-section h2,
        html[lang="ja"] .cta-section h2,
        html[lang="ko"] .cta-section h2 {
            font-size: clamp(32px, 5.5vw, 56px);
        }
        html[lang^="zh"] .story-hook-headline,
        html[lang="ja"] .story-hook-headline,
        html[lang="ko"] .story-hook-headline {
            font-size: clamp(22px, 3.5vw, 32px);
        }
        /* SoC headline was missing from this CJK list: it kept -.04em latin
           tracking, which crushes hanzi strokes together — the "can't read
           the title" report. Neutral tracking + a taller line + CJK size. */
        html[lang^="zh"] .soc-title,
        html[lang="ja"] .soc-title,
        html[lang="ko"] .soc-title {
            font-weight: 600;
            letter-spacing: 0;
            line-height: 1.22;
            font-size: clamp(30px, 5vw, 54px);
        }
        /* Body copy below H2: weight bump from 300 → 400 for CJK stroke readability */
        html[lang^="zh"] .preview-header p,
        html[lang="ja"] .preview-header p,
        html[lang="ko"] .preview-header p {
            font-weight: 400;
            letter-spacing: .01em;
        }

        /* ══════════════════════════════════════════════════════════════════════════════
           PRICING — v7: DJI Combo + Mercedes configurator + Apple Music hybrid
           Visual consistency rules (fixing v6's "incoherent" feedback):
             • Card bg         = #1d1d1f → #161618 gradient (IDENTICAL across all 3 tiers)
             • Badge pill      = same geometry/border/bg for Plus & Pro, text+border-color only differs
             • CTA             = brand orange for ALL 3 tiers (no per-tier CTA colors)
             • Tagline accent  = brand orange for ALL 3 tiers (no per-tier gradients)
             • Spec structure  = INCLUDES (shared 4) + divider + EXCLUSIVE (tier-specific 2-3)
             • Per-tier cue    = Pro gets a 1px gold hairline box-shadow — that's it.
           DJI-inspired dual-section spec list ensures every card shows full shared specs.
           Mercedes-inspired eyebrow + divider + category-grouped specs.
           Apple-inspired restraint: single CTA color, minimal glow, clean typography.
           ══════════════════════════════════════════════════════════════════════════════ */

        /* Segment — Apple shop pill: neutral glass indicator, white active text */
        .pricing-segment {
            display: flex; justify-content: center;
            position: relative; z-index: 1;
            margin: 0 auto clamp(36px, 5.5vh, 56px);
            background: rgba(255,255,255,.045);
            border: 1px solid rgba(255,255,255,.07);
            border-radius: 100px;
            padding: 3px;
            max-width: 460px;
        }
        .pricing-seg-indicator {
            position: absolute; top: 3px; left: 3px;
            height: calc(100% - 6px); border-radius: 100px;
            background: rgba(255,255,255,.12);
            box-shadow: 0 1px 0 rgba(255,255,255,.1) inset, 0 2px 8px rgba(0,0,0,.3);
            transition: transform .35s cubic-bezier(.25,.1,.25,1),
                        width .35s cubic-bezier(.25,.1,.25,1);
            z-index: 1; pointer-events: none;
        }
        .pricing-seg-btn {
            flex: 1; padding: 10px 16px;
            font-size: 14px; font-weight: 500; font-family: inherit;
            color: rgba(255,255,255,.45); background: none; border: none;
            border-radius: 100px; cursor: pointer;
            transition: color .2s ease;
            position: relative; z-index: 2;
            letter-spacing: -.01em; text-align: center; white-space: nowrap;
        }
        .pricing-seg-btn:hover { color: rgba(255,255,255,.75); }
        .pricing-seg-btn.active { color: #f5f5f7; font-weight: 600; }

        /* Showcase — single card container (segment-driven switch) */
        .pricing-showcase {
            position: relative; z-index: 1;
            max-width: 640px; margin: 0 auto;
            min-height: 280px;
        }

        /* Panel — default hidden, .active fades in */
        .pricing-panel {
            display: none;
            flex-direction: column;
            align-items: stretch;
            text-align: center;
            opacity: 0;
            transform: translateY(10px);
        }
        .pricing-panel.active {
            display: flex;
            animation: pricingPanelIn .4s cubic-bezier(.25,.1,.25,1) forwards;
        }
        @keyframes pricingPanelIn { to { opacity: 1; transform: translateY(0); } }

        /* Card — ONE look for all three tiers. Flag pill (real element, i18n-able)
           floats at the top edge; the model name anchors the card like an Apple
           compare column, so top padding is modest and constant. */
        .pricing-panel-card {
            position: relative;
            width: 100%;
            background: linear-gradient(180deg, #1d1d1f 0%, #161618 100%);
            border: 1px solid rgba(255,255,255,.08);
            border-radius: 28px;
            padding: clamp(48px, 6vh, 64px) clamp(48px, 7vw, 80px) clamp(36px, 5vh, 48px);
            text-align: center;
            box-shadow: 0 24px 80px rgba(0,0,0,.45);
        }
        .pricing-panel-kicker { display: none; }

        /* ─ Flag pill (Plus & Pro) — identical geometry; only text + border tint differs ─ */
        .pricing-panel-flag {
            position: absolute;
            top: -13px; left: 50%; transform: translateX(-50%);
            font-size: 11px; font-weight: 600; letter-spacing: .14em;
            text-transform: uppercase;
            padding: 6px 14px; border-radius: 100px;
            background: #101114;
            border: 1px solid rgba(255,255,255,.12);
            box-shadow: 0 2px 10px rgba(0,0,0,.4);
            white-space: nowrap;
        }
        .pricing-panel-flag-plus { color: #7ab8ff; border-color: rgba(41,151,255,.4); }
        .pricing-panel-flag-pro { color: #e5c564; border-color: rgba(212,175,55,.45); }

        /* ─ Model name — quiet mono eyebrow above the tagline ─ */
        .pricing-panel-name {
            font-family: var(--font-mono);
            font-size: 12px; font-weight: 500;
            letter-spacing: .18em; text-transform: uppercase;
            color: rgba(245,245,247,.42);
            margin-bottom: 14px;
        }

        /* ─ Pro: ONE subtle identity cue — 1px gold hairline (no glow, no color shift) ─ */
        .pricing-panel[data-panel="pro"] .pricing-panel-card {
            box-shadow:
                0 0 0 1px rgba(212,175,55,.32),
                0 24px 80px rgba(0,0,0,.55);
        }

        /* Tagline — Apple-big white; accent .txt-grad-orange is UNIFIED brand orange for all tiers */
        /* The price. There was nowhere for it before -- every panel ran
           "Final price at launch" where the number belongs -- so it goes
           directly under the product name and above the tagline, which is the
           order a reader scans: what is it, what does it cost, why this one.
           Tabular figures so the three panels' digits line up when the
           segmented control switches between them. */
        .pricing-panel-price {
            font-size: clamp(30px, 3.4vw, 44px);
            font-weight: 700;
            letter-spacing: -.03em;
            color: #f5f5f7;
            font-variant-numeric: tabular-nums;
            margin: 6px 0 4px;
        }
        .pricing-panel-tagline {
            font-size: clamp(38px, 5.5vw, 52px);
            font-weight: 400; letter-spacing: -.028em; line-height: 1.08;
            color: #f5f5f7;
            margin: 0 0 16px;
        }
        .pricing-panel-tagline .txt-grad-orange {
            background: linear-gradient(135deg, #ffb340, #ff7a3d);
            -webkit-background-clip: text; -webkit-text-fill-color: transparent;
            background-clip: text;
            font-weight: 600;
        }

        /* Description — softer Apple secondary gray */
        .pricing-panel-desc {
            font-size: clamp(16px, 1.6vw, 18px);
            color: rgba(245,245,247,.55);
            line-height: 1.5;
            margin: 0 auto clamp(32px, 4vh, 44px);
            max-width: 460px; font-weight: 400;
        }

        /* ═══════ Tier specs — cards carry ONLY what differs; shared specs live once
           in the .pricing-shared band below (Apple compare-column discipline) ═══════ */
        .pricing-panel-specs {
            width: 100%; max-width: 420px;
            margin: 0 auto clamp(28px, 4vh, 40px);
            text-align: left;
        }

        /* Eyebrow — tier feature label, mono instrument voice like the rest of the page */
        .spec-eyebrow {
            font-family: var(--font-mono);
            font-size: 10px; font-weight: 500;
            letter-spacing: .18em; text-transform: uppercase;
            color: rgba(245,245,247,.38);
            padding-bottom: 11px;
            border-bottom: 1px solid rgba(255,255,255,.08);
            margin-bottom: 6px;
        }

        .spec-list {
            list-style: none; padding: 0; margin: 0;
            display: flex; flex-direction: column;
        }
        .spec-list li {
            display: flex; align-items: flex-start; gap: 11px;
            padding: 8px 0;
            font-size: 14px; color: rgba(245,245,247,.9);
            line-height: 1.45; font-weight: 500;
        }
        .spec-list li::before {
            content: '';
            width: 4px; height: 4px; border-radius: 50%;
            background: #2997ff;
            box-shadow: 0 0 6px rgba(41,151,255,.4);
            flex-shrink: 0; margin-top: 8px;
        }

        /* ═══════ Buy section — deposit figure is the only "price" on the page ═══════ */
        .pricing-panel-deposit {
            margin-bottom: 18px; line-height: 1.4;
        }
        .pricing-panel-deposit strong {
            display: block;
            font-size: clamp(38px, 4vw, 50px);
            font-weight: 300;
            letter-spacing: -.02em;
            line-height: 1.1;
            color: #f5f5f7;
            font-variant-numeric: tabular-nums;
        }
        .pricing-panel-deposit span {
            display: block;
            margin-top: 6px;
            font-size: 13px;
            color: rgba(245,245,247,.5);
        }
        /* Legal fine-print — one quiet line under the CTA */
        .pricing-panel-legal {
            font-size: 11px;
            color: rgba(245,245,247,.32);
            letter-spacing: .03em;
            margin-top: 14px;
            font-weight: 400;
        }

        /* ═══════ CTA — UNIFIED brand orange pill for ALL tiers ═══════ */
        .pricing-panel-cta {
            display: inline-flex; align-items: center; justify-content: center;
            min-height: 50px; padding: 0 40px;
            border-radius: 980px; font-size: 16px; font-weight: 600;
            font-family: inherit; cursor: pointer; border: 0;
            transition: background .2s ease, box-shadow .3s ease, transform .2s ease;
            text-decoration: none; letter-spacing: -.01em;
        }
        .pricing-panel-cta-primary {
            background: linear-gradient(180deg, #47a9ff 0%, #0071e3 100%);
            color: #fff;
            box-shadow:
                0 1px 0 rgba(255,255,255,.3) inset,
                0 6px 20px rgba(41,151,255,.4),
                0 2px 6px rgba(41,151,255,.22);
        }
        .pricing-panel-cta-primary:hover {
            background: linear-gradient(180deg, #5cb3ff 0%, #147ce8 100%);
            transform: translateY(-1px);
            box-shadow:
                0 1px 0 rgba(255,255,255,.35) inset,
                0 10px 28px rgba(41,151,255,.55),
                0 3px 8px rgba(41,151,255,.28);
        }
        .pricing-panel-cta-secondary {
            background: rgba(255,255,255,.07); color: rgba(255,255,255,.65);
            border: 1px solid rgba(255,255,255,.1);
        }
        .pricing-panel-cta-secondary:hover {
            background: rgba(255,255,255,.1); color: #fff;
            border-color: rgba(255,255,255,.18);
        }

        /* ─ Buy block: tighten for in-card layout ─ */
        .pricing-panel-buy { margin-top: auto; padding-top: 4px; display: flex; flex-direction: column; align-items: center; }
        .pricing-panel-cta { width: 100%; max-width: 320px; }

        /* ═══════ Shared spec band — the four all-model features, stated once ═══════ */
        .pricing-shared {
            max-width: 1240px;
            margin: clamp(28px, 4vh, 44px) auto 0;
            padding: 0 clamp(16px, 4vw, 24px);
            text-align: center;
        }
        .pricing-shared-label {
            font-family: var(--font-mono);
            font-size: 10px; font-weight: 500;
            letter-spacing: .18em; text-transform: uppercase;
            color: rgba(245,245,247,.35);
            margin-bottom: 16px;
        }
        .pricing-shared-row {
            list-style: none; padding: 0; margin: 0;
            display: flex; justify-content: center; align-items: center;
            flex-wrap: wrap;
            column-gap: clamp(20px, 3vw, 36px); row-gap: 10px;
        }
        .pricing-shared-row li {
            position: relative;
            font-size: 13.5px; color: rgba(245,245,247,.6);
            white-space: nowrap;
        }
        .pricing-shared-row li + li::before {
            content: '';
            position: absolute; left: calc(-.5 * clamp(20px, 3vw, 36px));
            top: 50%; transform: translate(-50%, -50%);
            width: 3px; height: 3px; border-radius: 50%;
            background: rgba(245,245,247,.28);
        }

        /* ════════════════════════════════════════════════════════════════════
           DESKTOP ≥1024px: three-column compare layout (Apple-product hybrid)
           Segment hidden; all three panels render side-by-side; Plus elevated;
           Pro keeps gold hairline. Tagline/desc/spec sizes tuned for column width.
           ════════════════════════════════════════════════════════════════════ */
        @media (min-width: 1024px) {
            .pricing-segment { display: none; }
            .pricing-showcase {
                display: grid;
                grid-template-columns: 1fr 1.06fr 1fr;
                gap: clamp(16px, 1.6vw, 24px);
                max-width: 1240px;
                min-height: 0;
                align-items: stretch;
            }
            .pricing-panel {
                display: flex !important;
                opacity: 1 !important;
                transform: none !important;
                animation: none !important;
                height: 100%;
            }
            .pricing-panel[data-panel="plus"] {
                transform: translateY(-12px) !important;
            }
            .pricing-panel-card {
                padding: 48px 32px 36px;
                border-radius: 22px;
                height: 100%;
                display: flex;
                flex-direction: column;
                box-shadow:
                    0 20px 50px rgba(0,0,0,.4),
                    0 0 0 1px rgba(255,255,255,.04);
            }
            .pricing-panel[data-panel="plus"] .pricing-panel-card {
                background: linear-gradient(180deg, #1b1e24 0%, #14161a 100%);
                box-shadow:
                    0 0 0 1px rgba(41,151,255,.22),
                    0 28px 80px rgba(41,151,255,.14),
                    0 28px 80px rgba(0,0,0,.5);
            }
            .pricing-panel[data-panel="pro"] .pricing-panel-card {
                box-shadow:
                    0 0 0 1px rgba(212,175,55,.4),
                    0 28px 80px rgba(212,175,55,.1),
                    0 28px 80px rgba(0,0,0,.55);
            }
            .pricing-panel-flag { font-size: 10px; letter-spacing: .16em; padding: 5px 12px; }
            .pricing-panel-name { font-size: 11px; margin-bottom: 12px; }
            .pricing-panel-tagline {
                font-size: clamp(24px, 2.2vw, 30px);
                line-height: 1.12;
                margin-bottom: 12px;
            }
            .pricing-panel-desc {
                font-size: 14px;
                line-height: 1.55;
                max-width: 100%;
                margin-bottom: 24px;
            }
            .pricing-panel-specs { max-width: 100%; margin-bottom: 28px; }
            .spec-eyebrow { letter-spacing: .16em; }
            .spec-list li { font-size: 13px; padding: 6px 0; }
            .pricing-panel-cta {
                font-size: 14px;
                min-height: 44px;
                padding: 0 24px;
            }
            .pricing-panel-deposit strong { font-size: clamp(34px, 3vw, 42px); }
            .pricing-panel-deposit span { font-size: 12px; }
            .pricing-panel-legal { font-size: 10.5px; }
        }
        @media (min-width: 1280px) {
            .pricing-panel-card { padding: 56px 40px 40px; }
            .pricing-panel-tagline { font-size: clamp(26px, 2vw, 32px); }
        }

        /* Mobile */
        @media (max-width: 640px) {
            .pricing-segment { max-width: 100%; }
            .pricing-seg-btn { padding: 9px 8px; font-size: 13px; }
            .pricing-panel-card { padding: 40px 22px 28px; border-radius: 22px; }
            .pricing-panel-flag { font-size: 10px; letter-spacing: .14em; }
            .pricing-panel-tagline { font-size: 30px; }
            .pricing-panel-desc { font-size: 15px; }
            .pricing-panel-specs { max-width: 100%; }
            .spec-eyebrow { letter-spacing: .16em; }
            .spec-list li { font-size: 13.5px; padding: 7px 0; }
            .pricing-panel-cta { width: 100%; font-size: 15px; }
            .pricing-shared-row { flex-direction: column; row-gap: 8px; }
            .pricing-shared-row li + li::before { display: none; }
        }

        /* ═══ Pricing conversion pass — the hardware IS the pitch ═══
           Every tier opens with its own product render (same body, smarter brain
           as you go right), the popular tier sits under a stage light with the
           only warm CTA on the row, and cards physically respond to intent. */
        .pricing-panel-photo {
            display: flex; align-items: flex-end; justify-content: center;
            height: clamp(92px, 12vh, 128px);
            margin: 0 0 16px;
            position: relative;
        }
        /* Product pedestal: a soft tier-tinted pool of light under the render —
           the hardware sits ON something instead of floating in the card.
           Silver / blue / gold follows the tier identity system. */
        .pricing-panel-photo::before {
            content: '';
            position: absolute;
            left: 50%; bottom: -6px; transform: translateX(-50%);
            width: 72%; height: 30px;
            border-radius: 50%;
            background: radial-gradient(ellipse at center, var(--tier-pool, rgba(255,255,255,.10)) 0%, transparent 70%);
            filter: blur(6px);
            pointer-events: none;
        }
        .pricing-panel[data-panel="plus"] .pricing-panel-photo::before { --tier-pool: rgba(41,151,255,.16); }
        .pricing-panel[data-panel="pro"] .pricing-panel-photo::before { --tier-pool: rgba(212,175,55,.15); }
        .pricing-panel-photo img {
            position: relative;
            max-height: 100%; width: auto; max-width: 84%;
            filter: drop-shadow(0 16px 30px rgba(0,0,0,.6));
            transition: transform .5s cubic-bezier(.22,.61,.36,1);
        }
        .pricing-panel:hover .pricing-panel-photo img { transform: translateY(-5px) scale(1.035); }
        /* per-tier bullet identity: silver / blue / gold */
        .pricing-panel[data-panel="lite"] .spec-list li::before { background: rgba(255,255,255,.55); box-shadow: 0 0 6px rgba(255,255,255,.25); }
        .pricing-panel[data-panel="pro"] .spec-list li::before { background: #e5c564; box-shadow: 0 0 6px rgba(212,175,55,.45); }
        /* Plus — the one warm CTA on the row pulls the eye to the popular tier */
        .pricing-panel[data-panel="plus"] .pricing-panel-cta-primary {
            background: linear-gradient(180deg, #ffbf47 0%, #ff821f 100%);
            color: #1a0d02;
            box-shadow:
                0 1px 0 rgba(255,255,255,.45) inset,
                0 8px 26px rgba(255,140,40,.42),
                0 2px 8px rgba(255,140,40,.25);
        }
        .pricing-panel[data-panel="plus"] .pricing-panel-cta-primary:hover {
            background: linear-gradient(180deg, #ffc95e 0%, #ff8d2e 100%);
            box-shadow:
                0 1px 0 rgba(255,255,255,.5) inset,
                0 12px 34px rgba(255,140,40,.55),
                0 3px 10px rgba(255,140,40,.3);
        }
        @media (min-width: 1024px) {
            .pricing-section { position: relative; overflow: hidden; }
            /* stage light falls on the Plus column */
            .pricing-showcase::before {
                content: ''; position: absolute;
                left: 50%; top: -12%; transform: translateX(-50%);
                width: 46%; height: 120%;
                background: radial-gradient(50% 46% at 50% 26%, rgba(41,151,255,.13), transparent 72%);
                pointer-events: none; z-index: -1;
            }
            .pricing-panel { transition: transform .5s cubic-bezier(.22,.61,.36,1); }
            .pricing-panel:hover { transform: translateY(-8px) !important; }
            .pricing-panel[data-panel="plus"]:hover { transform: translateY(-20px) !important; }
            .pricing-panel:hover .pricing-panel-card { box-shadow: 0 34px 80px rgba(0,0,0,.55), 0 0 0 1px rgba(255,255,255,.08); }
            .pricing-panel[data-panel="plus"]:hover .pricing-panel-card {
                box-shadow:
                    0 0 0 1px rgba(41,151,255,.34),
                    0 38px 100px rgba(41,151,255,.2),
                    0 38px 100px rgba(0,0,0,.55);
            }
            .pricing-panel[data-panel="pro"]:hover .pricing-panel-card {
                box-shadow:
                    0 0 0 1px rgba(212,175,55,.5),
                    0 38px 100px rgba(212,175,55,.14),
                    0 38px 100px rgba(0,0,0,.6);
            }
            .pricing-panel-card { transition: box-shadow .5s ease; }
        }
        @media (max-width: 1023px) {
            .pricing-panel-photo { height: clamp(88px, 11vh, 116px); }
        }
        /* Tier cards: elevated mini-cards with layered surface; active gets warm accent ring + subtle inner glow */
        .tier-summary {
            display: flex; align-items: center; gap: 12px;
            margin-bottom: 22px;
            padding: 14px 14px 14px 16px;
            border: 1px solid rgba(41,151,255,.28);
            border-radius: 16px;
            background: rgba(41,151,255,.05);
        }
        .tier-sum-copy { flex: 1 1 auto; min-width: 0; }
        .tier-sum-label {
            display: block;
            font-family: var(--font-mono, ui-monospace, 'SF Mono', Menlo, Consolas, monospace);
            font-size: 9.5px; letter-spacing: .2em; text-transform: uppercase;
            color: rgba(255,255,255,.4);
            margin-bottom: 3px;
        }
        .tier-sum-name {
            display: block;
            font-size: 15px; font-weight: 700; letter-spacing: -.015em;
            color: #f5f5f7;
            white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
        }
        .tier-sum-price {
            flex: none;
            font-size: 15px; font-weight: 600; letter-spacing: -.02em;
            color: rgba(255,255,255,.72);
        }
        .tier-sum-change {
            flex: none;
            padding: 7px 12px;
            border: 1px solid rgba(255,255,255,.14); border-radius: 999px;
            background: none; cursor: pointer;
            font-family: inherit; font-size: 12px; font-weight: 500;
            color: rgba(255,255,255,.66);
            transition: background .2s ease, color .2s ease, border-color .2s ease;
        }
        .tier-sum-change:hover {
            background: rgba(255,255,255,.08); color: #fff;
            border-color: rgba(255,255,255,.24);
        }
        /* Arriving from a pricing CTA: one pulse, so the carry-over is
           something the visitor SEES happen rather than has to trust. */
        .tier-summary.is-set { animation: tier-sum-pulse 1.15s ease both; }
        @keyframes tier-sum-pulse {
            0%   { border-color: rgba(41,151,255,.28); box-shadow: 0 0 0 0 rgba(41,151,255,.28); }
            22%  { border-color: rgba(41,151,255,.85); box-shadow: 0 0 0 5px rgba(41,151,255,.14); }
            100% { border-color: rgba(41,151,255,.28); box-shadow: 0 0 0 0 rgba(41,151,255,0); }
        }
        @media (prefers-reduced-motion: reduce) {
            .tier-summary.is-set { animation: none; }
        }
        /* The choice is still here; it is just no longer the resting state. */
        .tier-select-row { display: none; }
        .reserve-card.is-picking .tier-summary { display: none; }
        .reserve-card.is-picking .tier-select-row {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 10px;
            margin-bottom: 22px;
            /* Explicit: the row is one control, so a tier whose subtitle runs
               to two lines must not make its neighbours look like a different
               size of button. */
            align-items: stretch;
        }
        .tier-select-btn {
            padding: 16px 10px 14px;
            font-family: inherit;
            background: rgba(255,255,255,.025);
            border: 1px solid rgba(255,255,255,.07);
            border-radius: 18px;
            color: var(--gray-400);
            cursor: pointer;
            transition: background .22s ease, border-color .22s ease, color .22s ease, transform .22s ease, box-shadow .22s ease;
            text-align: center;
            position: relative;
            overflow: hidden;
        }
        .tier-select-btn::before {
            content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
            background: radial-gradient(ellipse 80% 60% at 50% 0%, rgba(41,151,255,.12), transparent 70%);
            opacity: 0; transition: opacity .3s ease;
        }
        .tier-select-btn:hover {
            border-color: rgba(255,255,255,.14);
            color: rgba(255,255,255,.9);
            background: rgba(255,255,255,.04);
            transform: translateY(-1px);
        }
        .tier-select-btn.active {
            background: rgba(41,151,255,.05);
            border-color: rgba(41,151,255,.42);
            color: #fff;
            box-shadow:
                0 0 0 1px rgba(41,151,255,.18),
                0 8px 24px rgba(41,151,255,.12);
        }
        .tier-select-btn.active::before { opacity: 1; }
        .tier-sel-name {
            display: block;
            font-size: 15px; font-weight: 700;
            letter-spacing: -.01em;
            margin-bottom: 4px;
        }
        .tier-select-btn.active .tier-sel-name {
            background: linear-gradient(135deg, #64b5ff, #2997ff);
            -webkit-background-clip: text; -webkit-text-fill-color: transparent;
            background-clip: text;
        }
        .tier-sel-sub {
            display: block;
            font-size: 11px; font-weight: 500;
            opacity: .55;
            letter-spacing: 0;
            /* Two short lines beat one long one and one orphan. At 390px each
               of these buttons is 96px wide, which is three words at most. */
            text-wrap: balance;
            line-height: 1.3;
            hyphens: auto;
        }
        .tier-select-btn.active .tier-sel-sub { opacity: .75; }
        .coupon-row { margin-bottom: 8px; }
        .coupon-toggle { font-size: 12px; color: var(--gray-500); cursor: pointer; text-align: center; margin-bottom: 0; transition: color .2s; border: none; background: none; font-family: inherit; width: 100%; padding: 6px 0; }
        .coupon-toggle:hover { color: var(--blue-light); }
        .coupon-field { max-height: 0; overflow: hidden; transition: max-height .3s ease; }
        .coupon-field.open { max-height: 60px; }
        /* CTA submit button — handled in .form-submit block above */
        /* value-section CSS removed */
        /* testimonial styles moved into trust-panel */
        /* ===== GUARANTEE BADGE ===== */
        .guarantee-row { display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 14px; position: relative; z-index: 2; }
        .guarantee-row svg { flex-shrink: 0; }
        .guarantee-text { font-size: 12px; color: var(--green-accent); font-weight: 500; }
        .guarantee-sub { font-size: 11px; color: var(--gray-500); margin-left: 4px; }
        /* ===== HERO VIDEO MODAL ===== */
        /* The render is decorative and must not take clicks off the button
           sitting on top of it. It is not a z-index problem and raising the
           button does not fix it: index.app.js drives a cursor tilt on
           .hero-product via gsap.quickTo(..., 'rotationY'), and that transform
           gives the render its own stacking context which lifts it over a
           z-index: 5 sibling. Measured with tests/_playbtn_hit.py, which walks
           a grid across the disc and asks elementFromPoint what would receive
           the click: with the pointer anywhere on the hero — that is, while a
           visitor is moving toward the button — the right-hand two fifths of the
           disc were hitting #heroImgDeployed instead. The centre always worked,
           which is why this survived: every casual test aims at the middle.
           The tilt keeps working, because its listener is on the hero section
           and pointer-events: none does not stop an event reaching an ancestor. */
        .hero-product { pointer-events: none; }
        .hero-play-btn { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); width: 72px; height: 72px; border-radius: 50%; background: rgba(255,255,255,.12); -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px); border: 1px solid rgba(255,255,255,.15); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: transform 0.3s, background 0.3s; z-index: 5; }
        .hero-play-btn:hover { transform: translate(-50%,-50%) scale(1.08); background: rgba(255,255,255,.18); }
        .hero-play-btn svg { margin-left: 3px; }
        /* Caption under the disc. Overflows the 72px circle deliberately — the
           button keeps its geometry (the pinned-scroll tween scales and fades
           #heroPlayBtn as one box, and a wider button would have moved the disc
           off the render's centre) while the words hang below it. It stays
           inside the button so the same click opens the film. */
        .hero-play-cap { position: absolute; top: calc(100% + 13px); left: 50%; transform: translateX(-50%); display: flex; flex-direction: column; align-items: center; gap: 3px; white-space: nowrap; }
        .hero-play-cap b { font-size: 13px; font-weight: 600; letter-spacing: .005em; color: rgba(255,255,255,.94); text-shadow: 0 1px 3px rgba(0,0,0,.6), 0 2px 16px rgba(0,0,0,.75); }
        .hero-play-cap i { font-family: 'SF Mono','Monaco','Consolas',monospace; font-size: 10px; font-style: normal; font-weight: 500; letter-spacing: .16em; text-transform: uppercase; color: rgba(255,255,255,.55); text-shadow: 0 1px 10px rgba(0,0,0,.8); }
        /* Where there is a pointer, the words wait for it. The disc sits dead
           centre on the machine, so anything typeset under it lands on the
           brightest, busiest part of the render — legible with a shadow, but it
           reads as a caption stuck onto the hero rather than part of it. Hover is
           also exactly the moment the runtime is worth knowing, and the disc is
           still there at rest doing the job it did before.
           Touch gets it permanently: there is no hover to wait for, and a tap
           should not be the only way to find out what it opens. */
        @media (hover: hover) and (pointer: fine) {
            .hero-play-cap { opacity: 0; transform: translateX(-50%) translateY(-5px); transition: opacity .32s ease, transform .32s cubic-bezier(.22,1,.36,1); }
            .hero-play-btn:hover .hero-play-cap,
            .hero-play-btn:focus-visible .hero-play-cap { opacity: 1; transform: translateX(-50%) translateY(0); }
        }
        @media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: reduce) {
            .hero-play-cap { transform: translateX(-50%); transition: opacity .2s linear; }
            .hero-play-btn:hover .hero-play-cap,
            .hero-play-btn:focus-visible .hero-play-cap { transform: translateX(-50%); }
        }
        /* Phones give the hero render less room and the caption sits closer to
           the machine; the meta line is the first thing to go. */
        @media (max-width: 560px) {
            .hero-play-cap { top: calc(100% + 10px); }
            .hero-play-cap b { font-size: 12px; }
            .hero-play-cap i { display: none; }
        }
        /* visibility, not just opacity: pointer-events keeps the mouse out but
           leaves every control inside focusable, so a keyboard user tabbing the
           page fell into an invisible dialog. That was one stray stop when the
           close button was alone in here; the chapter scale adds thirteen more.
           The 0.4s delay on the way out lets the fade finish before it hides. */
        /* Opaque. At .92 the page was visible through the film: the nav bar
           at z-index 1000, the grain layer at 9999 and the hero render all
           sat behind it as a ghost, which is what "the video is obstructed"
           turned out to be -- not something drawn over the picture, but the
           picture being watched through a tinted window. */
        .video-modal { position: fixed; inset: 0; z-index: 10000; background: #000; display: flex; align-items: center; justify-content: center; opacity: 0; pointer-events: none; visibility: hidden; transition: opacity 0.4s ease, visibility 0s linear 0.4s; }
        .video-modal.active { opacity: 1; pointer-events: auto; visibility: visible; transition: opacity 0.4s ease, visibility 0s linear 0s; }
        /* 900px was the frame for a 1080 delivery encode. The film is a 4K
           master now, and a two-minute product film is the one thing on this
           page worth giving the screen to — capped at 1600 and by height, so
           it still clears the close button and never crops itself on a laptop.
           This is also what makes the 4K rung reachable: at 900 px a 2x display
           asks for 1800 and 1080 covers it, so the 4K file would have shipped
           to nobody. */
        /* The width cap moved up to the stage, which is the film plus the chapter
           scale under it. 200px of viewport height is reserved rather than 120:
           the close button still needs its corner, and the scale needs about 60
           more — without that the frame sizes itself to the old budget and the
           scale is pushed off the bottom of a laptop screen. */
        .video-modal-stage { position: relative; width: 92%; max-width: min(1600px, calc((100vh - 200px) * 16 / 9)); display: flex; flex-direction: column; }
        /* Fullscreen: the stage IS the screen, so the caps above -- which
           exist to leave the close button its corner and keep the film off
           the page edges -- have nothing left to leave room for.
           The film is sized from the HEIGHT here, unlike the windowed case.
           A 16:9 box at 100% width overflows any screen shorter than 16:9
           once the chapter scale is under it, and the scale is the reason
           this element goes fullscreen rather than the video. */
        .video-modal-stage:fullscreen {
            width: 100%; height: 100%; max-width: none;
            align-items: center; justify-content: center;
            gap: clamp(8px, 1.6vh, 18px);
            background: #000;
        }
        /* The box takes the height the scale leaves it and the film letterboxes
           inside it, which is what object-fit: contain on .video-modal-player is
           for. Deriving the box from the aspect ratio instead needs 100vw, and
           100vw counts the scrollbar the fullscreen element does not have -- the
           film came out 1440 wide inside a 1430 stage and lost 5px off each
           edge. */
        .video-modal-stage:fullscreen .video-modal-content {
            border-radius: 0;
            width: 100%;
            aspect-ratio: auto;
            flex: 1 1 auto;
            min-height: 0;
        }
        .video-modal-stage:fullscreen .film-chapters { width: min(1600px, 92%); }
        .video-modal-content { width: 100%; aspect-ratio: 16/9; background: #111; border-radius: 16px; overflow: hidden; position: relative; display: flex; align-items: center; justify-content: center; }
        .video-modal-close { position: absolute; top: 14px; right: 14px; width: 40px; height: 40px; border-radius: 50%; background: rgba(255,255,255,.1); border: none; color: #fff; font-size: 20px; cursor: pointer; z-index: 10001; display: flex; align-items: center; justify-content: center; transition: background 0.2s; }
        .video-modal-close:hover { background: rgba(255,255,255,.2); }
        /* The film is 16:9 and so is the frame, so contain and cover agree —
           contain is the safer of the two if a future cut is not. */
        .video-modal-player { width: 100%; height: 100%; display: block; object-fit: contain; background: #000; }
        /* ===== FILM CHAPTERS — the headings the 无字 master does not carry =====
           Set where the film set them: lower left, and at 20% up rather than
           against the edge so the native control bar can sit under it without
           either one covering the other. */
        .film-card { position: absolute; left: 6%; bottom: 20%; z-index: 4; display: flex; align-items: baseline; gap: 11px; max-width: 62%; opacity: 0; transform: translateY(7px); transition: opacity .55s ease, transform .55s cubic-bezier(.22,1,.36,1); pointer-events: none; }
        .film-card.is-on { opacity: 1; transform: translateY(0); }
        .film-card-dot { flex: none; width: 6px; height: 6px; border-radius: 50%; background: #ff9f0a; transform: translateY(-.34em); box-shadow: 0 0 12px rgba(255,159,10,.7); }
        /* The shadow, not a plate: these land on gym floors and daylight as
           often as on dark frames, and a box behind them would read as a
           subtitle burn-in — the exact thing shipping the clean master avoided. */
        /* Sized against the film's own cards rather than by eye: theirs run about
           3.5% of frame height, and the first pass at 21px came to 2.2% — legible,
           but reading as a caption bolted on rather than as the film's own type. */
        .film-card-text { font-size: clamp(15px, 2.1vw, 28px); font-weight: 600; letter-spacing: .055em; text-transform: uppercase; line-height: 1.25; color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,.55), 0 2px 22px rgba(0,0,0,.8); }
        /* CJK is already full-width and has no case to change; the Latin
           tracking pulls the glyphs apart and uppercase does nothing. */
        html[lang^="zh"] .film-card-text, html[lang="ja"] .film-card-text, html[lang="ko"] .film-card-text { letter-spacing: .02em; text-transform: none; font-size: clamp(15px, 1.95vw, 26px); }
        .film-chapters { margin-top: 15px; }
        .film-ch-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: 9px; }
        .film-ch-kicker { flex: none; font-family: 'SF Mono','Monaco','Consolas',monospace; font-size: 10px; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: rgba(255,255,255,.34); }
        .film-ch-now { display: flex; align-items: baseline; gap: 9px; min-width: 0; }
        .film-ch-now b { flex: none; font-family: 'SF Mono','Monaco','Consolas',monospace; font-size: 11px; font-weight: 600; letter-spacing: .06em; color: rgba(255,159,10,.85); }
        .film-ch-now em { flex: none; font-family: 'SF Mono','Monaco','Consolas',monospace; font-size: 11px; font-style: normal; letter-spacing: .04em; color: rgba(255,255,255,.36); }
        .film-ch-now i { font-style: normal; font-size: 13px; font-weight: 500; letter-spacing: .01em; color: rgba(255,255,255,.82); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        /* 26px tall so a finger has something to land on; the mark inside is 1px. */
        .film-ch-scale { position: relative; height: 26px; }
        .film-ch-track, .film-ch-fill { position: absolute; top: 12px; height: 2px; border-radius: 2px; pointer-events: none; }
        .film-ch-track { left: 0; right: 0; background: rgba(255,255,255,.11); }
        .film-ch-fill { left: 0; width: 0; background: linear-gradient(90deg, rgba(255,159,10,.5), #ff9f0a); box-shadow: 0 0 10px rgba(255,159,10,.45); }
        .film-ch-tick { position: absolute; top: 0; height: 26px; width: 16px; margin-left: -8px; padding: 0; border: 0; background: none; cursor: pointer; -webkit-appearance: none; appearance: none; }
        /* The mark is drawn, so the button can stay finger-sized without a 16px
           block of colour sitting on the scale. */
        .film-ch-tick::after { content: ''; position: absolute; left: 50%; top: 7px; width: 2px; height: 12px; margin-left: -1px; border-radius: 1px; background: rgba(255,255,255,.26); transition: background .25s ease, height .25s ease, top .25s ease; }
        .film-ch-tick.is-past::after { background: rgba(255,255,255,.5); }
        .film-ch-tick:hover::after, .film-ch-tick:focus-visible::after { background: #fff; height: 18px; top: 4px; }
        .film-ch-tick.is-on::after { background: #ff9f0a; height: 20px; top: 3px; box-shadow: 0 0 10px rgba(255,159,10,.8); }
        .film-ch-tick:focus { outline: none; }
        .film-ch-tick:focus-visible { outline: 1px solid rgba(255,159,10,.7); outline-offset: 2px; border-radius: 3px; }
        @media (prefers-reduced-motion: reduce) {
            .film-card { transition: opacity .2s linear; transform: none; }
            .film-card.is-on { transform: none; }
            .film-ch-fill { transition: none; }
        }
        /* Short viewports: the scale would eat the frame it belongs to, so the
           film keeps the screen and the chapters step aside. The width cap goes
           back to the pre-scale budget in the same breath. */
        @media (max-height: 520px) {
            .video-modal-stage { max-width: min(1600px, calc((100vh - 110px) * 16 / 9)); }
            .film-chapters { display: none; }
        }
        @media (max-width: 640px) {
            .film-ch-kicker { display: none; }
            .film-card { left: 5%; bottom: 22%; max-width: 78%; }
        }
        /* ===== DESIGN GALLERY — editorial grid of real hardware renders ===== */
        /* No content-visibility here: the culled section used to repaint a
           beat late while scrolling in (a black region on the black page — the
           gallery's "flash"), and the 900px size estimate vs the real grid
           height shifted layout on first approach. The images are lazy-loaded
           anyway, so skipping the render culling costs almost nothing. */
        .design-section { background:#000; padding:clamp(72px,11vh,120px) clamp(16px,4vw,24px); border-top:1px solid rgba(255,255,255,.03); }
        .design-container { max-width:1200px; margin:0 auto; }
        .design-header { text-align:center; max-width:760px; margin:0 auto clamp(36px,5vh,56px); }
        .design-eyebrow { font-size:12px; font-weight:600; letter-spacing:.18em; text-transform:uppercase; color:rgba(255,159,10,.8); margin-bottom:14px; }
        .design-header h2 { font-size:clamp(36px,5.6vw,64px); font-weight:600; letter-spacing:-.045em; line-height:1.06; background:linear-gradient(180deg,#fff 25%,rgba(255,255,255,.62) 100%); -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent; }
        .design-header p { margin-top:14px; font-size:clamp(14px,1.6vw,17px); color:rgba(255,255,255,.55); line-height:1.6; }
        html[lang^="zh"] .design-header h2, html[lang="ja"] .design-header h2, html[lang="ko"] .design-header h2 { font-size:clamp(32px,5vw,56px); letter-spacing:0; line-height:1.18; }
        .design-grid { display:grid; grid-template-columns:repeat(3, 1fr); grid-auto-rows:clamp(200px,30vh,320px); gap:clamp(10px,1.4vw,16px); }
        .design-card { position:relative; margin:0; overflow:hidden; border-radius:20px; border:1px solid rgba(255,255,255,.07); background:#07090c; cursor:default; contain:layout paint; isolation:isolate; }
        /* Per-card stage lighting — a soft key light so transparent renders sit in space, not on flat black */
        .design-card::before { content:''; position:absolute; inset:0; z-index:0; background:radial-gradient(90% 75% at 50% 18%, rgba(255,255,255,.07) 0%, rgba(255,255,255,.018) 45%, transparent 70%); pointer-events:none; }
        .design-card::after { content:''; position:absolute; left:12%; right:12%; bottom:9%; z-index:0; height:8%; background:radial-gradient(50% 100% at 50% 50%, rgba(0,0,0,.72), transparent 75%); filter:blur(6px); pointer-events:none; }
        .design-card-tall { grid-row:span 2; }
        .design-card-wide { grid-column:span 2; }
        .design-card img { position:absolute; inset:8% 6% 12%; z-index:1; width:88%; height:80%; object-fit:contain; filter:drop-shadow(0 26px 40px rgba(0,0,0,.62)); transition:transform .8s cubic-bezier(.22,1,.36,1), filter .6s ease; }
        .design-card:hover img { transform:scale(1.045) translateY(-1%); }
        .design-card figcaption { position:absolute; left:0; right:0; bottom:0; z-index:3; display:flex; align-items:baseline; gap:10px; padding:22px 18px 14px; background:linear-gradient(180deg, transparent, rgba(0,0,0,.9) 60%); }
        .design-cap-num { font-family:'SF Mono','Monaco','Consolas',monospace; font-size:11px; font-weight:600; color:rgba(255,159,10,.75); letter-spacing:.08em; }
        .design-cap-text { font-size:clamp(12px,1.25vw,14px); font-weight:500; color:rgba(255,255,255,.78); letter-spacing:.01em; }
        @media (max-width: 900px) {
            .design-grid { grid-template-columns:repeat(2, 1fr); grid-auto-rows:clamp(170px,24vh,260px); }
            .design-card-wide { grid-column:span 2; }
        }
        @media (max-width: 560px) {
            .design-grid { grid-template-columns:1fr; grid-auto-rows:clamp(210px,30vh,280px); }
            .design-card-tall { grid-row:span 1; }
            .design-card-wide { grid-column:span 1; }
            .design-card img { inset:7% 5% 14%; height:79%; }
        }
        /* ── Design grid v2 — editorial close-up wall from the real 3D asset.
           Named areas, asymmetric spans, one mono kicker per cell: reads like a
           camera contact sheet, not a uniform thumbnail grid. ── */
        .design-grid-v2 {
            grid-template-columns: repeat(4, 1fr);
            grid-auto-rows: clamp(190px, 24vh, 285px);
            grid-template-areas:
                "a a b c"
                "a a b c"
                "d d e f"
                "g g e n";
        }
        .design-grid-v2 .dg-1 { grid-area: a; }
        .design-grid-v2 .dg-2 { grid-area: b; }
        .design-grid-v2 .dg-3 { grid-area: c; }
        .design-grid-v2 .dg-4 { grid-area: d; }
        .design-grid-v2 .dg-5 { grid-area: e; }
        .design-grid-v2 .dg-6 { grid-area: f; }
        .design-grid-v2 .dg-7 { grid-area: g; }
        .design-grid-v2::after {
            /* the note cell — folded dimensions, instrument voice */
            content: '49 × 35 × 21 cm';
            grid-area: n;
            display: flex; align-items: center; justify-content: center;
            border: 1px dashed rgba(255,213,138,.22); border-radius: 20px;
            font-family: ui-monospace,'SF Mono','Cascadia Mono',Menlo,Consolas,monospace;
            font-size: clamp(14px, 1.5vw, 19px); letter-spacing: .08em;
            color: rgba(255,213,138,.82);
            background:
                radial-gradient(80% 80% at 50% 50%, rgba(255,160,60,.05), transparent 75%);
        }
        .design-grid-v2 .design-kicker {
            position: absolute; top: 14px; left: 18px; z-index: 3;
            font-family: ui-monospace,'SF Mono','Cascadia Mono',Menlo,Consolas,monospace;
            font-size: 9.5px; font-weight: 500; letter-spacing: .22em;
            color: rgba(255,255,255,.38); text-transform: uppercase;
            pointer-events: none;
        }
        .design-grid-v2 .dg-4 .design-kicker,
        .design-grid-v2 .dg-6 .design-kicker { color: rgba(255,213,138,.66); }
        /* Every cell gets its own dark studio: court-blue, warm LED amber, or a
           barely-there engineering grid. This gives depth without washing the wall. */
        .design-grid-v2 .dg-1 {
            background:
                radial-gradient(75% 70% at 67% 28%, rgba(38,86,145,.22), transparent 68%),
                linear-gradient(145deg, #0b1119, #060708 70%);
        }
        .design-grid-v2 .dg-2 {
            background:
                radial-gradient(65% 60% at 50% 22%, rgba(255,95,62,.11), transparent 68%),
                linear-gradient(180deg, #101217, #060708);
        }
        .design-grid-v2 .dg-3 {
            background:
                radial-gradient(70% 66% at 55% 25%, rgba(45,107,160,.16), transparent 70%),
                linear-gradient(155deg, #0c1117, #050607);
        }
        .design-grid-v2 .dg-4 {
            background:
                linear-gradient(rgba(115,160,220,.025) 1px, transparent 1px),
                linear-gradient(90deg, rgba(115,160,220,.025) 1px, transparent 1px),
                radial-gradient(70% 90% at 72% 38%, rgba(255,143,56,.16), transparent 62%),
                linear-gradient(120deg, #0b0e12, #050607 72%);
            background-size: 28px 28px, 28px 28px, auto, auto;
        }
        .design-grid-v2 .dg-5 {
            background:
                radial-gradient(34% 82% at 50% 4%, rgba(235,242,255,.1), transparent 72%),
                linear-gradient(180deg, #0b0e13, #050607);
        }
        .design-grid-v2 .dg-6 {
            background:
                radial-gradient(85% 65% at 35% 72%, rgba(255,143,56,.13), transparent 65%),
                linear-gradient(145deg, #0b0d10, #050607);
        }
        .design-grid-v2 .dg-7 {
            background:
                linear-gradient(115deg, rgba(33,80,125,.11), transparent 46%),
                linear-gradient(180deg, #0b0e12, #050607);
        }
        /* Fill the Bento: hero/static renders use their full card; GLB macros crop
           deliberately like product photography rather than floating thumbnails. */
        .design-grid-v2 .dg-1 img { inset:3% 2% 8%; width:96%; height:89%; }
        .design-grid-v2 .dg-2 img,
        .design-grid-v2 .dg-3 img { inset:4% 3% 10%; width:94%; height:86%; }
        .design-grid-v2 .dg-4 img {
            inset:0; width:100%; height:100%;
            object-fit:cover; object-position:50% 40%;
            transform:scale(1.06);
        }
        .design-grid-v2 .dg-4:hover img { transform:scale(1.12); filter:drop-shadow(0 30px 55px rgba(0,0,0,.7)); }
        .design-grid-v2 .dg-5 img { inset:4% 3% 9%; width:94%; height:87%; }
        .design-grid-v2 .dg-6 img {
            inset:0; width:100%; height:100%;
            object-fit:cover; object-position:50% 62%;
            transform:scale(1.04);
        }
        .design-grid-v2 .dg-6:hover img { transform:scale(1.1); }
        .design-grid-v2 .dg-7 img { inset:4% 2% 10%; width:96%; height:86%; }
        @media (max-width: 1024px) and (min-width: 641px) {
            .design-grid-v2 {
                grid-template-columns: repeat(2, 1fr);
                grid-template-areas:
                    "a a"
                    "a a"
                    "b c"
                    "d d"
                    "e f"
                    "g g"
                    "n n";
            }
        }
        @media (max-width: 640px) {
            /* Bento-style compact wall on phones: the old single column made
               seven ~300px slabs (≈2100px of thumb travel). Two-up staggered
               rows cut the section to ~45% of that scroll at the same read. */
            .design-grid-v2 {
                grid-template-columns: 1fr 1fr;
                gap: 10px;
                grid-auto-rows: minmax(150px, 21vh);
                grid-template-areas:
                    "a a"
                    "b c"
                    "d e"
                    "f f"
                    "g n";
            }
            .design-grid-v2 .dg-1 img { inset:2% 2% 6%; width:96%; height:92%; }
            .design-grid-v2 .dg-7 img { inset:12% 4% 16%; width:92%; height:72%; }
            .design-grid-v2 .design-kicker { top: 10px; left: 12px; font-size: 8.5px; letter-spacing: .18em; }
            .design-grid-v2 .design-card figcaption { padding: 16px 12px 10px; }
            .design-grid-v2::after { min-height: 0; font-size: 12px; border-radius: 16px; }
        }
        /* ===== APP PREVIEW — Apple-grade cleaned up: one subtle halo, no multi-color chaos ===== */
        /* NOTE: no content-visibility here — the desktop app stage PINS this
           section (spacer ≫ any intrinsic placeholder), and content-visibility's
           late render used to shift layout mid-scroll → jumpy/buggy scrolling. */
        .app-section { padding: clamp(72px, 11vh, 120px) 0; background: linear-gradient(180deg, #010204 0%, #000 100%); position: relative; overflow: hidden; overflow-x: clip; }
        @media (max-width:768px) { .app-section { padding: clamp(40px, 6vh, 56px) 0; } }
        /* App section background: pure black (HomeCourt-style restraint). No halo. */
        .app-container { max-width: 1200px; margin: 0 auto; position: relative; z-index: 1; }
        .app-header { text-align: center; margin-bottom: clamp(22px, 4vh, 28px); padding: 0 clamp(16px, 4vw, 24px); max-width: 820px; margin-left: auto; margin-right: auto; }
        /* Eyebrow — Apple-style small caps brand tag with blue accent dot for visual hook */
        .app-eyebrow {
            display: inline-flex; align-items: center; gap: 8px;
            font-size: 12px; font-weight: 600;
            letter-spacing: 0.18em; text-transform: uppercase;
            color: rgba(41,151,255,.85);
            margin-bottom: 16px;
            padding: 6px 14px;
            background: rgba(41,151,255,.08);
            border: 1px solid rgba(41,151,255,.18);
            border-radius: 999px;
            backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
        }
        .app-eyebrow::before {
            content: ''; width: 6px; height: 6px; border-radius: 50%;
            background: #2997ff;
            box-shadow: 0 0 10px rgba(41,151,255,.5);
        }
        @media (max-width:768px) { .app-eyebrow { font-size: 11px; padding: 5px 12px; margin-bottom: 12px; } }
        .app-header h2 { font-size: clamp(38px, 6vw, 64px); font-weight: 600; letter-spacing: -0.045em; background: linear-gradient(180deg, #fff 24%, rgba(255,255,255,.72) 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; text-shadow: 0 0 24px rgba(41,151,255,.08); text-wrap: balance; }
        /* Accent phrase never breaks mid-word on phones ("一目了/然" read as a typo) */
        @media (max-width: 768px) { .app-header h2 span { white-space: nowrap; } }
        .app-header p { font-size: clamp(16px, 1.9vw, 18px); color: rgba(255,255,255,.62); margin: 14px auto 0; line-height: 1.6; max-width: 640px; }
        .app-phones {
            display: flex; gap: 20px;
            overflow-x: auto;
            scroll-snap-type: x mandatory;
            padding: 32px 48px 44px;
            scrollbar-width: none;
            position: relative; z-index: 1;
            justify-content: center;
            overscroll-behavior-x: contain;
            touch-action: pan-x;
            max-width: 100%;
            /* Soft edge fade — phones dissolve at the viewport edge instead of being
               sliced off mid-bezel ("手机被砍掉" fix). */
            -webkit-mask-image: linear-gradient(to right, transparent 0, #000 56px, #000 calc(100% - 56px), transparent 100%);
            mask-image: linear-gradient(to right, transparent 0, #000 56px, #000 calc(100% - 56px), transparent 100%);
        }
        .app-phones::-webkit-scrollbar { display: none; }
        /* Phone: unified bezel for all phones — no more pulsing ring on featured. Bigger size (260x520 desktop). */
        .app-phone {
            width: 260px; min-width: 260px; height: 520px;
            background: linear-gradient(180deg, rgba(18,22,35,.98), rgba(10,12,20,.96));
            border: 1px solid rgba(255,255,255,.12);
            border-radius: 36px;
            padding: 9px;
            /* transition only transform + border-color. box-shadow excluded — was causing per-frame
               shadow repaints during drag (scaleFocal writes transform on every scroll frame). */
            transition: transform 0.28s cubic-bezier(.22,.61,.36,1), border-color 0.25s ease;
            overflow: hidden;
            position: relative; z-index: 1;
            flex-shrink: 0;
            scroll-snap-align: center;
            will-change: transform;
            box-shadow:
                0 1px 0 rgba(255,255,255,.06) inset,
                0 18px 48px rgba(0,0,0,.55),
                0 2px 8px rgba(0,0,0,.35);
        }
        /* While the user is actively dragging the carousel, JS sets transform every frame.
           Removing transition makes the phones track the finger 1:1 instead of lagging
           behind the 0.28s ease curve — fixes the "划不动" sluggish feel. */
        .app-phones.is-dragging .app-phone { transition: none; }
        .app-phone:hover { border-color: rgba(255,255,255,.18); }
        /* Featured phone: scale-only distinction — no shadow, no glow, clean geometry. */
        .app-phone.is-featured {
            transform: scale(1.05);
            border-color: rgba(41,151,255,.28);
            box-shadow: none;
        }
        .app-phone.is-featured:hover {
            border-color: rgba(41,151,255,.42);
            box-shadow: none;
        }
        .app-phone-screen { width: 100%; height: 100%; border-radius: 28px; overflow: hidden; position: relative; }
        .app-phone-notch { position: absolute; top: 0; left: 50%; transform: translateX(-50%); width: 36%; height: 22px; background: #000; border-radius: 0 0 12px 12px; z-index: 3; }
        .app-status-bar { position: absolute; top: 0; left: 0; right: 0; height: 22px; display: flex; align-items: center; justify-content: space-between; padding: 4px 14px 0; font-size: 9px; font-weight: 600; color: rgba(255,255,255,.5); z-index: 2; }
        .app-tab-bar { position: absolute; bottom: 0; left: 0; right: 0; height: 44px; background: rgba(0,0,0,.6); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); border-top: 0.5px solid rgba(255,255,255,.06); display: flex; align-items: center; justify-content: space-around; padding: 0 8px; z-index: 2; }
        /* phones: five live blur surfaces under 3D transforms are a
           compositing tax — solid ink reads identically at this size */
        @media (max-width: 900px) {
            .app-tab-bar { -webkit-backdrop-filter: none; backdrop-filter: none; background: rgba(8,10,16,.88); }
        }
        .app-ghost-num { will-change: opacity; contain: paint; }
        /* 15m range card — baseline-machine coverage diagram on the left */
        .range-court {
            position: absolute; left: 12px; top: 14px;
            width: 56%; max-width: 250px; height: auto;
            z-index: 4; pointer-events: none; opacity: .92;
            filter: drop-shadow(0 4px 14px rgba(0,0,0,.5));
        }
        .app-tab { display: flex; flex-direction: column; align-items: center; gap: 2px; }
        .app-tab svg { opacity: 0.4; }
        .app-tab.active svg { opacity: 1; }
        .app-tab span { font-size: 8px; color: rgba(255,255,255,.3); font-weight: 500; }
        .app-tab.active span { color: var(--blue-light); }
        .app-screen-content { width: 100%; height: calc(100% - 44px); display: flex; flex-direction: column; align-items: center; padding: 30px 12px 6px; position: relative; overflow-y: auto; scrollbar-width: none; }
        .app-screen-content::-webkit-scrollbar { display: none; }
        .app-screen-label { font-size: 10px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; margin-bottom: 6px; }
        .app-screen-stat { font-size: 42px; font-weight: 900; letter-spacing: -0.04em; line-height: 1; margin-bottom: 2px; }
        .app-screen-sub { font-size: 9px; color: var(--gray-500); text-align: center; line-height: 1.4; }
        .app-mini-stats { display: flex; gap: 5px; width: 100%; margin-top: 8px; }
        .app-mini-stat { flex: 1; background: rgba(255,255,255,.04); border-radius: 8px; padding: 7px 3px; text-align: center; }
        .app-mini-stat .ms-val { font-size: 13px; font-weight: 800; color: var(--white); letter-spacing: -0.02em; }
        .app-mini-stat .ms-label { font-size: 6.5px; color: var(--gray-500); text-transform: uppercase; letter-spacing: 0.05em; margin-top: 2px; }
        .app-card { width: 100%; background: rgba(255,255,255,.045); border-radius: 10px; padding: 8px 10px; border: 0.5px solid rgba(255,255,255,.08); margin-top: 8px; box-shadow: inset 0 1px 0 rgba(255,255,255,.02); }
        .app-card-title { font-size: 7px; color: rgba(255,255,255,.25); text-transform: uppercase; letter-spacing: .06em; margin-bottom: 5px; }
        .app-explore-cta { display:flex; flex-direction:column; align-items:center; gap:14px; margin-top:clamp(20px,3vh,32px); padding:0 24px; }
        /* Apple-style segmented indicator — short dashes with the active one stretched brighter. Replaces pills. */
        .app-dots {
            display: flex; justify-content: center;
            gap: 6px;
            margin-top: 8px; padding: 0 24px;
        }
        .app-dot {
            width: 18px; height: 3px;
            border-radius: 2px;
            background: rgba(255,255,255,.18);
            transition: width .45s cubic-bezier(.22,.61,.36,1), background .3s ease;
            cursor: pointer;
            border: none;
        }
        .app-dot:hover { background: rgba(255,255,255,.32); }
        .app-dot.active {
            width: 40px;
            background: rgba(255,255,255,.82);
        }
        /* ═══════════════════════════════════════════════════════════════════════
           SCREEN DESIGN SYSTEM — one type voice inside every phone, echoing the
           page system: plain sans for content, tabular numerals for data, the
           shared mono stack for kickers/meta. No per-element inline typography.
           ═══════════════════════════════════════════════════════════════════════ */
        .app-screen-content { row-gap: 7px; }
        .scr-head { width: 100%; display: flex; align-items: flex-end; justify-content: space-between; gap: 8px; margin-bottom: 1px; }
        .scr-brand {
            display: inline-flex; align-items: center; gap: 4px;
            font-family: var(--font-mono); font-size: 7px; letter-spacing: .2em;
            color: rgba(255,255,255,.36); text-transform: uppercase;
        }
        .scr-brand::before { content: ''; width: 5px; height: 5px; border-radius: 2px; background: linear-gradient(135deg, #2997ff, #30d158); }
        .scr-title { font-size: 15px; font-weight: 700; letter-spacing: -.02em; color: #f5f5f7; margin-top: 3px; line-height: 1.1; }
        .scr-meta { font-family: var(--font-mono); font-size: 6.5px; letter-spacing: .05em; color: rgba(255,255,255,.32); text-transform: uppercase; text-align: right; line-height: 1.4; max-width: 40%; }
        .scr-live { display: inline-flex; align-items: center; gap: 3px; font-family: var(--font-mono); font-size: 6.5px; letter-spacing: .14em; color: #30d158; flex: none; }
        .scr-live i { width: 4px; height: 4px; border-radius: 50%; background: #30d158; box-shadow: 0 0 5px rgba(48,209,88,.8); animation: scrLive 1.7s ease-in-out infinite; }
        @keyframes scrLive { 50% { opacity: .3; } }
        .scr-card {
            width: 100%;
            background: rgba(255,255,255,.05);
            border: .5px solid rgba(255,255,255,.09);
            border-radius: 12px;
            padding: 9px 11px;
            box-shadow: inset 0 1px 0 rgba(255,255,255,.03);
        }
        .scr-cell { text-align: center; padding: 7px 4px; }
        .scr-kick {
            display: block;
            font-family: var(--font-mono); font-size: 6.8px; font-weight: 500;
            letter-spacing: .12em; text-transform: uppercase;
            color: rgba(255,255,255,.36); margin-bottom: 5px;
        }
        .scr-num { font-variant-numeric: tabular-nums; font-weight: 700; color: #f5f5f7; line-height: 1; letter-spacing: -.02em; }
        .scr-num i { font-style: normal; font-family: var(--font-mono); font-size: .52em; font-weight: 500; margin-left: 1px; color: rgba(255,255,255,.42); }
        .scr-sub { font-size: 8.5px; color: rgba(255,255,255,.44); line-height: 1.45; }
        .scr-tip-t { font-size: 9.5px; font-weight: 600; color: #f5f5f7; margin-bottom: 1px; }
        .scr-row { display: flex; align-items: center; justify-content: space-between; gap: 6px; width: 100%; }
        .scr-cols { display: flex; gap: 6px; width: 100%; }
        .scr-cols > * { flex: 1; min-width: 0; }
        .scr-bar { flex: 1; height: 3px; border-radius: 2px; background: rgba(255,255,255,.07); overflow: hidden; }
        .scr-bar b { display: block; height: 100%; border-radius: 2px; background: var(--c, #2997ff); }
        .scr-pips { display: flex; gap: 2.5px; width: 100%; margin-top: 6px; }
        .scr-pips i { flex: 1; height: 3px; border-radius: 2px; background: rgba(255,255,255,.08); }
        .scr-pips i.on { background: #30d158; }
        .scr-seg { display: flex; gap: 3px; width: 100%; }
        .scr-seg span {
            flex: 1; text-align: center; padding: 4.5px 0; border-radius: 7px;
            font-family: var(--font-mono); font-size: 7px; letter-spacing: .1em;
            color: rgba(255,255,255,.34);
        }
        .scr-seg span.on { color: #7cb8ff; background: rgba(41,151,255,.12); }
        .scr-btn {
            width: 100%; padding: 9px 0; text-align: center;
            font-size: 10px; font-weight: 600; letter-spacing: .01em; color: #fff;
            border-radius: 11px;
            background: linear-gradient(180deg, #33d15e, #22a447);
            box-shadow: 0 4px 14px rgba(48,209,88,.2), inset 0 1px 0 rgba(255,255,255,.22);
        }
        .scr-btn.ghost {
            background: rgba(41,151,255,.1);
            border: .5px solid rgba(41,151,255,.22);
            color: #7cb8ff; box-shadow: none;
        }
        .scr-btn.ghost.purple { background: rgba(167,139,250,.1); border-color: rgba(167,139,250,.24); color: #c9b8fd; }
        .scr-chip { display: inline-flex; align-items: center; gap: 3px; font-family: var(--font-mono); font-size: 6.5px; letter-spacing: .06em; color: rgba(255,255,255,.4); text-transform: uppercase; }
        .scr-chip i { width: 5px; height: 5px; border-radius: 50%; }
        .scr-dot { width: 16px; height: 16px; min-width: 16px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; margin-top: 1px; }
        .scr-video { position: relative; width: 100%; aspect-ratio: 16/7; border-radius: 8px; overflow: hidden; background: linear-gradient(135deg, rgba(167,139,250,.05), rgba(0,0,0,.3)); border: .5px solid rgba(255,255,255,.07); }
        .scr-play {
            position: absolute; inset: 0; margin: auto; width: 26px; height: 26px;
            border-radius: 50%; display: flex; align-items: center; justify-content: center;
            background: rgba(167,139,250,.18);
            backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
        }
        /* tab bar labels join the mono system + iOS home indicator */
        .app-tab span { font-family: var(--font-mono); font-size: 7px; letter-spacing: .1em; text-transform: uppercase; }
        .app-tab-bar::after {
            content: ''; position: absolute; bottom: 3px; left: 50%; transform: translateX(-50%);
            width: 72px; height: 4px; border-radius: 2px; background: rgba(255,255,255,.26);
        }
        /* ═══════════════════════════════════════════════════════════════════════
           APP STAGE — desktop pinned coverflow. The row becomes a 3D deck: the
           focused screen holds center stage, the rest fan behind with depth blur.
           Scroll steps through the five screens; a headline + two instrument chips
           swap per screen; the key numeral ghosts across the backdrop in Michroma.
           Mobile keeps the swipe carousel untouched (this block is ≥901px only).
           ═══════════════════════════════════════════════════════════════════════ */
        .app-stage-headline, .app-stage-chips, .app-ghost-num { display: none; }
        @media (min-width: 901px) {
            .app-section.is-stage {
                /* full-viewport stage (unpinned ambient carousel) */
                min-height: 100vh; min-height: 100lvh;
                display: flex; flex-direction: column; justify-content: center;
                padding: 0;
                content-visibility: visible;
                contain-intrinsic-size: auto;
                /* Ghost numeral (30vw Michroma) can poke past the viewport — keep the
                   stage from inflating document scroll width. */
                overflow: clip;
            }
            .app-section.is-stage .app-container { width: 100%; max-width: none; }
            .app-section.is-stage .app-header { margin-bottom: 0; }
            .app-section.is-stage .app-header p { display: none; }
            /* Deck replaces the scroll row */
            .app-section.is-stage .app-phones {
                overflow: visible;
                height: clamp(480px, 62vh, 590px);
                padding: 18px 0 8px;
                -webkit-mask-image: none; mask-image: none;
                perspective: 1500px;
                touch-action: auto;
            }
            .app-section.is-stage .app-phone {
                position: absolute;
                left: 50%; top: 24px;
                margin-left: -130px;
                scroll-snap-align: none;
                transition: none;
                cursor: pointer;
            }
            .app-section.is-stage .app-phone.is-featured { transform: none; box-shadow: 0 18px 48px rgba(0,0,0,.55); }
            /* is-live: border tint ONLY. The old double shadow (30px drop +
               60px glow) repainted a 260×520 rounded layer at 3x DPR on every
               step — mid-slide, that paint spike was the deck's stutter. The
               base card shadow stays constant; focus reads via border + the
               composited --focus overlay. */
            .app-section.is-stage .app-phone.is-live {
                border-color: rgba(120, 165, 255, .38);
            }
            /* Inner text churn (count-ups) must never invalidate layout or
               paint outside its own card while cards are mid-transform. */
            .app-section.is-stage .app-phone { contain: layout style paint; }
            /* Focused screen: rings draw themselves in */
            .app-section.is-stage .app-phone .app-phone-screen svg circle[stroke-linecap="round"] {
                transition: stroke-dasharray 1.1s cubic-bezier(.22,.61,.36,1) .25s;
            }
            .app-section.is-stage .app-phone:not(.is-live) .app-phone-screen svg circle[stroke-linecap="round"] {
                stroke-dasharray: 0 999;
                transition: stroke-dasharray .3s ease;
            }
            /* Ghost numeral — mission-control backdrop */
            .app-section.is-stage .app-ghost-num {
                display: block;
                position: absolute; left: 50%; top: 46%;
                transform: translate(-50%, -50%);
                font-family: var(--font-display);
                font-size: clamp(220px, 30vw, 430px);
                line-height: 1;
                color: transparent;
                -webkit-text-stroke: 1px rgba(255,255,255,.07);
                letter-spacing: -.01em;
                pointer-events: none; z-index: 0;
                font-variant-numeric: tabular-nums;
                white-space: nowrap;
            }
            /* Rotating headline under the header */
            .app-section.is-stage .app-stage-headline {
                display: block;
                position: relative; height: 40px; margin-top: 6px;
                text-align: center; z-index: 2; pointer-events: none;
            }
            .app-section.is-stage .app-stage-headline p {
                position: absolute; left: 0; right: 0; top: 0;
                font-size: clamp(17px, 1.7vw, 21px);
                font-weight: 300; letter-spacing: -.01em;
                color: rgba(255,255,255,.66);
                opacity: 0; transform: translateY(12px);
                transition: opacity .45s ease, transform .45s cubic-bezier(.22,.61,.36,1);
            }
            .app-section.is-stage .app-stage-headline p.is-on { opacity: 1; transform: translateY(0); }
            /* Instrument chips flanking the featured phone */
            .app-section.is-stage .app-stage-chips { display: block; position: absolute; inset: 0; pointer-events: none; z-index: 6; }
            .asc-group { position: absolute; inset: 0; }
            .app-chip {
                position: absolute;
                max-width: 240px;
                padding: 11px 15px 10px;
                border-radius: 3px;
                display: flex; flex-direction: column; gap: 4px;
                opacity: 0;
            }
            .app-chip.is-left  { left: calc(50% - 480px); text-align: right; align-items: flex-end; }
            .app-chip.is-right { left: calc(50% + 240px); text-align: left; align-items: flex-start; }
            .app-chip .ac-tag {
                font-size: 10px; letter-spacing: .14em; color: rgba(140,180,255,.85);
                font-family: var(--font-mono);
            }
            .app-chip .ac-text { font-size: 13px; line-height: 1.45; color: rgba(255,255,255,.78); font-weight: 400; }
            /* connector dash toward the phone (span injected by the stage JS) */
            .app-chip-line {
                position: absolute; top: 50%; width: 56px; height: 1px;
                background: linear-gradient(90deg, rgba(160,195,255,.55), rgba(160,195,255,0));
            }
            .app-chip.is-left .app-chip-line { right: -60px; transform: scaleX(-1); }
            .app-chip.is-right .app-chip-line { left: -60px; }
            @media (max-width: 1180px) {
                .app-chip.is-left  { left: calc(50% - 420px); max-width: 200px; }
                .app-chip.is-right { left: calc(50% + 220px); max-width: 200px; }
            }
        }
        /* ── Portrait parity: the SAME coverflow stage on phones — compact deck
           geometry, rotating headline and ghost numeral included. Only the
           flanking chips stay retired (no side gutters to live in). ── */
        @media (max-width: 900px) {
            .app-section.is-stage {
                /* full-viewport stage (unpinned ambient carousel) */
                min-height: 100vh; min-height: 100lvh;
                display: flex; flex-direction: column; justify-content: center;
                padding: 0;
                content-visibility: visible;
                contain-intrinsic-size: auto;
                overflow: clip;
            }
            .app-section.is-stage .app-container { width: 100%; max-width: none; }
            .app-section.is-stage .app-header { margin-bottom: 0; }
            .app-section.is-stage .app-header p { display: none; }
            .app-section.is-stage .app-phones {
                overflow: visible;
                height: clamp(430px, 56vh, 520px);
                padding: 12px 0 6px;
                -webkit-mask-image: none; mask-image: none;
                perspective: 1100px;
                touch-action: pan-y;
                justify-content: center;
            }
            .app-section.is-stage .app-phone {
                position: absolute;
                left: 50%; top: 14px;
                /* one deck geometry across the whole compact range */
                width: 220px; min-width: 220px; height: 440px;
                margin-left: -110px;
                border-radius: 32px;
                scroll-snap-align: none;
                transition: none;
                cursor: pointer;
            }
            .app-section.is-stage .app-phone.is-featured { transform: none; box-shadow: 0 18px 48px rgba(0,0,0,.55); }
            /* focus reads via border tint only — the extra glow layer forced
               a full card repaint on every step (mobile scroll jank) */
            .app-section.is-stage .app-phone.is-live {
                border-color: rgba(120, 165, 255, .38);
            }
            .app-section.is-stage .app-phone { contain: layout style paint; }
            .app-section.is-stage .app-phone .app-phone-screen svg circle[stroke-linecap="round"] {
                transition: stroke-dasharray 1.1s cubic-bezier(.22,.61,.36,1) .25s;
            }
            .app-section.is-stage .app-phone:not(.is-live) .app-phone-screen svg circle[stroke-linecap="round"] {
                stroke-dasharray: 0 999;
                transition: stroke-dasharray .3s ease;
            }
            .app-section.is-stage .app-ghost-num {
                display: block;
                position: absolute; left: 50%; top: 45%;
                transform: translate(-50%, -50%);
                font-family: var(--font-display);
                font-size: clamp(120px, 42vw, 220px);
                line-height: 1;
                color: transparent;
                -webkit-text-stroke: 1px rgba(255,255,255,.07);
                letter-spacing: -.01em;
                pointer-events: none; z-index: 0;
                font-variant-numeric: tabular-nums;
                white-space: nowrap;
            }
            .app-section.is-stage .app-stage-headline {
                display: block;
                position: relative; height: 34px; margin-top: 4px;
                text-align: center; z-index: 2; pointer-events: none;
                padding: 0 18px;
            }
            .app-section.is-stage .app-stage-headline p {
                position: absolute; left: 0; right: 0; top: 0;
                font-size: clamp(13.5px, 3.8vw, 17px);
                font-weight: 300; letter-spacing: -.01em;
                color: rgba(255,255,255,.66);
                opacity: 0; transform: translateY(12px);
                transition: opacity .45s ease, transform .45s cubic-bezier(.22,.61,.36,1);
            }
            .app-section.is-stage .app-stage-headline p.is-on { opacity: 1; transform: translateY(0); }
        }
        @media (max-width: 768px) {
            .app-phones {
                gap: 14px; padding: 20px 24px 32px; justify-content: flex-start;
                -webkit-mask-image: linear-gradient(to right, transparent 0, #000 28px, #000 calc(100% - 28px), transparent 100%);
                mask-image: linear-gradient(to right, transparent 0, #000 28px, #000 calc(100% - 28px), transparent 100%);
            }
            .app-phone { width: 220px; min-width: 220px; height: 440px; border-radius: 32px; }
            .app-phone-screen { border-radius: 24px; }
        }
        @media (max-width: 600px) {
            .app-header p { font-size: 15px; }
            .app-phones {
                gap: 12px; padding: 16px 16px 26px; justify-content: flex-start;
                -webkit-mask-image: linear-gradient(to right, transparent 0, #000 20px, #000 calc(100% - 20px), transparent 100%);
                mask-image: linear-gradient(to right, transparent 0, #000 20px, #000 calc(100% - 20px), transparent 100%);
            }
            .app-phone { width: 210px; min-width: 210px; height: 420px; border-radius: 30px; }
            .app-phone-screen { border-radius: 22px; }
        }
        /* visibility, not just opacity. An opacity:0 element is still visible to
           the renderer: this one is fixed, full-screen and carries a 32px
           backdrop-filter, so while closed it was still holding a compositor
           layer and re-blurring the entire viewport on every scrolled frame —
           measured at roughly a third of the frame rate across the whole page
           (teardown 36 -> 56 fps once it stops painting). visibility is delayed
           by .45s on the way out, matching the slowest child transition
           (.story-exp-shell's transform), so the fade is unchanged; on the way
           in it flips immediately. */
        .story-exp { position:fixed; inset:0; z-index:10030; background:rgba(0,0,0,.82); backdrop-filter:blur(32px); -webkit-backdrop-filter:blur(32px); opacity:0; visibility:hidden; pointer-events:none; transition:opacity .35s cubic-bezier(.4,0,.2,1), visibility 0s linear .45s; display:flex; align-items:center; justify-content:center; }
        .story-exp.active { opacity:1; visibility:visible; pointer-events:all; transition:opacity .35s cubic-bezier(.4,0,.2,1), visibility 0s; }
        .story-exp-close { position:fixed; top:28px; right:32px; width:44px; height:44px; border-radius:50%; border:1px solid rgba(255,255,255,.1); background:rgba(255,255,255,.06); color:#fff; display:flex; align-items:center; justify-content:center; cursor:pointer; z-index:10032; transition:background .25s, transform .25s; }
        .story-exp-close:hover { background:rgba(255,255,255,.14); transform:scale(1.08) rotate(90deg); }
        .story-exp-close svg { width:18px; height:18px; stroke:#fff; stroke-width:2; fill:none; }
        .story-exp-nav { position:fixed; top:50%; z-index:10032; width:44px; height:44px; border-radius:50%; border:1px solid rgba(255,255,255,.1); background:rgba(255,255,255,.06); backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px); color:#fff; display:flex; align-items:center; justify-content:center; cursor:pointer; transition:background .25s, transform .25s, opacity .25s; opacity:0; pointer-events:none; }
        .story-exp.active .story-exp-nav { opacity:1; pointer-events:all; }
        .story-exp-nav:hover { background:rgba(255,255,255,.14); transform:translateY(-50%) scale(1.08); }
        .story-exp-nav svg { width:18px; height:18px; stroke:#fff; stroke-width:2; fill:none; }
        .story-exp-prev { left:max(12px, env(safe-area-inset-left, 12px)); transform:translateY(-50%); }
        .story-exp-next { right:max(12px, env(safe-area-inset-right, 12px)); transform:translateY(-50%); }
        @media (max-width: 640px) { .story-exp-prev { left:8px; width:36px; height:36px; } .story-exp-next { right:8px; width:36px; height:36px; } .story-exp-nav svg { width:14px; height:14px; } }
        .story-exp-shell { width:min(680px, calc(100vw - 48px)); max-height:calc(100vh - 64px); overflow-y:auto; text-align:center; transform:scale(.92) translateY(24px); opacity:0; transition:transform .45s cubic-bezier(.16,1,.3,1), opacity .35s ease; --swipe:0px; --swipe-op:1; }
        .story-exp.active .story-exp-shell { transform:scale(1) translateY(0); opacity:1; }
        .story-exp-shell::-webkit-scrollbar { width:4px; } .story-exp-shell::-webkit-scrollbar-thumb { background:rgba(255,255,255,.1); border-radius:4px; }
        /* Layered parallax during swipe — each element tracks --swipe at its own ratio for depth */
        .story-exp-shell > .story-exp-media,
        .story-exp-shell > .story-exp-kicker,
        .story-exp-shell > .story-exp-title,
        .story-exp-shell > .story-exp-desc,
        .story-exp-shell > .story-exp-body,
        .story-exp-shell > .story-exp-metrics {
            opacity: var(--swipe-op);
            will-change: transform, opacity;
        }
        .story-exp-shell > .story-exp-media    { transform: translate3d(calc(var(--swipe) * 1), 0, 0); }
        .story-exp-shell > .story-exp-kicker   { transform: translate3d(calc(var(--swipe) * 0.72), 0, 0); }
        .story-exp-shell > .story-exp-title    { transform: translate3d(calc(var(--swipe) * 0.58), 0, 0); }
        .story-exp-shell > .story-exp-desc     { transform: translate3d(calc(var(--swipe) * 0.46), 0, 0); }
        .story-exp-shell > .story-exp-body     { transform: translate3d(calc(var(--swipe) * 0.34), 0, 0); }
        .story-exp-shell > .story-exp-metrics  { transform: translate3d(calc(var(--swipe) * 0.24), 0, 0); }
        .story-exp-media { position:relative; width:100%; aspect-ratio:16/9; border-radius:20px; overflow:hidden; background:#050505; margin-bottom:32px; }
        .story-exp-media video,
        .story-exp-media img { width:100%; height:100%; object-fit:cover; opacity:.98; filter:brightness(.98) saturate(1.08); }
        /* picture is inline by default, which leaves a text baseline gap
           under it inside the 16:9 frame. */
        .story-exp-media picture { display:block; width:100%; height:100%; }
        .story-exp-media::after { content:''; position:absolute; inset:0; background:linear-gradient(180deg, transparent 56%, rgba(0,0,0,.2) 100%); pointer-events:none; }
        .story-exp-stat { position:absolute; left:20px; bottom:20px; z-index:2; padding:10px 14px; border-radius:14px; background:rgba(0,0,0,.5); border:1px solid rgba(255,255,255,.08); color:#fff; backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px); font-size:clamp(28px,5vw,36px); font-weight:900; letter-spacing:-0.03em; }
        .story-exp-kicker { font-size:11px; font-weight:700; letter-spacing:.16em; text-transform:uppercase; color:rgba(255,255,255,.48); margin-bottom:14px; }
        .story-exp-title { font-size:clamp(26px, 4vw, 38px); font-weight:800; line-height:1.08; letter-spacing:-0.04em; margin:0 auto; color:#f5f5f7; max-width:560px; }
        /* Per-story accent gradients — refined hues; color lives in type only */
        .story-exp-title .em,
        .story-exp-desc em, .story-exp-body em { font-style:normal; background:linear-gradient(135deg,#30d158,#63e6a5); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
        .story-exp[data-accent="arc"] .story-exp-title .em,
        .story-exp[data-accent="rapid"] .story-exp-title .em,
        .story-exp[data-accent="arc"] .story-exp-desc em, .story-exp[data-accent="arc"] .story-exp-body em,
        .story-exp[data-accent="rapid"] .story-exp-desc em, .story-exp[data-accent="rapid"] .story-exp-body em { background:linear-gradient(135deg,#ffb340,#ff7a3d); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
        .story-exp[data-accent="multi"] .story-exp-title .em,
        .story-exp[data-accent="multi"] .story-exp-desc em, .story-exp[data-accent="multi"] .story-exp-body em { background:linear-gradient(135deg,#bf5af2,#8e5bf7); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
        .story-exp[data-accent="safety"] .story-exp-title .em,
        .story-exp[data-accent="stop"] .story-exp-title .em,
        .story-exp[data-accent="safety"] .story-exp-desc em, .story-exp[data-accent="safety"] .story-exp-body em,
        .story-exp[data-accent="stop"] .story-exp-desc em, .story-exp[data-accent="stop"] .story-exp-body em { background:linear-gradient(135deg,#ff5f6d,#ff8a80); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
        .story-exp[data-accent="local"] .story-exp-title .em,
        .story-exp[data-accent="encrypt"] .story-exp-title .em,
        .story-exp[data-accent="delete"] .story-exp-title .em,
        .story-exp[data-accent="child"] .story-exp-title .em,
        .story-exp[data-accent="local"] .story-exp-desc em, .story-exp[data-accent="local"] .story-exp-body em,
        .story-exp[data-accent="encrypt"] .story-exp-desc em, .story-exp[data-accent="encrypt"] .story-exp-body em,
        .story-exp[data-accent="delete"] .story-exp-desc em, .story-exp[data-accent="delete"] .story-exp-body em,
        .story-exp[data-accent="child"] .story-exp-desc em, .story-exp[data-accent="child"] .story-exp-body em { background:linear-gradient(135deg,#2997ff,#64d2ff); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
        .story-exp-desc { margin:14px auto 0; font-size:16px; line-height:1.6; color:rgba(255,255,255,.6); max-width:520px; }
        .story-exp-body { display:flex; flex-direction:column; gap:12px; margin:28px auto 0; max-width:520px; text-align:left; }
        .story-exp-body p { margin:0; font-size:15px; line-height:1.72; color:rgba(255,255,255,.52); }
        .story-exp-metrics { display:flex; flex-wrap:wrap; gap:8px; margin:28px auto 0; justify-content:center; max-width:520px; }
        .story-exp-chip { display:inline-flex; align-items:center; min-height:34px; padding:7px 14px; border-radius:999px; background:rgba(255,255,255,.04); border:1px solid rgba(255,255,255,.07); color:rgba(255,255,255,.68); font-size:12px; font-weight:600; letter-spacing:.01em; transition:background .2s, border-color .2s; }
        .story-exp-chip:hover { background:rgba(255,255,255,.08); border-color:rgba(255,255,255,.14); }
        @media (max-width: 640px) { .story-exp-shell { width:calc(100vw - 32px); max-height:calc(100vh - 32px); } .story-exp-media { border-radius:14px; margin-bottom:24px; } .story-exp-close { top:16px; right:16px; width:38px; height:38px; } .story-exp-title { font-size:clamp(22px,5vw,30px); } }
        .app-phone { cursor: pointer; }
        .app-phone::after { display:none; }
        .app-section {
            isolation: isolate;
            /* Real court under the UI — graded down to near-noir so the section
               sits in a real place rather than on a flat gradient. The frame is
               now ours: the film's poolside court at 84.0s, machine deployed and
               tracking rings on a player, in place of a stock beach court full
               of strangers. Under this much grading almost none of it reads,
               which is the point — what it has to be is true, not legible. */
            background:
                linear-gradient(180deg, rgba(1,2,4,.92) 0%, rgba(0,0,0,.78) 34%, rgba(0,0,0,.8) 66%, rgba(1,2,3,.94) 100%),
                url('/static/court-aerial.webp?v=4k60c') center 32% / cover no-repeat,
                #010204;
        }
        .app-section::before {
            content:'';
            position:absolute;
            inset:0;
            z-index:0;
            pointer-events:none;
            background:
                radial-gradient(90% 70% at 50% 42%, transparent 40%, rgba(0,0,0,.55) 100%),
                linear-gradient(110deg, transparent 0 24%, rgba(41,151,255,.10) 42%, transparent 58% 100%),
                linear-gradient(rgba(255,255,255,.022) 1px, transparent 1px),
                linear-gradient(90deg, rgba(255,255,255,.016) 1px, transparent 1px);
            background-size: 100% 100%, 100% 100%, 72px 72px, 72px 72px;
            opacity:.8;
            transform: translateZ(0);
        }
        .app-section::after {
            content:'';
            position:absolute;
            left:-25%;
            right:-25%;
            top:14%;
            height:48%;
            z-index:0;
            pointer-events:none;
            background: linear-gradient(105deg, transparent 18%, rgba(41,151,255,.07) 40%, rgba(41,151,255,.12) 50%, rgba(41,151,255,.07) 60%, transparent 82%);
            opacity:.42;
            transform: translate3d(-10%,0,0) skewY(-7deg);
            animation: appLightSweep 14s cubic-bezier(.4,0,.2,1) infinite;
        }
        @keyframes appLightSweep {
            0%,100% { transform: translate3d(-18%,0,0) skewY(-7deg); opacity:.22; }
            45% { opacity:.54; }
            70% { transform: translate3d(18%,0,0) skewY(-7deg); opacity:.34; }
        }
        .app-header h2 {
            font-size: clamp(42px, 7vw, 68px);
            line-height: .98;
        }
        .app-header p { color: rgba(245,245,247,.72); }
        .app-phones {
            perspective: 1400px;
            transform-style: preserve-3d;
        }
        .app-phone {
            transform-style: preserve-3d;
            border-color: rgba(255,255,255,.16);
            box-shadow:
                0 1px 0 rgba(255,255,255,.12) inset,
                0 -1px 0 rgba(0,0,0,.55) inset,
                0 22px 64px rgba(0,0,0,.62),
                0 0 0 1px rgba(255,255,255,.04);
        }
        .app-phone::before {
            content:'';
            position:absolute;
            left:18px;
            right:18px;
            top:10px;
            height:1px;
            z-index:5;
            background:linear-gradient(90deg, transparent, rgba(255,255,255,.42), transparent);
            opacity:.8;
            pointer-events:none;
        }
        .app-phone::after {
            content:'';
            display:block;
            position:absolute;
            inset:7px;
            border-radius:30px;
            border:1px solid rgba(41,151,255,.16);
            box-shadow:0 0 22px rgba(41,151,255,.12) inset;
            opacity:var(--focus, .22);
            pointer-events:none;
            transition:opacity .28s ease, border-color .28s ease;
        }
        .app-phone-screen {
            isolation:isolate;
            transform: rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateZ(0);
            transition: transform .45s cubic-bezier(.16,1,.3,1), filter .35s ease;
        }
        .app-phone-screen::before {
            content:'';
            position:absolute;
            inset:0;
            z-index:1;
            pointer-events:none;
            background:
                linear-gradient(180deg, rgba(255,255,255,.10) 0%, transparent 16%),
                linear-gradient(120deg, transparent 28%, rgba(255,255,255,.08) 48%, transparent 68%);
            opacity:.52;
        }
        .app-phone-screen::after {
            content:'';
            position:absolute;
            left:0;
            right:0;
            top:-20%;
            height:26%;
            z-index:2;
            pointer-events:none;
            background:linear-gradient(180deg, transparent, rgba(41,151,255,.16), transparent);
            opacity:.5;
            animation: appScanLine 4.8s ease-in-out infinite;
        }
        @keyframes appScanLine {
            0%,100% { transform: translateY(0); opacity:.08; }
            45% { opacity:.45; }
            70% { transform: translateY(420%); opacity:.22; }
        }
        .app-phone.is-featured {
            border-color: rgba(41,151,255,.52);
            box-shadow:
                0 1px 0 rgba(255,255,255,.18) inset,
                0 34px 90px rgba(0,0,0,.72),
                0 18px 58px rgba(41,151,255,.22),
                0 0 0 1px rgba(41,151,255,.25);
        }
        .app-phone.is-featured::after {
            border-color: rgba(41,151,255,.34);
            box-shadow:0 0 28px rgba(41,151,255,.22) inset, 0 0 30px rgba(41,151,255,.10);
        }
        .app-card,
        .app-mini-stat {
            background: rgba(255,255,255,.06);
            border-color: rgba(255,255,255,.10);
            box-shadow: 0 1px 0 rgba(255,255,255,.05) inset;
        }
        /* ═══════════════════════════════════════════════════════════════════
           COURT INSTRUMENT KIT — the five screens talk basketball instead of
           step-counter: a real half-court shot chart, release arcs stacked on
           one axis, the pose read at the set point, and the machine's own feed
           plan. Every graphic is flat-filled SVG with no filter, blur or
           backdrop-filter, so the low tier renders it byte-identical to the
           high tier, and the only motion is decorative loops that the global
           reduced-motion rule collapses onto their finished frame.
           ═══════════════════════════════════════════════════════════════════ */
        /* Figure box — the frame every court/arc/pose diagram lives in. */
        .scr-fig { position: relative; width: 100%; border-radius: 9px; overflow: hidden; background: rgba(0,0,0,.24); border: .5px solid rgba(255,255,255,.06); }
        .scr-fig > svg { display: block; width: 100%; height: auto; }
        /* Instrument readouts pinned into the corners of a figure. */
        .scr-ov {
            position: absolute; z-index: 2;
            font-family: var(--font-mono); font-size: 6px; letter-spacing: .1em;
            color: rgba(255,255,255,.44); text-transform: uppercase;
            font-variant-numeric: tabular-nums; white-space: nowrap; pointer-events: none;
        }
        .scr-ov.tl { top: 5px; left: 6px; }
        .scr-ov.tr { top: 5px; right: 6px; }
        .scr-ov.bl { bottom: 5px; left: 6px; }
        .scr-ov.br { bottom: 5px; right: 6px; }
        /* Verdict badge — the call the vision stack just made on a live ball. */
        .scr-call {
            position: absolute; top: 5px; right: 6px; z-index: 2;
            display: inline-flex; align-items: center; gap: 3px;
            padding: 2.5px 6px; border-radius: 5px;
            font-family: var(--font-mono); font-size: 6.5px; font-weight: 700;
            letter-spacing: .14em; text-transform: uppercase;
            color: #59e57f; background: rgba(48,209,88,.16); border: .5px solid rgba(48,209,88,.34);
        }
        .scr-call::before { content: ''; width: 4px; height: 4px; border-radius: 50%; background: #30d158; }
        /* Make / miss legend and any icon+number pair. */
        .scr-legend { display: flex; align-items: center; justify-content: center; gap: 9px; flex-wrap: wrap; width: 100%; }
        .scr-lg {
            display: inline-flex; align-items: center; gap: 3.5px;
            font-family: var(--font-mono); font-size: 6.4px; letter-spacing: .07em;
            text-transform: uppercase; color: rgba(255,255,255,.42); min-width: 0;
        }
        .scr-lg svg { flex: none; }
        .scr-lg b { font-weight: 700; color: #f5f5f7; font-variant-numeric: tabular-nums; font-size: 8.5px; letter-spacing: -.01em; }
        /* Shot log — one cell per rep, filled = down, hollow = rim out. */
        .scr-ticker { display: flex; gap: 2px; width: 100%; align-items: stretch; }
        .scr-ticker i { flex: 1 1 0; min-width: 0; height: 11px; border-radius: 2px; background: rgba(48,209,88,.8); }
        .scr-ticker i.ms { background: transparent; box-shadow: inset 0 0 0 1px rgba(255,69,58,.5); }
        .scr-ticker i.nx { background: rgba(255,255,255,.07); box-shadow: none; }
        /* Feed progress — how much of the hopper has gone through the wheels. */
        .scr-feed { width: 100%; height: 4px; border-radius: 2px; background: rgba(255,255,255,.07); overflow: hidden; margin-top: 7px; }
        .scr-feed b { display: block; height: 100%; border-radius: 2px; background: linear-gradient(90deg, rgba(41,151,255,.65), #2997ff); }
        /* Metric trio — the Shot-Science numbers. Labels wrap rather than clip:
           "Release Angle" is "Abwurfwinkel" in German and twice that in Korean. */
        .scr-metrics { display: flex; gap: 5px; width: 100%; align-items: stretch; }
        .scr-metric {
            flex: 1 1 0; min-width: 0;
            display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
            gap: 3px; text-align: center; padding: 7px 3px 6px;
            border-radius: 9px; background: rgba(255,255,255,.05);
            border: .5px solid rgba(255,255,255,.09);
            box-shadow: inset 0 1px 0 rgba(255,255,255,.03);
        }
        .scr-metric .mv {
            font-variant-numeric: tabular-nums; font-weight: 700; font-size: 13px;
            color: #f5f5f7; letter-spacing: -.02em; line-height: 1;
        }
        .scr-metric .mv i {
            font-style: normal; font-family: var(--font-mono); font-size: .58em;
            font-weight: 500; color: rgba(255,255,255,.44); margin-left: .5px;
        }
        .scr-metric .ml {
            font-family: var(--font-mono); font-size: 6px; line-height: 1.32;
            letter-spacing: .06em; text-transform: uppercase;
            color: rgba(255,255,255,.38);
            overflow-wrap: anywhere; hyphens: auto;
        }
        /* Feed-plan chips — ball count and cadence, icon-led so they read the
           same in every locale. */
        .scr-plan { display: flex; gap: 5px; width: 100%; }
        .scr-pchip {
            flex: 1 1 0; min-width: 0;
            display: inline-flex; align-items: center; justify-content: center; gap: 4px;
            padding: 4.5px 4px; border-radius: 7px;
            background: rgba(167,139,250,.1); border: .5px solid rgba(167,139,250,.2);
            font-family: var(--font-mono); font-size: 8px; font-weight: 600;
            color: #c9b8fd; font-variant-numeric: tabular-nums; letter-spacing: .02em;
        }
        .scr-pchip svg { flex: none; }
        /* Live ball in flight. The dots march along the track rather than the
           track drawing itself in: the static frame is already the finished
           picture, so the global reduced-motion collapse (0.01ms duration)
           lands on a complete diagram instead of a blank one. Offset only —
           no geometry, no filter, nothing the low tier has to give up. */
        .scr-flight { animation: scrFlight 2.1s linear infinite; }
        @keyframes scrFlight { to { stroke-dashoffset: -14; } }
        .scr-ballmark { animation: scrBallMark 2.1s ease-in-out infinite; }
        @keyframes scrBallMark { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
        /* Timeline playhead inside the pose frame. */
        .scr-scrub { position: absolute; left: 6px; right: 6px; bottom: 6px; height: 2px; border-radius: 1px; background: rgba(255,255,255,.1); z-index: 2; }
        .scr-scrub b { position: absolute; left: 0; top: 0; bottom: 0; width: 61%; border-radius: 1px; background: rgba(167,139,250,.7); }
        .scr-scrub b::after { content: ''; position: absolute; right: -2px; top: -2.5px; width: 6px; height: 7px; border-radius: 2px; background: #c9b8fd; }
        /* 30-day spark for one spot — no axis, no legend, just the shape. */
        .scr-spark { display: block; width: 100%; height: auto; }
        /* The five phases the tracker cuts a rep into. Pure glyphs: nothing to
           translate, and the highlighted cell says which frame is on screen. */
        .scr-frames { display: flex; gap: 4px; width: 100%; }
        .scr-frame {
            flex: 1 1 0; min-width: 0; padding: 2px;
            border-radius: 6px; background: rgba(255,255,255,.035);
            border: .5px solid rgba(255,255,255,.07);
        }
        .scr-frame.on { background: rgba(167,139,250,.14); border-color: rgba(167,139,250,.34); }
        .scr-frame svg { display: block; width: 100%; height: auto; }
        /* Five tabs, one per screen, so the bar doubles as a map of the app. */
        .app-tab { flex: 1 1 0; min-width: 0; }
        .app-tab span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

        /* FOOTER */
        /* Footer — quiet Apple-grade close: brand echo left, three link
           columns right, legal row beneath a single hairline. */
        footer { background: #050506; padding: clamp(40px, 6vh, 64px) clamp(20px, 4vw, 28px) clamp(20px, 3vh, 28px); }
        .footer-content {
            max-width: 980px; margin: 0 auto;
            display: flex; justify-content: space-between; align-items: flex-start;
            gap: clamp(28px, 5vw, 64px); flex-wrap: wrap;
        }
        .footer-brand { max-width: 260px; }
        .footer-logo { height: 14px; width: auto; opacity: .8; display: block; }
        .footer-tag { margin-top: 12px; font-size: 13px; line-height: 1.55; color: rgba(255,255,255,.38); }
        .footer-cols { display: flex; gap: clamp(36px, 6vw, 80px); flex-wrap: wrap; }
        .footer-col { display: flex; flex-direction: column; gap: 10px; min-width: 88px; }
        .footer-col-t {
            font-size: 11px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
            color: rgba(255,255,255,.35); margin-bottom: 2px;
        }
        .footer-col a { font-size: 13px; color: #86868b; text-decoration: none; transition: color .2s ease; width: fit-content; }
        .footer-col a:hover { color: #f5f5f7; }
        .footer-legal {
            max-width: 980px; margin: clamp(28px, 4vh, 40px) auto 0;
            padding-top: 16px; border-top: 1px solid rgba(255,255,255,.07);
            display: flex; justify-content: space-between; align-items: center; gap: 16px;
        }
        .footer-copy { font-size: 12px; color: rgba(255,255,255,.3); }
        .footer-admin { font-size: 11px; color: rgba(255,255,255,.22); text-decoration: none; transition: color .2s ease; }
        .footer-admin:hover { color: rgba(255,255,255,.5); }
        @media (max-width: 640px) {
            .footer-content { flex-direction: column; gap: 28px; }
            .footer-cols { gap: 36px; }
            .footer-legal { flex-direction: column-reverse; align-items: flex-start; gap: 10px; }
        }
        /* RESPONSIVE */
        @media (max-width: 1024px) {
            /* grid-template-rows is deliberately not set here any more. It said
               `auto`, which declares exactly one explicit track and leaves the
               rest to grid-auto-rows -- and every bento card's children are
               absolutely positioned, so an `auto` track has nothing to measure
               and resolves to the border box. Between 769 and 1024 that made row
               one 1.6px tall and the wide track card a hairline: the first card
               in the grid, invisible, on every tablet and every half-width
               desktop window.
               Phones never showed it because their own block sets a min-height
               on .bento-card.wide, which is also why this survived -- the two
               widths that exercise this rule fail differently, and the one that
               fails visibly is the one nobody resizes to.
               Removing it lets both widths fall through to the base rule, where
               the rows are a real length. The phone block still sets `auto` for
               itself, further up the file, and is unaffected. */
            .bento-grid { grid-template-columns: 1fr 1fr; gap: 12px; }
            .bento-card.wide { grid-column: span 2; }
            /* .bento-card.tall is deliberately not un-spanned here. It said
               `grid-row: span 1`, and max-width 1024 includes every phone,
               so it beat the phone block's own `span 2` further up the file
               and left the wall with an empty cell at the bottom left: the
               tall card stopped after one row, and the range card is pinned
               to the right-hand column, so nothing flowed into the space it
               vacated. The 769-1024 block sets span 1 for tablets itself,
               which is the only width that wanted it. */
            .bento-card { border-radius: 12px; }
        }
        @media (max-width: 768px) {
            .nav-menu { display: none; }
            .sensor-cinema { max-width:100%; border-radius:12px; }
            .sensor-hud-tag { font-size:7px; }
            .sensor-hud-focus { width:40px; height:40px; }
            .footer-content { flex-direction: column; gap: 12px; }
            /* ── Mobile Performance ── */
            nav[role="navigation"] { -webkit-backdrop-filter: saturate(150%) blur(20px); backdrop-filter: saturate(150%) blur(20px); }
            .motor-card { -webkit-backdrop-filter: none; backdrop-filter: none; }
            .td-dot::after { animation: none; opacity: 0; }
            .reserve-card { -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); }
            .cloud-layer { will-change: auto; }
            .app-phone { will-change: auto; }
            html { scroll-behavior: auto; }
        }
        /* ═══════════════════════════════════════════════════════════════════════
           LOW-END MOBILE PERF PASS — light-touch version
           Keep ALL Apple/DJI glass texture intact. Only trim what truly hurts low-end GPUs:
           (1) reduce compound backdrop-blur intensity (not remove — keeps glass feel),
           (2) halve oversize blur radii only (visible blur still reads),
           (3) drop `will-change` on elements that finished their animation,
           (4) stop pulsing loops that don't add info at scroll speed.
           Backgrounds / borders / shadows / grain / gradients all preserved.
           ═══════════════════════════════════════════════════════════════════════ */
        @media (hover: none) and (pointer: coarse) {
            /* (1) Backdrop-filter — REDUCE intensity, do not remove. Glass surfaces keep their identity. */
            .bento-card { -webkit-backdrop-filter: blur(10px) saturate(140%); backdrop-filter: blur(10px) saturate(140%); }
            .pricing-panel-card { -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px); }
            .reserve-card { -webkit-backdrop-filter: blur(16px) saturate(130%); backdrop-filter: blur(16px) saturate(130%); }
            /* Nav bar — 70px blur is the single most expensive composite on mobile (always on-screen).
               Drop to 18px: still reads as frosted glass, ~15x cheaper GPU shader pass per frame. */
            nav { -webkit-backdrop-filter: saturate(160%) blur(18px); backdrop-filter: saturate(160%) blur(18px); }
            /* Pricing panel (28px) and hero-play-btn (20px) — halved */
            .pricing-panel { -webkit-backdrop-filter: blur(14px) saturate(140%); backdrop-filter: blur(14px) saturate(140%); }
            .hero-play-btn { -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); }
            /* Motor card 12px blur — a decorative base effect; strip it (card still has gradient + border) */
            .motor-card { -webkit-backdrop-filter: none; backdrop-filter: none; }
            /* (2) Halve the BIGGEST blur radii only — subtle ones kept at full intensity */
            .hero-glow { filter: blur(40px); }
            .hero-image::before { filter: blur(26px); }
            /* (3) will-change — remove only from elements whose animations are one-shot (reveal-then-done) */
            .bento-card {
                will-change: auto;
            }
            /* (4) Idle infinite animations — KEEP them (user wants no animation sacrifice),
               but slow down the ones that don't need high fps to read correctly. Slower
               animations = fewer composite frames per second = cheaper GPU/CPU. */
            .td-dot::after { animation-duration: 3s; opacity: .35; }
            .sensor-hud-focus { animation-duration: 6s; }
            .sensor-hud-rec::before { animation-duration: 3s; }
            .skeleton-anim .arm-l,
            .skeleton-anim .arm-r { animation-duration: 3s; }
            /* (5) Paint containment for card repeats — prevents child repaints from propagating */
            .bento-card,
            .motor-card { contain: layout paint; }
            .teardown-visual,
            .hero-image { transform: translateZ(0); }
        }
    
        /* ═══════════════════════════════════════════════════════════════════════
           TOUCH PERF GUARD — retired for visual parity: modern phone GPUs carry
           these small localized loops fine, and stripping them made the mobile
           page read flatter than desktop (the "worse on iPhone" complaint).
           Heavy work is still budgeted in JS (LiDAR cloud profiles, DPR caps);
           prefers-reduced-motion remains fully respected.
           ═══════════════════════════════════════════════════════════════════════ */

        /* ═══════════════════════════════════════════════════════════════════════
           IDLE CHAPTERS DO NOT ANIMATE
           The observer in index.app.js puts .rb-idle on any section further than
           one viewport away. Everything decorative inside it stops until it comes
           back, because a keyframe running nine screens down the document still
           keeps its element on a composited layer and still forces a commit on
           every frame the visitor scrolls.
           This is not the "strip effects on weak devices" pass further up the
           file, and it takes nothing away from anyone: an animation is only ever
           paused while it is off screen, so every device sees every effect in
           full whenever it is actually being looked at.
           animation-play-state rather than `animation: none`. `none` re-resolves
           the property and snaps the element back to its unanimated position,
           which on the two drifting full-bleed photographs is a visible jump the
           moment they scroll back in. Paused holds the frame it stopped on and
           carries on from there, so there is nothing to see.
           !important is load-bearing here. Nearly all of these loops are declared
           with the `animation` shorthand, which resets play-state to running, and
           several sit behind three-class selectors (.soc-board.is-alive
           .soc-leds i) that would otherwise outrank this rule.
           Measured over a full-page scroll: frames over the 60Hz budget fell from
           38% to 22%, p95 frame time from 50ms to 42ms
           (tests/_perfscroll_whatif.py, tests/audit_scroll_smooth.py).
           ═══════════════════════════════════════════════════════════════════════ */
        .rb-idle,
        .rb-idle *,
        .rb-idle *::before,
        .rb-idle *::after {
            animation-play-state: paused !important;
        }

        /* ═══════════════════════════════════════════════════════════════════════
           CINEMATIC-LOCK SKIP PILL — fades in ~0.9s into any locked film so the
           freeze reads as a playing chapter, not a broken page. Click (or any
           counter-scroll / fresh swipe / arrow key) fast-forwards to the final
           frame and releases the scroll lock. Injected by the scroll locker JS.
           ═══════════════════════════════════════════════════════════════════════ */
        .rb-skip-hint {
            position: fixed;
            left: 50%;
            bottom: calc(22px + env(safe-area-inset-bottom, 0px));
            z-index: 4000;
            transform: translateX(-50%) translateY(10px);
            display: inline-flex;
            align-items: center;
            gap: 8px;
            padding: 10px 20px;
            border-radius: 100px;
            background: rgba(12, 14, 18, .72);
            border: 1px solid rgba(255, 255, 255, .14);
            -webkit-backdrop-filter: blur(14px);
            backdrop-filter: blur(14px);
            box-shadow: 0 10px 32px rgba(0, 0, 0, .35);
            color: rgba(255, 255, 255, .68);
            /* instrument voice — matches the site's mono micro-labels */
            font-family: var(--font-mono, ui-monospace, 'SF Mono', Menlo, Consolas, monospace);
            font-size: 10px;
            font-weight: 500;
            letter-spacing: .2em;
            text-transform: uppercase;
            white-space: nowrap;
            appearance: none;
            -webkit-appearance: none;
            cursor: pointer;
            opacity: 0;
            pointer-events: none;
            transition: opacity .45s ease, transform .45s cubic-bezier(.16, 1, .3, 1),
                        border-color .3s ease, color .3s ease;
        }
        .rb-skip-hint.is-on {
            opacity: 1;
            pointer-events: auto;
            transform: translateX(-50%) translateY(0);
        }
        .rb-skip-hint:hover {
            border-color: rgba(255, 255, 255, .3);
            color: rgba(255, 255, 255, .92);
        }
        .rb-skip-hint:active { transform: translateX(-50%) translateY(0) scale(.96); }

        @media (max-width: 700px) {
        }

        @media (prefers-reduced-motion: reduce) {
        }
@media (max-width: 860px) {
}
@media (max-width: 640px) {
}
