/* global React, PAOLA_DATA, Reveal, Hero, Marquee, Lightbox */ // Pages: Home, Portfolio, About, Contact const { useState, useEffect, useRef, useMemo } = React; /* ========================================================= HOME ========================================================= */ function HomePage({ go, heroCopy, categories = PAOLA_DATA.categories }) { return (
{/* ABOUT TEASER */}
FRAME · 002 Paola García
Sobre mí

Soy Paola.

Llevo más de diez años traduciendo en luz los momentos que las familias no quieren olvidar. Sesiones materno-infantiles, recién nacidos, bautizos, primeras comuniones. Cada encuadre es una promesa.

Trabajo desde nuestro estudio in‑house en Sabaneta, junto a mi familia. Lo nuestro no es un servicio: es una experiencia íntima donde la cámara llega después de la conversación, el café y la complicidad.

— con cariño, Paola.
{/* PORTFOLIO CATEGORIES */}
Portafolio

Conoce mis
líneas de servicio.

Cada categoría es una colección viva. Galerías inmersivas, con la luz y la temperatura de cada etapa. Elige por dónde quieres empezar.

{[ { idx:'01', name:'Maternidad', desc:'La espera más hermosa de tu vida, convertida en un momento de amor al que podrás volver siempre.' }, { idx:'02', name:'Recién nacidos', desc:'Sus primeros días, sus detalles más pequeños y el amor más inmenso capturados con delicadeza.' }, { idx:'03', name:'Infantil', desc:'Una etapa que cambia demasiado rápido y merece ser guardada en recuerdos llenos de vida y ternura.' }, { idx:'04', name:'Familiar', desc:'Abrazos, miradas y conexiones reales que cuentan la historia de lo que son juntos.' }, { idx:'05', name:'Eventos especiales', desc:'Celebraciones que pasan en un instante, pero se convierten en recuerdos para toda la vida.' }, ].map((c) => (
go('portfolio')}>
{c.idx}
{c.name}
{c.desc}
Ver colección →
))}
{/* EXPERIENCE */}
Todo lo que pasa antes de

Una experiencia
in situ.

Conceptualización, maquillaje, peinado, escenografía, dirección de arte — una sesión con nosotras dura horas. Porque queremos que cuando levantes el álbum, vuelvas a vivir toda la experiencia.

{PAOLA_DATA.experience.map((x) => (
N° {x.num}
${x.em}`) }} />
))}
{/* CTA FINAL */}

Te espero en mi studio para que juntos
coleccionemos momentos de amor.

); } /* ========================================================= PORTFOLIO ========================================================= */ function PortfolioPage({ initialCat = 'all', gallery = PAOLA_DATA.gallery, categories = PAOLA_DATA.categories }) { const [cat, setCat] = useState(initialCat); const [lb, setLb] = useState(null); const masonryRef = useRef(null); useEffect(() => { setCat(initialCat); }, [initialCat]); // Marca como cargadas las imágenes ya en caché (no disparan onLoad) useEffect(() => { if (!masonryRef.current) return; masonryRef.current.querySelectorAll('img').forEach(img => { if (img.complete && img.naturalWidth > 0) { const tile = img.closest('.tile'); if (tile) tile.classList.add('img-ok'); } }); }); const filtered = useMemo(() => cat === 'all' ? gallery : gallery.filter(g => g.cat === cat), [cat, gallery] ); const cats = useMemo(() => { const map = {}; categories.forEach(c => map[c.id] = c.name); return map; }, [categories]); const activeCat = useMemo(() => categories.find(c => c.id === cat), [cat, categories] ); return (
Portafolio · 2026

Una colección
de momentos de amor.

Conoce un poco de mi portafolio: resultados de sesiones mágicas que cuentan una historia y conectan a nivel emocional.

{categories.map(c => ( ))}
{filtered.map((g, i) => (
setLb(i)}> {g.alt { const tile = e.currentTarget.closest('.tile'); if (tile) tile.classList.add('img-ok'); }} onError={(e) => { const tile = e.currentTarget.closest('.tile'); if (tile) tile.style.display = 'none'; }} />
{g.label} · {cats[g.cat] || ''}
))}
{/* PACKAGES — siempre visibles, todos los paquetes sin depender de fotos */}
Inversión

Elige tu paquete
perfecto.

Cada paquete incluye fotos impresas en papel fotográfico 20×25 cm y archivos digitales en alta resolución. Separación de cita con el 50%.

{PAOLA_DATA.categories.filter(c => c.packages?.length).map(c => ( ))}
{lb !== null && ( setLb(null)} onPrev={()=>setLb((lb - 1 + filtered.length) % filtered.length)} onNext={()=>setLb((lb + 1) % filtered.length)} /> )}
); } function PkgNav() { const cats = PAOLA_DATA.categories.filter(c => c.packages?.length); const [active, setActive] = React.useState(null); const navRef = React.useRef(null); React.useEffect(() => { const observers = []; cats.forEach(c => { const el = document.getElementById(`paquetes-${c.id}`); if (!el) return; const obs = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting) { setActive(c.id); const btn = navRef.current?.querySelector(`[data-id="${c.id}"]`); btn?.scrollIntoView({ block: 'nearest', inline: 'center' }); } }, { rootMargin: '-30% 0px -60% 0px' } ); obs.observe(el); observers.push(obs); }); return () => observers.forEach(o => o.disconnect()); }, []); const scrollTo = (id) => { const el = document.getElementById(`paquetes-${id}`); if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' }); }; return ( ); } function PackagesSection({ cat, compact = false }) { return (
Inversión · {cat.name}

Paquetes {cat.name.toLowerCase()}

{cat.desc}

{cat.note &&

{cat.note}

}
{cat.packages.map((group, gi) => (

· {group.group}

{group.tiers.map((t, ti) => (
{t.tier}
{t.desc}
{PAOLA_DATA.fmt(t.price)} COP
))}
))}
{e.preventDefault(); window.location.hash='contact'; window.scrollTo({top:0});}}> Reserva tu {cat.name.toLowerCase()}
); } /* ========================================================= ABOUT ========================================================= */ function AboutPage({ go }) { return (
Sobre mí

Soy Paola García,
storyteller visual.

"Estoy convencida de que a través de la fotografía podemos inmortalizar recuerdos. Hace más de 18 años decidí dedicar mi vida a esto desde mi propia experiencia de ser mamá."

Paola en el studio

Antes de ser fotógrafa,
soy mamá.

Tengo dos hijos: Tatiana, quien me motivó a estudiar la carrera de fotografía, e Isaac, quien me encaminó en mi especialidad. Y fue mi maternidad la que me enseñó que el tiempo sí pasa demasiado rápido.

Empecé tomando fotos de mi propia historia: de una espera que quería recordar, de momentos cotidianos que parecían simples — y entendí que la fotografía tiene un poder inmenso. Congelar en el tiempo los instantes llenos de amor es crear para siempre una forma de volver a ellos.

Con el tiempo, esa necesidad de coleccionar el amor se convirtió en mi profesión, en mi pasión y también en un lugar: mi studio, creado desde el amor, la calma y la sensibilidad de quien también reconoce la importancia de coleccionar el amor en fotografías.

Hoy acompaño a otras mamás y familias a conservar lo que un día van a extrañar: su primera pancita, los primeros días de vida de sus hijos, las risas, los abrazos, las etapas que pasan en silencio mientras crecen frente a nosotros.

Creo en las fotografías que se sienten años después. En los recuerdos que se heredan. Y en coleccionar momentos de amor como la manera más bonita de permanecer.

{[ { num: '01', ttl: 'Confiable', desc: 'Más de 18 años de respaldo profesional. Sé exactamente cómo cargar a tu bebé, calmarlo en mitad de la sesión y devolvértelo dormido.' }, { num: '02', ttl: 'Sensible', desc: 'Cada familia tiene su tono. Mi trabajo empieza escuchando — la sesión es la consecuencia, no el inicio.' }, { num: '03', ttl: 'Poética', desc: 'Las fotos cuentan historias. Te entrego un álbum, pero también un relato escrito sobre lo que vivimos ese día.' }, ].map((v, i) => (
{v.num}

{v.ttl}

{v.desc}

))}

Una línea de tiempo hecha de personas.

{PAOLA_DATA.timeline.map(t => (
{t.year}
{t.ttl}
{t.desc}
))}

¿Listo para empezar
a coleccionar momentos de amor?

); } /* ========================================================= CONTACT ========================================================= */ function ContactPage({ categories }) { const c = PAOLA_DATA.contact; // Solo categorías con paquetes definidos const cats = PAOLA_DATA.categories.filter(function(cat) { return cat.packages && cat.packages.length > 0; }); const tipos = cats.map(function(cat) { return cat.name; }); const firstTipo = tipos.length > 0 ? tipos[0] : 'Maternidad'; const [form, setForm] = useState({ name: '', email: '', phone: '', tipo: firstTipo, mensaje: '' }); const [errors, setErrors] = useState({}); const [sending, setSending] = useState(false); const [sent, setSent] = useState(false); const [serverErr, setServerErr] = useState(''); const update = (k) => (e) => setForm(Object.assign({}, form, { [k]: e.target.value })); function validate() { const errs = {}; if (!form.name || form.name.trim().length < 2) errs.name = 'Escribe tu nombre completo'; if (!form.email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email)) errs.email = 'Email no válido'; if (!form.phone || !/^\+?[\d\s\-()\\.]{7,20}$/.test(form.phone.trim())) errs.phone = 'Celular no válido'; if (!form.tipo) errs.tipo = 'Elige el tipo de sesión'; if (form.mensaje.length > 3000) errs.mensaje = 'Máximo 3000 caracteres'; return errs; } function handleSubmit(e) { e.preventDefault(); const errs = validate(); setErrors(errs); if (Object.keys(errs).length > 0) return; setSending(true); setServerErr(''); const body = new FormData(); body.append('name', form.name.trim()); body.append('email', form.email.trim()); body.append('phone', form.phone.trim()); body.append('tipo', form.tipo); body.append('mensaje', form.mensaje.trim()); fetch('/api/contact.php', { method: 'POST', body: body }) .then(function(r) { return r.json(); }) .then(function(data) { setSending(false); if (data.ok) { setSent(true); } else { setServerErr(data.error || 'Error al enviar. Intenta de nuevo.'); } }) .catch(function() { setSending(false); setServerErr('Error de conexión. Intenta de nuevo.'); }); } const f = (err) => 'field' + (err ? ' field-err' : ''); return (
Contacto

Antes de la sesión,
la intención.

Cuéntame qué etapa estás viviendo y diseñaremos una experiencia pensada para ti, con asesoría, conceptualización y cada detalle cuidado con sensibilidad. Respondo personalmente en menos de 1 hora.

Studio
{c.studio}
Horario
{c.horario}
Correo
{c.email}
Instagram
@{c.instagram} ↗
{sent ? (
Gracias por escribir.
Te respondo a {form.email} en menos de 1 hora. Mientras tanto, prepara un café — vale la pena.
— Paola
) : (
{serverErr &&
{serverErr}
}
{errors.name &&
{errors.name}
}
{errors.email &&
{errors.email}
}
{errors.phone &&
{errors.phone}
}
{errors.tipo &&
{errors.tipo}
}