// desidium-helpers.jsx — shared primitives, exported to window
const { useState, useEffect, useRef } = React;
// pick a localized string: L({en,es}, lang)
function L(obj, lang) { return obj ? (obj[lang] ?? obj.en) : ""; }
// Scroll-reveal wrapper. Adds .is-in when scrolled into view.
// `motion=false` -> always visible (reduced motion / tweak off).
function Reveal({ children, motion = true, delay = 0, y = 26, as = "div", className = "", style = {} }) {
const ref = useRef(null);
const [seen, setSeen] = useState(false);
useEffect(() => {
if (!motion) { setSeen(true); return; }
const el = ref.current; if (!el) return;
const io = new IntersectionObserver((es) => {
es.forEach((e) => { if (e.isIntersecting) { setSeen(true); io.disconnect(); } });
}, { threshold: 0.12, rootMargin: "0px 0px -8% 0px" });
io.observe(el);
return () => io.disconnect();
}, [motion]);
const Tag = as;
const baseT = motion ? `opacity .9s cubic-bezier(.22,.61,.36,1) ${delay}ms, transform 1.05s cubic-bezier(.22,.61,.36,1) ${delay}ms` : "none";
return (