// ============================================ // App — Grupo Luna // ============================================ const TWEAKS_DEFAULTS = /*EDITMODE-BEGIN*/ { motion: "medium", heroVariant: "stacked", density: "comfortable", }; /*EDITMODE-END*/ const MOTION_MAP = { subtle: 0.3, medium: 0.6, strong: 1 }; const DENSITY_MAP = { compact: 0.85, comfortable: 1, airy: 1.15 }; function App() { const [lang, setLang] = React.useState("es"); const [navOpen, setNavOpen] = React.useState(false); const [scrolled, setScrolled] = React.useState(false); const [scrollY, setScrollY] = React.useState(0); const [mouseX, setMouseX] = React.useState(0); const [mouseY, setMouseY] = React.useState(0); const [tweaksOpen, setTweaksOpen] = React.useState(false); const [tweaks, setTweaks] = React.useState(TWEAKS_DEFAULTS); const [activeSection, setActiveSection] = React.useState("inicio"); const progressRef = React.useRef(null); const t = window.GL_I18N[lang]; // Apply tweaks to CSS React.useEffect(() => { document.documentElement.style.setProperty( "--motion", MOTION_MAP[tweaks.motion], ); document.documentElement.style.setProperty( "--type-density", DENSITY_MAP[tweaks.density], ); }, [tweaks]); // Scroll listener React.useEffect(() => { let raf; const onScroll = () => { if (raf) return; raf = requestAnimationFrame(() => { const y = window.scrollY; setScrollY(y); setScrolled(y > 60); // Scroll progress const max = document.documentElement.scrollHeight - window.innerHeight; const pct = max > 0 ? y / max : 0; if (progressRef.current) { progressRef.current.style.transform = `scaleX(${pct})`; } // Active section const sections = [ "inicio", "nosotros", "servicios", "proyectos", "stats", "clientes", "contacto", ]; for (const id of sections) { const el = document.getElementById(id); if (!el) continue; const rect = el.getBoundingClientRect(); if (rect.top <= 120 && rect.bottom > 120) { setActiveSection(id); break; } } raf = null; }); }; onScroll(); window.addEventListener("scroll", onScroll, { passive: true }); return () => window.removeEventListener("scroll", onScroll); }, []); // Mouse parallax on hero React.useEffect(() => { const onMove = (e) => { const x = e.clientX / window.innerWidth - 0.5; const y = e.clientY / window.innerHeight - 0.5; setMouseX(x); setMouseY(y); }; window.addEventListener("mousemove", onMove); return () => window.removeEventListener("mousemove", onMove); }, []); // Use reveal hook useReveal(); // ----- Edit mode (Tweaks toggle) protocol ----- React.useEffect(() => { const onMsg = (e) => { const d = e.data || {}; if (d.type === "__activate_edit_mode") setTweaksOpen(true); if (d.type === "__deactivate_edit_mode") setTweaksOpen(false); }; window.addEventListener("message", onMsg); window.parent.postMessage({ type: "__edit_mode_available" }, "*"); return () => window.removeEventListener("message", onMsg); }, []); const updateTweak = (key, value) => { const next = { ...tweaks, [key]: value }; setTweaks(next); window.parent.postMessage( { type: "__edit_mode_set_keys", edits: { [key]: value } }, "*", ); }; const closeTweaks = () => { setTweaksOpen(false); window.parent.postMessage({ type: "__edit_mode_dismissed" }, "*"); }; const navLinks = [ ["inicio", t.nav.inicio], ["nosotros", t.nav.nosotros], ["servicios", t.nav.servicios], ["proyectos", t.nav.proyectos], ["contacto", t.nav.contacto], ]; return ( <>
GRUPO LUNA
{t.footer.rights}
Tweaks
{["split", "stacked", "cinematic"].map((v) => ( ))}
{["subtle", "medium", "strong"].map((v) => ( ))}
{["compact", "comfortable", "airy"].map((v) => ( ))}
); } ReactDOM.createRoot(document.getElementById("root")).render();