// Convertidor Sell-U — App principal (Hero · Analizando · Resultados · Captura lead)
// Llama al backend Laravel para datos reales de PageSpeed + HTML scraping.

const { useState, useEffect, useRef } = React;

const ANALYZE_STEPS = window.ConvierteEngine.CATEGORIES.map((c) => c.label);

function scrollToId(id) {
  const el = document.getElementById(id);
  if (!el) return;
  const reduce = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
  const y = el.getBoundingClientRect().top + window.scrollY - 24;
  window.scrollTo({ top: y, behavior: reduce ? "auto" : "smooth" });
}

function normalizeUrl(raw) {
  let u = (raw || "").trim();
  if (!u) return "";
  if (!/^https?:\/\//i.test(u)) u = "https://" + u;
  return u;
}
function isValidUrl(raw) {
  const u = normalizeUrl(raw);
  return /^https?:\/\/([\w-]+\.)+[a-z]{2,}(\/\S*)?$/i.test(u);
}
function prettyHost(url) {
  try { return new URL(normalizeUrl(url)).host.replace(/^www\./, ""); }
  catch { return url; }
}

/* Nav y Footer del convertidor se eliminaron — ahora la vista Blade
   envuelve el #root con <x-nav /> y <x-footer /> de Sell-U para que
   el convertidor se integre visualmente con el resto del sitio. */

/* ===================== HERO ===================== */
function Hero({ url, setUrl, device, setDevice, onAnalyze, error }) {
  const inputRef = useRef(null);
  return (
    <section className="hero" aria-labelledby="hero-h1">
      <div className="hero-glow" aria-hidden="true" />
      <div className="container hero-inner">
        <span className="eyebrow hero-eyebrow">Diagnóstico de conversión · gratis · datos reales</span>
        <h1 id="hero-h1" className="hero-h1">
          Descubre por qué tu página<br className="hide-mobile" /> no convierte —<span className="hl">en 30 segundos</span>
        </h1>
        <p className="hero-sub">
          Pega tu enlace y recibe un diagnóstico real con Performance, Accessibility,
          Best Practices, SEO y Core Web Vitals — más los problemas que están frenando tus ventas.
        </p>

        <div className="hero-form" role="group" aria-label="Analizar una URL">
          <div className="url-row">
            <div className={"url-field" + (error ? " url-field--err" : "")}>
              <span className="url-proto" aria-hidden="true">https://</span>
              <input
                ref={inputRef}
                type="url"
                inputMode="url"
                className="url-input"
                placeholder="tu-sitio.com"
                aria-label="URL de tu página web"
                aria-invalid={!!error}
                aria-describedby={error ? "url-err" : "url-hint"}
                value={url}
                onChange={(e) => setUrl(e.target.value)}
                onKeyDown={(e) => { if (e.key === "Enter") onAnalyze(); }}
              />
            </div>
            <button className="btn btn-primary btn-lg url-btn" onClick={onAnalyze}>
              Analizar gratis <Icon name="arrow" size={18} stroke={2} />
            </button>
          </div>

          <div className="hero-controls">
            <div className="device-toggle" role="radiogroup" aria-label="Dispositivo a analizar">
              <button role="radio" aria-checked={device === "desktop"}
                className={"seg" + (device === "desktop" ? " seg--on" : "")}
                onClick={() => setDevice("desktop")}>
                <Icon name="desktop" size={16} /> Escritorio
              </button>
              <button role="radio" aria-checked={device === "mobile"}
                className={"seg" + (device === "mobile" ? " seg--on" : "")}
                onClick={() => setDevice("mobile")}>
                <Icon name="mobile" size={16} /> Móvil
              </button>
            </div>
            {error
              ? <span id="url-err" className="url-msg url-msg--err" role="alert"><Icon name="alert" size={15} stroke={2} /> {error}</span>
              : <span id="url-hint" className="url-msg">Sin registro. Datos reales de Google PageSpeed.</span>}
          </div>
        </div>

        <ul className="hero-chips" aria-label="Garantías">
          <li><Icon name="check" size={15} stroke={2} /> 100% gratis</li>
          <li><Icon name="check" size={15} stroke={2} /> Sin registro</li>
          <li><Icon name="check" size={15} stroke={2} /> Datos reales</li>
        </ul>
      </div>
    </section>
  );
}

/* ===================== ANALIZANDO ===================== */
function Analyzing({ url, device, done, error, onRetry }) {
  const reduce = useRef(window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches).current;
  const total = ANALYZE_STEPS.length;
  // 'done' viene del padre cuando termina el fetch — pero animamos visualmente
  // un progreso suave mientras esperamos la respuesta del backend.
  const [step, setStep] = useState(0);

  useEffect(() => {
    if (done) { setStep(total); return; }
    const perStep = reduce ? 280 : 1100; // analisis backend tarda ~10–20s
    const id = setInterval(() => {
      setStep((s) => Math.min(total - 1, s + 1));
    }, perStep);
    return () => clearInterval(id);
  }, [done]);

  const pct = Math.round((step / total) * 100);

  if (error) {
    return (
      <section className="analyzing" aria-live="polite">
        <div className="container an-inner">
          <div className="an-pulse" style={{background:"var(--danger-100)", color:"var(--danger-500)"}} aria-hidden="true">
            <Icon name="alert" size={30} stroke={2} />
          </div>
          <h2 className="an-title">No pudimos completar el análisis</h2>
          <p className="an-sub" style={{color:"var(--danger-700)"}}>{error}</p>
          <div style={{marginTop:24}}>
            <button className="btn btn-primary btn-lg" onClick={onRetry}>← Intentar con otra URL</button>
          </div>
        </div>
      </section>
    );
  }

  return (
    <section className="analyzing" aria-live="polite" aria-busy="true">
      <div className="container an-inner">
        <div className="an-pulse" aria-hidden="true">
          <Icon name="search" size={30} stroke={1.75} />
        </div>
        <h2 className="an-title">Analizando {prettyHost(url)}…</h2>
        <p className="an-sub">
          Ejecutando auditoría en {device === "mobile" ? "móvil" : "escritorio"} con Google PageSpeed.
          Esto puede tomar 10–20 segundos.
        </p>

        <div className="an-progress">
          <div className="an-progress-track" role="progressbar" aria-valuenow={pct} aria-valuemin={0} aria-valuemax={100} aria-label="Progreso del análisis">
            <div className="an-progress-fill" style={{ width: pct + "%", transition: reduce ? "none" : "width .35s var(--ease-out)" }} />
          </div>
          <span className="an-progress-pct">{pct}%</span>
        </div>

        <ul className="an-checklist">
          {ANALYZE_STEPS.map((label, i) => {
            const state = i < step ? "done" : i === step ? "active" : "wait";
            return (
              <li key={label} className={"an-item an-item--" + state}>
                <span className="an-check" aria-hidden="true">
                  {state === "done" ? <Icon name="check" size={14} stroke={2.5} />
                    : state === "active" ? <span className="an-spin" /> : null}
                </span>
                <span className="an-label">{label}</span>
                <span className="an-status">{state === "done" ? "Listo" : state === "active" ? "Revisando" : ""}</span>
              </li>
            );
          })}
        </ul>
      </div>
    </section>
  );
}

/* ===================== RESULTADOS ===================== */
function PotentialCard({ pct }) {
  return (
    <div className="potential-card">
      <div className="potential-icon" aria-hidden="true"><Icon name="trending" size={22} stroke={2} /></div>
      <div>
        <div className="potential-label">Mejora potencial de conversión estimada</div>
        <div className="potential-num">+{pct}<span>%</span></div>
      </div>
      <p className="potential-note">Resolviendo los problemas detectados, basado en tu puntaje actual.</p>
    </div>
  );
}

/* ===================== CORE WEB VITALS (nuevo: datos reales PSI) ===================== */
function CWVCard({ label, value, unit, threshold, lowerBetter = true }) {
  if (value === null || value === undefined) {
    return (
      <div className="kpi-card">
        <div className="kpi-value" style={{opacity:.4}}>—</div>
        <div className="kpi-label">{label}</div>
        <p className="kpi-hint">Sin datos suficientes.</p>
      </div>
    );
  }
  // Determinar tono según threshold ([bueno, necesita-mejora])
  let tone = "success";
  if (lowerBetter) {
    if (value > threshold[1]) tone = "danger";
    else if (value > threshold[0]) tone = "warning";
  } else {
    if (value < threshold[0]) tone = "danger";
    else if (value < threshold[1]) tone = "warning";
  }
  const display = unit === "ms" && value > 1000
    ? (value / 1000).toFixed(1) + "s"
    : unit === "ms" ? Math.round(value) + "ms"
    : unit === "cls" ? value.toFixed(2)
    : value;
  return (
    <div className="kpi-card">
      <span className="kpi-icon" style={{color:TONE[tone].color, background:TONE[tone].bg}}>
        <Icon name="gauge" size={18} />
      </span>
      <div className="kpi-value">{display}</div>
      <div className="kpi-label">{label}</div>
      <p className="kpi-hint">
        {tone === "success" ? "Bueno" : tone === "warning" ? "Necesita mejora" : "Pobre"}
        {" "}— meta: {lowerBetter ? "≤" : "≥"} {unit === "ms" && threshold[0] > 1000 ? (threshold[0]/1000) + "s" : threshold[0] + (unit === "cls" ? "" : unit)}
      </p>
    </div>
  );
}

function CoreWebVitalsSection({ cwv }) {
  if (!cwv) return null;
  return (
    <section className="res-section container" aria-labelledby="cwv-h">
      <div className="section-head">
        <span className="eyebrow">Core Web Vitals · datos reales (Google Lighthouse)</span>
        <h2 id="cwv-h" className="section-h2">Métricas de rendimiento</h2>
        <p className="section-lead">
          Estas son las métricas oficiales de Google que afectan tu posicionamiento en búsqueda
          y la experiencia del usuario. Medidas en vivo en tu URL.
        </p>
      </div>
      <div className="traffic-kpis">
        <CWVCard label="LCP — Largest Contentful Paint" value={cwv.lcp_ms} unit="ms" threshold={[2500, 4000]} />
        <CWVCard label="CLS — Cumulative Layout Shift" value={cwv.cls} unit="cls" threshold={[0.1, 0.25]} />
        <CWVCard label="FCP — First Contentful Paint" value={cwv.fcp_ms} unit="ms" threshold={[1800, 3000]} />
        <CWVCard label="TBT — Total Blocking Time" value={cwv.tbt_ms} unit="ms" threshold={[200, 600]} />
      </div>
    </section>
  );
}

/* ===================== AUDITORÍA TÉCNICA (datos REALES) ===================== */
function AuditoriaTecnicaSection({ a }) {
  if (!a || !a.ok) return null;
  const { fmtNum, fmtCompact } = window.ConvierteEngine;

  const w = a.whois || {};
  const c = a.crawl || {};
  const s = a.seo || {};
  const idx = a.indexed || {};
  const t = a.tech || {};
  const ssl = a.ssl || {};
  const sec = a.security || {};
  const ct = a.content || {};

  // 12 KPIs reales (sin estimaciones)
  const kpis = [];

  // 1. Páginas indexadas
  if (idx.indexed_pages !== null && idx.indexed_pages !== undefined) {
    kpis.push({ icon:"globe", label:"Páginas indexadas en Google", value:fmtCompact(idx.indexed_pages),
      hint: idx.method === 'bing-scrape' ? "Medido vía site: query en Bing (proxy a Google)." : "Estimado del sitemap.xml.",
      tone: idx.indexed_pages > 10 ? "success" : "warning" });
  }
  // 2. Edad del dominio
  if (w.domain_age_years) {
    kpis.push({ icon:"network", label:"Edad del dominio", value:w.domain_age_years, suffix:" años",
      hint: w.registrar ? `Registrado en ${w.registrar}.` : "Antigüedad real (WHOIS/RDAP).",
      tone: w.domain_age_years >= 3 ? "success" : (w.domain_age_years >= 1 ? "info" : "warning") });
  }
  // 3. Páginas crawleadas con OK
  kpis.push({ icon:"check", label:"Páginas auditadas", value:c.pages_crawled || 0,
    hint:`${c.pages_ok || 0} OK · ${c.broken_count || 0} con problemas. Profundidad máx ${c.max_depth || 0}.`,
    tone: (c.broken_count || 0) === 0 ? "success" : "warning" });
  // 4. Links rotos
  if ((c.broken_count || 0) > 0) {
    kpis.push({ icon:"alert", label:"Links rotos detectados", value:c.broken_count,
      hint:"Páginas internas que devuelven 4xx/5xx. Reducen UX y SEO.",
      tone: c.broken_count > 5 ? "danger" : "warning" });
  }
  // 5. SSL grade
  if (ssl.grade) {
    kpis.push({ icon:"shield", label:"Grado SSL", value:ssl.grade, suffix:"/A+",
      hint: ssl.cert_expiry ? `Certificado válido hasta ${ssl.cert_expiry}.` : "Evaluación SSL Labs.",
      tone: ssl.grade.startsWith('A') ? "success" : (ssl.grade === 'B' ? "info" : "warning") });
  } else if (ssl.has_https === false) {
    kpis.push({ icon:"alert", label:"HTTPS", value:"NO", hint:"Tu sitio no usa HTTPS — crítico.", tone:"danger" });
  }
  // 6. Security headers
  kpis.push({ icon:"shield", label:"Security headers", value:sec.grade || "F",
    suffix:` · ${sec.present_count || 0}/${(sec.present_count || 0) + (sec.missing_count || 0)}`,
    hint: (sec.missing_count || 0) > 0 ? `Faltan: ${(sec.missing || []).slice(0,2).map(m=>m.header).join(', ')}.` : "Todos presentes.",
    tone: (sec.grade || "F") === "A" ? "success" : ((sec.grade || "F") === "B" ? "info" : "warning") });
  // 7. CMS detectado
  if (t.cms) {
    kpis.push({ icon:"layers", label:"CMS detectado", value:t.cms, hint:"Plataforma identificada por fingerprinting.", tone:"info" });
  } else if (t.frameworks?.length) {
    kpis.push({ icon:"layers", label:"Framework principal", value:t.frameworks[0], hint:"Detectado por análisis del HTML.", tone:"info" });
  }
  // 8. E-commerce
  if (t.ecommerce) {
    kpis.push({ icon:"cursor", label:"Plataforma e-commerce", value:t.ecommerce, hint:"Tienda online detectada.", tone:"info" });
  }
  // 9. Schema.org
  if ((s.meta?.schema_count || 0) > 0) {
    kpis.push({ icon:"check", label:"Schema.org markup", value:s.meta.schema_count, suffix:" bloques",
      hint: s.meta.schema_types?.length ? `Tipos: ${s.meta.schema_types.slice(0,3).join(', ')}.` : "Datos estructurados detectados.",
      tone:"success" });
  } else {
    kpis.push({ icon:"alert", label:"Schema.org markup", value:"0", hint:"Sin datos estructurados — pierdes rich snippets en Google.", tone:"warning" });
  }
  // 10. Open Graph
  const ogOk = s.meta?.has_og_image && s.meta?.has_og_title && s.meta?.has_og_desc;
  kpis.push({ icon:"globe", label:"Open Graph completo", value: ogOk ? "Sí" : "No",
    suffix: " · " + (s.meta?.og_count || 0) + " tags",
    hint: ogOk ? "Bien previsualizado en redes sociales." : "Faltan tags og:image / og:title / og:description.",
    tone: ogOk ? "success" : "warning" });
  // 11. Sitemap
  kpis.push({ icon:"layers", label:"Sitemap.xml", value: s.sitemap?.exists ? `${s.sitemap.urls_count} URLs` : "No",
    hint: s.sitemap?.exists ? "Encontrado y procesado." : "Sin sitemap — Google rastrea peor.",
    tone: s.sitemap?.exists ? "success" : "warning" });
  // 12. Contenido
  kpis.push({ icon:"message", label:"Palabras en la home", value: fmtCompact(ct.word_count || 0),
    suffix: ` · ${ct.text_html_ratio || 0}%`,
    hint: ct.has_thin_content ? "Contenido muy corto (<250 palabras). Google penaliza thin content." : "Densidad de contenido aceptable.",
    tone: ct.has_thin_content ? "warning" : "success" });

  return (
    <section className="res-section container" aria-labelledby="audit-h">
      <div className="section-head">
        <span className="eyebrow">
          Auditoría técnica · datos reales del sitio
          <span style={{marginLeft:6, padding:"2px 7px", borderRadius:8, background:"var(--success-100)", color:"var(--success-700)", fontWeight:700}}>REAL</span>
        </span>
        <h2 id="audit-h" className="section-h2">Auditoría técnica profunda</h2>
        <p className="section-lead">
          Crawleamos hasta {c.pages_crawled || 0} páginas internas, analizamos tecnologías, verificamos
          SSL, security headers, Schema.org, sitemap y más. <strong>Todos los datos son reales</strong>,
          medidos directamente sobre tu sitio.
        </p>
      </div>

      <div className="traffic-kpis">
        {kpis.map((m, i) => (
          <div className="kpi-card" key={i}>
            <span className="kpi-icon" style={m.tone ? { color: TONE[m.tone].color, background: TONE[m.tone].bg } : null}>
              <Icon name={m.icon} size={18} />
            </span>
            <div className="kpi-value">{m.value}{m.suffix ? <span className="kpi-suffix">{m.suffix}</span> : null}</div>
            <div className="kpi-label">{m.label}</div>
            <p className="kpi-hint">{m.hint}</p>
          </div>
        ))}
      </div>

      {/* Tecnologías y analytics si hay */}
      {(t.analytics?.length > 0 || t.frameworks?.length > 0) && (
        <div style={{marginTop:24, padding:"20px 24px", background:"var(--white)", border:"1px solid var(--border-default)", borderRadius:"var(--radius-lg)"}}>
          <div style={{display:"grid", gridTemplateColumns:"repeat(auto-fit, minmax(220px, 1fr))", gap:24}}>
            {t.frameworks?.length > 0 && (
              <div>
                <div className="eyebrow" style={{marginBottom:8}}>Frameworks / librerías</div>
                <div style={{display:"flex", flexWrap:"wrap", gap:6}}>
                  {t.frameworks.map((f) => (
                    <span key={f} style={{padding:"4px 10px", borderRadius:8, background:"var(--info-100)", color:"var(--info-700)", fontSize:12, fontWeight:600}}>{f}</span>
                  ))}
                </div>
              </div>
            )}
            {t.analytics?.length > 0 && (
              <div>
                <div className="eyebrow" style={{marginBottom:8}}>Analytics / pixels</div>
                <div style={{display:"flex", flexWrap:"wrap", gap:6}}>
                  {t.analytics.map((a) => (
                    <span key={a} style={{padding:"4px 10px", borderRadius:8, background:"var(--accent-100)", color:"var(--accent-700)", fontSize:12, fontWeight:600}}>{a}</span>
                  ))}
                </div>
              </div>
            )}
            {t.hosting && (
              <div>
                <div className="eyebrow" style={{marginBottom:8}}>Hosting / CDN</div>
                <span style={{padding:"4px 10px", borderRadius:8, background:"var(--navy-100)", color:"var(--navy-700)", fontSize:12, fontWeight:600}}>{t.hosting}</span>
              </div>
            )}
          </div>
        </div>
      )}

      <p className="traffic-foot">
        <Icon name="check" size={14} stroke={2} />
        <strong>100% datos reales</strong> — sin estimaciones. Auditamos tu sitio en vivo
        (crawler propio + WHOIS + SSL Labs + Bing index + análisis HTML).
        Las métricas de backlinks externos NO se incluyen aquí (requieren Ahrefs/Semrush).
      </p>
    </section>
  );
}

function CategoryGrid({ categories }) {
  return (
    <div className="cat-grid">
      {categories.map((c, i) => (
        <div className="cat-card" key={c.key}>
          <div className="cat-head">
            <span className="cat-icon" style={{ color: TONE[c.band.tone].color, background: TONE[c.band.tone].bg }}>
              <Icon name={c.icon} size={20} />
            </span>
            <span className="cat-score" style={{ color: "var(--navy-900)" }}>{c.score}</span>
          </div>
          <div className="cat-name">{c.label}</div>
          <CatBar score={c.score} tone={c.band.tone} delay={i * 70} />
        </div>
      ))}
    </div>
  );
}

function ProblemCard({ p, index, locked, onUnlock }) {
  return (
    <article className={"problem" + (locked ? " problem--locked" : "")}>
      <div className="problem-top">
        <SeverityBadge severity={p.severity} />
        <span className="problem-cat">{p.catLabel}</span>
        {!locked && <span className="quickwin"><Icon name="bolt" size={13} stroke={2} /> Quick win</span>}
      </div>
      <h3 className="problem-title">{p.problema}</h3>
      <div className="problem-impact">
        <span className="impact-tag" aria-hidden="true"><Icon name="trending" size={14} stroke={2} /></span>
        <p><strong>Impacto en ventas:</strong> {p.impacto}</p>
      </div>

      <div className="solution-wrap">
        <div className="solution-label">
          <Icon name={locked ? "lock" : "check"} size={15} stroke={2} />
          {locked ? "Solución bloqueada" : "Cómo solucionarlo"}
        </div>
        {locked ? (
          <div className="solution-locked">
            <p className="solution-text solution-text--blur" aria-hidden="true">{p.solucion}</p>
            <div className="lock-overlay">
              <span className="lock-badge" aria-hidden="true"><Icon name="lock" size={18} stroke={2} /></span>
              <button className="btn btn-accent btn-sm" onClick={onUnlock}>Desbloquea las soluciones completas</button>
            </div>
          </div>
        ) : (
          <p className="solution-text">{p.solucion}</p>
        )}
      </div>
    </article>
  );
}

/* Banner de verificación: muestra cuándo se hizo el análisis y permite
   comparar con pagespeed.web.dev en una pestaña nueva. */
function VerificacionBanner({ dx, onReanalizar }) {
  const [reanalizando, setReanalizando] = useState(false);
  const fetched = dx.meta?.fetched_at ? new Date(dx.meta.fetched_at) : null;
  const ageMin = fetched ? Math.round((Date.now() - fetched.getTime()) / 60000) : null;
  const psWebUrl = `https://pagespeed.web.dev/analysis?url=${encodeURIComponent(dx.url)}&form_factor=${dx.device}`;

  const handleRe = async () => {
    setReanalizando(true);
    await onReanalizar();
    setReanalizando(false);
  };

  return (
    <div className="container" style={{paddingTop: 16, paddingBottom: 0}}>
      <div style={{
        background: "var(--info-100)",
        border: "1px solid var(--info-500)",
        borderRadius: 12, padding: "14px 18px",
        display: "flex", flexWrap: "wrap", alignItems: "center", justifyContent: "space-between",
        gap: 12,
      }}>
        <div style={{display: "flex", alignItems: "flex-start", gap: 10, flexWrap: "wrap", flex: "1 1 280px", minWidth: 0}}>
          <Icon name="check" size={20} stroke={2} style={{color: "var(--info-700)", flexShrink: 0, marginTop: 2}} />
          <div style={{fontSize: 13, color: "var(--info-700)", lineHeight: 1.5, minWidth: 0}}>
            <strong>Datos reales medidos por Google PageSpeed Insights.</strong>
            {fetched && <> Analizado {ageMin < 1 ? "ahora mismo" : `hace ${ageMin} min`} · {fetched.toLocaleString('es-LA')}</>}
            <br />
            <span style={{fontSize: 12, opacity: 0.85}}>
              Lighthouse tiene ±5-10 puntos de variabilidad normal entre runs.
            </span>
          </div>
        </div>
        <div style={{display: "flex", gap: 8, flexWrap: "wrap"}}>
          <a href={psWebUrl} target="_blank" rel="noopener noreferrer"
             className="btn btn-secondary btn-sm" title="Abrir en pagespeed.web.dev para comparar">
            Verificar en pagespeed.web.dev <Icon name="arrow" size={14} stroke={2} />
          </a>
          <button className="btn btn-primary btn-sm" onClick={handleRe} disabled={reanalizando}>
            {reanalizando ? "Re-analizando…" : "Re-analizar"}
          </button>
        </div>
      </div>
    </div>
  );
}

function Results({ dx, onReset, onReanalizar }) {
  const free = dx.problems.slice(0, dx.freeCount);
  const locked = dx.problems.slice(dx.freeCount);
  const lockedCount = locked.length;

  return (
    <main id="resultados" className="results">
      <VerificacionBanner dx={dx} onReanalizar={onReanalizar} />
      <section className="res-hero container" aria-labelledby="res-h">
        <div className="res-hero-left">
          <div className="res-target">
            <span className="res-target-label">Diagnóstico para</span>
            <a href={normalizeUrl(dx.url)} target="_blank" rel="noopener noreferrer" className="res-target-url">
              {prettyHost(dx.url)} <Icon name="arrow" size={14} stroke={2} />
            </a>
            <span className="res-device">{dx.device === "mobile" ? "Móvil" : "Escritorio"}</span>
          </div>
          <h1 id="res-h" className="res-h1">Tu <span className="hl">diagnóstico de conversión</span></h1>
          <p className="res-sub">
            Detectamos <strong>{dx.problems.length} problemas</strong> que están frenando tus ventas.
            Aquí están tus quick wins gratis y la ruta para arreglar el resto.
          </p>
          <div className="res-actions">
            <button className="btn btn-primary btn-lg" onClick={() => scrollToId("captura")}>Quiero que lo arreglen por mí</button>
            <button className="btn btn-secondary btn-lg" onClick={() => scrollToId("captura")}>
              <Icon name="download" size={18} /> Recibir reporte completo
            </button>
          </div>
        </div>

        <div className="res-hero-right">
          <div className="score-card">
            <RingGauge score={dx.overall} tone={dx.band.tone} label={dx.band.label} />
            <PotentialCard pct={dx.potentialPct} />
          </div>
        </div>
      </section>

      {/* Core Web Vitals reales */}
      {dx.meta?.psi_ok && <CoreWebVitalsSection cwv={dx.core_web_vitals || dx.meta?.core_web_vitals} />}

      {/* Auditoría técnica REAL (sustituye a la sección Tráfico antigua) */}
      {dx.auditoria?.ok && <AuditoriaTecnicaSection a={dx.auditoria} />}

      {/* Categorías */}
      <section className="res-section container" aria-labelledby="cat-h">
        <div className="section-head">
          <span className="eyebrow">Optimización de conversión por área</span>
          <h2 id="cat-h" className="section-h2">Puntaje por categoría</h2>
        </div>
        <CategoryGrid categories={dx.categories} />
      </section>

      {/* Problemas detectados */}
      <section className="res-section container" aria-labelledby="prob-h">
        <div className="section-head">
          <span className="eyebrow">Problemas de tu página web</span>
          <h2 id="prob-h" className="section-h2">Problemas detectados y cómo resolverlos</h2>
          <p className="section-lead">
            Las <strong>3 primeras</strong> son quick wins con la solución completa. Las {lockedCount} restantes
            se desbloquean con el reporte completo.
          </p>
        </div>

        <div className="problem-list">
          {free.map((p, i) => (
            <ProblemCard key={p.id} p={p} index={i} locked={false} />
          ))}
          {locked.map((p, i) => (
            <ProblemCard key={p.id} p={p} index={i} locked={true} onUnlock={() => scrollToId("captura")} />
          ))}
        </div>

        <div className="reanalyze">
          <button className="btn btn-ghost btn-sm" onClick={onReset}>← Analizar otra página</button>
        </div>
      </section>

      <Methodology categories={dx.categories} />
    </main>
  );
}

function Methodology({ categories }) {
  return (
    <section className="res-section container methodology" aria-labelledby="method-h">
      <div className="section-head">
        <span className="eyebrow">Cómo se calcula este puntaje</span>
        <h2 id="method-h" className="section-h2">¿En qué se basa tu diagnóstico?</h2>
        <p className="section-lead">
          Combinamos <strong>Google PageSpeed Insights</strong> (Performance, Accessibility,
          Best Practices, SEO, Core Web Vitals reales) con análisis del HTML de tu página
          (titular, CTAs, formularios, jerarquía). Cada categoría pondera por su impacto en conversión.
        </p>
      </div>

      <div className="method-grid">
        {categories.map((c) => (
          <div className="method-card" key={c.key}>
            <div className="method-head">
              <span className="method-icon" aria-hidden="true"><Icon name={c.icon} size={18} /></span>
              <h3 className="method-name">{c.label}</h3>
              <span className="method-weight">
                <Icon name="trending" size={13} stroke={2} /> Impacto {c.weight >= 1.3 ? "alto" : c.weight >= 1.1 ? "medio" : "base"}
              </span>
            </div>
            <ul className="method-signals">
              {c.signals.map((s, i) => (
                <li key={i}><Icon name="check" size={14} stroke={2.25} /> {s}</li>
              ))}
            </ul>
          </div>
        ))}
      </div>

      <div className="method-note">
        <span className="method-note-icon" aria-hidden="true"><Icon name="search" size={18} stroke={1.75} /></span>
        <p>
          <strong>Sobre los datos:</strong> Performance, Accessibility, Best Practices, SEO y Core Web Vitals
          son <strong>reales</strong> de Google PageSpeed Insights API. Tráfico orgánico y backlinks son
          <strong> estimaciones</strong> derivadas — la versión completa los mide con Ahrefs.
        </p>
      </div>
    </section>
  );
}

/* ===================== CAPTURA ===================== */
function Capture({ url, dx }) {
  const [data, setData] = useState({ nombre: "", correo: "", sitio: url || "", objetivo: "", mensaje: "" });
  const [touched, setTouched] = useState({});
  const [sent, setSent] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [serverErr, setServerErr] = useState("");

  useEffect(() => { setData((d) => ({ ...d, sitio: d.sitio || url || "" })); }, [url]);

  const errors = {
    nombre: !data.nombre.trim() ? "Ingresa tu nombre." : "",
    correo: !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.correo) ? "Ingresa un correo válido." : "",
    objetivo: !data.objetivo ? "Elige una opción." : "",
  };
  const valid = !errors.nombre && !errors.correo && !errors.objetivo;

  const set = (k) => (e) => setData((d) => ({ ...d, [k]: e.target.value }));
  const blur = (k) => () => setTouched((t) => ({ ...t, [k]: true }));

  const submit = async () => {
    setTouched({ nombre: true, correo: true, objetivo: true });
    if (!valid) return;
    setSubmitting(true);
    setServerErr("");
    try {
      // Snapshot del diagnóstico actual para guardarlo con el lead
      const payload = {
        nombre: data.nombre.trim(),
        correo: data.correo.trim().toLowerCase(),
        sitio: data.sitio.trim() || null,
        objetivo: data.objetivo,
        mensaje: data.mensaje.trim() || null,
      };
      if (dx) {
        payload.url_analizada = dx.url;
        payload.dispositivo = dx.device;
        payload.score_general = dx.overall;
        payload.scores_categorias = Object.fromEntries(dx.categories.map(c => [c.key, c.score]));
        payload.core_web_vitals = dx.core_web_vitals || dx.meta?.core_web_vitals || null;
        payload.problemas_count = dx.problems.length;
        payload.snapshot_diagnostico = {
          overall: dx.overall,
          potentialPct: dx.potentialPct,
          problems: dx.problems,
          categories: dx.categories.map(c => ({ key: c.key, label: c.label, score: c.score })),
        };
      }
      await window.ConvierteEngine.submitLead(payload);
      setSent(true);
    } catch (e) {
      setServerErr(e.message || "No pudimos enviar tu solicitud. Intenta de nuevo.");
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <section id="captura" className="capture section-inverse" aria-labelledby="cap-h">
      <div className="container capture-inner">
        <div className="capture-copy">
          <span className="eyebrow">Implementamos las mejoras por ti</span>
          <h2 id="cap-h" className="capture-h2">¿Quieres que solucionemos<br className="hide-mobile" /> estos problemas por ti?</h2>
          <p className="capture-lead">
            En Sell-U LATAM implementamos las mejoras y aumentamos tu tasa de conversión.
            Déjanos tus datos y te enviamos el <strong>reporte completo</strong> + una propuesta a tu medida.
          </p>
          <ul className="capture-points">
            <li><Icon name="check" size={16} stroke={2} /> Reporte completo con todas las soluciones</li>
            <li><Icon name="check" size={16} stroke={2} /> Plan de mejoras priorizado por impacto</li>
            <li><Icon name="check" size={16} stroke={2} /> Propuesta sin compromiso en menos de 24h</li>
          </ul>
        </div>

        <div className="capture-form-wrap">
          {sent ? (
            <div className="capture-success" role="status" aria-live="polite">
              <span className="success-check" aria-hidden="true"><Icon name="check" size={30} stroke={2.5} /></span>
              <h3 className="success-h">¡Listo! Te contactaremos en menos de 24h</h3>
              <p>Recibimos tu solicitud para <strong>{prettyHost(data.sitio)}</strong>. Nuestro equipo te enviará el reporte completo y propuesta a <strong>{data.correo}</strong>.</p>
              <button className="btn btn-on-dark btn-sm" onClick={() => { setSent(false); setData({nombre:"",correo:"",sitio:url||"",objetivo:"",mensaje:""}); }}>Enviar otra solicitud</button>
            </div>
          ) : (
            <div className="capture-form" role="group" aria-labelledby="cap-form-h">
              <div id="cap-form-h" className="capture-form-title">Recibe tu reporte completo</div>

              {/* Honeypot anti-bot (oculto) */}
              <input type="text" name="website" tabIndex="-1" autoComplete="off"
                style={{position:"absolute", left:"-9999px", opacity:0, height:0, width:0}}
                onChange={() => {}} />

              <Field label="Nombre" id="f-nombre" error={touched.nombre && errors.nombre}>
                <input id="f-nombre" className={"inp" + (touched.nombre && errors.nombre ? " inp--err" : "")}
                  placeholder="María González" value={data.nombre}
                  onChange={set("nombre")} onBlur={blur("nombre")} autoComplete="name" />
              </Field>

              <Field label="Correo" id="f-correo" error={touched.correo && errors.correo}>
                <input id="f-correo" type="email" className={"inp" + (touched.correo && errors.correo ? " inp--err" : "")}
                  placeholder="maria@empresa.com" value={data.correo}
                  onChange={set("correo")} onBlur={blur("correo")} autoComplete="email" />
              </Field>

              <Field label="URL del sitio" id="f-sitio">
                <input id="f-sitio" className="inp inp--mono" value={data.sitio}
                  onChange={set("sitio")} placeholder="tu-sitio.com" />
              </Field>

              <Field label="¿Qué buscas?" id="f-obj" error={touched.objetivo && errors.objetivo}>
                <select id="f-obj" className={"inp" + (touched.objetivo && errors.objetivo ? " inp--err" : "")}
                  value={data.objetivo} onChange={set("objetivo")} onBlur={blur("objetivo")}>
                  <option value="">Selecciona una opción…</option>
                  <option value="ventas">Más ventas</option>
                  <option value="registros">Más registros</option>
                  <option value="reservas">Más reservas</option>
                  <option value="otro">Otro</option>
                </select>
              </Field>

              <Field label="Mensaje (opcional)" id="f-msg">
                <textarea id="f-msg" className="inp" rows="2" value={data.mensaje}
                  onChange={set("mensaje")} placeholder="Cuéntanos más sobre tu negocio…"
                  style={{resize:"vertical", minHeight:60}} />
              </Field>

              {serverErr && (
                <div className="field-err" role="alert" style={{marginBottom:12}}>{serverErr}</div>
              )}

              <button className="btn btn-accent btn-lg capture-submit" onClick={submit} disabled={submitting}>
                {submitting ? "Enviando…" : <>Enviar y recibir mi reporte <Icon name="arrow" size={18} stroke={2} /></>}
              </button>
              <p className="capture-fine">Sin compromiso. Usamos tus datos solo para enviarte el reporte y la propuesta.</p>
            </div>
          )}
        </div>
      </div>
    </section>
  );
}

function Field({ label, id, error, children }) {
  return (
    <div className="field">
      <label htmlFor={id}>{label}</label>
      {children}
      {error ? <span className="field-err" role="alert">{error}</span> : null}
    </div>
  );
}

/* ===================== APP ===================== */
function App() {
  const [phase, setPhase] = useState("hero"); // hero | analyzing | results
  const [url, setUrl] = useState("");
  const [device, setDevice] = useState("desktop");
  const [error, setError] = useState("");
  const [dx, setDx] = useState(null);
  const [analyzeError, setAnalyzeError] = useState("");
  const [analyzeDone, setAnalyzeDone] = useState(false);

  const analyze = async (noCacheArg) => {
    // Defensa: si esta función se conecta directamente a un onClick, React
    // pasa el SyntheticEvent como primer argumento. Solo aceptamos `true`
    // literal (desde reanalizar()) — cualquier otra cosa es falsy.
    const noCache = noCacheArg === true;

    if (!isValidUrl(url)) {
      setError("Ingresa una URL válida, por ejemplo: tu-sitio.com");
      return;
    }
    setError("");
    setAnalyzeError("");
    setAnalyzeDone(false);
    if (!noCache) {
      setPhase("analyzing");
      window.scrollTo({ top: 0, behavior: "auto" });
    }

    try {
      const diag = await window.ConvierteEngine.fetchDiagnosis(normalizeUrl(url), device, noCache);
      // Mover core_web_vitals al meta también para que el componente CWV los encuentre
      if (diag.meta) diag.meta.core_web_vitals = diag.core_web_vitals;
      setDx(diag);
      setAnalyzeDone(true);
      if (!noCache) {
        // pequeña pausa visual para que termine la animación del progreso
        setTimeout(() => {
          setPhase("results");
          window.scrollTo({ top: 0, behavior: "auto" });
        }, 350);
      }
    } catch (e) {
      const msg = (e && typeof e.message === "string") ? e.message : "Error inesperado.";
      if (noCache) {
        // Si falló el re-análisis, no romper la UI — mostrar alerta y seguir
        alert("No pudimos re-analizar: " + msg);
      } else {
        setAnalyzeError(msg);
      }
    }
  };

  /** Re-analizar invalidando el cache del backend. Se queda en la vista de resultados. */
  const reanalizar = async () => analyze(true);

  const reset = () => {
    setPhase("hero");
    setDx(null);
    setAnalyzeError("");
    setAnalyzeDone(false);
    window.scrollTo({ top: 0, behavior: "auto" });
  };

  return (
    <div id="top" data-screen-label={"Convertidor Sell-U · " + phase}>
      {/* Botón flotante para reset/volver al inicio del convertidor */}
      {phase !== "hero" && (
        <div style={{
          position:"sticky", top:0, zIndex:40, background:"rgba(251,250,247,.92)",
          backdropFilter:"blur(8px)", borderBottom:"1px solid var(--border-default)",
          padding:"10px 0", width:"100%", maxWidth:"100%"
        }}>
          <div className="container" style={{display:"flex", justifyContent:"space-between", alignItems:"center", gap:12, flexWrap:"wrap"}}>
            <button className="btn btn-ghost btn-sm" onClick={reset}>← Analizar otra página</button>
            {phase === "results" && (
              <button className="btn btn-primary btn-sm" onClick={() => scrollToId("captura")}>Quiero que lo arreglen</button>
            )}
          </div>
        </div>
      )}

      {phase === "hero" && (
        <main>
          <Hero url={url} setUrl={(v) => { setUrl(v); if (error) setError(""); }}
            device={device} setDevice={setDevice} onAnalyze={analyze} error={error} />
        </main>
      )}

      {phase === "analyzing" && (
        <main><Analyzing url={url} device={device} done={analyzeDone} error={analyzeError} onRetry={reset} /></main>
      )}

      {phase === "results" && dx && (
        <>
          <Results dx={dx} onReset={reset} onReanalizar={reanalizar} />
          <Capture url={normalizeUrl(url)} dx={dx} />
        </>
      )}
    </div>
  );
}

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