/* ───── Main app: scroll-driven reading-table experience ───── */ const { useState, useEffect, useRef } = React; // helpers const clamp = (v, a = 0, b = 1) => Math.min(b, Math.max(a, v)); const lerp = (a, b, t) => a + (b - a) * t; const easeOut = t => 1 - Math.pow(1 - t, 3); const easeIO = t => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; const STAGES = { IDLE: [0.00, 0.08], SHUFFLE: [0.08, 0.32], FAN: [0.32, 0.55], DEAL: [0.55, 0.88], SETTLE: [0.88, 1.00], }; const stageP = (p, [a, b]) => clamp((p - a) / (b - a)); const DECK_COUNT = 10; // Returns the transform values for card i at progress p. function deckCardTransform(i, p) { const shuf = stageP(p, STAGES.SHUFFLE); const fan = stageP(p, STAGES.FAN); const deal = stageP(p, STAGES.DEAL); // base — stacked at center, slight offset for depth let x = -i * 0.4; let y = -i * 0.6; let z = i * 0.3; let rot = ((i * 7) % 5) - 2; // -2..2 jitter let opacity = 1; let scale = 1; // ── SHUFFLE ── if (shuf > 0) { const t = shuf; const fade = 1 - t; // dies out as we approach fan const phase = Math.sin(t * Math.PI * 7 + i * 0.7); const phase2 = Math.cos(t * Math.PI * 9 + i * 1.3); x += phase * 14 * fade; y += phase2 * 8 * fade; rot += phase * 6 * fade; // tiny scale pulse scale = 1 + Math.sin(t * Math.PI * 12 + i) * 0.02 * fade; } // ── FAN ── if (fan > 0) { const t = easeOut(fan); const half = (DECK_COUNT - 1) / 2; const offset = i - half; const fanAngle = offset * 9; // -40..40 const fanRadius = 230; // fan upward, like a hand of cards held above the table const fx = Math.sin(fanAngle * Math.PI / 180) * fanRadius; const fy = -Math.abs(offset) * 4 - 30; // slight arc lift x = lerp(x, fx, t); y = lerp(y, fy, t); rot = lerp(rot, fanAngle, t); z = lerp(z, i * 0.8, t); } // ── DEAL ── if (deal > 0) { // top 3 (i=0..2) fly out to positions; the rest gather back to a tidy stack const targets = [ { x: -280, y: 30, rot: 0 }, // Past { x: 0, y: 30, rot: 0 }, // Present { x: 280, y: 30, rot: 0 }, // Future ]; if (i < 3) { // stagger start times const delay = i * 0.13; const local = clamp((deal - delay) / 0.45); const e = easeOut(local); const target = targets[i]; x = lerp(x, target.x, e); y = lerp(y, target.y, e); rot = lerp(rot, target.rot, e); // arc lift y -= Math.sin(local * Math.PI) * 60; z = 100 + i; } else { // collapse rest back to tidy stack just below the candle, slightly to right const t = easeOut(deal); const idx = i - 3; x = lerp(x, 240 + idx * 0.4, t); y = lerp(y, -120 + idx * 0.6, t); rot = lerp(rot, -6 + (idx * 3) % 4, t); scale = lerp(scale, 0.55, t); opacity = lerp(opacity, 0.7, t); z = lerp(z, idx * 0.3, t); } } return { x, y, rot, z, opacity, scale }; } const TopBar = () => (
A
Astro Annie
astroanjilina.com
The Reader Services Training
); const IntroShowcase = () => (
{/* Left stat column */}
12
राशियाँ
All Twelve Signs Read
{/* Center — the banner image */}
Astro Anjlina — Tarot Card Reading, Numerology, Vastu Expert
{/* Right stat column */}
4
courses
Small Batches of Six
{/* Tagline below the banner */}
Print · Hindi & English · Available at the Studio
{/* CTA buttons */}
); const Footer = () => ( ); const ShuffleText = () => (
shuffling…
); const Embers = ({ active }) => { const [pts] = useState(() => Array.from({ length: 18 }).map((_, i) => ({ x: 40 + Math.random() * 20, // % horizontal start delay: i * 0.6 + Math.random(), duration: 5 + Math.random() * 4, drift: (Math.random() - 0.5) * 18, size: 1 + Math.random() * 1.8, }))); if (!active) return null; return (
{pts.map((p, i) => ( ))}
); }; const IS_DAY = (typeof window !== 'undefined' && window.__THEME === 'day'); // Card flip audio — references the