const { useState, useEffect } = React; /* ── useIsMobile ─────────────────────────────────── */ const useIsMobile = (breakpoint = 768) => { const [mobile, setMobile] = useState(() => window.innerWidth < breakpoint); useEffect(() => { const handler = () => setMobile(window.innerWidth < breakpoint); window.addEventListener('resize', handler); return () => window.removeEventListener('resize', handler); }, [breakpoint]); return mobile; }; /* ── Nav ─────────────────────────────────────────── */ const Nav = ({ page, setPage, onContact }) => { const [scrolled, setScrolled] = useState(false); const [open, setOpen] = useState(false); const isMobile = useIsMobile(); useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 24); window.addEventListener('scroll', onScroll); return () => window.removeEventListener('scroll', onScroll); }, []); useEffect(() => { document.body.style.overflow = open ? 'hidden' : ''; return () => { document.body.style.overflow = ''; }; }, [open]); const links = [ { id: 'empieza', label: 'Empieza aquí' }, { id: 'articulos', label: 'Artículos' }, { id: 'laboratorio', label: 'Laboratorio' }, { id: 'sobre', label: 'Sobre mí' }, ]; const go = (id) => { setPage(id); setOpen(false); window.scrollTo(0, 0); }; const bar = (rot, ty, opacity = 1) => ({ display: 'block', width: 22, height: 2, background: 'var(--primary)', borderRadius: 2, transformOrigin: 'center', transition: 'all 0.3s cubic-bezier(0.4,0,0.2,1)', transform: `rotate(${rot}deg) translateY(${ty}px)`, opacity, }); return ( <> {isMobile && ( <>
setOpen(false)} style={{ position: 'fixed', inset: 0, zIndex: 190, background: 'rgba(26,43,38,0.3)', backdropFilter: 'blur(2px)', opacity: open ? 1 : 0, pointerEvents: open ? 'all' : 'none', transition: 'opacity 0.3s ease', }} />
{links.map((l, i) => ( ))}
)} ); }; /* ── Footer ───────────────────────────────────────── */ const Footer = ({ setPage }) => { const go = (id) => { setPage(id); window.scrollTo(0, 0); }; return (
la consulta de javi

Psicología honesta para quien quiere entenderse mejor.

Secciones
{[['empieza', 'Empieza aquí'], ['articulos', 'Artículos'], ['laboratorio', 'Laboratorio'], ['sobre', 'Sobre mí']].map(([id, label]) => (
))}
© 2026 Laconsultadejavi.es · Todos los derechos reservados Hecho con honestidad, sin tecnicismos innecesarios
); }; /* ── Shared UI ────────────────────────────────────── */ const Btn = ({ onClick, variant = 'solid', children, small }) => { const [hov, setHov] = useState(false); if (variant === 'ghost') { return ( ); } if (variant === 'outline') { return ( ); } return ( ); }; const SectionLabel = ({ children }) => (
{children}
); const Card = ({ children, onClick, hover = true, style = {} }) => { const [hov, setHov] = useState(false); return (
hover && setHov(true)} onMouseLeave={() => hover && setHov(false)} style={{ background: 'var(--white)', borderRadius: 'var(--radius-lg)', border: '1px solid var(--border)', boxShadow: hov ? 'var(--shadow-md)' : 'var(--shadow)', transform: hov && onClick ? 'translateY(-3px)' : 'translateY(0)', transition: 'all 0.25s ease', cursor: onClick ? 'pointer' : 'default', ...style, }} > {children}
); }; /* ── Contact Modal ────────────────────────────────── */ const ContactModal = ({ onClose }) => { const [name, setName] = useState(''); const [email, setEmail] = useState(''); const [msg, setMsg] = useState(''); const [sent, setSent] = useState(false); const [sending, setSending] = useState(false); const [error, setError] = useState(''); const handleSubmit = async (e) => { e.preventDefault(); setError(''); setSending(true); try { const res = await fetch('/api/v1/contacts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: name.trim(), email: email.trim(), message: msg.trim() }), }); const json = await res.json(); if (!res.ok) { const detail = json?.error?.message || 'Error al enviar. Inténtalo de nuevo.'; setError(detail); setSending(false); return; } setSent(true); } catch (_) { setError('No se pudo conectar con el servidor. Comprueba tu conexión.'); } finally { setSending(false); } }; const inputStyle = { width: '100%', padding: '12px 16px', borderRadius: 10, fontSize: 15, border: '1.5px solid var(--border)', background: 'var(--white)', fontFamily: 'DM Sans', color: 'var(--text)', outline: 'none', transition: 'border-color 0.2s', }; return ( <>
Déjame tu consulta

Es privada. Te respondo personalmente.

{sent ? (
✉️

¡Consulta enviada!

He recibido tu mensaje. Te respondo en menos de 48 horas de forma personal y privada.

) : (
setName(e.target.value)} placeholder="Tu nombre" style={inputStyle} onFocus={e => e.target.style.borderColor = 'var(--primary-light)'} onBlur={e => e.target.style.borderColor = 'var(--border)'} />
setEmail(e.target.value)} placeholder="tu@email.com" style={inputStyle} onFocus={e => e.target.style.borderColor = 'var(--primary-light)'} onBlur={e => e.target.style.borderColor = 'var(--border)'} />