// waiv-factory.jsx — pinned, scroll-scrubbed "making of a waiv" scene. // Blank → UV print → chip embed → finish → activate → a dimensional tunnel of // cards flowing toward the viewer → the finished card resolves. Lusion-flavoured. (function () { const { useRef, useEffect } = React; const clamp = (v, a, b) => Math.min(b, Math.max(a, v)); const ease = t => t * t * (3 - 2 * t); const es = (p, a, b) => ease(clamp((p - a) / (b - a), 0, 1)); const ss = (x, a, b) => { const t = clamp((x - a) / (b - a), 0, 1); return t * t * (3 - 2 * t); }; const mix = (a, b, t) => a + (b - a) * t; const frac = x => x - Math.floor(x); const FINISHES = [ 'radial-gradient(135% 110% at 18% -5%,rgba(255,231,170,.26),transparent 52%),linear-gradient(125deg,#11203f,#26406a 46%,#02060f)', 'radial-gradient(135% 110% at 18% -5%,rgba(180,205,255,.4),transparent 52%),linear-gradient(125deg,#0a2a8a,#2f6ff0 46%,#040d2b)', 'radial-gradient(135% 110% at 18% -5%,rgba(255,180,180,.3),transparent 52%),linear-gradient(125deg,#3a0a10,#b3202a 46%,#1a0305)', 'radial-gradient(135% 110% at 18% -5%,rgba(255,255,255,.6),transparent 55%),linear-gradient(125deg,#9fc4cd,#eef6f8 46%,#86acb6)', 'radial-gradient(135% 110% at 18% -5%,rgba(255,255,255,.3),transparent 52%),linear-gradient(125deg,#16171a,#5d5f64 46%,#070708)', 'radial-gradient(135% 110% at 18% -5%,rgba(180,255,210,.3),transparent 52%),linear-gradient(125deg,#06251a,#1f8a5b 46%,#04130d)', ]; function FactoryScene() { const trackRef = useRef(null), stickyRef = useRef(null), rigRef = useRef(null); const printedRef = useRef(null), headRef = useRef(null), xrayRef = useRef(null); const coilRef = useRef(null), chipRef = useRef(null), glossRef = useRef(null), stampRef = useRef(null); const bedRef = useRef(null), gridRef = useRef(null), vglowRef = useRef(null); const tunnelRef = useRef(null), portalRef = useRef(null), finalRef = useRef(null), ctaRef = useRef(null), fogRef = useRef(null); const flyRefs = useRef([]), starRefs = useRef([]), ringRefs = useRef([]), capRefs = useRef([]), railRefs = useRef([]), stepRef = useRef(null); // stable descriptors for the wormhole const fly = useRef(null); if (!fly.current) { fly.current = Array.from({ length: 12 }, (_, i) => ({ off: i / 12 + Math.random() * 0.02, x: (Math.random() * 2 - 1) * 230, y: (Math.random() * 2 - 1) * 145, rxs: (Math.random() * 1.3 + 0.4) * (Math.random() < .5 ? -1 : 1), rys: (Math.random() * 1.3 + 0.4) * (Math.random() < .5 ? -1 : 1), rzs: (Math.random() * 1.1 + 0.3) * (Math.random() < .5 ? -1 : 1), s: 0.82 + Math.random() * 0.5, g: FINISHES[i % FINISHES.length], })); } const stars = useRef(null); if (!stars.current) { stars.current = Array.from({ length: 110 }, () => { const a = Math.random() * Math.PI * 2, rad = 18 + Math.random() * 360; return { off: Math.random(), x: Math.cos(a) * rad, y: Math.sin(a) * rad, deg: a * 180 / Math.PI + 90 }; }); } const rings = useRef(null); if (!rings.current) rings.current = Array.from({ length: 14 }, (_, i) => ({ off: i / 14 })); useEffect(() => { let raf; const tick = () => { const track = trackRef.current, sticky = stickyRef.current; if (track && sticky && !window.matchMedia('(max-width:860px)').matches) { const r = track.getBoundingClientRect(); const vh = window.innerHeight; const p = clamp(-r.top / (r.height - vh), 0, 1); // ambient if (gridRef.current) gridRef.current.style.transform = `translate3d(0,${(p * -60).toFixed(1)}px,0)`; if (vglowRef.current) vglowRef.current.style.opacity = (0.5 + Math.sin(p * Math.PI) * 0.5).toFixed(2); // rig rotation + fade as tunnel takes over const rigOp = 1 - es(p, 0.82, 0.90); const ry = mix(-16, 12, es(p, 0.05, 0.70)) + Math.sin(p * 6) * 2; const rx = mix(10, 4, es(p, 0.05, 0.55)); if (rigRef.current) { rigRef.current.style.transform = `rotateX(${rx.toFixed(2)}deg) rotateY(${ry.toFixed(2)}deg) rotateZ(2deg)`; rigRef.current.style.opacity = rigOp.toFixed(3); } if (bedRef.current) bedRef.current.style.opacity = (1 - es(p, 0.50, 0.66)).toFixed(2); // UV print sweep const printSeg = es(p, 0.14, 0.35); if (printedRef.current) printedRef.current.style.clipPath = `inset(0 ${((1 - printSeg) * 100).toFixed(2)}% 0 0)`; if (headRef.current) { headRef.current.style.left = mix(-4, 104, printSeg) + '%'; headRef.current.style.opacity = (es(p, 0.12, 0.16) * (1 - es(p, 0.33, 0.37))).toFixed(3); } // chip embed x-ray const xrayOp = es(p, 0.36, 0.45) * (1 - es(p, 0.53, 0.60)); if (xrayRef.current) xrayRef.current.style.opacity = xrayOp.toFixed(3); if (coilRef.current) { const rings = coilRef.current.children; for (let i = 0; i < rings.length; i++) rings[i].style.opacity = (es(p, 0.37 + i * 0.012, 0.45 + i * 0.012)).toFixed(3); } if (chipRef.current) { const c = es(p, 0.46, 0.54); chipRef.current.style.opacity = c.toFixed(3); chipRef.current.style.transform = `translateY(${((1 - c) * -34).toFixed(1)}px) scale(${(0.7 + 0.3 * c).toFixed(3)})`; } // finish gloss sweep if (glossRef.current) { glossRef.current.style.opacity = (es(p, 0.57, 0.62) * (1 - es(p, 0.68, 0.72))).toFixed(3); glossRef.current.style.transform = `translateX(${mix(-70, 70, es(p, 0.56, 0.71)).toFixed(1)}%)`; } // activation stamp + pulse if (stampRef.current) { const sp = es(p, 0.70, 0.785); stampRef.current.style.opacity = (sp * (1 - es(p, 0.83, 0.89))).toFixed(3); stampRef.current.style.transform = `translate(-50%,-50%) scale(${mix(0.4, 1, sp).toFixed(3)}) rotate(-11deg)`; } sticky.classList.toggle('is-live', p > 0.70 && p < 0.92); // final resolved card rises straight out of the activated card const fp = es(p, 0.82, 0.97); if (finalRef.current) { finalRef.current.style.opacity = fp.toFixed(3); finalRef.current.style.transform = `translate(-50%,-50%) scale(${mix(0.6, 1, fp).toFixed(3)}) rotateY(${mix(26, 0, fp).toFixed(1)}deg)`; } if (ctaRef.current) ctaRef.current.style.opacity = es(p, 0.92, 1).toFixed(3); // captions + rail (5 stages) const idx = p >= 0.82 ? 4 : p >= 0.56 ? 3 : p >= 0.36 ? 2 : p >= 0.13 ? 1 : 0; capRefs.current.forEach((el, i) => el && el.classList.toggle('is-on', i === idx)); railRefs.current.forEach((el, i) => el && el.classList.toggle('is-on', i <= idx)); if (stepRef.current) stepRef.current.innerHTML = `STAGE 0${idx + 1} / 05`; } raf = requestAnimationFrame(tick); }; raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf); }, []); const caps = [ <>It begins as a blank., <>UV-printed in high-build layers., <>A secure NFC chip, sealed inside., <>Cured, bevelled, finished., <>Tested live — ready to tap., ]; return (
); } window.FactoryScene = FactoryScene; })();