/* ------------------------------------------------------------------ * * Shared components for Aqua Signature * ------------------------------------------------------------------ */ const { useState, useEffect, useRef, useMemo, useCallback } = React; // ---------- Reveal-on-scroll ---------- function useReveal() { const ref = useRef(null); useEffect(() => { if (!ref.current) return; const el = ref.current; const io = new IntersectionObserver( (entries) => { entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add("is-in"); io.unobserve(e.target); } }); }, { threshold: 0.12, rootMargin: "0px 0px -8% 0px" } ); io.observe(el); return () => io.disconnect(); }, []); return ref; } function Reveal({ as = "div", className = "", variant = "", children, ...rest }) { const ref = useReveal(); const Tag = as; const cls = ["reveal", variant && `reveal--${variant}`, className].filter(Boolean).join(" "); return {children}; } function RevealStagger({ as = "div", className = "", children, ...rest }) { const ref = useReveal(); const Tag = as; return {children}; } // ---------- Logo ---------- // To swap in the real logo: drop a file at `assets/logo.svg` (or .png). // The falls back to a CSS-drawn droplet if the file is missing. function Droplet({ className = "" }) { const [imgFailed, setImgFailed] = React.useState(false); if (!imgFailed) { return ( setImgFailed(true)} /> ); } return ( ); } function Logo({ size = "sm", variant = "", stack = false, className = "" }) { const { t } = useLang(); const cls = [ "logo", size === "lg" && "logo--lg", size === "xl" && "logo--xl", stack && "logo--stack", variant && `logo--${variant}`, className, ].filter(Boolean).join(" "); return ( AQUA Signature {t("logo.country")} ); } // ---------- Nav ---------- function Nav({ route, navigate, dark }) { const [scrolled, setScrolled] = useState(false); const { t } = useLang(); useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 30); onScroll(); window.addEventListener("scroll", onScroll, { passive: true }); return () => window.removeEventListener("scroll", onScroll); }, []); const items = [ { id: "collection", label: t("nav.collection") }, { id: "story", label: t("nav.story") }, { id: "materials", label: t("nav.materials") }, { id: "contact", label: t("nav.contact") }, ]; return ( { e.preventDefault(); navigate("/"); }}> {items.map((it) => ( { e.preventDefault(); navigate("/" + it.id); }} > {it.label} ))} { e.preventDefault(); navigate("/contact"); }} > {t("nav.cta")} ); } // ---------- LangSwitch — sits below the Request Quote CTA, centered ---------- function LangSwitch() { const { lang, setLang } = useLang(); return ( setLang("en")} aria-pressed={lang === "en"} >EN · setLang("es")} aria-pressed={lang === "es"} >ES ); } // ---------- Footer ---------- function Footer({ navigate, onTradeLogin }) { const { t } = useLang(); return ( ); } // ---------- Placeholder media ---------- function Media({ tone = "sand", label = "image", note = "", center = "", src, alt = "", className = "", style }) { const t = tone === "ocean" ? "media--ocean" : tone === "teal" ? "media--teal" : tone === "pale" ? "media--pale" : "media--sand"; return ( {src ? : } {center && !src && {center}} {(label || note) && ( {label} {note} )} ); } // ---------- Marquee ---------- function Marquee({ items }) { return ( {[...items, ...items, ...items].map((it, i) => ( {it} ))} ); } // ---------- Toast ---------- function Toast({ message, show }) { return {message}; } // ---------- Section head ---------- function SectionHead({ eyebrow, title, kicker, count }) { return ( {eyebrow && {eyebrow}} {title} {kicker && {kicker}} {count && {count}} ); } // Expose to other Babel scripts Object.assign(window, { Reveal, RevealStagger, useReveal, Logo, Nav, LangSwitch, Footer, Media, Marquee, Toast, SectionHead });
{kicker}