/* Reusable components: Header, Footer, Buttons, Cards, Modal */
const NAV_ITEMS = [
{ key: "pacientes", label: "Pacientes" },
{ key: "medicos", label: "Médicos" },
{ key: "clinicas", label: "Clínicas" },
{ key: "empresas", label: "Empresas" },
{ key: "catalogo", label: "Estudios" },
{ key: "sucursales", label: "Sucursales" },
{ key: "vacunacion", label: "Vacunación" },
{ key: "resultados", label: "Resultados" },
{ key: "contacto", label: "Contacto" },
];
/* Ruta real aprobada para una página (si existe). Vive en src/app.jsx. */
const rutaReal = (nav) => (window.RUTAS_WEB && window.RUTAS_WEB[nav]) || "#";
function Header({ page, navigate, onOpenLead }) {
const [drawer, setDrawer] = React.useState(false);
const [openAcc, setOpenAcc] = React.useState(null);
const APP = "https://app.infectolab.mx/";
const ORDEN = "https://app.infectolab.mx/solicitud";
const FACT = "https://facturacion.infectolab.mx/";
const go = (k) => { navigate(k); setDrawer(false); };
// Mega-menu data. Items use either nav (internal page) or href (external).
const CATS = [
/* 3 columnas desde el lote Salud sexual: el panel se ensancha a 720, dentro
* del rango que ya usa el megamenú (InfectoLab ocupa 800). */
{ key: "pacientes", label: "Pacientes", nav: "pacientes", width: 720, align: "left",
cols: [
{ label: "Servicios", items: [
{ icon: "microscope", name: "Estudios y diagnóstico", desc: "Más de 200 pruebas disponibles", nav: "catalogo" },
{ icon: "drop", name: "Vacunación", desc: "Vacunas para adultos con orientación profesional", nav: "vacunacion" },
{ icon: "home", name: "Toma domiciliaria", desc: "Muestras en la comodidad de tu hogar", nav: "pacientes" },
]},
/* Se amplía a ginecología con el lote Salud ginecológica: mismo grupo
* temático del mapa de contenidos, sin abrir una cuarta columna. */
{ label: "Salud sexual y ginecológica", items: [
{ icon: "shield", name: "Prueba ETS · ETS Box", desc: "Detección de infecciones de transmisión sexual", nav: "ets-box" },
{ icon: "virus", name: "Prueba de VIH", desc: "Detección, confirmación y seguimiento", nav: "vih" },
{ icon: "beaker", name: "Salud ginecológica", desc: "Perfiles hormonales, embarazo e infección genital", nav: "perfiles-ginecologicos" },
{ icon: "microscope", name: "PCR Vaginitis / Vaginosis 33", desc: "Panel sindromático del tracto genital", nav: "vaginitis-vaginosis" },
]},
{ label: "Acceso", items: [
{ icon: "docCheck", name: "Mis resultados", desc: "Portal seguro 24/7", nav: "resultados" },
{ icon: "layers", name: "Ejemplos de resultados", desc: "Cómo se ve un informe de laboratorio", nav: "ejemplos" },
{ icon: "route", name: "Cómo funciona", desc: "Proceso de 3 pasos para tu prueba", nav: "pacientes" },
]},
],
foot: { variant: "light", lead: "¿Primera vez?", linkText: "Conoce InfectoLab →", nav: "pacientes" } },
{ key: "medicos", label: "Médicos", nav: "medicos", width: 480, align: "left",
cols: [
{ label: "Plataforma", items: [
{ icon: "monitor", name: "InfectoApp", desc: "Gestión de órdenes y resultados en línea", href: APP },
{ icon: "clipboard", name: "Orden de laboratorio", desc: "Genera y envía órdenes en segundos", href: ORDEN },
]},
{ label: "Catálogo", items: [
{ icon: "dna", name: "Catálogo de estudios", desc: "Búsqueda por nombre, clave o patología", nav: "catalogo" },
{ icon: "layers", name: "Diagnóstico sindromático", desc: "Paneles PCR múltiplex por cuadro clínico", nav: "sindromatico" },
{ icon: "star", name: "Programa Médico Lealtad", desc: "Descuentos y línea directa con el especialista", nav: "lealtad" },
]},
],
foot: { variant: "light", lead: "¿Eres médico con convenio?", linkText: "Ingresar a InfectoApp →", href: APP } },
{ key: "clinicas", label: "Clínicas", nav: "clinicas", width: 480, align: "left",
cols: [
{ label: "Operaciones", items: [
{ icon: "hospital", name: "Convenios institucionales", desc: "Condiciones especiales para tu clínica", nav: "clinicas" },
{ icon: "home", name: "Toma domiciliaria", desc: "Servicio para pacientes hospitalizados", nav: "clinicas" },
]},
{ label: "Digital", items: [
{ icon: "monitor", name: "InfectoApp para clínicas", desc: "Panel de gestión multi-usuario", href: APP },
{ icon: "trending", name: "Reportes y seguimiento", desc: "Dashboard de resultados en tiempo real", nav: "clinicas" },
]},
],
foot: { variant: "light", linkText: "Agendar cita con área de ventas →", nav: "contacto" } },
{ key: "empresas", label: "Empresas", nav: "empresas", width: 420, align: "right",
cols: [
{ items: [
{ icon: "building", name: "Convenios corporativos", desc: "Pruebas para tu equipo con factura", nav: "empresas" },
{ icon: "stethoscope", name: "Pruebas ocupacionales", desc: "Salud laboral y medicina del trabajo", nav: "empresas" },
{ icon: "briefcase", name: "Cotización corporativa", desc: "Precios por volumen personalizados", action: onOpenLead },
{ icon: "receipt", name: "Facturación", desc: "Portal de facturación electrónica", href: FACT },
]},
],
foot: { variant: "naranja", linkText: "¿Más de 10 colaboradores? Escríbenos →", nav: "contacto" } },
{ key: "infectolab", label: "InfectoLab", nav: null, width: 800, align: "right",
cols: [
{ label: "Encuéntranos", items: [
{ icon: "pin", name: "Sucursales", desc: "6 ubicaciones en Tijuana · una 24/7", nav: "sucursales" },
{ icon: "phone", name: "Contacto", desc: "WhatsApp · teléfono · email", nav: "contacto" },
{ icon: "receipt", name: "Facturación", desc: "Genera tu CFDI en línea", href: FACT },
]},
{ label: "Contenido", items: [
{ icon: "dna", name: "Estudios PCR múltiplex", desc: "La especialidad molecular y sus paneles", nav: "pcr-multiplex" },
{ icon: "docCheck", name: "Blog InfectoLab", desc: "Artículos y videos sobre diagnóstico y prevención", nav: "blog" },
{ icon: "user", name: "Dr. Samuel Navarro", desc: "Infectólogo e internista · Director Médico", nav: "autor" },
]},
{ label: "Nosotros", items: [
/* «Acerca de InfectoLab» se retira del menú hasta que exista su
* destino: apuntaba a home, que no es esa página. */
{ icon: "shield", name: "Calidad y certificación", desc: "ISO 9001:2015 · alcance y documento", nav: "calidad" },
]},
],
foot: { variant: "dark" } },
];
const itemHandler = (it) => {
if (it.href) return null; // anchor handles it
return (e) => { e.preventDefault(); if (it.action) { it.action(); setDrawer(false); } else { go(it.nav); } };
};
const PanelLink = ({ it }) => {
const inner = (
<>
{it.name} {it.desc}
>
);
if (it.href) return {inner} ;
return {inner} ;
};
const PanelFoot = ({ foot }) => {
if (foot.variant === "dark") {
return (
© 2026 InfectoLab · Tijuana, B.C.
);
}
return (
);
};
const isActive = (cat) =>
cat.nav === page || (cat.key === "infectolab" && page === "sucursales");
return (
<>
{/* Mobile drawer */}
setDrawer(false)}>
setDrawer(false)}>
{CATS.map((cat, i) => {
const items = cat.cols.flatMap(c => c.items);
return (
setOpenAcc(openAcc === i ? null : i)}>
{cat.label}
);
})}
go("resultados")}>
Resultados
go("resultados")}>
Resultados
{ onOpenLead(); setDrawer(false); }}>
Agendar prueba
>
);
}
const HEADER_CSS = `
.il-nav { position: sticky; top: 0; z-index: 100; background: #fff; border-bottom: 1px solid var(--ink-200); }
.il-nav-inner { display: flex; align-items: center; gap: 20px; height: 72px; }
.il-logo { flex: 0 0 auto; display: inline-flex; align-items: center; }
.il-logo img { height: 34px; width: auto; }
.il-menu { display: flex; align-items: center; gap: 2px; list-style: none; margin: 0; padding: 0; flex: 1; justify-content: center; }
.il-item { position: relative; }
.il-trigger {
display: inline-flex; align-items: center; gap: 6px;
background: none; border: none; font: inherit; font-size: 15px; font-weight: 600; color: var(--ink-700);
padding: 10px 14px; border-radius: 8px; position: relative; cursor: pointer;
}
.il-trigger .chev { transition: transform .2s; }
.il-trigger::after {
content: ""; position: absolute; left: 50%; bottom: 4px; height: 2px; width: 0;
background: var(--brand-yellow); border-radius: 2px; transform: translateX(-50%); transition: width .22s ease;
}
.il-item:hover > .il-trigger, .il-item:focus-within > .il-trigger, .il-item.is-active > .il-trigger { color: var(--brand-primary); }
.il-item:hover > .il-trigger::after, .il-item:focus-within > .il-trigger::after, .il-item.is-active > .il-trigger::after { width: calc(100% - 28px); }
.il-item:hover > .il-trigger .chev, .il-item:focus-within > .il-trigger .chev { transform: rotate(180deg); }
.il-result {
display: inline-flex; align-items: center; gap: 7px;
border: 1.5px solid var(--brand-primary); color: var(--brand-primary);
font-size: 14px; font-weight: 700; border-radius: 8px; padding: 8px 16px; margin-left: 6px;
transition: background .15s, color .15s;
}
.il-result svg { transition: stroke .15s; }
.il-result:hover, .il-result.is-on { background: var(--brand-primary); color: #fff; }
.il-result:hover svg, .il-result.is-on svg { stroke: #fff; }
.il-panel {
position: absolute; top: 100%; left: 0; padding-top: 12px;
opacity: 0; visibility: hidden; transform: translateY(8px);
transition: opacity .18s ease, transform .18s ease, visibility .18s; z-index: 60;
}
.il-panel.right { left: auto; right: 0; }
.il-item:hover > .il-panel, .il-item:focus-within > .il-panel { opacity: 1; visibility: visible; transform: translateY(0); }
.il-panel-card { background: #fff; border: 1px solid var(--ink-200); border-radius: 16px; box-shadow: 0 8px 40px rgba(57,83,164,.12); overflow: hidden; }
.il-panel-body { display: grid; gap: 4px; padding: 16px; }
.il-col { display: flex; flex-direction: column; }
.il-col-label { font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-500); padding: 6px 12px 8px; }
.il-link { display: flex; gap: 12px; align-items: flex-start; padding: 10px 12px; border-radius: 10px; transition: background .14s; text-decoration: none; }
.il-link:hover { background: var(--ink-50); }
.il-link-ic { width: 36px; height: 36px; border-radius: 9px; background: rgba(57,83,164,.08); color: var(--brand-primary); display: flex; align-items: center; justify-content: center; flex: 0 0 36px; }
.il-link-tx b { display: block; font-size: 14px; font-weight: 700; color: var(--ink-900); line-height: 1.25; }
.il-link:hover .il-link-tx b { color: var(--brand-primary); }
.il-link-tx small { display: block; font-size: 12px; color: var(--ink-500); margin-top: 3px; line-height: 1.4; }
.il-panel-foot { padding: 12px 20px; border-top: 1px solid var(--ink-200); background: var(--ink-50); font-size: 13px; color: var(--ink-500); }
.il-panel-foot a { color: var(--brand-primary); font-weight: 700; }
.il-panel-foot a:hover { text-decoration: underline; }
.il-panel-foot.naranja a { color: #F07B35; }
.il-panel-foot.dark { background: linear-gradient(120deg, #1A2A5E, #30477F); color: rgba(255,255,255,.78); border-top: none; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.il-panel-foot.dark img { height: 20px; }
.il-actions { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
.il-ghost {
display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
border: 1px solid var(--ink-200); background: #fff; color: var(--ink-900);
font: inherit; font-size: 14px; font-weight: 600; border-radius: 8px; padding: 9px 16px; transition: .15s;
}
.il-ghost:hover { border-color: var(--brand-primary); color: var(--brand-primary); }
.il-primary {
display: inline-flex; align-items: center; gap: 8px; cursor: pointer; border: none;
background: var(--brand-yellow); color: var(--brand-deep);
font: inherit; font-size: 14px; font-weight: 700; border-radius: 8px; padding: 10px 18px;
box-shadow: 0 6px 16px -8px rgba(253,195,69,.9); transition: .15s;
}
.il-primary:hover { background: var(--brand-yellow-600); transform: translateY(-1px); }
.il-burger { display: none; width: 42px; height: 42px; border-radius: 10px; border: 1px solid var(--ink-200); background: #fff; align-items: center; justify-content: center; color: var(--ink-900); }
.il-overlay { position: fixed; inset: 0; background: rgba(26,42,94,.45); backdrop-filter: blur(2px); opacity: 0; visibility: hidden; transition: opacity .25s, visibility .25s; z-index: 200; }
.il-overlay.open { opacity: 1; visibility: visible; }
.il-drawer { position: fixed; top: 0; right: 0; height: 100%; width: 85%; max-width: 380px; background: #fff; transform: translateX(100%); transition: transform .28s cubic-bezier(.4,0,.2,1); z-index: 201; display: flex; flex-direction: column; }
.il-drawer.open { transform: translateX(0); }
.il-drawer-head { display: flex; align-items: center; justify-content: space-between; padding: 0 20px; height: 72px; border-bottom: 1px solid var(--ink-200); flex: 0 0 auto; }
.il-drawer-head img { height: 30px; }
.il-x { width: 40px; height: 40px; border-radius: 10px; border: 1px solid var(--ink-200); background: #fff; display: flex; align-items: center; justify-content: center; color: var(--ink-900); cursor: pointer; }
.il-drawer-body { flex: 1; overflow-y: auto; }
.il-acc-trigger { width: 100%; display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; border: none; border-bottom: 1px solid var(--ink-200); background: none; font: inherit; font-size: 15.5px; font-weight: 700; color: var(--ink-900); text-align: left; cursor: pointer; }
.il-acc-trigger .chev { transition: transform .2s; color: var(--ink-500); }
.il-acc-trigger.open .chev { transform: rotate(180deg); }
.il-acc-content { display: none; background: var(--ink-50); }
.il-acc-content.open { display: block; }
.il-acc-link { display: flex; gap: 12px; align-items: flex-start; padding: 14px 20px; border-bottom: 1px solid var(--ink-200); text-decoration: none; }
.il-acc-link:last-child { border-bottom: none; }
.il-acc-link .il-link-ic { width: 32px; height: 32px; flex: 0 0 32px; }
.il-acc-link b { font-size: 14px; font-weight: 700; color: var(--ink-900); display: block; }
.il-acc-link small { font-size: 12px; color: var(--ink-500); display: block; margin-top: 2px; }
.il-drawer-foot { flex: 0 0 auto; padding: 16px 20px; border-top: 1px solid var(--ink-200); display: flex; flex-direction: column; gap: 10px; }
.il-drawer-foot .il-ghost, .il-drawer-foot .il-primary { width: 100%; justify-content: center; }
@media (max-width: 960px) {
.il-menu, .il-actions .il-ghost, .il-actions .il-primary { display: none; }
.il-burger { display: inline-flex; }
.il-nav-inner { justify-content: space-between; gap: 0; }
}
`;
function Footer({ navigate }) {
return (
Laboratorio clínico especializado en diagnóstico, prevención y soluciones para pacientes, médicos, clínicas y empresas.
© 2026 InfectoLab.mx — Todos los derechos reservados.
Métodos de pago:
VISA
MC
SPEI
);
}
/* ─────────── Section heading helper ─────────── */
function SectionHead({ eyebrow, title, sub, aside, light = false }) {
return (
{eyebrow &&
{eyebrow}
}
{title}
{sub &&
{sub}
}
{aside}
);
}
/* ─────────── Segment card ─────────── */
function SegmentCard({ icon, title, desc, cta, featured, onClick, sub }) {
return (
{title}
{desc}
{sub &&
{sub}
}
{cta}
);
}
/* ─────────── Service card (grid) ─────────── */
function ServiceCard({ icon, title, count, tag }) {
return (
{title}
{count} estudios
{tag && {tag} }
);
}
/* ─────────── FAQ accordion ─────────── */
function FaqItem({ q, a, defaultOpen = false }) {
const [open, setOpen] = React.useState(defaultOpen);
return (
setOpen(!open)} aria-expanded={open}>
{q}
);
}
/* ─────────── Lead modal ─────────── */
function LeadModal({ onClose, defaultSegment = "" }) {
const [step, setStep] = React.useState(0);
const [data, setData] = React.useState({ tipo: defaultSegment, nombre: "", email: "", tel: "", mensaje: "" });
const production = window.INFECTOLAB_PRODUCTION === true;
const update = (k, v) => setData({ ...data, [k]: v });
const continueRequest = () => {
if (!production) { setStep(2); return; }
const tipoLabel = { paciente: "Paciente", medico: "Médico", clinica: "Clínica", empresa: "Empresa" };
const message = [
"Solicitud de atención en InfectoLab",
"Tipo: " + (tipoLabel[data.tipo] || data.tipo),
"Nombre: " + data.nombre,
"Email: " + data.email,
"Teléfono: " + data.tel,
"Mensaje: " + data.mensaje,
].join("\n");
window.open("https://wa.me/526646985015?text=" + encodeURIComponent(message), "_blank", "noopener,noreferrer");
};
return (
e.stopPropagation()}>
{step === 0 && (
Agendar prueba
Cuéntanos quién eres
Te conectamos con el equipo de atención correcto para tu necesidad.
{[
["paciente", "Paciente", "user"],
["medico", "Médico", "stethoscope"],
["clinica", "Clínica", "hospital"],
["empresa", "Empresa", "building"],
].map(([key, label, ic]) => (
{ update("tipo", key); setStep(1); }}
style={{
padding: "18px 16px",
border: "1px solid var(--ink-200)",
borderRadius: 12,
background: data.tipo === key ? "var(--brand-cyan-50)" : "#fff",
textAlign: "left",
display: "flex", alignItems: "center", gap: 12,
}}>
{label}
))}
)}
{step === 1 && (
Paso 2 de 2
Tus datos
{production ? "Al continuar se abrirá WhatsApp con tus datos. Revisa y envía el mensaje allí para solicitar atención." : "Un asesor te contactará en menos de 24 horas."}
Nombre completo
update("nombre", e.target.value)} placeholder="Ej. Ana Ramírez" />
¿En qué te podemos ayudar?
setStep(0)}>Atrás
{production ? "Continuar en WhatsApp" : "Enviar solicitud"}
)}
{step === 2 && (
Solicitud recibida
Gracias, {data.nombre || "te contactaremos"}. Un asesor de InfectoLab se pondrá en contacto a la brevedad.
Cerrar
)}
);
}
Object.assign(window, {
Header, Footer, SectionHead, SegmentCard, ServiceCard, FaqItem, LeadModal, NAV_ITEMS,
});