// JOXE ASESORES DE IMAGEN — Componentes // Paleta: noir, ivory, bronze const Placeholder = ({ label, ratio = "4/5", tone = "ivory", note }) => { const bg = tone === "noir" ? "#141212" : "#EDE6DA"; const fg = tone === "noir" ? "rgba(230,220,200,0.55)" : "rgba(20,18,18,0.55)"; const stripe = tone === "noir" ? "rgba(230,220,200,0.06)" : "rgba(20,18,18,0.05)"; return (
placeholder
{label}
{note &&
{note}
}
); }; const Mono = ({ children, style }) => ( {children} ); // —————————————————————————————————————————————— // NAV // —————————————————————————————————————————————— const Nav = ({ onReserveClick, scrolled }) => { const [open, setOpen] = React.useState(false); return ( <> {open && (
{[ ["Servicios", "#servicios"], ["Galería", "#galeria"], ["Ubicación", "#ubicacion"], ].map(([label, href]) => ( setOpen(false)} style={{ color: "var(--ivory)", textDecoration: "none", fontFamily: "var(--display)", fontSize: 34, letterSpacing: "0.02em", }}>{label} ))} setOpen(false)} style={{ color: "var(--bronze)", textDecoration: "none", fontFamily: "var(--sans)", fontSize: 15, letterSpacing: "0.2em", textTransform: "uppercase", }}>Check-In →
)} ); }; // —————————————————————————————————————————————— // HERO // —————————————————————————————————————————————— const Hero = ({ onReserveClick }) => (
Soacha · San Mateo

La imagen
no se improvisa.
Se diseña.

Un espacio donde el corte, el color y la asesoría se trabajan con precisión. Atención personalizada, sin apuros, sin plantillas.

Ver trabajos
Desde 2018
Trabajo reciente
Color correction + corte · 3.5 hrs
); // —————————————————————————————————————————————— // MARQUEE // —————————————————————————————————————————————— const Marquee = () => { const items = ["Corte", "Color", "Balayage", "Keratina", "Asesoría de imagen", "Novias", "Extensiones", "Peinados"]; return (
{[...items, ...items, ...items].map((item, i) => (
{item}
))}
); }; // —————————————————————————————————————————————— // SERVICIOS // —————————————————————————————————————————————— const formatPrice = (price, note) => { const formatted = "$" + Number(price).toLocaleString("es-CO"); return note ? `${note} ${formatted}` : formatted; }; const formatDur = (mins) => { if (!mins) return ""; if (mins >= 60 && mins % 60 === 0) return `${mins / 60} hrs`; if (mins > 60) return `${(mins / 60).toFixed(1).replace(".0", "")} hrs`; return `${mins} min`; }; const FALLBACK_SERVICES = [ { id:"s1", name:"Corte mujer", price:85000, dur:60, active:true }, { id:"s2", name:"Corte hombre", price:45000, dur:40, active:true }, { id:"s3", name:"Balayage", price:280000, dur:180, active:true, note:"desde" }, { id:"s4", name:"Color correction", price:320000, dur:240, active:true, note:"desde" }, { id:"s5", name:"Color raíz", price:120000, dur:90, active:true }, { id:"s6", name:"Keratina", price:260000, dur:180, active:true, note:"desde" }, { id:"s7", name:"Asesoría de imagen", price:180000, dur:90, active:true }, { id:"s8", name:"Peinado novia", price:220000, dur:120, active:true, note:"desde" }, ]; const Services = () => { const [services, setServices] = React.useState(FALLBACK_SERVICES); React.useEffect(() => { fetch("/api/catalog") .then(r => r.ok ? r.json() : Promise.reject()) .then(data => { if (data.services?.length) setServices(data.services); }) .catch(() => {}); }, []); return (
01 — Servicios

Precios claros.
Sin sorpresas.

La consulta inicial siempre es gratis. Te contamos qué necesita tu cabello antes de tocarlo.

Los precios pueden variar según largo, densidad y estado del cabello. Te confirmamos el valor exacto en la consulta.

{services.map((item, i) => (

{item.name}

{item.dur > 0 && ( {formatDur(item.dur)} )}
{formatPrice(item.price, item.note)}
))}
); }; // —————————————————————————————————————————————— // ANTES / DESPUÉS // —————————————————————————————————————————————— const BeforeAfter = () => { const [pos, setPos] = React.useState(50); return (
{ if (e.buttons !== 1) return; const rect = e.currentTarget.getBoundingClientRect(); const p = ((e.clientX - rect.left) / rect.width) * 100; setPos(Math.max(0, Math.min(100, p))); }} onTouchMove={(e) => { const rect = e.currentTarget.getBoundingClientRect(); const p = ((e.touches[0].clientX - rect.left) / rect.width) * 100; setPos(Math.max(0, Math.min(100, p))); }} >
Antes
Después
); }; // —————————————————————————————————————————————— // GALERÍA // —————————————————————————————————————————————— const Gallery = () => { const [idx, setIdx] = React.useState(0); const cases = [ { title: "Balayage miel sobre base oscura", meta: "Color correction · 4 hrs" }, { title: "Corte bob francés", meta: "Corte + styling · 90 min" }, { title: "Recuperación post-decoloración", meta: "Tratamiento + color · 3.5 hrs" }, { title: "Rubio platino", meta: "Decoloración + matiz · 5 hrs" }, ]; return (
02 — Trabajos reales

El resultado habla.
Arrastra para ver.

Caso {String(idx + 1).padStart(2, "0")} / {String(cases.length).padStart(2, "0")}

{cases[idx].title}

Cada trabajo parte de un diagnóstico honesto del cabello. Nunca prometemos lo que no podemos sostener.

Servicio
{cases[idx].meta}
Estilista
Joxe
{cases.map((_, i) => (
); }; // —————————————————————————————————————————————— // TESTIMONIOS // —————————————————————————————————————————————— // —————————————————————————————————————————————— // MAPA DE UBICACIÓN // —————————————————————————————————————————————— const LocationMap = () => (
06 — Ubicación

Nos encontrarás
en San Mateo, Soacha.

Abrir en Google Maps ↗