/* app.jsx — Main App, routing, tweaks */ const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "heroVariant": "lifestyle", "brandHue": "reflex", "density": "regular", "cards": "default", "fontFamily": "inter", "dark": false }/*EDITMODE-END*/; /* ── Brand hue presets — vary tone but stay within Pantone family ── */ const BRAND_HUES = { reflex: { name: 'Reflex Blue (oficial)', '--brand-900': '#000043', '--brand-800': '#0D1F5C', '--brand-700': '#192f7c', '--brand-600': '#2A4595', '--brand-500': '#3D5DB0', '--brand-400': '#5087c7', '--brand-300': '#84AFDB', '--brand-200': '#C2D7ED', '--brand-100': '#DCE7F4', '--brand-50': '#EEF3FA', }, brighter: { name: 'Más luminoso', '--brand-900': '#0A1A52', '--brand-800': '#13297A', '--brand-700': '#1E40A8', '--brand-600': '#2F58C6', '--brand-500': '#4574DD', '--brand-400': '#5087c7', '--brand-300': '#8DB2DD', '--brand-200': '#C7DAEF', '--brand-100': '#E0EAF6', '--brand-50': '#F1F5FB', }, classic: { name: 'Navy clásico', '--brand-900': '#000043', '--brand-800': '#000F4A', '--brand-700': '#0D1F5C', '--brand-600': '#192f7c', '--brand-500': '#2A4595', '--brand-400': '#5087c7', '--brand-300': '#7BA3D2', '--brand-200': '#B5CCE8', '--brand-100': '#D5E1EF', '--brand-50': '#EAF0F8', }, }; function App() { const [t, setTweak] = useTweaks(TWEAK_DEFAULTS); const [page, setPage] = React.useState('home'); // Apply tweaks to via data attributes + CSS vars React.useEffect(() => { const root = document.documentElement; root.setAttribute('data-theme', t.dark ? 'dark' : 'light'); root.setAttribute('data-density', t.density); root.setAttribute('data-cards', t.cards); root.setAttribute('data-font', t.fontFamily); // Brand hue const hue = BRAND_HUES[t.brandHue] || BRAND_HUES.reflex; Object.entries(hue).forEach(([k, v]) => { if (k.startsWith('--')) root.style.setProperty(k, v); }); }, [t]); // Scroll to top when page switches React.useEffect(() => { window.scrollTo({ top: 0, behavior: 'auto' }); }, [page]); // ── Scroll-reveal: fade + rise sections/cards as they enter the viewport ── React.useEffect(() => { const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches; let observer = null; const timer = setTimeout(() => { const root = document.documentElement; root.classList.add('reveal-ready'); // Curated targets — meaningful blocks, not every node. const groups = [ { sel: '.hero-copy', cls: 'reveal reveal-left' }, { sel: '.hero-video-copy', cls: 'reveal reveal-left' }, { sel: '.hero-visual', cls: 'reveal reveal-right' }, { sel: '.hero-split-top', cls: 'reveal' }, { sel: '.hero-split-stage', cls: 'reveal reveal-zoom' }, { sel: '.section-head', cls: 'reveal' }, { sel: '.about-copy', cls: 'reveal reveal-left' }, { sel: '.about-visual', cls: 'reveal reveal-right' }, { sel: '.about-point', cls: 'reveal' }, { sel: '.kpi-item', cls: 'reveal' }, { sel: '.service-card', cls: 'reveal' }, { sel: '.lb-cat-card', cls: 'reveal' }, { sel: '.why-head', cls: 'reveal reveal-left' }, { sel: '.why-item', cls: 'reveal' }, { sel: '.proj-card', cls: 'reveal' }, { sel: '.brands-head', cls: 'reveal' }, { sel: '.logo-tile:not(.logo-tile-marquee)', cls: 'reveal' }, { sel: '.comfort-feature', cls: 'reveal' }, { sel: '.contact-copy', cls: 'reveal reveal-left' }, { sel: '.contact-form', cls: 'reveal reveal-right' }, { sel: '.footer-brand, .footer-col', cls: 'reveal' }, ]; const all = []; groups.forEach(({ sel, cls }) => { document.querySelectorAll(sel).forEach((el) => { if (el.dataset.revealReady) return; el.dataset.revealReady = '1'; cls.split(' ').forEach((c) => el.classList.add(c)); all.push(el); }); }); // Stagger siblings within the same parent for a cascading feel. all.forEach((el) => { const sibs = Array.from(el.parentElement.children).filter((c) => c.classList.contains('reveal')); const idx = Math.max(0, sibs.indexOf(el)); el.style.transitionDelay = Math.min(idx, 6) * 75 + 'ms'; }); if (reduce || !('IntersectionObserver' in window)) { all.forEach((el) => el.classList.add('is-in')); return; } observer = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add('is-in'); observer.unobserve(e.target); } }); }, { threshold: 0.12, rootMargin: '0px 0px -8% 0px' }); all.forEach((el) => observer.observe(el)); // Safety net: reveal anything already within (or above) the viewport // right away, so above-the-fold content never stays hidden if the // observer is slow to fire. const vh = window.innerHeight || 800; all.forEach((el) => { const top = el.getBoundingClientRect().top; if (top < vh * 0.92) { el.classList.add('is-in'); observer.unobserve(el); } }); }, 90); return () => { clearTimeout(timer); if (observer) observer.disconnect(); }; }, [page]); return (