/* global React, ReactDOM, TRINITY */ const { useState, useEffect, useRef, useContext, createContext, useMemo, useCallback } = React; // ============= LANGUAGE CONTEXT ============= const LangCtx = createContext({ lang: "es", setLang: () => {} }); window.LangCtx = LangCtx; function LangProvider({ children, initial = "es" }) { const [lang, setLang] = useState(initial); useEffect(() => { document.documentElement.lang = lang; }, [lang]); return React.createElement(LangCtx.Provider, { value: { lang, setLang } }, children); } // Hook → fetches localized string from {es, en} obj function useT() { const { lang } = useContext(LangCtx); return (m) => { if (!m) return ""; if (typeof m === "string") return m; if (Array.isArray(m)) return m.map((x) => (typeof x === "string" ? x : x[lang] || x.en)).join(" "); if (typeof m === "object" && (m.es !== undefined || m.en !== undefined)) return m[lang] || m.en || ""; return String(m); }; } // ============= REVEAL ON SCROLL ============= function useReveal() { const ref = useRef(null); useEffect(() => { const el = ref.current; if (!el) return; if (typeof IntersectionObserver === "undefined") { el.classList.add("in"); return; } const io = new IntersectionObserver( (entries) => { entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); } }); }, { threshold: 0.12, rootMargin: "0px 0px -80px 0px" } ); io.observe(el); return () => io.disconnect(); }, []); return ref; } // ============= ICONS (minimal, accessible inline SVG, no slop) ============= const Icon = ({ name, size = 20, stroke = 1.5 }) => { const common = { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", }; switch (name) { case "arrow-right": return ; case "arrow-left": return ; case "phone": return ; case "mail": return ; case "map-pin": return ; case "clock": return ; case "star": return ; case "integrated": return ; case "evidence": return ; case "tech": return ; case "heart": return ; case "check": return ; case "globe": return ; default: return null; } }; // ============= LANGUAGE TOGGLE ============= function LangToggle() { const { lang, setLang } = useContext(LangCtx); return (
); } // ============= HEADER ============= function Header({ active, onJump }) { const T = useT(); const [scrolled, setScrolled] = useState(false); const [open, setOpen] = useState(false); useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 12); onScroll(); window.addEventListener("scroll", onScroll, { passive: true }); return () => window.removeEventListener("scroll", onScroll); }, []); useEffect(() => { document.body.style.overflow = open ? "hidden" : ""; }, [open]); const nav = [ { id: "services", label: TRINITY.ui.services }, { id: "about", label: TRINITY.ui.about }, { id: "testimonials", label: TRINITY.ui.testimonials }, { id: "faq", label: TRINITY.ui.faq }, { id: "contact", label: TRINITY.ui.contact }, ]; const handleJump = (id) => (e) => { e.preventDefault(); setOpen(false); const el = document.getElementById(id); if (el) { const top = el.getBoundingClientRect().top + window.scrollY - 72; window.scrollTo({ top, behavior: "smooth" }); } }; return ( <>
Trinity Comprehensive Health Care
{T(TRINITY.ui.book)}
{TRINITY.contact.phone} {TRINITY.contact.address}
); } // ============= HERO ============= function Hero() { const T = useT(); const ref = useReveal(); const { lang } = useContext(LangCtx); const titleLines = TRINITY.hero.title[lang] || TRINITY.hero.title.en; const emWord = (TRINITY.hero.titleEm[lang] || TRINITY.hero.titleEm.en).trim(); // Render last word as italic serif accent const renderLine = (line, isLast) => { if (!isLast) return line; const idx = line.toLowerCase().lastIndexOf(emWord.toLowerCase()); if (idx === -1) return line; return ( <> {line.slice(0, idx)} {line.slice(idx, idx + emWord.length)} {line.slice(idx + emWord.length)} ); }; return (
{T(TRINITY.hero.eyebrow)}

{titleLines.map((line, i) => ( {renderLine(line, i === titleLines.length - 1)} ))}

{T(TRINITY.hero.sub)}

{ e.preventDefault(); document.getElementById("contact")?.scrollIntoView({ behavior: "smooth", block: "start" }); }}> {T(TRINITY.ui.book)} {TRINITY.contact.phone}
{TRINITY.hero.meta.map((m, i) => ( {T(m)} ))}
{T({
4.9★ {T({ es: "Promedio paciente", en: "Patient avg." })}
{TRINITY.trust.map((tr, i) => (
{tr.num} {T(tr.lbl)}
))}
); } // ============= SECTION HEAD helper ============= function SectionHead({ eyebrow, title, sub, split = true }) { const T = useT(); const { lang } = useContext(LangCtx); const lines = title?.[lang] || title?.en || []; return (
{eyebrow && {T(eyebrow)}}

{lines.map((l, i) => { // italicize middle line if 3 lines if (lines.length === 3 && i === 1) return {l}; return {l}; })}

{sub &&

{T(sub)}

}
); } // ============= SERVICE PILLARS ============= function Pillars() { const T = useT(); const ref = useReveal(); return (
{TRINITY.pillars.items.map((p, i) => (
{p.num}

{T(p.title)}

{T(p.body)}

{p.tags.map((tg, j) => {T(tg)})}
))}
); } // ============= FEATURED TREATMENTS ============= function Featured() { const T = useT(); const ref = useReveal(); return (
{T({ {T({ es: "Estética médica", en: "Medical aesthetics" })}
{T({ {T({ es: "Bienvenida bilingüe", en: "Bilingual welcome" })}
{TRINITY.featured.items.map((f) => (
{f.n}
{T(f.name)}
{T(f.cat)}

{T(f.desc)}

{T(f.time)} { e.preventDefault(); document.getElementById("contact")?.scrollIntoView({ behavior: "smooth" }); }}> {T({ es: "Reservar", en: "Book" })}
))}
); } // ============= ABOUT / DOCTOR ============= function About() { const T = useT(); const ref = useReveal(); return (
{T({
{T(TRINITY.about.eyebrow)}

{T({ es: "Una práctica donde la medicina, la regeneración y la estética se hablan.", en: "A practice where medicine, regeneration, and aesthetics talk to each other.", })}

{T(TRINITY.about.quote)}

{T(TRINITY.about.desc)}

{TRINITY.about.team.map((member, i) => (
{member.avatar}
{member.name}
{T(member.role)}
))}
); } // ============= WHY US ============= function WhyUs() { const T = useT(); const ref = useReveal(); return (
{TRINITY.why.items.map((w, i) => (

{T(w.title)}

{T(w.body)}

))}
); } // ============= TESTIMONIALS CAROUSEL ============= function Testimonials() { const T = useT(); const ref = useReveal(); const [idx, setIdx] = useState(0); const items = TRINITY.testimonials.items; const prev = () => setIdx((i) => (i - 1 + items.length) % items.length); const next = () => setIdx((i) => (i + 1) % items.length); useEffect(() => { const timer = setInterval(() => setIdx((i) => (i + 1) % items.length), 7000); return () => clearInterval(timer); }, [items.length]); return (
{T(TRINITY.testimonials.eyebrow)}

{(TRINITY.testimonials.title[useContext(LangCtx).lang] || TRINITY.testimonials.title.en).map((l, i, arr) => ( {arr.length === 3 && i === 1 ? {l} : l} ))}

{items.map((t2, i) => (
{Array.from({ length: t2.rating }).map((_, j) => )}

{T(t2.quote)}

{T(t2.name).charAt(0)}
{T(t2.name)}
{T(t2.treatment)} · {T(t2.loc)}
))}
{items.map((_, i) => (
); } // ============= FAQ ============= function FAQ() { const T = useT(); const ref = useReveal(); const [cat, setCat] = useState("general"); const [openIdx, setOpenIdx] = useState(0); const items = TRINITY.faq.items.filter((it) => it.cat === cat); return (
{TRINITY.faq.cats.map((c) => ( ))}
{items.length === 0 && (

{T({ es: "Próximamente más preguntas.", en: "More questions coming soon." })}

)} {items.map((it, i) => { const open = openIdx === i; return (

{T(it.a)}

); })}
); } // ============= CTA BANNER ============= function CTABanner() { const T = useT(); const ref = useReveal(); const { lang } = useContext(LangCtx); const lines = TRINITY.cta.title[lang] || TRINITY.cta.title.en; return (

{lines.map((l, i, arr) => ( {arr.length === 3 && i === 1 ? {l} : l} ))}

{T(TRINITY.cta.sub)}

{ e.preventDefault(); document.getElementById("contact")?.scrollIntoView({ behavior: "smooth" }); }}> {T(TRINITY.ui.book)} {T(TRINITY.ui.call)}
); } // ============= CONTACT ============= function ContactSection() { const T = useT(); const ref = useReveal(); const [form, setForm] = useState({ name: "", email: "", phone: "", interest: "", message: "" }); const [errors, setErrors] = useState({}); const [sent, setSent] = useState(false); const { lang } = useContext(LangCtx); const validate = () => { const e = {}; if (!form.name.trim()) e.name = TRINITY.contactSection.errors.name; if (!form.email.trim() || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email)) e.email = TRINITY.contactSection.errors.email; if (!form.phone.trim()) e.phone = TRINITY.contactSection.errors.phone; if (!form.interest) e.interest = TRINITY.contactSection.errors.interest; setErrors(e); return Object.keys(e).length === 0; }; const submit = (ev) => { ev.preventDefault(); if (!validate()) return; setSent(true); setTimeout(() => { setSent(false); setForm({ name: "", email: "", phone: "", interest: "", message: "" }); }, 4200); }; const onField = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value })); return (