/* global React */ const { useState, useEffect, useRef, useMemo } = React; const fmtDate = s => { if (!s) return "—"; const p = s.slice(0, 10).split("-"); return p.length === 3 ? `${p[2]}/${p[1]}/${p[0]}` : s.slice(0, 10); }; /* ---------- Iconos minimalistas ---------- */ const Icon = ({ name, size = 18, stroke = 1.7 }) => { const props = { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round" }; switch (name) { case "arrow-right": return ; case "arrow-down": return ; case "code": return ; case "cpu": return ; case "refresh": return ; case "download": return ; case "upload": return ; case "x": return ; case "sun": return ; case "moon": return ; case "mail": return ; case "phone": return ; case "pin": return ; case "check": return ; case "lock": return ; case "image": return ; case "search": return ; case "github": return ; case "file-text": return ; case "images": return ; case "grid": return ; case "list": return ; default: return null; } }; /* ---------- Reveal hook ---------- */ const useReveal = (trigger) => { useEffect(() => { const els = document.querySelectorAll(".reveal:not(.is-in)"); if (!els.length) return; const obs = new IntersectionObserver((entries) => { entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add("is-in"); obs.unobserve(e.target); }}); }, { threshold: 0.12 }); els.forEach(el => obs.observe(el)); return () => obs.disconnect(); }, [trigger]); }; /* ---------- Datos ---------- */ const SERVICES = [ { n: "01", icon: "code", title: "Desarrollo a medida", desc: "Aplicaciones web, móviles y de escritorio diseñadas exactamente para tu operativa, sin compromisos.", items: ["Web apps & PWA", "Backends & APIs", "Apps móviles Android/iOS", "Integraciones ERP/CRM"] }, { n: "02", icon: "cpu", title: "Automatización con IA", desc: "Agentes y flujos inteligentes que clasifican, redactan, procesan documentos y conversan con tus clientes.", items: ["Agentes conversacionales", "Procesado de documentos", "Workflows con LLMs", "Análisis predictivo"] }, { n: "03", icon: "refresh", title: "Modernización de sistemas", desc: "Migramos software heredado a stacks modernos, mantenibles y conectados, sin parar tu negocio.", items: ["Migración legacy", "Refactor & cloud", "Auditoría técnica", "Soporte continuo"] } ]; const PROCESS = [ { n: "01", title: "Descubrimiento", desc: "Reunión inicial. Entendemos tu proceso, identificamos oportunidades y delimitamos el alcance." }, { n: "02", title: "Propuesta", desc: "Documento técnico, plazos y presupuesto cerrado. Sin sorpresas." }, { n: "03", title: "Construcción", desc: "Sprints quincenales con demos. Tú validas, nosotros iteramos." }, { n: "04", title: "Despliegue", desc: "Puesta en producción, formación a tu equipo y soporte continuo." } ]; // La lista se carga desde /api/list.php — los datos de demo solo se muestran si la API // no responde (entorno de previsualización). En tu hosting Arsys verás los datos reales. const DOWNLOADS_FALLBACK = [ { id: 1, title: "MASoft Backup Tool", description: "Utilidad de copias de seguridad incrementales con cifrado AES-256 y programación automática.", category: "free", lang: "powershell", platform: "Windows", version: "v2.4.1", size_label: "12.4 MB", created_at: "2026-04-22", tags: ["PowerShell","Windows","CLI"], has_file: true, screenshot_url: null }, { id: 2, title: "Facturas IA Lite", description: "Extrae datos de facturas PDF/imagen y los exporta a Excel o CSV. Ideal para gestorías.", category: "trial", lang: "python", platform: "Windows / Mac", version: "v1.2.0 · 30 días", size_label: "84 MB", created_at: "2026-04-10", tags: ["Python","OCR","IA"], has_file: true, screenshot_url: null }, { id: 3, title: "ServerPulse", description: "Dashboard ligero para monitorizar servidores Windows y servicios críticos en tiempo real.", category: "demo", lang: "pwa", platform: "Web · PWA", version: "Demo pública", size_label: "—", created_at: "2026-03-28", tags: ["PWA","JavaScript","Web"], has_file: true, screenshot_url: null }, { id: 4, title: "Control Presencia", description: "App Android para fichaje con geolocalización y sincronización offline. Versión gratuita 1 usuario.", category: "free", lang: "android", platform: "Android 8+", version: "v3.0.2", size_label: "9.8 MB", created_at: "2026-04-18", tags: ["Android","Kotlin"], has_file: true, screenshot_url: null }, { id: 5, title: "StockSync Pro", description: "Sincronizador de inventarios entre tienda física y e-commerce (PrestaShop, WooCommerce).", category: "trial", lang: "python", platform: "Windows / Linux", version: "v0.9.4 · Beta", size_label: "36 MB", created_at: "2026-04-02", tags: ["Python","API","ETL"], has_file: true, screenshot_url: null }, { id: 6, title: "Helpdesk IA Demo", description: "Demo interactiva de asistente IA conectado a base de conocimiento corporativa.", category: "demo", lang: "pwa", platform: "Web", version: "Demo · sin instalación", size_label: "—", created_at: "2026-03-15", tags: ["PWA","RAG","IA"], has_file: true, screenshot_url: null } ]; const CATEGORIES = [ { id: "all", label: "Todos" }, { id: "free", label: "Gratuito" }, { id: "trial", label: "Prueba" }, { id: "demo", label: "Demo" } ]; const LANG_FILTERS = [ { id: "all", label: "Todos" }, { id: "python", label: "Python" }, { id: "powershell", label: "PowerShell" }, { id: "pwa", label: "PWA" }, { id: "android", label: "Android" } ]; /* ---------- Hero ---------- */ const HeroCode = () => ( ); const HeroBento = () => ( ); const Hero = ({ variant, onCtaScroll }) => { if (variant === "minimal") { return (
Disponibles · Q2 2026

Software a medida.
Hecho con cabeza.

Desarrollamos aplicaciones e integramos IA en el día a día de tu empresa. Sin plantillas, sin tonterías.

); } return (
Disponibles · Q2 2026

Software a medida y automatización con IA.

Construimos las herramientas que tu empresa necesita —y que ningún software estándar resuelve. Desarrollo, modernización e IA aplicada de verdad.

+85Proyectos entregados
Expertosen el sector IT
24/7Soporte clientes
{variant === "bento" ? : }
); }; /* ---------- Servicios ---------- */ const Services = () => (
Servicios

Tres áreas, un único objetivo.

Tres líneas de trabajo y un único objetivo: que tu empresa funcione mejor con software hecho a su medida.

{SERVICES.map(s => (
{s.n} / Servicio

{s.title}

{s.desc}

    {s.items.map(i =>
  • {i}
  • )}
))}
Stack: Python· TypeScript· React / Next.js· FastAPI· PostgreSQL· Docker· Kotlin· PowerShell· Azure / AWS· Claude · GPT · Llama
); /* ---------- Proceso ---------- */ const Process = () => (
Proceso

De la primera reunión a producción en semanas, no meses.

Un método sencillo, transparente y con entregas frecuentes. Sin sorpresas en la factura ni en el calendario.

{PROCESS.map(p => (
{p.n} Fase {p.n}

{p.title}

{p.desc}

))}
); /* ---------- Modal galería de capturas ---------- */ const ScreenshotsModal = ({ open, item, onClose }) => { if (!item) return null; const allShots = []; if (item.screenshot_url) allShots.push(item.screenshot_url); if (item.screenshots_extra_urls) allShots.push(...item.screenshots_extra_urls); return (
e.stopPropagation()} style={{maxWidth: 720, maxHeight: '85vh', overflowY: 'auto'}}>  Capturas

{item.title}

{allShots.map((url, i) => ( {`Captura ))} {allShots.length === 0 &&

No hay capturas disponibles.

}
); }; /* ---------- Descargas ---------- */ const ScreenshotPlaceholder = ({ label }) => ( <>
 {label}
); const Downloads = ({ items, onSubscribe, onDownload }) => { const [cat, setCat] = useState("all"); const [lang, setLang] = useState("all"); const [view, setView] = useState("grid"); const [shotsOpen, setShotsOpen] = useState(false); const [shotsItem, setShotsItem] = useState(null); const counts = useMemo(() => ({ all: items.length, free: items.filter(i => i.category === "free").length, trial: items.filter(i => i.category === "trial").length, demo: items.filter(i => i.category === "demo").length, }), [items]); const langCounts = useMemo(() => { const c = { all: items.length }; items.forEach(i => { c[i.lang] = (c[i.lang] || 0) + 1; }); return c; }, [items]); const filtered = items.filter(i => (cat === "all" || i.category === cat) && (lang === "all" || i.lang === lang) ); const catLabel = c => c === "free" ? "Gratis" : c === "trial" ? "Prueba" : "Demo"; const catClass = c => c === "free" ? "" : c === "trial" ? "is-trial" : "is-demo"; return (
Descargas

Software gratuito, demos y pruebas.

Una colección de utilidades y demos que hemos liberado para nuestros clientes y la comunidad. Descarga gratis tras un breve registro.

{CATEGORIES.map(c => ( ))}
{LANG_FILTERS.map(l => ( ))}
{filtered.map(item => (
{catLabel(item.category)} {item.screenshot_url ? {item.title} : }
{view === "list" ? (

{item.title}

{item.description}

{item.size_label && item.size_label !== "—" ? item.size_label + " · " : ""} {fmtDate(item.created_at)} {item.platform ? " · " + item.platform : ""} {item.version ? " · " + item.version : ""} {(item.tags || []).map(t => {t})}
{item.manual_url && ( )} {item.has_file ? : Sin archivo }
) : (

{item.title}

{item.platform || "—"} · {item.version || "—"}

{item.description}

{(item.tags || []).map(t => ( {t} ))}
{item.size_label || "—"} · {fmtDate(item.created_at)}
{item.manual_url && ( )} {((item.screenshot_url) || (item.screenshots_extra_urls && item.screenshots_extra_urls.length > 0)) && ( )} {item.has_file ? : Sin archivo }
)}
))} {filtered.length === 0 && (

No hay software en esta categoría todavía.

)}
¿Quieres estar al día?

Recibe un aviso cuando publiquemos nuevo software o actualizaciones. Sin spam.

setShotsOpen(false)}/>
); }; /* ---------- Caja privacidad (reutilizable) ---------- */ const PrivacyBox = ({ email }) => { const addr = email || "hola@masoft.es"; return (
Información básica sobre protección de datos (RGPD)

Responsable: MASoft Sistemas Informáticos — {addr}

Finalidad: Gestionar tu suscripción y enviarte comunicaciones sobre nuevos productos, actualizaciones y herramientas publicados en MASoft.es.

Base jurídica: Consentimiento expreso del interesado (art. 6.1.a del Reglamento UE 2016/679 — RGPD).

Conservación: Hasta que solicites la baja. Puedes cancelar tu suscripción en cualquier momento mediante el enlace incluido en cada comunicación o escribiendo a la dirección indicada.

Destinatarios: No se ceden datos a terceros salvo obligación legal. Los datos se tratan en servidores dentro de la UE.

Derechos: Puedes ejercer tus derechos de acceso, rectificación, supresión, oposición, limitación y portabilidad escribiendo a {addr}. Si consideras que el tratamiento no es conforme, puedes reclamar ante la Agencia Española de Protección de Datos (aepd.es).

); }; /* ---------- Modal Descarga ---------- */ const DownloadModal = ({ open, item, onClose, adminEmail }) => { const [accept, setAccept] = useState(false); const [showTerms, setShowTerms] = useState(false); useEffect(() => { if (open) { setAccept(false); setShowTerms(false); document.body.style.overflow = "hidden"; } else { document.body.style.overflow = ""; } return () => { document.body.style.overflow = ""; }; }, [open, item]); if (!item) return null; return (
e.stopPropagation()}>  Descarga gratuita

{item.title}

{item.size_label && item.size_label !== "—" ? `${item.size_label} · ` : ""}{item.platform || ""}

Aviso legal y limitación de responsabilidad

Este software se distribuye tal cual, sin garantías de ningún tipo, expresas o implícitas. MASoft Sistemas Informáticos no se hace responsable de daños directos, indirectos o consecuentes derivados de su uso.

El usuario es el único responsable de verificar la compatibilidad con su sistema y de realizar las copias de seguridad pertinentes antes de la instalación.

{showTerms && } e.preventDefault()} > Descargar ahora
); }; /* ---------- Modal Suscripción ---------- */ const SubscribeModal = ({ open, onClose, adminEmail }) => { const [form, setForm] = useState({ name: "", email: "", company: "", accept: false }); const [done, setDone] = useState(false); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [showTerms, setShowTerms] = useState(false); useEffect(() => { if (open) { setDone(false); setError(null); setShowTerms(false); setForm({ name: "", email: "", company: "", accept: false }); document.body.style.overflow = "hidden"; } else { document.body.style.overflow = ""; } return () => { document.body.style.overflow = ""; }; }, [open]); const submit = async (e) => { e.preventDefault(); if (!form.email || !form.accept) return; setLoading(true); setError(null); try { const res = await fetch("api/subscribe.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(form) }); const data = await res.json(); if (data.ok) setDone(true); else setError(data.error === "email_invalid" ? "Email no válido" : "Error al suscribirse"); } catch { setError("No se ha podido conectar con el servidor"); } setLoading(false); }; return (
e.stopPropagation()}> {!done ? ( <>  Mantenme informado

Recibe nuestras novedades

Te avisaremos cuando publiquemos nuevo software, actualizaciones y herramientas gratuitas. Sin spam, puedes darte de baja cuando quieras.

setForm({...form, name: e.target.value})}/>
setForm({...form, company: e.target.value})}/>
setForm({...form, email: e.target.value})} required/>
{showTerms && } {error &&

{error}

} ) : (

¡Ya estás en la lista!

Te avisaremos cuando publiquemos novedades.

)}
); }; /* ---------- Contacto ---------- */ const Contact = ({ contactInfo }) => { const [sent, setSent] = useState(false); const [form, setForm] = useState({ name: "", email: "", company: "", message: "", topic: "Desarrollo a medida" }); const submit = async (e) => { e.preventDefault(); try { const res = await fetch("api/contact.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(form) }); const data = await res.json(); if (data.ok) setSent(true); } catch {} }; const email = contactInfo.contact_email || "hola@masoft.es"; const phone = contactInfo.contact_phone || "+34 600 123 456"; const location = contactInfo.contact_location || "Madrid · España"; return (
Contacto

Cuéntanos qué necesitas. Respondemos en menos de 24 h.

Una llamada inicial gratuita, sin compromiso. Si tu proyecto encaja, te enviamos propuesta cerrada en 5 días.

  • {email}Respuesta < 24h
  • {phone}L-V · 9:00 – 18:00
  • {location}Trabajamos remoto en toda la UE
{!sent ? (
setForm({...form, name:e.target.value})} required/>
setForm({...form, email:e.target.value})} required/>
setForm({...form, company:e.target.value})}/>