// 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 (
);
}
Object.assign(window, { MonjaSpinSection, StoresSection });