/* ============================================================================
   FRAME — generator scroll deconstruction (shared, Phase 11)
   Loaded by the homepage and the 12 dedicated generator pages, after the
   page's own layers. generator-scroll-motion.js adds html.gx-3d (desktop
   >= 1180px with motion allowed only) and scrubs CSS variables on
   #generator; everything below maps those numbers onto transforms.

   NOT pinned: the generator scrolls with the page while its three cards
   progressively separate and its controls scatter; the timeline only opens
   on the way down and rewinds on the way up. Unset variables = the normal
   layout, so without the class (reduced motion, tablet, mobile, no JS) the
   generator is exactly the static approved one.
     --lx/--lry/--lrz/--lsd  left card x / rotateY / rotateZ / scale delta
     --ly/--lz               left card lift / depth   (polish: asymmetric)
     --rx/--rry/--rrz/--rsd  right card               --ry/--rz  right lift/depth
     --cy/--csd/--crx        centre stage lift / scale delta / rotateX
     --fan   0→1 control scatter (per-item offsets live on each .gx-fan)
     --qsd   QR tile scale delta   --ce viewfinder spread   --glow stage glow
     --dsd   Download emphasis     --hod hand-off fade (delta)
     --prog  progress line         --k  typing calm factor
   (scale variables are deltas: neutral is 0, the value GSAP gives an unset
   custom property, so nothing can ever collapse to scale 0)
   ========================================================================= */
html.gx-3d #generator#generator{--k:1;}
html.gx-3d #generator#generator .studio.gx-typing{--k:.3;}
/* !important: dedicated pages carry the legacy .reveal class on the studio,
   whose opacity:1!important would otherwise block the hand-off fade */
html.gx-3d #generator#generator .studio{perspective:1400px;perspective-origin:50% 40%;opacity:calc(1 - var(--hod,0))!important;}
/* the cards may travel past the narrow .wrap clip (outward travel is capped
   in JS so nothing leaves the screen); the full-width section clips instead
   as a safety net, so the scattered state can never widen the page */
html.gx-3d #generator#generator{overflow-x:clip;}
html.gx-3d #generator#generator > .wrap{overflow-x:visible!important;}
/* left card: its surface (::before), the type tiles and the step-1 panel
   move as ONE panel — same transform, same origin (the card's centre;
   --l-oy is that centre measured from each piece's own top) */
html.gx-3d #generator#generator .studio::before,
html.gx-3d #generator#generator .type-toolbar,
html.gx-3d #generator#generator .controls{
  transform:translate3d(calc(var(--lx,0px) * var(--k)),calc(var(--ly,0px) * var(--k)),calc(var(--lz,0px) * var(--k)))
            rotateY(calc(var(--lry,0deg) * var(--k))) rotateZ(calc(var(--lrz,0deg) * var(--k)))
            scale(calc(1 + var(--lsd,0) * var(--k)));
  transform-origin:50% var(--l-oy,50%);
}
html.gx-3d #generator#generator .studio::before{transform-origin:50% 50%;}
html.gx-3d #generator#generator .type-toolbar,html.gx-3d #generator#generator .type-tabs{transform-style:preserve-3d;}
/* centre: the navy surface (::after) and the preview share one origin */
html.gx-3d #generator#generator .studio::after{
  transform:translate3d(0,calc(var(--cy,0px) * var(--k)),0) scale(calc(1 + var(--csd,0))) rotateX(calc(var(--crx,0deg) * var(--k)));
  transform-origin:50% 50%;
}
html.gx-3d #generator#generator .preview{
  transform:translate3d(0,calc(var(--cy,0px) * var(--k)),0) scale(calc(1 + var(--csd,0))) rotateX(calc(var(--crx,0deg) * var(--k)));
  transform-origin:50% var(--c-oy,50%);
}
/* right card */
html.gx-3d #generator#generator .customize{
  transform:translate3d(calc(var(--rx,0px) * var(--k)),calc(var(--ry,0px) * var(--k)),calc(var(--rz,0px) * var(--k)))
            rotateY(calc(var(--rry,0deg) * var(--k))) rotateZ(calc(var(--rrz,0deg) * var(--k)))
            scale(calc(1 + var(--rsd,0) * var(--k)));
  transform-origin:50% 50%;transform-style:preserve-3d;
}
/* scatter: each control's own offset × its staggered share of --fan */
html.gx-3d #generator#generator .gx-fan{
  --p:calc(clamp(0, (var(--fan,0) - var(--fd,0)) * 2.2, 1) * var(--k));
  transform:translate3d(calc(var(--fx,0px) * var(--p)),calc(var(--fy,0px) * var(--p)),calc(var(--fz,0px) * var(--p))) rotate(calc(var(--fr,0deg) * var(--p)))!important;
  transition-property:border-color,background-color,color,box-shadow!important;
}
/* the step-1 panel is itself a scattered item inside the left card: it keeps
   the card's transform and adds its own offset on top */
html.gx-3d #generator#generator .controls.gx-fan{
  transform:translate3d(calc(var(--lx,0px) * var(--k) + var(--fx,0px) * var(--p)),calc(var(--ly,0px) * var(--k) + var(--fy,0px) * var(--p)),calc(var(--lz,0px) * var(--k) + var(--fz,0px) * var(--p)))
            rotateY(calc(var(--lry,0deg) * var(--k))) rotateZ(calc(var(--lrz,0deg) * var(--k) + var(--fr,0deg) * var(--p)))
            scale(calc(1 + var(--lsd,0) * var(--k)))!important;
}
/* typing calm: generator-scroll-motion.js adds .gx-typing while a text field
   has focus (released 500ms after blur, so no target moves mid-click); --k
   then scales every card AND every control's scatter to 30% */
/* while the tiles are apart, the active tile carries its own highlight
   (the sliding pill stays where the tile normally sits) */
html.gx-3d #generator#generator.gx-fanning .type-tab-pill{opacity:0!important;}
html.gx-3d #generator#generator.gx-fanning #typeToolbar .type-tabs > .type-tab.active{
  background:var(--hx-blue-soft)!important;border-color:var(--hx-blue-2)!important;
}
/* QR tile: a gentle scale only — the code itself never rotates */
html.gx-3d #generator#generator .preview .frame{transform:scale(calc(1 + var(--qsd,0)));}
/* viewfinder corners spread: the homepage's animated span, and the
   frame-drawn corners on the dedicated pages */
html.gx-3d #generator#generator .hx-corners{inset:calc(-14px - var(--ce,0px));}
html.gx-3d #generator#generator .preview .frame:not(:has(.hx-corners))::after{inset:calc(-14px - var(--ce,0px))!important;}
/* stage glow: a soft blue/violet halo behind the tile, opacity only */
html.gx-3d #generator#generator .preview .frame::before{
  content:''!important;display:block!important;position:absolute;inset:-60px;z-index:-1;pointer-events:none;border-radius:50%;
  background:radial-gradient(closest-side,rgba(99,102,241,.6),rgba(59,130,246,.24) 55%,rgba(59,130,246,0));
  opacity:var(--glow,0);transform:none!important;border:0!important;
}
/* Download gets more present as the result takes over (scale + a halo below
   it, opacity only) */
html.gx-3d #generator#generator .preview > #downloadBtn{scale:calc(1 + var(--dsd,0));}
html.gx-3d #generator#generator .preview{--dl-halo:calc(var(--dsd,0) * 40);}
html.gx-3d #generator#generator .preview > #resRow::after{
  content:'';position:absolute;left:8%;right:8%;top:66px;height:46px;border-radius:50%;pointer-events:none;z-index:-1;
  background:radial-gradient(closest-side,rgba(59,130,246,.55),rgba(59,130,246,0));opacity:var(--dl-halo,0);
}
html.gx-3d #generator#generator .preview > #resRow{position:relative;}
/* progress line: travels with the section in the gap just above the cards
   (under them, pointer-events none); 0 → 100% as the generator scatters,
   then it stays full until the user scrolls back up */
#generator .gx-progress,#generator .hx-progress{display:none;}
html.gx-3d #generator .gx-progress{
  display:block;position:absolute;left:max(24px,calc((100% - min(94vw,1400px)) / 2 + 24px));right:max(24px,calc((100% - min(94vw,1400px)) / 2 + 24px));
  top:var(--pg-top,24px);height:3px;border-radius:3px;pointer-events:none;z-index:0;
  background:linear-gradient(90deg,var(--hx-blue-2),var(--hx-violet));
  transform-origin:0 50%;transform:scaleX(var(--prog,0));opacity:min(1,calc(var(--prog,0) * 40));
}
/* Phase 14.2: the result column stays CSS-sticky here too (frame-theme.css).
   It is bounded by the studio, so it releases at the cards' bottom edge, and
   generator-scroll-motion.js never starts the scatter before that release:
   while the user customises, the result stays in view and nothing scatters;
   once it has released, the whole generator moves with the page as before. */
