/* ============================================================================
   HOME — "THE IDEAL AI ENGINE" NESTED GYROSPHERES  (prototype)
   Standalone stylesheet for public/index.html only.
   Reuses the LOCKED gyrosphere stage look from marketing-engine-gyro.css
   (v9 ring/glow/poster feel) verbatim, then adds the HOME-specific pieces:
     - .engine-links / .engine-link  — the aqua-gradient engine names pinned UNDER
       each mini-gyro by the WebGL module (the clickable "open me" links).
     - .engines-band / .hgrid / .hcard — the humanity tiles (one human photo + a
       SOLID AQUA nameplate panel), which are ALSO the reliable click-through nav.
   Layers on top of /styles.css + /glass.css + /site-glass.css (colors, type,
   .btn/.container/.nav/.footer/.calc-*/.ctaband come from there).
   Nothing here touches or overrides site-wide selectors used by real pages.

   HUMANITY-TILE UNIFICATION (2026-07-24): .hcard / .hcard-plate /
   .hcard-name / .hcard-go below consume the shared --tile-* design
   tokens (nameplate bg/shadow/radius/padding, tile shadow/hover-ring,
   border-hover, transition, lift) defined in humanity-tile.css, which
   this page links specifically for those tokens — so the home
   engine-summary cards (16:10) and the engine pages' per-tool tiles
   (.humanity-tile / .ht-*, 3:4) read as ONE visual system even though
   the aspect ratio + layout stay deliberately different (engine-level
   summary vs per-tool granularity). Do not hardcode a value here that
   has a --tile-* equivalent — change the token in humanity-tile.css
   instead so both systems move together.
   ============================================================================ */

/* ---------- prototype strip (unmistakable — never confuse with the live site) ---------- */
.proto-strip{position:sticky;top:74px;z-index:55;text-align:center;font:600 12.5px/1 var(--body);
  color:#08090c;background:linear-gradient(90deg,#5ee4ff,#00d2ff);padding:7px 14px;letter-spacing:.01em}
.proto-strip b{font-weight:800}

/* =========================================================================
   GYROSPHERE STAGE — pinned scroll choreography (WebGL) / static poster
   (LOCKED look reused from the engine pages)
   ========================================================================= */
/* 2026-07-25 VERTICAL FIX: .gyro-stage is a <section>, which site-wide gets
   `padding:120px 0` (styles.css). For every other section that 120px top
   pad is the right breathing room — but here it just pushed the whole
   pinned stage (and the ring vertically centered inside it) another 120px
   further from the hero above it, on top of the ring already sitting at
   the MIDDLE of a 100vh box (see .gyro-viewport below). Zeroed here;
   restored for the mobile/.no-scrub stacked layout below (unchanged there). */
.gyro-stage{position:relative;min-height:100vh;overflow:clip;padding-top:0}
.gyro-progress{position:absolute;top:0;left:0;height:3px;width:0%;z-index:6;
  background:linear-gradient(90deg,var(--blue),var(--blue-2));box-shadow:0 0 14px var(--glow)}

.gyro-sticky{position:relative;height:100vh;min-height:560px;overflow:hidden}

/* HOME HERO — headline + subhead live ABOVE the gyro stage, in normal
   document flow (stacked, not overlaid on the rings) — moved 2026-07-24 per
   owner request. .gyro-hero is a plain block section directly before
   .gyro-stage; ScrollTrigger's pin (home-engine-gyro.webgl.js) measures
   .gyro-sticky's own position at scroll time, so it is unaffected by this
   block sitting above it — the pin/travel/multiply choreography, and the
   engine-link pedestal pinning, are untouched.
   2026-07-24 repositioning pass: padding trimmed top+bottom (was 78/34) so
   the copy block sits close to the gyro's tallest ring with a modest gap,
   not floating far above it — CSS-only (no change to .gyro-stage/.gyro-
   sticky/.gyro-viewport or the WebGL camera/pin math).
   2026-07-24 pass 2: top padding trimmed again (44px -> 16px) — the gap
   under the nav/proto-strip read as too much empty space before the
   headline; bottom padding left alone (still 12px) so the gap to the gyro
   stays a modest breathing room, not zero. */
.gyro-hero{padding:16px 24px 12px;text-align:center}
.gyro-copy{position:relative;z-index:1;text-align:center;width:min(58ch,90vw,100%);margin:0 auto}
.gyro-copy .eyebrow{justify-content:center}
/* HERO HEADLINE — "The Ideal AI Engine". Matches the LIVE SITE's hero h1
   ceiling exactly (clamp(2.7rem,7.2vw,6rem), styles.css `h1{}`) — 2026-07-24
   pass 2: was clamp(2.8rem,7.5vw,6.4rem) (larger than the live hero); now
   uniform with the live site AND with the 4 engine-page hero names below
   (same clamp on all 5 pages). Sizing/spacing only lives here; the actual
   aqua gradient fill + inner (inset) shadow on the glyphs is the shared
   .aqua-fx treatment in aqua-headline-fx.css (2026-07-24 — replaces the old
   .accent flat-gradient + outer filter:drop-shadow, which is GONE from this
   rule now). line-height/padding-bottom kept (descender-safe) so the "g"
   descender in "Engine" never clips against the tight h1 line-box — the
   inner .aqua-fx span inherits this line-height. */
.gyro-copy h1.gyro-hero-title{font-size:clamp(2.7rem,7.2vw,6rem);letter-spacing:-.03em;
  line-height:1.15;margin:.2em 0 .14em;padding-bottom:.05em}
/* SUBHEAD — 2026-07-24: was inheriting the site's small/grey default .lead
   (~1rem, ~#c7cfdb) which read as "tiny in comparison and grey" against the
   much bigger aqua hero name above it. Enlarged to scale with the hero and
   lifted off dull grey onto a bright near-white so it reads clearly. */
.gyro-copy .lead{margin:0 auto;max-width:52ch;
  font-size:clamp(1.15rem,2.2vw,1.6rem);line-height:1.5;color:#fff}

/* the gyrosphere fills the pinned frame, rings centred */
/* ★ 2026-07-25 CLIP FIX — NO TRANSFORM HERE. DO NOT RE-ADD ONE.
   HISTORY: this element used to carry `transform:translateY(-16vh)` (earlier
   -20vh) to pull the rendered gyro up closer to the hero. That was a real bug,
   not just a hack. While GSAP pins .gyro-sticky (`start:'top top'` → position:
   fixed at top:0), .gyro-sticky IS the literal browser viewport (height:100vh,
   overflow:hidden) — so translating the canvas up 16vh pushed the top 16% of
   every rendered frame ABOVE the physical top edge of the window (genuinely
   unviewable) and left the bottom 16vh of the window (84vh–100vh) with no
   canvas under it at all. Result: rings were sliced by an invisible horizontal
   line at 84vh with dead space below it, and during the rev-2 column travel the
   whole top mini-gyro rendered entirely inside the lost band at the singles-
   overview + multiply beats. That is the "mini-gyros render cut off" bug.
   THE FIX: the canvas now fills .gyro-sticky exactly (inset:0, no transform), so
   the rendered frame is 1:1 with the visible 0–100vh band and nothing can ever
   render outside the window. The upward framing nudge that this transform used
   to fake now lives INSIDE the 3D projection — `off` in the KF[] camera
   keyframes of home-engine-gyro.webgl.js (a fraction of the frame height, 0.16
   at the master beat = pixel-identical to the old -16vh, eased to 0 by the
   singles overview so the full 4-gyro column fits the frame). If the gyro ever
   needs to sit higher or lower again, change `off` in the WebGL keyframes —
   NEVER translate this box. */
.gyro-viewport{position:absolute;inset:0;width:100%;height:100%;z-index:2;
  display:flex;align-items:center;justify-content:center}

.gyro-canvas{position:absolute;inset:0;width:100%;height:100%;display:block;
  opacity:0;transition:opacity .6s ease}
.gyro-canvas.ready{opacity:1}

/* ---------- STATIC POSTER (pre-WebGL flash-guard + full mobile/reduced-motion look) ----
   Concentric glowing rings = the frozen master gyrosphere. Premium, no motion required. */
.gyro-static{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  transition:opacity .6s ease}
.gyro-static.hidden{opacity:0;pointer-events:none}
.gs-core{position:absolute;width:82px;height:82px;border-radius:50%;
  background:radial-gradient(circle,#fff 0%,#8ff2ff 36%,rgba(0,210,255,.6) 62%,transparent 78%);
  filter:blur(1px);box-shadow:0 0 80px 18px var(--glow)}
.gs-ring{position:absolute;border-radius:50%;border:2.5px solid rgba(150,242,255,.85);
  box-shadow:0 0 34px var(--glow),inset 0 0 30px rgba(0,210,255,.35)}
.gs-r1{width:120px;height:120px;transform:rotate(12deg) scaleY(.42)}
.gs-r2{width:210px;height:210px;border-color:rgba(0,210,255,.78);transform:rotate(-24deg) scaleY(.5)}
.gs-r3{width:308px;height:308px;border-color:rgba(93,168,255,.66);transform:rotate(40deg) scaleY(.38)}
.gs-r4{width:404px;height:404px;border-color:rgba(124,240,255,.58);transform:rotate(-64deg) scaleY(.55)}
@media(prefers-reduced-motion:no-preference){
  .gs-r1{animation:gsSpin1 26s linear infinite}
  .gs-r2{animation:gsSpin2 34s linear infinite}
  .gs-r3{animation:gsSpin3 44s linear infinite}
  .gs-r4{animation:gsSpin4 56s linear infinite}
}
@keyframes gsSpin1{from{transform:rotate(0deg) scaleY(.42)}to{transform:rotate(360deg) scaleY(.42)}}
@keyframes gsSpin2{from{transform:rotate(0deg) scaleY(.5)}to{transform:rotate(-360deg) scaleY(.5)}}
@keyframes gsSpin3{from{transform:rotate(0deg) scaleY(.38)}to{transform:rotate(360deg) scaleY(.38)}}
@keyframes gsSpin4{from{transform:rotate(0deg) scaleY(.55)}to{transform:rotate(-360deg) scaleY(.55)}}

.gyro-hint{position:absolute;bottom:5vh;left:0;right:0;z-index:4;
  font:600 .82rem/1 var(--mono);letter-spacing:.08em;
  text-transform:uppercase;color:#fff;text-align:center;
  text-shadow:0 2px 5px rgba(0,0,0,.5);transition:opacity .3s var(--ease)}

/* =========================================================================
   ENGINE LINKS — one unit per engine gyro in the settled VERTICAL COLUMN.
   Each unit = [ transparent ring area on top (the WebGL gyro shows through — part
   of the click target) ] + [ a card carrying a TILE + the engine NAME (aqua
   .accent) + an "Open →" cue, pinned FULLY BELOW the gyro's bottom edge so no
   ring geometry overlaps it ]. The WebGL module sets each box's left/top/height
   inline every frame (top = the gyro's projected top edge, height reaches past the
   card just below the gyro's bottom edge) and adds .show once the gyros multiply.
   The WHOLE box (ring area + card) is one keyboard-focusable click target.
   SIZING: the module (positionLinks) sets each card's width + height every frame as a
   fraction of the gyro's on-screen diameter, so the card sits SLIGHTLY SMALLER than the
   gyro above it at every scroll stop. Borderless, with a soft aqua glow; the engine NAME
   sits in a band at the card's BOTTOM over the photo. ===== */
.engine-links{position:absolute;inset:0;z-index:6;pointer-events:none}
.engine-link{position:absolute;left:50%;top:0;transform:translate(-50%,0);
  width:min(340px,86vw);box-sizing:border-box;
  display:flex;flex-direction:column;align-items:center;justify-content:flex-end;
  text-decoration:none;text-align:center;
  opacity:0;pointer-events:none;transition:opacity .35s var(--ease)}
.engine-links.show .engine-link{pointer-events:auto}
/* transparent spacer that sits OVER the gyro (part of the click target, nothing drawn) */
.engine-link .el-ringspace{flex:1 1 auto;min-height:0;width:100%}
/* the card that carries the photo + a name band, pinned just UNDER the gyro.
   BORDERLESS with a soft aqua GLOW; the photo fills it; the engine NAME sits in a
   band at the BOTTOM. Width comes from the WebGL (= link width) and height is set inline
   each frame — both are fractions of the gyro's on-screen size, so the card reads as a
   substantial tile SLIGHTLY SMALLER than the gyro above it. */
.engine-link .el-card{flex:0 0 auto;position:relative;width:100%;
  border:0;border-radius:16px;overflow:hidden;
  background:linear-gradient(200deg,#12151c,#0a0c10);
  backdrop-filter:blur(var(--blur,13px)) saturate(150%);
  -webkit-backdrop-filter:blur(var(--blur,13px)) saturate(150%);
  box-shadow:0 0 24px rgba(0,210,255,.32),0 0 54px rgba(0,210,255,.17),0 20px 44px rgba(0,0,0,.5),var(--edge),var(--curve),var(--rim);
  transition:transform .18s var(--ease),box-shadow .22s var(--ease)}
/* MOUSE-REACTIVE GLASS GLINT — same --lx/--ly/--l-angle runtime (glass.js) and
   recipe as .humanity-tile/.hcard below; z-index:1 sits above the photo
   (.el-tile, unpositioned z-index) and below the name band (.el-meta, z:2). */
.engine-link .el-card::after{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;z-index:1;
  background:
    radial-gradient(24% 2px at calc(50% + var(--lx,.5)*50%) 1px,rgba(255,255,255,.9),rgba(255,255,255,0) 72%),
    radial-gradient(42% 9px at calc(50% + var(--lx,.5)*50%) 1px,rgba(150,225,255,.36),rgba(150,225,255,0) 74%),
    radial-gradient(2px 24% at calc(100% - 1px) calc(50% - var(--ly,.72)*50%),rgba(255,255,255,.9),rgba(255,255,255,0) 72%),
    radial-gradient(9px 40% at calc(100% - 1px) calc(50% - var(--ly,.72)*50%),rgba(150,225,255,.36),rgba(150,225,255,0) 74%),
    linear-gradient(var(--l-angle,217deg),rgba(255,255,255,.08),rgba(255,255,255,0) 15%);
}
/* the PHOTO fills the whole card (onerror swaps in the .is-placeholder navy look) */
.engine-link .el-tile{position:absolute;inset:0;border-radius:inherit;
  background:linear-gradient(200deg,#12151c,#0a0c10)}
.engine-link .el-tile.is-placeholder{box-shadow:inset 0 0 44px rgba(0,210,255,.12)}
.engine-link .el-tile img{width:100%;height:100%;object-fit:cover;display:block}
/* NAME BAND at the BOTTOM — dark scrim so the aqua name stays legible over any photo */
.engine-link .el-meta{position:absolute;left:0;right:0;bottom:0;z-index:2;
  display:flex;align-items:flex-end;justify-content:space-between;gap:10px;
  padding:16px 15px 13px;text-align:left;
  background:linear-gradient(180deg,rgba(6,10,16,0) 0%,rgba(6,10,16,.5) 46%,rgba(4,7,12,.9) 100%)}
.engine-link .el-name{font-family:var(--disp);font-weight:700;
  font-size:clamp(1.02rem,2.1vw,1.4rem);letter-spacing:-.02em;line-height:1.08;
  filter:drop-shadow(0 2px 12px rgba(2,6,16,.7));
  transition:transform .18s var(--ease)}
.engine-link .el-cue{flex:0 0 auto;display:inline-flex;align-items:center;gap:5px;
  font:700 .58rem/1 var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--blue-2);
  padding-bottom:3px}
.engine-link .el-cue i{font-style:normal;transition:transform .18s var(--ease)}
.engine-link:hover .el-card{transform:translateY(-3px);
  box-shadow:0 0 34px rgba(0,210,255,.5),0 0 72px rgba(0,210,255,.26),0 26px 56px rgba(0,0,0,.58),var(--edge),var(--curve),var(--rim)}
.engine-link:hover .el-name{transform:scale(1.02)}
.engine-link:hover .el-cue i{transform:translateX(3px)}
/* 2026-07-26 motion audit fix 1: touch twin for the three hover rules above —
   these cards are reachable on tablet (webgl-scrub still runs there) by a
   coarse-pointer/touch user who has no :hover at all. */
.engine-link:active .el-card{transform:translateY(-3px) scale(.98);
  box-shadow:0 0 34px rgba(0,210,255,.5),0 0 72px rgba(0,210,255,.26),0 26px 56px rgba(0,0,0,.58),var(--edge),var(--curve),var(--rim)}
.engine-link:active .el-name{transform:scale(1.02)}
.engine-link:active .el-cue i{transform:translateX(3px)}
.engine-link:focus-visible{outline:2px solid var(--blue);outline-offset:4px}
.no-scrub .engine-links{display:none}   /* fallback navigates via the humanity grid below */

/* =========================================================================
   THE FOUR ENGINES — HUMANITY TILES  (one human photo + a SOLID AQUA nameplate)
   Also the reliable click-through navigation in every mode.
   ========================================================================= */
.engines-band{padding:78px 0 40px}
.hgrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:22px;margin-top:42px}
@media(max-width:820px){.hgrid{grid-template-columns:1fr}}

/* GLASS TILE — same material as .humanity-tile (humanity-tile.css) and
   .el-card above: frosted --glass panel + canonical edge/curve/rim, so the
   home engine-summary cards read as the same glass system, not a flat
   opaque --bg-1 panel. */
.hcard{position:relative;display:block;border-radius:var(--r-lg);overflow:hidden;text-decoration:none;
  aspect-ratio:16/10;border:1px solid var(--glass-border,rgba(255,255,255,.14));background:var(--glass,rgba(16,24,40,.30));
  backdrop-filter:blur(var(--blur,13px)) saturate(150%) brightness(1.04);
  -webkit-backdrop-filter:blur(var(--blur,13px)) saturate(150%) brightness(1.04);
  box-shadow:0 20px 60px rgba(0,0,0,.45),var(--edge),var(--curve),var(--rim);
  transition:var(--tile-transition)}
.hcard:hover{transform:translateY(var(--tile-lift));border-color:var(--tile-border-hover);
  box-shadow:0 30px 82px rgba(0,0,0,.55),var(--tile-hover-ring),var(--edge),var(--curve),var(--rim)}
/* 2026-07-26 motion audit fix 1: these are the primary nav surface (4 engine
   tiles) and had NO touch equivalent to :hover at all — a visitor tapping one
   got zero feedback before the page navigated. Same border/glow language as
   hover (same design system) but: (a) a fast, near-instant transition instead
   of the shared .3s --tile-transition (a slow fade reads as lag under a
   finger) and (b) a slight scale-down instead of hover's lift-up — a finger
   is pressing IN, not hovering above, so "pressed" reads more correctly as a
   press-in than a further lift. */
.hcard:active{transition:transform .1s ease,box-shadow .15s ease,border-color .15s ease;
  transform:scale(.97);border-color:var(--tile-border-hover);
  box-shadow:0 14px 40px rgba(0,0,0,.5),var(--tile-hover-ring),var(--edge),var(--curve),var(--rim)}
.hcard:active .hcard-go{background:rgba(0,210,255,.32);border-color:rgba(0,210,255,.62)}
.hcard:focus-visible{outline:2px solid var(--blue);outline-offset:4px}
/* MOUSE-REACTIVE GLASS GLINT — z-index:1 sits above the photo (.hcard-photo,
   unpositioned z-index) and below the nameplate/go-button (z-index:2). */
.hcard::after{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;z-index:1;
  background:
    radial-gradient(24% 2px at calc(50% + var(--lx,.5)*50%) 1px,rgba(255,255,255,.9),rgba(255,255,255,0) 72%),
    radial-gradient(42% 9px at calc(50% + var(--lx,.5)*50%) 1px,rgba(150,225,255,.34),rgba(150,225,255,0) 74%),
    radial-gradient(2px 24% at calc(100% - 1px) calc(50% - var(--ly,.72)*50%),rgba(255,255,255,.9),rgba(255,255,255,0) 72%),
    radial-gradient(9px 40% at calc(100% - 1px) calc(50% - var(--ly,.72)*50%),rgba(150,225,255,.34),rgba(150,225,255,0) 74%),
    linear-gradient(var(--l-angle,217deg),rgba(255,255,255,.08),rgba(255,255,255,0) 15%);
}
.hcard-photo{position:absolute;inset:0}
.hcard-photo img{width:100%;height:100%;object-fit:cover;display:block}
/* gentle bottom scrim so the nameplate + photo both stay legible */
.hcard-photo::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,rgba(6,10,16,.04) 42%,rgba(5,8,13,.5) 100%)}
/* CLEAN placeholder variant (no photo yet): solid navy panel + the aqua nameplate.
   Component accepts a real photo later with NO redesign — just drop the <img> back in. */
.hcard.is-placeholder .hcard-photo{background:linear-gradient(200deg,#12151c,#0a0c10)}
.hcard.is-placeholder .hcard-photo img{display:none}
.hcard.is-placeholder .hcard-photo::after{background:none}

/* the SOLID AQUA nameplate panel — aqua gradient + drop shadow, matching .accent;
   dark ink on aqua so it stays legible over any photo. */
.hcard-plate{position:absolute;left:18px;bottom:18px;z-index:2;
  display:inline-flex;flex-direction:column;gap:3px;max-width:calc(100% - 36px);
  padding:var(--tile-plate-pad);border-radius:var(--tile-plate-r);
  background:var(--tile-plate-bg);
  box-shadow:var(--tile-plate-shadow)}
.hcard-name{font-family:var(--disp);font-weight:var(--tile-name-weight);font-size:1.16rem;letter-spacing:-.01em;
  color:#04121a;text-shadow:var(--tile-name-shadow)}
.hcard-sub{font:600 .8rem/1.25 var(--body);color:#06202b;opacity:.92}
/* the "Open →" affordance — unique to .hcard since it's the only whole-card LINK
   in the two tile systems (per-tool .humanity-tile cards have no destination, so
   they intentionally do NOT get a copy of this — see humanity-tile.css notes).
   Its hue still comes from the shared aqua family (--tile-border-hover base color)
   so it reads as the same design language even where the exact alpha differs
   (kept slightly more opaque here for legibility as a small icon-button over
   varied photos). */
.hcard-go{position:absolute;top:14px;right:14px;z-index:2;width:36px;height:36px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;color:#eaffff;font-size:17px;line-height:1;
  background:rgba(9,12,17,.5);border:1px solid rgba(255,255,255,.18);backdrop-filter:blur(6px);
  transition:background .2s var(--ease),border-color .2s var(--ease),transform .2s var(--ease)}
.hcard:hover .hcard-go{background:rgba(0,210,255,.32);border-color:rgba(0,210,255,.62);transform:translateX(2px)}

.engines-note{text-align:center;margin-top:22px;font-size:.9rem;color:#fff}
/* WHITE LETTERING — 2026-07-24: the "4 very powerful systems" subhead + the
   Book-a-Call sub-copy/fine-print inherited the site's default grey .lead/
   .dim/.fine (styles.css --ink-2/--muted). Scoped overrides (this file only
   loads on this page — no live-site impact) lift them to white to match the
   rest of the page's now-white body copy. */
.engines-band .center .lead{color:#fff}
.ctaband .lead{color:#fff}
.ctaband .dim{color:#fff}
.footer .fine{color:#fff}

/* NAV LINKS WHITE — 2026-07-25 (owner decision, approved): the plain nav
   links ("Live site" / "The engine" / "Four engines") inherited the site's
   grey nav convention (styles.css: .nav .links a:not(.btn){color:var(--ink-2)}).
   Everything else on this page was already white; the nav was the one open
   item (see website-build-STATE doc). Scoped to this page only — global
   styles.css untouched, live production unaffected. This file loads after
   styles.css so it wins at equal selector specificity (same load-order
   pattern as the WHITE LETTERING block above). Hover/focus goes aqua
   (--aqua-2) instead of the global's plain #fff-on-hover, which would be
   invisible now that the resting state is already white. The "Book a call"
   pill (.btn/.btn-primary) is untouched — it keeps its own existing styling. */
.nav .links a:not(.btn),
.nav-mobile a:not(.btn){color:#fff}
.nav .links a:not(.btn):hover,
.nav-mobile a:not(.btn):hover,
.nav-mobile a:not(.btn):focus{color:var(--aqua-2)}

/* =========================================================================
   MOBILE / REDUCED-MOTION FALLBACK
   Phones + prefers-reduced-motion never get the pinned WebGL scrub. The master
   gyrosphere shows as the static poster; the humanity engine grid below is the
   full, tap-friendly navigation.
   ========================================================================= */
.no-scrub .gyro-stage{min-height:auto;padding-top:120px}
.no-scrub .gyro-sticky{height:auto;min-height:0;padding:20px 0 30px;
  display:flex;flex-direction:column;align-items:center;overflow:visible}
.no-scrub .gyro-viewport{position:relative;inset:auto;width:min(92vw,520px);
  height:min(46vh,380px);margin:20px auto 0;transform:none}
.no-scrub .gyro-canvas{display:none}
.no-scrub .gyro-static{opacity:1}
.no-scrub .gyro-hint{position:static;bottom:auto;margin-top:16px}

/* ---------- GYRO-IDLE v3 (2026-07-26, THIRD PASS — real scroll length, not
   another smoothstep window) — v1 fixed a DEAD gyro (scrollP stuck at 0). v2
   fixed it reading as tiny/dead-space (grew the box, pulled the camera in).
   Both times the owner came back with the SAME complaint: "it all happens in
   a fraction of a swipe." Widening *-engine-gyro.webgl.js's IDLE_ENTER/EXIT
   smoothstep window (attempt 2) only re-windowed the SAME small pool of raw
   scroll — the section's entire on/off-screen lifetime, MOSTLY off-screen at
   either end — and smoothstep's ease then ate a chunk of even that window. A
   window can't manufacture scroll distance that isn't there; the section was
   just too SHORT. Fixing pacing needed a length fix, not another window.
   FIX: `.gyro-idle .gyro-stage` is now TALL (280vh/280dvh — real, dedicated
   scroll length) while `.gyro-idle .gyro-sticky` holds the canvas viewport-
   sized and visually still using NATIVE CSS `position:sticky;top:0` — NOT a
   GSAP ScrollTrigger pin. Native sticky is safe here in a way the GSAP pin
   was not: GSAP's pin measures innerHeight once (per pin/refresh) and holds
   the stage at a JS-computed position:fixed offset — THAT measurement is
   what goes stale when iOS Safari's address bar collapses mid-scroll, not 3D
   rendering itself. `position:sticky` is native to the browser's own
   compositor, with no JS measurement to go stale; paired with `dvh` (kept
   below) it tracks the real visible viewport cleanly through an address-bar
   collapse. Scroll is never intercepted — the user scrolls completely
   normally throughout; the sticky child just holds its place inside its own
   (now tall) `.gyro-stage`, the same way a sticky table header would.
   `.gyro-idle .gyro-viewport` goes back to the SAME full-bleed geometry the
   desktop/tablet scrub layout already uses (defined earlier in this file,
   unscoped) — still restated explicitly here (not simply deleted) because an
   existing, unscoped `@media(max-width:760px){.gyro-viewport{height:min(42vh
   ,340px)}}` rule further down this file has EQUAL selector specificity to
   the base `.gyro-viewport` rule and would otherwise win by source order on
   every phone-width screen. Restating it under the 2-class `.gyro-idle
   .gyro-viewport` selector pins it to full-bleed regardless of that rule's
   position in the file. Same reasoning for zeroing padding on `.gyro-idle
   .gyro-sticky` (an unscoped same-breakpoint rule sets `padding-top:64px`).
   See the matching idleProgress() rewrite in *-engine-gyro.webgl.js: it now
   reads `.gyro-stage`'s own scroll travel, not `.gyro-sticky`'s — the STICKY
   CHILD's own rect is constant (top≈0) for the whole time it's stuck, so the
   old sticky-rect formula would read a frozen value and go dead on this
   layout. */
/* ---------- v4 CORRECTION (2026-07-26) — native `position:sticky` verified
   BROKEN here, in both Chromium and WebKit: styles.css sets `overflow-x:hidden`
   on <html> AND <body> (site-wide, out of scope to change) — per the CSS
   Overflow spec, setting overflow-x alone forces the UA to also compute
   overflow-y to a scrolling value, which makes <html>/<body> a scroll
   container and the sticky containing-block reference instead of the true
   viewport. Measured: `.gyro-sticky`'s own rect.top tracked window.scrollY in
   exact 1:1 lockstep at every depth tested — it never stuck at all, so the
   canvas physically scrolled off-screen (and the whole gyro along with it)
   well before the scroll-mapped timeline below even finished, independent of
   TIMELINE_END. This is almost certainly the real, whole-saga explanation for
   "it cuts off and scrolls to the next section." `.gyro-sticky` is now
   `position:absolute;top:0` inside `.gyro-stage` (a plain, non-transformed
   positioned ancestor) and JS (idleProgress() in the matching .webgl.js)
   applies `transform:translateY(...)` every scroll to cancel out its natural
   upward drift — clamped to [0, total] so it engages/releases at exactly the
   points a working native sticky would. Same "read fresh every call, never
   cache innerHeight" discipline as the rest of this block, so it carries none
   of the old GSAP-pin staleness risk either. */
/* ---------- v5 CORRECTION (2026-07-26) — root cause fixed upstream, JS
   positioning removed. Owner: "much better but it jitters when scrolling" —
   the v4 JS transform (recomputed in a scroll handler) is inherently a frame
   behind the browser's scroll compositor; iOS momentum scrolling makes that
   lag visible as jitter. No throttling tuning fixes that — it's structural.
   Root cause of v4's "sticky never engages" finding was styles.css's
   `overflow-x:hidden` on <html>/<body> (site-wide) turning them into a
   scroll container per spec, stealing sticky's containing-block reference.
   Fixed at the source: styles.css now uses `overflow-x:clip` (@supports-
   guarded) instead, which blocks horizontal scroll without creating a scroll
   container. `.gyro-idle .gyro-sticky` is back to plain, native
   `position:sticky;top:0` below — no JS transform, nothing fighting the
   compositor, zero jitter by construction. The matching *-engine-gyro.webgl.js
   idleProgress() no longer writes to `.gyro-sticky`'s transform; it only
   reads `.gyro-stage`'s rect for the progress calc, same as before. */
.gyro-idle .gyro-stage{min-height:auto;padding-top:32px;height:280vh}
@supports (height:100dvh){.gyro-idle .gyro-stage{height:280dvh}}
.gyro-idle .gyro-sticky{position:sticky;top:0;width:100%;height:100vh;min-height:560px;
  overflow:hidden;padding:0;margin:0}
@supports (height:100dvh){.gyro-idle .gyro-sticky{height:100dvh}}
.gyro-idle .gyro-viewport{position:absolute;inset:0;width:100%;height:100%;
  margin:0}

@media(max-width:760px){
  .gyro-sticky{padding-top:64px}
  .gyro-viewport{height:min(42vh,340px)}
  .gs-r4{width:300px;height:300px}
  .gs-r3{width:238px;height:238px}
  .gs-r2{width:170px;height:170px}
  .gs-r1{width:100px;height:100px}
}

/* ---------- CTA band: room for the embedded calendar iframe ---------- */
.book-section{padding-top:10px}

/* 2026-07-25 MOBILE BOOKING-WIDGET CLIP FIX — audit confirmed the LeadConnector
   calendar iframe was clipping its Saturday column + the next-month arrow at
   phone widths. Root cause: styles.css's site-wide .ctaband padding (80px 30px)
   + margin (0 28px) squeeze the iframe to ~240-270px at 360-390px viewports,
   under what the widget's 7-day grid needs. styles.css is production and is
   NOT edited here — this file loads after it with equal selector specificity,
   so this wins in the cascade. Loosens the squeeze at phone widths only; the
   padding is purely decorative at that size anyway. Do NOT use transform:scale
   to "fix" this — blurs text and breaks tap targets. Verified via CDP at
   360x800 / 390x844 against the deployed preview. */
@media(max-width:560px){
  .ctaband{padding:56px 8px;margin:0 8px}
}

/* BOOK-A-CALL HEADLINE — same shared .aqua-fx gradient + inner-shadow
   treatment as the hero (aqua-headline-fx.css). Sizing/spacing only here;
   the old .accent flat-gradient + outer filter:drop-shadow is GONE
   (2026-07-24 — see aqua-headline-fx.css for why + the tunable values). */
.ctaband h2.book-title{font-size:clamp(2.2rem,5.2vw,4rem);letter-spacing:-.02em;
  line-height:1.15;margin:0 0 .14em;padding-bottom:.05em}
.book-sub{margin:0 auto 6px;max-width:44ch;font-size:1.22rem}
