// Scroll story: one continuous camera path through the page. // Given the scroll position and section metrics, returns the scene target. // Framework-agnostic: React preview reads metrics itself, Nuxt feeds it from GSAP ScrollTrigger. const base = { tilt: 0, turn: 0, explode: 0, dim: 1, spin: 0, constellations: 0 }; export const STATES = { desktop: { hero: { ...base, cx: 0.27, cy: 0.58, size: 0.78, spin: 1 }, eco: { ...base, cx: 0.28, cy: 0.55, size: 0.76 }, zij: { ...base, cx: 0.17, cy: 0.52, size: 0.56, constellations: 1 }, anatA: { ...base, cx: 0.3, cy: 0.52, size: 0.72 }, anatB: { ...base, cx: 0.32, cy: 0.56, size: 0.66, tilt: 0.88, turn: -0.42, explode: 1 }, contact: { ...base, cx: 0.27, cy: 0.52, size: 0.78, spin: 1 }, }, mobile: { hero: { ...base, cx: 0.5, cy: 0.29, size: 1.1, spin: 1 }, eco: { ...base, cx: 0.5, cy: 0.245, size: 0.92 }, zij: { ...base, cx: 0.5, cy: 0.1, size: 1.5, dim: 0.12, constellations: 1 }, anatA: { ...base, cx: 0.5, cy: 0.3, size: 0.95 }, anatB: { ...base, cx: 0.5, cy: 0.3, size: 0.8, tilt: 1.05, turn: -0.42, explode: 1 }, contact: { ...base, cx: 0.5, cy: 0.3, size: 1.1, spin: 1 }, }, }; const ease = (t) => t * t * (3 - 2 * t); export function storyAt(y, m, mobile) { const S = mobile ? STATES.mobile : STATES.desktop; const anat = m.anatTop + (m.anatEnd - m.anatTop) * 0.38; const stops = [ [0, S.hero], [m.ecoTop, S.eco], [m.ecoEnd, S.eco], [m.zijTop - m.vh * 0.25, S.zij], [m.anatTop - m.vh * 0.6, S.zij], [m.anatTop, S.anatA], [anat, S.anatB], [m.anatEnd, S.anatB], [m.anatEnd + m.vh * 0.9, S.contact], ]; let i = 0; while (i < stops.length - 2 && y > stops[i + 1][0]) i++; const [y0, a] = stops[i], [y1, b] = stops[i + 1]; const t = ease(Math.min(1, Math.max(0, (y - y0) / Math.max(1, y1 - y0)))); const out = {}; for (const k in a) out[k] = a[k] + (b[k] - a[k]) * t; const pe = (y - m.ecoTop) / Math.max(1, m.ecoEnd - m.ecoTop); const inEco = y > m.ecoTop - m.vh * 0.35 && y < m.ecoEnd + m.vh * 0.35; const ecoIndex = Math.min(14, Math.max(0, Math.floor(pe * 15))); const pa = (y - m.anatTop) / Math.max(1, m.anatEnd - m.anatTop); const focus = pa < 0.4 || pa > 1.15 ? -1 : Math.min(3, Math.floor(((pa - 0.4) / 0.6) * 4)); return { target: out, inEco, ecoIndex, focus, pe }; } // Scroll position that lands on product i inside the pinned ecosystem. export const ecoScrollFor = (i, m) => m.ecoTop + ((i + 0.5) / 15) * (m.ecoEnd - m.ecoTop);