/* 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 <svg {...props}><path d="M5 12h14M13 5l7 7-7 7" /></svg>;
    case "arrow-down": return <svg {...props}><path d="M12 5v14M5 13l7 7 7-7" /></svg>;
    case "code": return <svg {...props}><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>;
    case "cpu": return <svg {...props}><rect x="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6"/><path d="M9 1v3M15 1v3M9 20v3M15 20v3M20 9h3M20 14h3M1 9h3M1 14h3"/></svg>;
    case "refresh": return <svg {...props}><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>;
    case "download": return <svg {...props}><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>;
    case "upload": return <svg {...props}><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>;
    case "x": return <svg {...props}><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>;
    case "sun": return <svg {...props}><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/></svg>;
    case "moon": return <svg {...props}><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>;
    case "mail": return <svg {...props}><path d="M4 4h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>;
    case "phone": return <svg {...props}><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg>;
    case "pin": return <svg {...props}><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>;
    case "check": return <svg {...props}><polyline points="20 6 9 17 4 12"/></svg>;
    case "lock": return <svg {...props}><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>;
    case "image": return <svg {...props}><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>;
    case "search": return <svg {...props}><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>;
    case "github": return <svg {...props}><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>;
    case "file-text": return <svg {...props}><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>;
    case "images": return <svg {...props}><rect x="5" y="5" width="16" height="16" rx="2"/><rect x="3" y="3" width="16" height="16" rx="2"/><circle cx="9.5" cy="9.5" r="1.5"/><polyline points="19 13 14 8 3 19"/></svg>;
    case "grid":   return <svg {...props}><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>;
    case "list":   return <svg {...props}><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>;
    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 = () => (
  <div className="code-panel" aria-hidden="true">
    <div className="code-panel__bar">
      <div className="code-panel__dots"><span/><span/><span/></div>
      <span className="code-panel__title">~/masoft/agents/invoice_pipeline.py</span>
    </div>
    <div className="code-panel__body">
      <div><span className="ln">1</span><span className="tok-c"># Pipeline IA — facturas → ERP</span></div>
      <div><span className="ln">2</span><span className="tok-k">from</span> masoft <span className="tok-k">import</span> <span className="tok-f">Agent</span>, <span className="tok-f">OCR</span></div>
      <div><span className="ln">3</span></div>
      <div><span className="ln">4</span>agent = <span className="tok-f">Agent</span>(model=<span className="tok-s">"claude-haiku-4.5"</span>)</div>
      <div><span className="ln">5</span></div>
      <div><span className="ln">6</span><span className="tok-k">async def</span> <span className="tok-f">procesar</span>(pdf):</div>
      <div><span className="ln">7</span>{"\u00a0\u00a0"}data = <span className="tok-k">await</span> <span className="tok-f">OCR</span>.extract(pdf)</div>
      <div><span className="ln">8</span>{"\u00a0\u00a0"}<span className="tok-k">return</span> <span className="tok-k">await</span> agent.<span className="tok-f">map_to</span>(<span className="tok-s">"sap"</span>, data)</div>
      <div><span className="ln">9</span></div>
      <div><span className="ln">10</span><span className="tok-c"># → 12.400 facturas/mes · 0 errores</span><span className="cursor"/></div>
    </div>
  </div>
);

const HeroBento = () => (
  <div className="hero-bento" aria-hidden="true">
    <div className="b b--lg">
      <span className="b__t">/01 — Automatización</span>
      <div>
        <div className="b__big">−68%</div>
        <span className="b__h" style={{display:'block', marginTop:8}}>tiempo en tareas repetitivas</span>
      </div>
    </div>
    <div className="b">
      <span className="b__t">/02 — Stack</span>
      <span className="b__h">Python · Node · React · Kotlin</span>
    </div>
    <div className="b">
      <span className="b__t">/03 — Sectores</span>
      <span className="b__h">Industrial · Retail · Servicios</span>
    </div>
    <div className="b b--lg">
      <span className="b__t">/04 — Equipo</span>
      <span className="b__h" style={{fontSize:16, lineHeight:1.4}}>Ingenieros senior con +10 años desarrollando software a medida en España.</span>
    </div>
  </div>
);

const Hero = ({ variant, onCtaScroll }) => {
  if (variant === "minimal") {
    return (
      <section className="hero">
        <div className="hero__grid"/>
        <div className="container hero-minimal">
          <span className="pill"><span className="dot"/> Disponibles · Q2 2026</span>
          <h1 className="hero__title" style={{marginTop: 24}}>
            Software <em>a medida</em>.<br/>Hecho con cabeza.
          </h1>
          <p className="lead" style={{margin: "0 auto"}}>
            Desarrollamos aplicaciones e integramos IA en el día a día de tu empresa. Sin plantillas, sin tonterías.
          </p>
          <div className="hero__cta">
            <button className="btn btn--accent" onClick={() => onCtaScroll("contacto")}>Empezar un proyecto <Icon name="arrow-right" size={16}/></button>
            <button className="btn btn--ghost" onClick={() => onCtaScroll("descargas")}>Ver descargas</button>
          </div>
        </div>
      </section>
    );
  }

  return (
    <section className="hero">
      <div className="hero__grid"/>
      <div className="container hero__inner">
        <div>
          <span className="pill"><span className="dot"/> Disponibles · Q2 2026</span>
          <h1 className="hero__title" style={{marginTop: 18}}>
            Software <em>a medida</em> y automatización con IA.
          </h1>
          <p className="lead">
            Construimos las herramientas que tu empresa necesita —y que ningún software estándar resuelve. Desarrollo, modernización e IA aplicada de verdad.
          </p>
          <div className="hero__cta">
            <button className="btn btn--accent" onClick={() => onCtaScroll("contacto")}>Empezar un proyecto <Icon name="arrow-right" size={16}/></button>
            <button className="btn btn--ghost" onClick={() => onCtaScroll("descargas")}><Icon name="download" size={16}/> Software gratuito</button>
          </div>
          <div className="hero__meta">
            <div><b>+85</b><span>Proyectos entregados</span></div>
            <div><b>Expertos</b><span>en el sector IT</span></div>
            <div><b>24/7</b><span>Soporte clientes</span></div>
          </div>
        </div>
        <div>
          {variant === "bento" ? <HeroBento/> : <HeroCode/>}
        </div>
      </div>
    </section>
  );
};

/* ---------- Servicios ---------- */
const Services = () => (
  <section className="section" id="servicios">
    <div className="container">
      <div className="section-head reveal">
        <div>
          <span className="eyebrow">Servicios</span>
          <h2>Tres áreas, un único objetivo.</h2>
        </div>
        <p className="section-head__r">Tres líneas de trabajo y un único objetivo: que tu empresa funcione mejor con software hecho a su medida.</p>
      </div>
      <div className="svc-grid">
        {SERVICES.map(s => (
          <article key={s.n} className="svc-card reveal">
            <span className="svc-card__num">{s.n} / Servicio</span>
            <div className="svc-card__icon"><Icon name={s.icon} size={22} stroke={1.6}/></div>
            <h3>{s.title}</h3>
            <p style={{fontSize: 14.5}}>{s.desc}</p>
            <ul>{s.items.map(i => <li key={i}>{i}</li>)}</ul>
          </article>
        ))}
      </div>
      <div className="tech-marquee reveal">
        <span><b>Stack:</b></span>
        <span>Python</span><span>·</span>
        <span>TypeScript</span><span>·</span>
        <span>React / Next.js</span><span>·</span>
        <span>FastAPI</span><span>·</span>
        <span>PostgreSQL</span><span>·</span>
        <span>Docker</span><span>·</span>
        <span>Kotlin</span><span>·</span>
        <span>PowerShell</span><span>·</span>
        <span>Azure / AWS</span><span>·</span>
        <span>Claude · GPT · Llama</span>
      </div>
    </div>
  </section>
);

/* ---------- Proceso ---------- */
const Process = () => (
  <section className="section proceso" id="proceso">
    <div className="container">
      <div className="section-head reveal">
        <div>
          <span className="eyebrow">Proceso</span>
          <h2>De la primera reunión a producción en semanas, no meses.</h2>
        </div>
        <p className="section-head__r">Un método sencillo, transparente y con entregas frecuentes. Sin sorpresas en la factura ni en el calendario.</p>
      </div>
      <div className="process">
        {PROCESS.map(p => (
          <div key={p.n} className="process__step reveal">
            <span className="num">{p.n}</span>
            <b>Fase {p.n}</b>
            <h3>{p.title}</h3>
            <p>{p.desc}</p>
          </div>
        ))}
      </div>
    </div>
  </section>
);

/* ---------- 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 (
    <div className={`modal-bg ${open ? "is-open" : ""}`} onClick={onClose}>
      <div className="modal modal--wide" onClick={e => e.stopPropagation()} style={{maxWidth: 720, maxHeight: '85vh', overflowY: 'auto'}}>
        <button className="modal__close" onClick={onClose} aria-label="Cerrar"><Icon name="x" size={16}/></button>
        <span className="eyebrow"><Icon name="images" size={12}/> &nbsp;Capturas</span>
        <h3>{item.title}</h3>
        <div style={{display:'grid', gap:14, marginTop:16}}>
          {allShots.map((url, i) => (
            <img key={i} src={url} alt={`Captura ${i+1}`} style={{width:'100%', borderRadius:10, border:'1px solid var(--line)'}}/>
          ))}
          {allShots.length === 0 && <p style={{color:'var(--muted)', textAlign:'center', padding:'30px 0'}}>No hay capturas disponibles.</p>}
        </div>
        <div style={{textAlign:'center', marginTop:18}}>
          <button className="btn btn--ghost" onClick={onClose}>Cerrar</button>
        </div>
      </div>
    </div>
  );
};

/* ---------- Descargas ---------- */
const ScreenshotPlaceholder = ({ label }) => (
  <>
    <span className="dl-card__cat" style={{display:'none'}}/>
    <div className="dl-card__shot__inner">
      <Icon name="image" size={12}/> &nbsp;{label}
    </div>
  </>
);

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 (
    <section className="section" id="descargas" style={{background: "var(--bg)"}}>
      <div className="container">
        <div className="section-head reveal">
          <div>
            <span className="eyebrow">Descargas</span>
            <h2>Software gratuito, demos y pruebas.</h2>
            <p className="lead" style={{marginTop: 14}}>Una colección de utilidades y demos que hemos liberado para nuestros clientes y la comunidad. Descarga gratis tras un breve registro.</p>
          </div>
        </div>

        <div className="dl-toolbar reveal">
          <div className="dl-filters">
            {CATEGORIES.map(c => (
              <button key={c.id} className={`dl-filter ${cat === c.id ? "is-active" : ""}`} onClick={() => setCat(c.id)}>
                {c.label} <span className="count">{counts[c.id]}</span>
              </button>
            ))}
          </div>
          <div className="dl-filters">
            {LANG_FILTERS.map(l => (
              <button key={l.id} className={`dl-filter ${lang === l.id ? "is-active" : ""}`} onClick={() => setLang(l.id)} style={{fontFamily: "var(--mono)", fontSize: 12}}>
                {l.label} <span className="count">{langCounts[l.id] || 0}</span>
              </button>
            ))}
          </div>
          <div className="dl-view-toggle">
            <button className={`dl-view-btn ${view==="grid"?"is-active":""}`} onClick={() => setView("grid")} title="Vista fichas"><Icon name="grid" size={15}/></button>
            <button className={`dl-view-btn ${view==="list"?"is-active":""}`} onClick={() => setView("list")} title="Vista lista"><Icon name="list" size={15}/></button>
          </div>
        </div>

        <div className={`dl-grid${view==="list"?" dl-grid--list":""}`}>
          {filtered.map(item => (
            <article key={item.id} className="dl-card">
              <div className="dl-card__shot">
                <span className={`dl-card__cat ${catClass(item.category)}`}>{catLabel(item.category)}</span>
                {item.screenshot_url
                  ? <img src={item.screenshot_url} alt={item.title} style={{position:'absolute', inset:0, width:'100%', height:'100%', objectFit:'cover'}}/>
                  : <ScreenshotPlaceholder label={`screenshot · ${item.title}`}/>}
              </div>
              {view === "list" ? (
                <div className="dl-card__body">
                  <h3 className="dl-card__title" style={{marginBottom:4}}>{item.title}</h3>
                  <p className="dl-card__desc" style={{margin:'4px 0 0', WebkitLineClamp:2, display:'-webkit-box', WebkitBoxOrient:'vertical', overflow:'hidden'}}>{item.description}</p>
                  <div className="dl-card__foot" style={{marginTop:'auto', paddingTop:10}}>
                    <div style={{display:'flex', gap:5, alignItems:'center', flexWrap:'wrap', minWidth:0}}>
                      <span className="dl-card__size">
                        {item.size_label && item.size_label !== "—" ? item.size_label + " · " : ""}
                        {fmtDate(item.created_at)}
                        {item.platform ? " · " + item.platform : ""}
                        {item.version ? " · " + item.version : ""}
                      </span>
                      {(item.tags || []).map(t => <span key={t} className={`dl-tag lang-${item.lang}`}>{t}</span>)}
                    </div>
                    <div style={{display:'flex', gap:6, alignItems:'center', flexShrink:0}}>
                      {item.manual_url && (
                        <a href={item.manual_url} target="_blank" rel="noopener noreferrer" className="dl-card__btn dl-card__btn--sec" title="Manual"><Icon name="file-text" size={14}/></a>
                      )}
                      {item.has_file
                        ? <button className="dl-card__btn" onClick={() => onDownload(item)}><Icon name="download" size={14}/> Descargar</button>
                        : <span className="dl-card__btn" style={{opacity:.4, cursor:"default"}}>Sin archivo</span>
                      }
                    </div>
                  </div>
                </div>
              ) : (
                <div className="dl-card__body">
                  <div className="dl-card__head">
                    <div>
                      <h3 className="dl-card__title">{item.title}</h3>
                      <span className="dl-card__meta">{item.platform || "—"} · {item.version || "—"}</span>
                    </div>
                  </div>
                  <p className="dl-card__desc">{item.description}</p>
                  <div className="dl-card__tags">
                    {(item.tags || []).map(t => (
                      <span key={t} className={`dl-tag lang-${item.lang}`}>{t}</span>
                    ))}
                  </div>
                  <div className="dl-card__foot">
                    <span className="dl-card__size">{item.size_label || "—"} · {fmtDate(item.created_at)}</span>
                    <div style={{display:'flex', gap:6, alignItems:'center'}}>
                      {item.manual_url && (
                        <a href={item.manual_url} target="_blank" rel="noopener noreferrer" className="dl-card__btn dl-card__btn--sec" title="Manual"><Icon name="file-text" size={14}/></a>
                      )}
                      {((item.screenshot_url) || (item.screenshots_extra_urls && item.screenshots_extra_urls.length > 0)) && (
                        <button className="dl-card__btn dl-card__btn--sec" title="Ver capturas" onClick={() => { setShotsItem(item); setShotsOpen(true); }}><Icon name="images" size={14}/></button>
                      )}
                      {item.has_file
                        ? <button className="dl-card__btn" onClick={() => onDownload(item)}><Icon name="download" size={14}/> Descargar</button>
                        : <span className="dl-card__btn" style={{opacity:.4, cursor:"default"}}>Sin archivo</span>
                      }
                    </div>
                  </div>
                </div>
              )}
            </article>
          ))}
          {filtered.length === 0 && (
            <p style={{gridColumn: "1/-1", textAlign:"center", color:"var(--muted)", padding:"40px 0"}}>No hay software en esta categoría todavía.</p>
          )}
        </div>

        <div className="reveal" style={{marginTop:48, padding:"28px 32px", background:"var(--surface)", border:"1px solid var(--line)", borderRadius:16, display:"flex", alignItems:"center", justifyContent:"space-between", gap:24, flexWrap:"wrap"}}>
          <div>
            <b style={{fontSize:16}}>¿Quieres estar al día?</b>
            <p style={{margin:"4px 0 0", fontSize:13.5, color:"var(--muted)"}}>Recibe un aviso cuando publiquemos nuevo software o actualizaciones. Sin spam.</p>
          </div>
          <button className="btn btn--accent" onClick={onSubscribe}><Icon name="mail" size={15}/> Suscribirme</button>
        </div>

      </div>

      <ScreenshotsModal open={shotsOpen} item={shotsItem} onClose={() => setShotsOpen(false)}/>
    </section>
  );
};

/* ---------- Caja privacidad (reutilizable) ---------- */
const PrivacyBox = ({ email }) => {
  const addr = email || "hola@masoft.es";
  return (
    <div style={{background:"var(--bg)", border:"1px solid var(--line)", borderRadius:8, padding:"14px 16px", fontSize:12.5, color:"var(--muted)", marginBottom:14, lineHeight:1.65}}>
      <b style={{color:"var(--ink)", display:"block", marginBottom:8}}>Información básica sobre protección de datos (RGPD)</b>
      <p style={{margin:"0 0 5px"}}><b>Responsable:</b> MASoft Sistemas Informáticos &mdash; <a href={`mailto:${addr}`} style={{color:"var(--accent)"}}>{addr}</a></p>
      <p style={{margin:"0 0 5px"}}><b>Finalidad:</b> Gestionar tu suscripción y enviarte comunicaciones sobre nuevos productos, actualizaciones y herramientas publicados en MASoft.es.</p>
      <p style={{margin:"0 0 5px"}}><b>Base jurídica:</b> Consentimiento expreso del interesado (art. 6.1.a del Reglamento UE 2016/679 &mdash; RGPD).</p>
      <p style={{margin:"0 0 5px"}}><b>Conservación:</b> 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.</p>
      <p style={{margin:"0 0 5px"}}><b>Destinatarios:</b> No se ceden datos a terceros salvo obligación legal. Los datos se tratan en servidores dentro de la UE.</p>
      <p style={{margin:0}}><b>Derechos:</b> Puedes ejercer tus derechos de acceso, rectificación, supresión, oposición, limitación y portabilidad escribiendo a <a href={`mailto:${addr}`} style={{color:"var(--accent)"}}>{addr}</a>. Si consideras que el tratamiento no es conforme, puedes reclamar ante la <a href="https://www.aepd.es" target="_blank" rel="noopener noreferrer" style={{color:"var(--accent)"}}>Agencia Española de Protección de Datos (aepd.es)</a>.</p>
    </div>
  );
};

/* ---------- 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 (
    <div className={`modal-bg ${open ? "is-open" : ""}`} onClick={onClose}>
      <div className="modal" onClick={e => e.stopPropagation()}>
        <button className="modal__close" onClick={onClose} aria-label="Cerrar"><Icon name="x" size={16}/></button>
        <span className="eyebrow"><Icon name="download" size={12}/> &nbsp;Descarga gratuita</span>
        <h3>{item.title}</h3>
        <p style={{fontSize:14, color:"var(--muted)", marginBottom:18}}>
          {item.size_label && item.size_label !== "—" ? `${item.size_label} · ` : ""}{item.platform || ""}
        </p>

        <div style={{background:"var(--bg)", border:"1px solid var(--line)", borderRadius:8, padding:"14px 16px", fontSize:13, color:"var(--muted)", marginBottom:16, lineHeight:1.6}}>
          <b style={{color:"var(--ink)", display:"block", marginBottom:6}}>Aviso legal y limitación de responsabilidad</b>
          <p style={{margin:"0 0 6px"}}>Este software se distribuye <b>tal cual</b>, 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.</p>
          <p style={{margin:0}}>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.</p>
        </div>

        <label className="field--check" style={{marginBottom:8}}>
          <input type="checkbox" checked={accept} onChange={e => setAccept(e.target.checked)}/>
          He leído el aviso legal y acepto la{" "}
          <button type="button" onClick={() => setShowTerms(v => !v)} style={{background:"none", border:"none", padding:0, color:"var(--accent)", textDecoration:"underline", cursor:"pointer", fontSize:"inherit", fontFamily:"inherit"}}>
            política de privacidad
          </button>.
        </label>
        {showTerms && <PrivacyBox email={adminEmail}/>}

        <a
          href={accept ? `api/download.php?id=${item.id}` : undefined}
          className="btn btn--accent"
          style={{width:"100%", justifyContent:"center", display:"flex", opacity: accept ? 1 : 0.45, pointerEvents: accept ? "auto" : "none", marginTop:8}}
          onClick={accept ? onClose : e => e.preventDefault()}
        >
          <Icon name="download" size={16}/> Descargar ahora
        </a>
      </div>
    </div>
  );
};

/* ---------- 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 (
    <div className={`modal-bg ${open ? "is-open" : ""}`} onClick={onClose}>
      <div className="modal" onClick={e => e.stopPropagation()}>
        <button className="modal__close" onClick={onClose} aria-label="Cerrar"><Icon name="x" size={16}/></button>
        {!done ? (
          <>
            <span className="eyebrow"><Icon name="mail" size={12}/> &nbsp;Mantenme informado</span>
            <h3>Recibe nuestras novedades</h3>
            <p style={{fontSize: 14, color: "var(--muted)", marginBottom: 22}}>
              Te avisaremos cuando publiquemos nuevo software, actualizaciones y herramientas gratuitas. Sin spam, puedes darte de baja cuando quieras.
            </p>
            <form onSubmit={submit}>
              <div className="form-row">
                <div className="field"><label>Nombre</label><input value={form.name} onChange={e=>setForm({...form, name: e.target.value})}/></div>
                <div className="field"><label>Empresa</label><input value={form.company} onChange={e=>setForm({...form, company: e.target.value})}/></div>
              </div>
              <div className="field"><label>Email *</label><input type="email" value={form.email} onChange={e=>setForm({...form, email: e.target.value})} required/></div>
              <label className="field--check" style={{marginBottom: 8}}>
                <input type="checkbox" checked={form.accept} onChange={e=>setForm({...form, accept: e.target.checked})}/>
                Acepto la{" "}
                <button type="button" onClick={() => setShowTerms(v => !v)} style={{background:"none", border:"none", padding:0, color:"var(--accent)", textDecoration:"underline", cursor:"pointer", fontSize:"inherit", fontFamily:"inherit"}}>
                  política de privacidad
                </button>
                {" "}y el envío de comunicaciones.
              </label>
              {showTerms && <PrivacyBox email={adminEmail}/>}
              <button type="submit" className="btn btn--accent" style={{width:"100%", justifyContent:"center"}} disabled={!form.email || !form.accept || loading}>
                {loading ? "Enviando..." : <><Icon name="check" size={16}/> Suscribirme</>}
              </button>
              {error && <p style={{color:"#c33", fontSize:13, marginTop:10, textAlign:"center"}}>{error}</p>}
            </form>
          </>
        ) : (
          <div style={{textAlign:"center", padding:"20px 0"}}>
            <div style={{width:64, height:64, borderRadius:16, background:"var(--accent-soft)", color:"var(--accent)", display:"grid", placeItems:"center", margin:"0 auto 18px"}}>
              <Icon name="check" size={32} stroke={2.5}/>
            </div>
            <h3>¡Ya estás en la lista!</h3>
            <p style={{color:"var(--muted)", fontSize:14}}>Te avisaremos cuando publiquemos novedades.</p>
            <button className="btn btn--ghost" onClick={onClose} style={{marginTop:16}}>Cerrar</button>
          </div>
        )}
      </div>
    </div>
  );
};

/* ---------- 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 (
    <section className="section" id="contacto">
      <div className="container">
        <div className="section-head reveal">
          <div>
            <span className="eyebrow">Contacto</span>
            <h2>Cuéntanos qué necesitas. Respondemos en menos de 24 h.</h2>
          </div>
        </div>
        <div className="contact reveal">
          <div className="contact__info">
            <p style={{fontSize: 16, color: "var(--ink-2)", maxWidth: 380}}>
              Una llamada inicial gratuita, sin compromiso. Si tu proyecto encaja, te enviamos propuesta cerrada en 5 días.
            </p>
            <ul>
              <li><Icon name="mail" size={20}/><div><b>{email}</b><span>Respuesta &lt; 24h</span></div></li>
              <li><Icon name="phone" size={20}/><div><b>{phone}</b><span>L-V · 9:00 – 18:00</span></div></li>
              <li><Icon name="pin" size={20}/><div><b>{location}</b><span>Trabajamos remoto en toda la UE</span></div></li>
            </ul>
          </div>
          <div className="contact__form">
            {!sent ? (
              <form onSubmit={submit}>
                <div className="form-row">
                  <div className="field"><label>Nombre</label><input value={form.name} onChange={e=>setForm({...form, name:e.target.value})} required/></div>
                  <div className="field"><label>Email</label><input type="email" value={form.email} onChange={e=>setForm({...form, email:e.target.value})} required/></div>
                </div>
                <div className="form-row">
                  <div className="field"><label>Empresa</label><input value={form.company} onChange={e=>setForm({...form, company:e.target.value})}/></div>
                  <div className="field">
                    <label>Tema</label>
                    <select value={form.topic} onChange={e=>setForm({...form, topic:e.target.value})}>
                      <option>Desarrollo a medida</option>
                      <option>Automatización con IA</option>
                      <option>Modernización de sistemas</option>
                      <option>Consultoría / otros</option>
                    </select>
                  </div>
                </div>
                <div className="field"><label>Cuéntanos brevemente</label><textarea value={form.message} onChange={e=>setForm({...form, message:e.target.value})} required/></div>
                <button type="submit" className="btn btn--accent" style={{width: "100%", justifyContent: "center"}}>Enviar mensaje <Icon name="arrow-right" size={16}/></button>
              </form>
            ) : (
              <div style={{textAlign: "center", padding: "40px 20px"}}>
                <div style={{width: 64, height: 64, borderRadius: 16, background: "var(--accent-soft)", color: "var(--accent)", display: "grid", placeItems: "center", margin: "0 auto 18px"}}>
                  <Icon name="check" size={32} stroke={2.5}/>
                </div>
                <h3>Mensaje recibido</h3>
                <p style={{color: "var(--muted)"}}>Te contestamos en menos de 24 horas hábiles.</p>
              </div>
            )}
          </div>
        </div>
      </div>
    </section>
  );
};

/* ---------- Footer ---------- */
const Footer = ({ contactInfo }) => {
  const email = contactInfo.contact_email || "hola@masoft.es";
  const phone = contactInfo.contact_phone || "+34 600 123 456";
  const phoneTel = phone.replace(/\s+/g, "");

  return (
  <footer className="footer">
    <div className="container">
      <div className="footer__top">
        <div>
          <div className="brand">
            <span className="brand__mark">MA</span>
            <div>
              <div className="brand__name"><b>MASoft</b><i>.es</i></div>
              <span className="brand__sub">Sistemas Informáticos</span>
            </div>
          </div>
          <p style={{fontSize: 13.5, color: "var(--muted)", maxWidth: 320, marginTop: 18}}>
            Software a medida y automatización con IA para empresas que quieren funcionar mejor.
          </p>
        </div>
        <div>
          <h4>Servicios</h4>
          <ul>
            <li><a href="#servicios">Desarrollo a medida</a></li>
            <li><a href="#servicios">Automatización IA</a></li>
            <li><a href="#servicios">Modernización</a></li>
            <li><a href="#proceso">Proceso</a></li>
          </ul>
        </div>
        <div>
          <h4>Recursos</h4>
          <ul>
            <li><a href="#descargas">Descargas</a></li>
            <li><a href="#descargas">Demos online</a></li>
            <li><a href="#contacto">Soporte</a></li>
          </ul>
        </div>
        <div>
          <h4>Contacto</h4>
          <ul>
            <li><a href={`mailto:${email}`}>{email}</a></li>
            <li><a href={`tel:${phoneTel}`}>{phone}</a></li>
            <li><a href="#contacto">Formulario</a></li>
          </ul>
        </div>
      </div>
      <div className="footer__copy">
        <span>© 2026 MASoft.es · Sistemas Informáticos</span>
        <span>Hecho en Madrid · v2026.05</span>
      </div>
    </div>
  </footer>
  );
};

/* ---------- Navbar ---------- */
const Navbar = ({ theme, onToggleTheme, onScroll }) => (
  <header className="nav">
    <div className="container nav__inner">
      <a href="#top" className="brand" onClick={(e) => { e.preventDefault(); onScroll("top"); }}>
        <span className="brand__mark">MA</span>
        <div>
          <div className="brand__name"><b>MASoft</b><i>.es</i></div>
          <span className="brand__sub">Sistemas Informáticos</span>
        </div>
      </a>
      <nav className="nav__links">
        <a href="#servicios" onClick={(e) => { e.preventDefault(); onScroll("servicios"); }}>Servicios</a>
        <a href="#proceso" onClick={(e) => { e.preventDefault(); onScroll("proceso"); }}>Proceso</a>
        <a href="#descargas" onClick={(e) => { e.preventDefault(); onScroll("descargas"); }}>Descargas</a>
        <a href="#contacto" onClick={(e) => { e.preventDefault(); onScroll("contacto"); }}>Contacto</a>
      </nav>
      <div className="nav__cta">
        <button className="icon-btn" onClick={onToggleTheme} aria-label="Cambiar tema">
          <Icon name={theme === "dark" ? "sun" : "moon"} size={16}/>
        </button>
        <button className="btn btn--accent btn--sm" onClick={() => onScroll("contacto")}>Hablemos <Icon name="arrow-right" size={14}/></button>
      </div>
    </div>
  </header>
);

/* ---------- App ---------- */
const App = () => {
  const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
    "theme": "light",
    "accent": "#3D7BFF",
    "heroVariant": "code",
    "fontDisplay": "Manrope"
  }/*EDITMODE-END*/;

  const [t, setTweak] = window.useTweaks ? window.useTweaks(TWEAK_DEFAULTS) : [TWEAK_DEFAULTS, () => {}];
  const [downloads, setDownloads] = useState([]);
  const [subOpen, setSubOpen]     = useState(false);
  const [dlOpen, setDlOpen]       = useState(false);
  const [dlItem, setDlItem]       = useState(null);
  const [contactInfo, setContactInfo] = useState({});

  useReveal(downloads.length);

  // Carga inicial desde la API
  useEffect(() => {
    fetch("api/list.php")
      .then(r => r.json())
      .then(d => { if (d && d.items) setDownloads(d.items); else setDownloads(DOWNLOADS_FALLBACK); })
      .catch(() => setDownloads(DOWNLOADS_FALLBACK));

    fetch("api/settings.php?action=get_public")
      .then(r => r.json())
      .then(d => { if (d.ok && d.settings) setContactInfo(d.settings); })
      .catch(() => {});
  }, []);

  // Aplica tema + acento + tipografía
  useEffect(() => {
    document.documentElement.setAttribute("data-theme", t.theme || "light");
    document.documentElement.style.setProperty("--accent", t.accent);
    document.documentElement.style.setProperty("--accent-soft", `color-mix(in oklab, ${t.accent} 14%, transparent)`);
    document.documentElement.style.setProperty("--display", `"${t.fontDisplay}", system-ui, sans-serif`);
  }, [t]);

  const onScroll = (id) => {
    if (id === "top") { window.scrollTo({ top: 0, behavior: "smooth" }); return; }
    const el = document.getElementById(id);
    if (el) window.scrollTo({ top: el.offsetTop - 70, behavior: "smooth" });
  };

  const TweaksPanel = window.TweaksPanel;
  const TweakSection = window.TweakSection;
  const TweakRadio = window.TweakRadio;
  const TweakColor = window.TweakColor;
  const TweakSelect = window.TweakSelect;

  return (
    <>
      <Navbar theme={t.theme} onToggleTheme={() => setTweak("theme", t.theme === "dark" ? "light" : "dark")} onScroll={onScroll}/>
      <main id="top">
        <Hero variant={t.heroVariant} onCtaScroll={onScroll}/>
        <Services/>
        <Process/>
        <Downloads items={downloads} onSubscribe={() => setSubOpen(true)} onDownload={item => { setDlItem(item); setDlOpen(true); }}/>
        <Contact contactInfo={contactInfo}/>
      </main>
      <Footer contactInfo={contactInfo}/>

      <DownloadModal open={dlOpen} item={dlItem} onClose={() => setDlOpen(false)} adminEmail={contactInfo.contact_email || "hola@masoft.es"}/>
      <SubscribeModal open={subOpen} onClose={() => setSubOpen(false)} adminEmail={contactInfo.contact_email || "hola@masoft.es"}/>

      {TweaksPanel && (
        <TweaksPanel title="Tweaks">
          <TweakSection title="Apariencia">
            <TweakRadio label="Tema" value={t.theme} onChange={v => setTweak("theme", v)} options={[
              { value: "light", label: "Claro" },
              { value: "dark", label: "Oscuro" }
            ]}/>
            <TweakColor label="Color principal" value={t.accent} onChange={v => setTweak("accent", v)} options={["#3D7BFF", "#0066FF", "#00C2FF", "#7C3AED", "#10B981"]}/>
          </TweakSection>
          <TweakSection title="Tipografía">
            <TweakSelect label="Fuente display" value={t.fontDisplay} onChange={v => setTweak("fontDisplay", v)} options={[
              { value: "Manrope", label: "Manrope" },
              { value: "Inter", label: "Inter" },
              { value: "Space Grotesk", label: "Space Grotesk" },
              { value: "Geist", label: "Geist" },
              { value: "DM Sans", label: "DM Sans" }
            ]}/>
          </TweakSection>
          <TweakSection title="Hero">
            <TweakRadio label="Variante" value={t.heroVariant} onChange={v => setTweak("heroVariant", v)} options={[
              { value: "code", label: "Código" },
              { value: "bento", label: "Bento" },
              { value: "minimal", label: "Mínimo" }
            ]}/>
          </TweakSection>
        </TweaksPanel>
      )}
    </>
  );
};

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App/>);
