// Added immersive sections for the live UMAPRO site: // - MonjaSpinSection : the signature monja photo on a draggable, auto-rotating 3D plane // - StoresSection : the 12-store curved 3D gallery embedded as a full-bleed iframe // Matches the "Street Umai" design system (cream / ink / red / Anton). const { useEffect: xUE, useRef: xUR } = React; function MonjaSpinSection({ content }) { const c = content.monja; const spinRef = xUR(null); const stageRef = xUR(null); xUE(() => { const spin = spinRef.current, stage = stageRef.current; if (!spin || !stage) return; const reduced = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches; const fine = window.matchMedia && window.matchMedia("(pointer: fine)").matches; let rotY = -18, rotX = 6, vel = 0.35; // auto-spin velocity (deg/frame) let tiltTarget = 6, tilt = 6; let dragging = false, lastX = 0, idle = 0, raf = 0, visible = true; function render() { spin.style.transform = "rotateX(" + tilt.toFixed(2) + "deg) rotateY(" + rotY.toFixed(2) + "deg)"; } function loop() { if (!dragging) { idle++; // resume gentle auto-spin after a short idle once inertia has decayed if (Math.abs(vel) < 0.35 && idle > 40 && !reduced) vel += (0.35 - vel) * 0.03; rotY += vel; vel *= 0.96; if (Math.abs(vel) < 0.02) vel = reduced ? 0 : 0.06; } tilt += (tiltTarget - tilt) * 0.06; render(); if (visible && !reduced) raf = requestAnimationFrame(loop); else raf = 0; } render(); if (!reduced) raf = requestAnimationFrame(loop); function onDown(e) { dragging = true; idle = 0; vel = 0; lastX = (e.touches ? e.touches[0].clientX : e.clientX); stage.classList.add("grabbing"); if (!raf && !reduced) raf = requestAnimationFrame(loop); } function onMove(e) { if (fine && !dragging) { const r = stage.getBoundingClientRect(); tiltTarget = ((e.clientY - r.top) / r.height - 0.5) * -16; } if (!dragging) return; const x = (e.touches ? e.touches[0].clientX : e.clientX); const dx = x - lastX; lastX = x; rotY += dx * 0.5; vel = dx * 0.5; render(); } function onUp() { dragging = false; idle = 0; stage.classList.remove("grabbing"); } stage.addEventListener("pointerdown", onDown); window.addEventListener("pointermove", onMove); window.addEventListener("pointerup", onUp); const io = new IntersectionObserver(function (es) { visible = es[0].isIntersecting; if (visible && !reduced && !raf) raf = requestAnimationFrame(loop); if (!visible && raf) { cancelAnimationFrame(raf); raf = 0; } }, { threshold: 0 }); io.observe(stage); return function () { io.disconnect(); stage.removeEventListener("pointerdown", onDown); window.removeEventListener("pointermove", onMove); window.removeEventListener("pointerup", onUp); if (raf) cancelAnimationFrame(raf); }; }, []); const bg = { backgroundImage: "url('" + c.img + "')" }; return (
{c.eyebrow}

{c.title}

{c.body}

{c.tags.map((t) => {t})}
{c.cta}↗
); } function StoresSection({ content }) { const c = content.stores; return (
{c.eyebrow}

{c.title}

{c.meta}

{c.note}

); } Object.assign(window, { MonjaSpinSection, StoresSection });