/* PRODUCTO SAAS — página individual (template compartido) */ const { useState: useStateD, useEffect: useEffectD } = React; /* ===== Explorador de módulos interactivo (demo funcional · datos hardcodeados) ===== */ const MOD_ICONS = { cart: <>, box: <>, tag: <>, cash: <>, users: <>, user: <>, truck: <>, chart: <>, gear: <>, calendar: <>, bed: <>, bell: <>, broom: <>, food: <>, receipt: <>, folder: <>, cross: <>, flask: <>, pill: <>, }; const ModIcon = ({ name }) => ( {MOD_ICONS[name] || } ); const useTickerD = (interval = 2400) => { const [t, setT] = useStateD(0); useEffectD(() => { const id = setInterval(() => setT(x => x + 1), interval); return () => clearInterval(id); }, [interval]); return t; }; const useIsMobileD = () => { const [m, setM] = useStateD(false); useEffectD(() => { const mq = window.matchMedia('(max-width: 880px)'); const on = () => setM(mq.matches); on(); if (mq.addEventListener) mq.addEventListener('change', on); else mq.addListener(on); return () => { if (mq.removeEventListener) mq.removeEventListener('change', on); else mq.removeListener(on); }; }, []); return m; }; const AnimatedValue = ({ text }) => { const str = String(text); const match = str.match(/^([^\d]*)([\d.,]+)(.*)$/); const target = match ? parseFloat(match[2].replace(/,/g, '')) : null; const [v, setV] = useStateD(target || 0); useEffectD(() => { if (target === null) return; let raf; const start = performance.now(); const dur = 850; const step = (now) => { const t = Math.min(1, (now - start) / dur); const eased = 1 - Math.pow(1 - t, 3); setV(target * eased); if (t < 1) raf = requestAnimationFrame(step); }; raf = requestAnimationFrame(step); return () => cancelAnimationFrame(raf); }, [text]); if (target === null) return <>{text}; const decimals = (match[2].split('.')[1] || '').length; const shown = target >= 1000 ? Math.round(v).toLocaleString('es-MX') : v.toFixed(decimals); return <>{match[1]}{shown}{match[3]}; }; const DemoChart = ({ title, legend }) => { const tick = useTickerD(2400); const uid = (React.useId ? React.useId() : 'demo').replace(/[^a-zA-Z0-9]/g, ''); const W = 600, H = 120, P = 4; const gen = (base) => { let y = base; const out = []; for (let i = 0; i < 26; i++) { y += (Math.random() - 0.45) * 14; y = Math.max(18, Math.min(94, y)); out.push(y); } return out; }; const a = React.useMemo(() => gen(55), [tick]); const b = React.useMemo(() => a.map(y => Math.max(12, y - 14 - Math.random() * 10)), [a]); const line = (arr) => arr.map((y, i) => { const x = P + (i / (arr.length - 1)) * (W - P * 2); const yy = H - P - (y / 100) * (H - P * 2); return `${i ? 'L' : 'M'} ${x.toFixed(1)} ${yy.toFixed(1)}`; }).join(' '); const fill = (arr) => line(arr) + ` L ${W - P} ${H - P} L ${P} ${H - P} Z`; return (
{title} {legend && {legend.map((l, i) => {l.l})}}
{[0, 1, 2, 3].map(i => )}
); }; const classifyStatus = (v) => { if (/^↓/.test(v) || /-\d/.test(v)) return 'warn'; if (/^↑/.test(v)) return ''; const WARN = ['Bajo', 'Crítico', 'Inactivo', 'Pendiente', 'Ausente', 'Falta firma', 'Por confirmar', 'Anulada']; const INFO = ['En proceso', 'En consulta', 'Nuevo', 'Recordatorio enviado', 'Crédito']; if (WARN.includes(v)) return 'warn'; if (INFO.includes(v)) return 'info'; return ''; }; const ROOM_COLORS = { free: ['#22c55e', 'Libre'], busy: ['#06b6d4', 'Ocupada'], clean: ['#f59e0b', 'Limpieza'], maint: ['#ef4444', 'Mantenim.'] }; const DemoBlock = ({ b }) => { if (b.type === 'chart') return ; if (b.type === 'table') return (
{b.cols.map((c, i) => {c})}
{b.rows.map((r, ri) => (
{r.map((cell, ci) => { if (ci === r.length - 1 && b.status) return {cell}; return {cell}; })}
))}
); if (b.type === 'rows') return (
{b.items.map((it, i) => (
{it.a}
{it.n}
{it.t} {it.s}
))}
); if (b.type === 'grid') return (
{Object.keys(ROOM_COLORS).map(k => ( {ROOM_COLORS[k][1]} ))}
{b.items.map((it, i) => { const c = (ROOM_COLORS[it.s] || ROOM_COLORS.free)[0]; return (
{it.t}
); })}
); return null; }; const DemoKpis = ({ items }) => (
{items.map((k, i) => (
{k.l}
{k.down ? '↓' : '↑'} {k.d}
))}
); const ModuleExplorer = ({ product, slug }) => { const allDemos = (window.MartzProducts.moduleDemos && window.MartzProducts.moduleDemos[slug]) || {}; const mods = product.modules.filter(m => allDemos[m]); const [active, setActive] = useStateD(mods[0]); const isMobile = useIsMobileD(); const demo = allDemos[active] || { kpis: [], blocks: [] }; const app = slug === 'martz-pos' ? 'mi-tienda' : slug === 'martz-hospitality' ? 'mi-hotel' : 'mi-clinica'; const tabStyle = (on) => ({ flex: '0 0 auto', padding: '8px 14px', borderRadius: 8, border: '1px solid ' + (on ? 'rgba(34,197,94,0.4)' : 'rgba(255,255,255,0.1)'), background: on ? 'rgba(34,197,94,0.12)' : 'rgba(255,255,255,0.03)', color: on ? '#4ade80' : 'rgba(255,255,255,0.7)', fontSize: 12.5, fontWeight: on ? 600 : 500, cursor: 'pointer', whiteSpace: 'nowrap', fontFamily: 'inherit' }); return (
app.martzlab.com/{app}
{isMobile && (
{mods.map(m => )}
)}
{!isMobile && ( )}

{active}

En vivo · datos de demostración
{demo.kpis && demo.kpis.length > 0 && } {(demo.blocks || []).map((b, i) => )}
); }; const ScreenshotPlaceholder = ({ label, src }) => (
{label}
{src ? ( {label} ) : (
Captura de pantalla · próximamente
)}
); const DetailFaq = ({ items }) => { const [open, setOpen] = useStateD(0); return (
{items.map((f, i) => (
{open === i &&

{f.a}

}
))}
); }; const ProductoDetailApp = () => { const slug = window.PRODUCT_SLUG; const product = window.MartzProducts.details[slug]; if (!product) return
Producto no encontrado.
; const summary = window.MartzProducts.list.find(p => p.slug === slug); const isLive = product.status === 'Disponible'; const hasDetailedPlans = product.plans && product.plans.some(plan => plan.includes); return ( <>
Inicio/ Productos SaaS/ {product.name}
{product.status} {product.caseLink && ( {product.caseLink.text} → )}

{product.name}

{product.tag}

{product.heroDesc}

{isLive ? ( <> Solicitar demo Ver planes ) : ( Conocer más )}
Descripción

Hecho para {product.audiences.slice(0, 3).join(', ').toLowerCase()} y más.

{product.intro}

{product.audiences.map(a => ( {a} ))}
Características

Lo que incluye {product.name}.

{product.features.map(f => (

{f.t}

{f.d}

))}
Módulos

Todo lo que necesitas, integrado.

Explora cada módulo como si estuvieras dentro del sistema. Haz clic para navegar — los datos son de demostración.

Beneficios

Impacto medible en tu operación.

{product.benefits.map(b => (

{b.t}

{b.d}

))}
Vista previa

Así se ve {product.name}.

{slug === 'martz-pos' ? ( <> ) : ( <> )}
{product.plansEyebrow || 'Planes'}

{product.plansTitle || (isLive ? 'Planes simples para empezar y crecer.' : 'Próximamente con precios accesibles.')}

{(product.plansSubtitle || isLive) &&

{product.plansSubtitle || 'Elige el nivel de operación que mejor se adapta a tu negocio. Puedes empezar pequeño y crecer sin cambiar de sistema.'}

}
{product.plans.map(plan => (
{plan.featured && {plan.featuredLabel || (isLive ? 'Recomendado' : 'Lista de espera')}}

{plan.name}

{plan.value &&

{plan.value}

}
Precio
{plan.price}{plan.unit}
{plan.capacity || plan.note}
{plan.priceNote &&
{plan.priceNote}
}
{plan.includes && (
    {plan.includes.map(item => (
  • {item}
  • ))}
)} {plan.idealFor &&

Ideal para: {plan.idealFor}

} {plan.cta} →
))}
{product.plansNote && (

{product.plansNote.title}

{product.plansNote.text}

)} {(product.launchNote || product.waitlistNote) && (
{product.launchNote &&

{product.launchNote}

} {product.waitlistNote &&

{product.waitlistNote}

}
)} {product.addOns && (

{product.addOns.title}

{product.addOns.text}

{product.addOns.items.map(item => {item})}
)}
Preguntas frecuentes

Dudas sobre {product.name}.

{isLive ? <>Empieza con {product.name} hoy. : <>Sé de los primeros en probar {product.name}.}

{isLive ? 'Demo en vivo de 30 minutos. Sin compromiso, sin tarjeta de crédito.' : 'Únete a la lista de espera y recibe acceso anticipado con precio preferencial.'}

); }; ReactDOM.createRoot(document.getElementById('root')).render();