// COBERTURA DE FIBRA ÓPTICA — landing + módulo de verificación (modal 3 pasos).
// Consume la API pública de XP (CORS refleja nuestro origen). Si la API viene
// vacía, cae a una copia local del CSV (cobertura-zonas.json). El lead se envía
// a XP /lead; si falla, respaldo a nuestra propia /api/leads/landing.
const { useState, useEffect, useRef, useMemo } = React;

const XP = "https://xp.balderacomunicaciones.com/api/coverage/public";
const WA = "https://wa.me/18492209137";
const CLARO_RED = "#DA291C";

// Iconos SVG (sin emojis). currentColor para heredar el color del botón.
const Ic = {
  bolt: (s = 15) => (<svg width={s} height={s} viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M13 2 4 14h6l-1 8 9-12h-6z" /></svg>),
  signal: (s = 18) => (<svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true"><path d="M4.5 12.5a8 8 0 0 1 15 0" /><path d="M7.5 13.8a4.6 4.6 0 0 1 9 0" /><circle cx="12" cy="16" r="1.5" fill="currentColor" stroke="none" /></svg>),
  pin: (s = 16) => (<svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M12 21s-7-6.3-7-11a7 7 0 0 1 14 0c0 4.7-7 11-7 11z" /><circle cx="12" cy="10" r="2.5" /></svg>),
  chat: (s = 16) => (<svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M21 11.5a8.4 8.4 0 0 1-12.3 7.4L3 21l2.2-5.6A8.4 8.4 0 1 1 21 11.5z" /></svg>),
  check: (s = 16) => (<svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.6" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12" /></svg>),
  checkCircle: (s = 18) => (<svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M22 11.1V12a10 10 0 1 1-5.9-9.1" /><polyline points="22 4 12 14.5 9 11.5" /></svg>),
  building: (s = 16) => (<svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><rect x="5" y="3" width="14" height="18" rx="1.5" /><path d="M9 7h2M13 7h2M9 11h2M13 11h2M9 15h2M13 15h2" /></svg>),
  nav: (s = 16) => (<svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><polygon points="3 11 22 2 13 21 11 13 3 11" /></svg>),
  calendar: (s = 15) => (<svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><rect x="3" y="4.5" width="18" height="17" rx="2.5" /><path d="M3 9.5h18M8 2.5v4M16 2.5v4" /></svg>),
  tag: (s = 17) => (<svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M3 11.5V4a1 1 0 0 1 1-1h7.5a2 2 0 0 1 1.4.6l7.5 7.5a2 2 0 0 1 0 2.8l-7 7a2 2 0 0 1-2.8 0L3.6 13.4A2 2 0 0 1 3 11.5z" /><circle cx="7.5" cy="7.5" r="1.3" fill="currentColor" /></svg>),
  phone: (s = 17) => (<svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><rect x="6.5" y="2.5" width="11" height="19" rx="2.5" /><path d="M11 18.5h2" /></svg>),
  wa: (s = 18) => (<svg width={s} height={s} viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M17.47 14.38c-.3-.15-1.76-.87-2.03-.97-.27-.1-.47-.15-.67.15-.2.3-.77.97-.94 1.16-.17.2-.35.22-.64.08-.3-.15-1.26-.47-2.4-1.48-.88-.79-1.48-1.76-1.65-2.06-.17-.3-.02-.46.13-.6.13-.14.3-.35.45-.52.15-.18.2-.3.3-.5.1-.2.05-.37-.02-.52-.08-.15-.67-1.61-.92-2.21-.24-.58-.49-.5-.67-.51h-.57c-.2 0-.52.07-.8.37-.27.3-1.04 1.02-1.04 2.48 0 1.46 1.07 2.88 1.22 3.07.15.2 2.1 3.2 5.08 4.49.71.3 1.26.49 1.7.62.7.23 1.36.2 1.87.12.57-.08 1.76-.72 2-1.41.25-.7.25-1.29.18-1.42-.08-.12-.27-.2-.57-.35M12.05 21.78a9.87 9.87 0 0 1-5.03-1.38l-.36-.21-3.74.98 1-3.65-.24-.37a9.86 9.86 0 0 1-1.51-5.26C2.18 6.44 6.6 2 12.05 2c2.64 0 5.12 1.03 6.99 2.9a9.83 9.83 0 0 1 2.89 6.99c0 5.45-4.44 9.89-9.88 9.89M20.52 3.45A11.78 11.78 0 0 0 12.05 0C5.46 0 .1 5.36.1 11.89c0 2.1.55 4.14 1.6 5.95L0 24l6.3-1.65a11.88 11.88 0 0 0 5.69 1.45h.01c6.58 0 11.94-5.36 11.94-11.89 0-3.18-1.24-6.17-3.42-8.46" /></svg>),
};

// ─── Capa de datos (API XP con fallback local) ──────────────────────────────
let _zones = null;
async function zones() {
  if (_zones) return _zones;
  try { _zones = await fetch(window.balUrl("/cobertura-zonas.json")).then((r) => r.json()); }
  catch { _zones = { provincias: [], ciudades: {}, sectores: {} }; }
  return _zones;
}
async function apiGet(path) {
  try { const r = await fetch(XP + path); if (!r.ok) return null; return await r.json(); }
  catch { return null; }
}
// ¿La API tiene data cargada? (se resuelve una vez)
let _apiLive = null;
async function apiLive() {
  if (_apiLive !== null) return _apiLive;
  const d = await apiGet("/provincias");
  _apiLive = !!(d && d.items && d.items.length);
  return _apiLive;
}
async function getProvincias() {
  if (await apiLive()) return (await apiGet("/provincias")).items;
  return (await zones()).provincias;
}
async function getCiudades(prov) {
  if (await apiLive()) { const d = await apiGet("/ciudades?provincia=" + encodeURIComponent(prov)); return (d && d.items) || []; }
  return (await zones()).ciudades[prov] || [];
}
async function getSectores(prov, ciudad) {
  if (await apiLive()) { const d = await apiGet(`/sectores?provincia=${encodeURIComponent(prov)}&ciudad=${encodeURIComponent(ciudad)}`); return (d && d.items) || []; }
  return (await zones()).sectores[prov + "||" + ciudad] || [];
}
async function checkCoverage(prov, ciudad, sector) {
  if (await apiLive()) {
    const d = await apiGet(`/check?provincia=${encodeURIComponent(prov)}&ciudad=${encodeURIComponent(ciudad)}&sector=${encodeURIComponent(sector)}`);
    if (d) return d;
  }
  const list = (await zones()).sectores[prov + "||" + ciudad] || [];
  const hit = list.find((s) => s.sector === sector);
  return hit
    ? { covered: true, zone: { provincia: prov, ciudad, sector, tipoCobertura: hit.tipoCobertura } }
    : { covered: false, zone: null };
}
async function postLead(body) {
  // Proxy propio (servidor-a-servidor): reenvía a XP + guarda en nuestro CRM.
  // Es del mismo origen, así que no hay problema de CORS.
  try {
    const r = await fetch(window.balUrl("/api/cobertura/lead"), {
      method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body),
    });
    const d = await r.json().catch(() => ({}));
    if (r.ok && d.ok) return { ok: true, leadId: d.leadId };
    return { ok: false, error: d.error || ("HTTP " + r.status) };
  } catch (e) { return { ok: false, error: e.message }; }
}

// ─── Componentes pequeños ────────────────────────────────────────────────────
// Logo oficial de Claro (SVG en /assets). Imagen self-contained → CSP-safe.
function ClaroMark({ size = 22 }) {
  return (
    <img src={balUrl("/assets/claro-logo.svg")} alt="Claro" width={size} height={size}
      style={{ display: "block", flexShrink: 0 }} />
  );
}
const Spinner = ({ light }) => (
  <span style={{ display: "inline-block", width: 16, height: 16, border: "2px solid " + (light ? "rgba(255,255,255,0.4)" : "var(--line)"), borderTopColor: light ? "#fff" : CLARO_RED, borderRadius: "50%", animation: "cobSpin 0.6s linear infinite" }} />
);

// ─── Mapa de RD (silueta SVG self-contained — no la bloquea ningún filtro) ───
const DR_VIEW = "0 0 400 273";
const DR_BB = { minx: -72.00827, maxx: -68.31436, miny: 17.46775, maxy: 19.94108, W: 400, pad: 8 };
const DR_SX = (DR_BB.W - 2 * DR_BB.pad) / (DR_BB.maxx - DR_BB.minx);
const drX = (lon) => DR_BB.pad + (lon - DR_BB.minx) * DR_SX;
const drY = (lat) => DR_BB.pad + (DR_BB.maxy - lat) * DR_SX;
const DR_PATH = "M114.9 8.0 L 102.9 14.0 L 89.1 17.1 L 76.9 16.8 L 61.8 10.9 L 52.9 10.5 L 46.7 12.4 L 42.6 13.7 L 41.0 17.5 L 33.9 21.6 L 32.6 28.0 L 36.5 31.1 L 33.8 33.2 L 33.7 33.8 L 34.3 33.9 L 33.8 34.3 L 34.4 34.6 L 34.2 34.9 L 34.2 35.3 L 35.3 36.3 L 35.9 36.8 L 35.3 40.0 L 37.3 46.7 L 38.8 50.5 L 41.3 52.6 L 41.4 61.4 L 40.1 68.4 L 37.2 67.9 L 31.2 71.4 L 33.6 75.4 L 35.9 77.0 L 39.1 78.8 L 43.3 81.4 L 47.6 83.6 L 46.4 84.9 L 45.8 87.4 L 45.6 88.7 L 45.9 89.8 L 46.2 90.6 L 45.6 91.8 L 44.8 93.8 L 43.1 93.7 L 41.9 93.7 L 40.3 94.5 L 40.5 96.7 L 39.8 98.0 L 38.0 99.1 L 37.6 100.3 L 35.0 100.9 L 33.3 103.1 L 31.5 104.2 L 31.5 104.7 L 30.5 105.8 L 29.9 106.4 L 29.1 106.0 L 26.6 105.4 L 25.3 105.9 L 24.3 107.2 L 23.1 108.1 L 22.6 108.5 L 21.4 110.3 L 21.5 111.9 L 23.4 111.3 L 23.4 109.6 L 25.1 110.4 L 25.8 108.9 L 27.3 109.2 L 28.2 109.4 L 29.4 110.6 L 30.6 110.2 L 32.9 115.4 L 34.1 115.9 L 35.0 116.6 L 37.2 118.6 L 37.8 126.2 L 32.6 135.9 L 30.8 138.2 L 28.9 138.5 L 28.7 140.9 L 24.6 143.9 L 21.3 144.4 L 16.9 143.7 L 11.5 142.2 L 8.8 145.6 L 12.5 147.0 L 21.4 157.4 L 18.8 162.2 L 29.3 169.0 L 40.2 174.2 L 34.1 186.1 L 33.0 186.9 L 32.4 188.8 L 31.7 189.6 L 31.5 192.1 L 33.2 192.6 L 33.3 193.2 L 33.7 194.0 L 34.1 194.5 L 35.1 194.3 L 35.0 195.3 L 35.4 195.6 L 35.4 196.8 L 35.1 198.0 L 35.4 199.5 L 35.8 200.5 L 36.4 201.6 L 35.8 202.9 L 35.4 204.4 L 34.1 206.1 L 39.8 212.0 L 41.5 220.8 L 38.4 233.1 L 56.1 237.4 L 69.2 251.6 L 81.4 232.6 L 105.3 203.6 L 104.6 182.6 L 115.3 185.5 L 136.5 176.3 L 153.7 169.5 L 155.6 183.8 L 163.9 191.5 L 201.6 186.3 L 235.2 163.6 L 257.1 170.9 L 268.8 169.5 L 297.7 164.9 L 316.9 170.4 L 321.8 175.6 L 323.3 169.3 L 336.1 174.1 L 343.2 187.4 L 344.1 195.8 L 357.5 197.5 L 366.7 198.6 L 360.5 191.4 L 359.9 181.1 L 362.0 171.8 L 375.7 175.0 L 383.2 163.2 L 391.3 150.1 L 387.4 141.2 L 367.9 127.4 L 341.2 106.3 L 332.5 103.8 L 323.3 101.8 L 318.2 105.9 L 309.9 103.0 L 303.5 101.2 L 299.3 101.5 L 291.2 101.1 L 282.6 98.6 L 278.0 94.2 L 269.1 95.6 L 256.9 96.2 L 255.6 90.7 L 258.0 83.8 L 270.2 85.5 L 279.4 86.6 L 289.2 87.0 L 298.8 86.6 L 304.3 79.9 L 302.1 73.6 L 296.5 73.5 L 297.0 67.1 L 291.5 70.1 L 283.3 73.5 L 274.0 70.9 L 260.4 72.0 L 252.7 74.6 L 242.5 74.6 L 233.6 67.0 L 230.8 58.6 L 228.0 48.8 L 227.2 38.1 L 213.5 34.3 L 205.8 39.5 L 190.0 38.5 L 177.1 27.3 L 168.9 23.4 L 158.4 25.3 L 146.8 22.5 L 133.5 13.4 L 128.7 10.3 L 117.5 10.3 L 114.9 8.0 Z M58.1 248.7 L 56.5 250.3 L 54.4 254.8 L 54.4 257.4 L 57.8 258.4 L 63.0 257.0 L 65.2 252.5 L 63.2 252.0 L 61.9 250.5 L 58.1 248.7 Z M46.3 262.5 L 45.7 263.1 L 45.1 263.9 L 45.4 264.9 L 46.4 265.1 L 46.9 264.6 L 46.6 263.7 L 46.3 262.5 Z";
// Carga Leaflet (JS + CSS) bajo demanda desde unpkg (permitido por la CSP). Una sola vez.
let _leafletPromise = null;
function loadLeaflet() {
  if (typeof window !== "undefined" && window.L) return Promise.resolve(window.L);
  if (_leafletPromise) return _leafletPromise;
  _leafletPromise = new Promise((resolve, reject) => {
    const css = document.createElement("link");
    css.rel = "stylesheet";
    css.href = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.css";
    document.head.appendChild(css);
    const s = document.createElement("script");
    s.src = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.js";
    s.async = true;
    s.onload = () => resolve(window.L);
    s.onerror = reject;
    document.head.appendChild(s);
  });
  return _leafletPromise;
}

const DR_CENTER = { lat: 18.735, lng: -70.162 };  // centro aprox. de RD (vista inicial)

// Zonas con cobertura (centroides precomputados). Se cargan una sola vez.
let _zonesPromise = null;
function loadCoverageZones() {
  if (_zonesPromise) return _zonesPromise;
  _zonesPromise = fetch("/cobertura-geo.json").then((r) => (r.ok ? r.json() : [])).catch(() => []);
  return _zonesPromise;
}

// Mapa interactivo (Leaflet + tiles OSM), SIEMPRE visible. Sin GPS arranca con vista
// de RD y un pin que el usuario puede tocar/arrastrar a su zona; al soltarlo o tocar
// el mapa, re-geocodifica. Si Leaflet falla → silueta SVG de RD.
function LocationMap({ coords, onPick }) {
  const elRef = useRef(null);
  const mapRef = useRef(null);
  const markerRef = useRef(null);
  const [failed, setFailed] = useState(false);

  useEffect(() => {
    let cancelled = false;
    loadLeaflet().then((L) => {
      if (cancelled || !elRef.current) return;
      if (!mapRef.current) {
        const start = coords || DR_CENTER;
        const map = L.map(elRef.current, { zoomControl: true, scrollWheelZoom: false, attributionControl: true })
          .setView([start.lat, start.lng], coords ? 16 : 8);
        L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
          maxZoom: 19, attribution: "&copy; OpenStreetMap",
        }).addTo(map);

        // Zonas con cobertura — círculos verdes translúcidos.
        loadCoverageZones().then((zones) => {
          if (cancelled || !mapRef.current || !Array.isArray(zones)) return;
          const layer = L.layerGroup();
          zones.forEach((z) => {
            if (!Number.isFinite(z.lat) || !Number.isFinite(z.lng)) return;
            const radius = Math.min(6000, Math.max(1400, 1000 + Math.sqrt(z.n || 1) * 480));
            L.circle([z.lat, z.lng], {
              radius, color: "#16a34a", weight: 1, opacity: 0.45,
              fillColor: "#22c55e", fillOpacity: 0.14, interactive: true,
            }).bindTooltip(`Cobertura disponible · ${z.ciudad}, ${z.provincia}`, { sticky: true, direction: "top" }).addTo(layer);
          });
          layer.addTo(mapRef.current);
        });

        const icon = L.divIcon({ className: "cob-pin", html: "<span></span>", iconSize: [26, 26], iconAnchor: [13, 13] });
        const marker = L.marker([start.lat, start.lng], { draggable: true, icon, keyboard: true, opacity: coords ? 1 : 0.85, title: "Arrástrame a tu ubicación" }).addTo(map);
        marker.on("dragend", () => { const p = marker.getLatLng(); marker.setOpacity(1); if (onPick) onPick(p.lat, p.lng); });
        map.on("click", (e) => { marker.setLatLng(e.latlng); marker.setOpacity(1); if (onPick) onPick(e.latlng.lat, e.latlng.lng); });
        mapRef.current = map; markerRef.current = marker;
        // invalidateSize: el contenedor recién es visible dentro del modal.
        setTimeout(() => map.invalidateSize(), 60);
        setTimeout(() => map.invalidateSize(), 320);
      } else if (coords) {
        mapRef.current.setView([coords.lat, coords.lng], 16);
        markerRef.current.setLatLng([coords.lat, coords.lng]);
        markerRef.current.setOpacity(1);
      }
    }).catch(() => setFailed(true));
    return () => { cancelled = true; };
  }, [coords && coords.lat, coords && coords.lng]);

  // Limpia el mapa al desmontar (evita fugas al cerrar el modal).
  useEffect(() => () => { if (mapRef.current) { mapRef.current.remove(); mapRef.current = null; } }, []);

  if (failed) return <RDMap coords={coords} />;
  return <div ref={elRef} style={{ width: "100%", height: "100%" }} aria-label="Mapa interactivo de tu ubicación" />;
}

function RDMap({ coords }) {
  const inDR = coords && coords.lng >= DR_BB.minx - 0.3 && coords.lng <= DR_BB.maxx + 0.3 && coords.lat >= DR_BB.miny - 0.3 && coords.lat <= DR_BB.maxy + 0.3;
  const mx = inDR ? drX(coords.lng) : drX(-69.93);
  const my = inDR ? drY(coords.lat) : drY(18.48);
  return (
    <svg viewBox={DR_VIEW} width="100%" height="100%" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Mapa de República Dominicana" style={{ display: "block", padding: 12 }}>
      <path d={DR_PATH} fill="rgba(218,41,28,0.07)" stroke={CLARO_RED} strokeWidth="1.1" strokeLinejoin="round" strokeLinecap="round" />
      <circle cx={mx} cy={my} r="9" fill={CLARO_RED} opacity="0.18"><animate attributeName="r" values="7;15;7" dur="2s" repeatCount="indefinite" /><animate attributeName="opacity" values="0.25;0;0.25" dur="2s" repeatCount="indefinite" /></circle>
      <circle cx={mx} cy={my} r="4.5" fill={CLARO_RED} stroke="#fff" strokeWidth="1.6" />
    </svg>
  );
}


// ─── Modal de verificación ───────────────────────────────────────────────────
function CoverageModal({ onClose, initialPlan = "" }) {
  // Demo: ?demo=gracias abre directo el paso 3 con datos de ejemplo (para revisar el diseño).
  const demo = typeof location !== "undefined" && /[?&]demo=gracias/.test(location.search);
  const [step, setStep] = useState(demo ? 3 : 1);
  const [planMbps, setPlanMbps] = useState(initialPlan || "");  // plan seleccionado ("" = sin plan)
  const [provincias, setProvincias] = useState([]);
  const [ciudades, setCiudades] = useState([]);
  const [sectores, setSectores] = useState([]);
  const [prov, setProv] = useState(demo ? "Santo Domingo" : "");
  const [ciudad, setCiudad] = useState(demo ? "Santo Domingo Este" : "");
  const [sector, setSector] = useState(demo ? "Alma Rosa" : "");
  const [loading, setLoading] = useState("");     // "" | "prov" | "ciudad" | "sector" | "check" | "lead" | "geo"
  const [result, setResult] = useState(null);      // {covered, zone}
  const [form, setForm] = useState({ nombre: "", cedula: "", telefono: "" });
  const [err, setErr] = useState("");
  const [showManual, setShowManual] = useState(false);  // dirección detallada (no encuentra su zona)
  const [manualAddr, setManualAddr] = useState("");
  const [geoCoords, setGeoCoords] = useState(null);     // {lat,lng} del pin
  const [geoPlace, setGeoPlace] = useState("");          // nombre exacto del lugar

  useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("keydown", onKey);
    // Bloqueo robusto del fondo (iOS/Android): fija el body y restaura el scroll al cerrar.
    const scrollY = window.scrollY || window.pageYOffset || 0;
    const body = document.body;
    const prev = { position: body.style.position, top: body.style.top, width: body.style.width, overflow: body.style.overflow };
    body.style.position = "fixed";
    body.style.top = `-${scrollY}px`;
    body.style.width = "100%";
    body.style.overflow = "hidden";
    return () => {
      document.removeEventListener("keydown", onKey);
      body.style.position = prev.position; body.style.top = prev.top; body.style.width = prev.width; body.style.overflow = prev.overflow;
      window.scrollTo(0, scrollY);
    };
  }, [onClose]);

  useEffect(() => {
    setLoading("prov");
    getProvincias().then((items) => { setProvincias(items || []); setLoading(""); });
  }, []);

  async function onProv(v) {
    setProv(v); setCiudad(""); setSector(""); setCiudades([]); setSectores([]); setResult(null);
    if (!v) return;
    setLoading("ciudad");
    setCiudades(await getCiudades(v)); setLoading("");
  }
  async function onCiudad(v) {
    setCiudad(v); setSector(""); setSectores([]); setResult(null);
    if (!v) return;
    setLoading("sector");
    setSectores(await getSectores(prov, v)); setLoading("");
  }
  async function onSector(v) {
    setSector(v); setResult(null);
    if (!v) return;
    setLoading("check");
    setResult(await checkCoverage(prov, ciudad, v)); setLoading("");
  }

  // Resuelve provincia/ciudad/sector desde coords (GPS o pin arrastrado).
  async function resolveLocation(latitude, longitude) {
    setErr(""); setLoading("geo");
    setGeoCoords({ lat: latitude, lng: longitude });
    const norm = (s) => String(s || "").toLowerCase().normalize("NFD")
      .replace(new RegExp("[\\u0300-\\u036f]", "g"), "").replace(/\b(provincia|distrito nacional|de|del|la|el)\b/g, "").replace(/\s+/g, " ").trim();
    try {
      const ctrl = new AbortController();
      const to = setTimeout(() => ctrl.abort(), 8000);
      let guessProv = "", guessCity = "", guessSector = "", place = "";

      // 1) Mapbox (más preciso) vía nuestro proxy.
      try {
        const g = await fetch(`/api/geo/reverse?lat=${latitude}&lng=${longitude}`, { signal: ctrl.signal }).then((x) => x.json());
        if (g && g.ok) { guessProv = g.provincia || ""; guessCity = g.ciudad || ""; guessSector = g.sector || ""; place = g.place_name || ""; }
      } catch { /* sigue al respaldo */ }

      // 2) Respaldo BigDataCloud si Mapbox no devolvió nada (sin token o falló).
      if (!guessProv && !guessCity) {
        try {
          const r = await fetch(`https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=${latitude}&longitude=${longitude}&localityLanguage=es`, { signal: ctrl.signal }).then((x) => x.json());
          guessProv = r.principalSubdivision || "";
          guessCity = r.city || r.locality || "";
          if (!place) place = [r.locality, r.city, r.principalSubdivision, r.countryName].filter(Boolean).join(", ");
        } catch { /* sin geocoding; el usuario elige manual */ }
      }
      clearTimeout(to);
      // Garantiza que el campo "Ubicación detectada" siempre quede con algo útil.
      if (!place) place = [guessSector, guessCity, guessProv].filter(Boolean).join(", ");
      setGeoPlace(place);

      const list = provincias.length ? provincias : await getProvincias();
      const match = list.find((p) => { const np = norm(p.provincia), ng = norm(guessProv); return np && ng && (np.includes(ng) || ng.includes(np)); });
      if (match) {
        await onProv(match.provincia);
        if (guessCity) {
          const cs = await getCiudades(match.provincia);
          const cm = cs.find((c) => { const nc = norm(c), ng = norm(guessCity); return nc && ng && (nc.includes(ng) || ng.includes(nc)); });
          if (cm) {
            setCiudad(cm);
            const ss = await getSectores(match.provincia, cm);
            setSectores(ss);
            // intenta acertar el sector también
            if (guessSector) {
              const sm = ss.find((s) => { const ns = norm(s.sector), ng = norm(guessSector); return ns && ng && (ns.includes(ng) || ng.includes(ns)); });
              if (sm) { setSector(sm.sector); setResult(await checkCoverage(match.provincia, cm, sm.sector)); }
            }
          }
        }
        setErr("Te ubicamos — arrastra el pin para afinar y continúa abajo.");
      } else {
        setErr(place ? "Ubicación marcada — continúa abajo." : "Marca tu ubicación en el mapa o ajusta tu dirección arriba.");
      }
    } catch { setErr("Ubicación marcada — continúa abajo."); }
    finally { setLoading(""); }
  }

  function useMyLocation() {
    if (!navigator.geolocation) { setErr("Tu navegador no permite ubicación."); return; }
    setErr(""); setLoading("geo");
    navigator.geolocation.getCurrentPosition(
      (pos) => resolveLocation(pos.coords.latitude, pos.coords.longitude),
      (geoErr) => {
        setLoading("");
        setErr(geoErr && geoErr.code === 1 ? "Permiso de ubicación denegado. Escribe tu dirección arriba." : "No pudimos obtener tu ubicación. Escribe tu dirección arriba.");
      },
      { timeout: 12000, enableHighAccuracy: true, maximumAge: 60000 }
    );
  }

  // Busca la dirección escrita y la ubica en el mapa (búsqueda más detallada).
  async function searchManual() {
    const q = manualAddr.trim();
    if (!q) return;
    setErr(""); setLoading("geo");
    try {
      const g = await fetch(`/api/geo/reverse?q=${encodeURIComponent(q)}`).then((x) => x.json());
      if (g && g.ok && Number.isFinite(g.lat) && Number.isFinite(g.lng)) {
        await resolveLocation(g.lat, g.lng);   // mueve el pin + verifica cobertura
      } else {
        setErr("No encontramos esa dirección exacta. Mueve el pin en el mapa para marcarla.");
        setLoading("");
      }
    } catch { setErr("No pudimos buscar. Mueve el pin en el mapa para marcar tu ubicación."); setLoading(""); }
  }

  function goToData() { setErr(""); setStep(2); }

  async function submitLead() {
    if (!form.nombre.trim()) { setErr("El nombre es obligatorio."); return; }
    if (!/^\d{10}$/.test(form.telefono)) { setErr("El teléfono debe tener 10 dígitos (ej. 8095550000)."); return; }
    setErr(""); setLoading("lead");
    const planObj = COB_PLANS.find((p) => String(p.mbps) === String(planMbps));
    const body = {
      nombre: form.nombre.trim(), telefono: form.telefono.trim(),
      cedula: form.cedula.trim() || undefined,
      provincia: prov, ciudad, sector,
      direccion: manualAddr.trim() || geoPlace || undefined,
      ubicacion: geoPlace || undefined,
      lat: geoCoords ? geoCoords.lat : undefined,
      lng: geoCoords ? geoCoords.lng : undefined,
      tipoCobertura: result?.zone?.tipoCobertura,
      covered: !!result?.covered,
      // Plan seleccionado → define la plantilla del gestor de leads.
      plan: planObj ? `Claro Hogar ${speedLabel(planObj)} Mbps` : undefined,
      plan_precio: planObj ? planObj.price / 2 : undefined,
      plan_mbps: planObj ? planObj.mbps : undefined,
      template: planObj ? "cobertura_solicitud" : "cobertura_sin_plan",
    };

    // event_id compartido entre pixel (cliente) y Conversions API (servidor) → dedup.
    const eventId = (window.crypto && window.crypto.randomUUID)
      ? window.crypto.randomUUID()
      : "lead-" + Date.now() + "-" + Math.floor(Math.random() * 1e9);
    const cookie = (k) => { const m = document.cookie.match(new RegExp("(?:^|; )" + k + "=([^;]*)")); return m ? decodeURIComponent(m[1]) : undefined; };
    body.event_id = eventId;
    body.fbp = cookie("_fbp");
    body.fbc = cookie("_fbc");

    const res = await postLead(body);
    setLoading("");
    if (res.ok) {
      // Evento de conversión para Meta Pixel (lo que la campaña optimiza: fb_pixel_lead).
      try {
        if (window.fbq) window.fbq("track", "Lead", {
          content_name: "Cobertura fibra óptica",
          content_category: [prov, ciudad].filter(Boolean).join(" · ") || "RD",
          value: planObj ? planObj.price / 2 : 0,
          currency: "DOP",
        }, { eventID: eventId });
        if (window.gtag) window.gtag("event", "generate_lead", { event_category: "cobertura" });
      } catch (e) { /* tracking no debe romper el flujo */ }
      setStep(3);
    } else setErr(res.error || "No se pudo enviar. Intenta de nuevo.");
  }

  const covered = result?.covered;
  const tipo = result?.zone?.tipoCobertura;
  const tipoLabel = tipo === "total" ? "Cobertura total" : tipo === "parcial" ? "Cobertura parcial" : tipo === "calles_parciales" ? "Cobertura en calles seleccionadas" : "";

  const selStyle = { width: "100%", padding: "13px 14px", borderRadius: 12, border: "1.5px solid var(--line)", background: "#fff", color: "var(--ink)", fontSize: 14.5, fontFamily: "inherit", appearance: "auto" };
  const inputStyle = { ...selStyle };

  return (
    <div className="cob-backdrop" onClick={onClose}>
      <div className="cob-modal" onClick={(e) => e.stopPropagation()} role="dialog" aria-modal="true" aria-label="Verificar cobertura de fibra">
        <span className="cob-handle" aria-hidden="true" />
        <button onClick={onClose} aria-label="Cerrar" className="cob-close">
          <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2"><path d="M4 4l8 8M12 4l-8 8" strokeLinecap="round" /></svg>
        </button>

        {/* Progreso */}
        <div className="cob-steps">
          {["Ubicación", "Tus datos", "Listo"].map((s, i) => (
            <div key={s} className={"cob-step" + (step === i + 1 ? " is-active" : "") + (step > i + 1 ? " is-done" : "")}>
              <span className="cob-step-dot">{step > i + 1 ? Ic.check(13) : i + 1}</span>{s}
            </div>
          ))}
        </div>

        {/* PASO 1 — Confirma tu ubicación */}
        {step === 1 && (
          <div className="cob-body">
            {planMbps && (() => {
              const pp = COB_PLANS.find((p) => String(p.mbps) === String(planMbps));
              return pp ? (
                <div className="cob-plan-chip">{Ic.signal(14)} Plan elegido: <b>{speedLabel(pp)} Mbps</b> · RD${fmtRD(pp.price / 2)}/mes</div>
              ) : null;
            })()}

            {/* Promo compacta (pill) */}
            <div className="cob-promo-pill"><span style={{ fontSize: 15, lineHeight: 1 }}>🔥</span> <b>50%</b> de descuento por <b>12 meses</b></div>

            <div className="cob-field">
              <label>Tu ubicación</label>
              <div className="cob-search">
                <input value={manualAddr} onChange={(e) => setManualAddr(e.target.value)} style={inputStyle}
                  placeholder="Ej.: Calle Duarte #45, Los Prados, Santiago" aria-label="Tu ubicación"
                  onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); searchManual(); } }} />
                <button type="button" onClick={searchManual} disabled={loading === "geo" || !manualAddr.trim()} className="cob-search-btn" aria-label="Buscar en el mapa">
                  {loading === "geo" ? <Spinner /> : <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="11" cy="11" r="7" /><path d="M21 21l-4.3-4.3" strokeLinecap="round" /></svg>}
                  Buscar
                </button>
              </div>
              <div className="cob-search-hint">Escribe tu calle, sector y ciudad — o una referencia cercana. También puedes mover el pin en el mapa para afinar.</div>
            </div>

            {/* Mapa interactivo Leaflet+OSM, siempre visible */}
            <div className="cob-map"><LocationMap coords={geoCoords} onPick={resolveLocation} /></div>
            <div className="cob-map-hint">
              <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>{Ic.checkCircle(13)} También puedes mover el pin para afinar la ubicación.</span>
              <span className="cob-legend"><i /> Cobertura</span>
            </div>

            {/* Campo auto-rellenado al ubicar (lugar exacto). Las coordenadas se
                capturan y se envían al CRM, pero no se muestran. */}
            {geoCoords && (
              <div className="cob-geo-fields">
                <div className="cob-field">
                  <label>Ubicación detectada</label>
                  <input type="text" value={geoPlace} onChange={(e) => setGeoPlace(e.target.value)}
                    placeholder="Se llena al ubicarte en el mapa" style={inputStyle} />
                </div>
              </div>
            )}

            {err && <div className="cob-note">{err}</div>}
            {loading === "check" && <div className="cob-checking"><Spinner /> Verificando tu zona…</div>}

            {/* Resultado de la zona seleccionada */}
            {result && loading !== "check" && (
              covered ? (
                <div className="cob-result ok">
                  <strong style={{ display: "inline-flex", alignItems: "center", gap: 7 }}>{Ic.check(17)} ¡Hay fibra en tu zona!</strong>
                  {tipoLabel && <span>{tipoLabel}</span>}
                </div>
              ) : (
                <div className="cob-result no">
                  <strong>Aún no hay fibra en ese sector.</strong>
                  <span>Déjanos tus datos y te avisamos apenas llegue.</span>
                </div>
              )
            )}

          </div>
        )}

        {/* Footer FIJO del paso 1: el CTA primario siempre visible (no se pierde por
            scroll → no perdemos leads). Cambia según el estado. */}
        {step === 1 && (
          <div className="cob-foot">
            {(result || geoCoords || manualAddr.trim()) ? (
              <button onClick={goToData} className="btn cob-confirm" style={{ width: "100%", justifyContent: "center", minHeight: 50, color: "#fff", border: 0 }}>
                {Ic.pin(17)} Confirmar ubicación
              </button>
            ) : (
              <button onClick={useMyLocation} disabled={loading === "geo"} className="btn cob-confirm" style={{ width: "100%", justifyContent: "center", minHeight: 50, color: "#fff", border: 0 }}>
                {loading === "geo" ? <Spinner /> : Ic.pin(17)} Usar mi ubicación actual
              </button>
            )}
          </div>
        )}

        {/* PASO 2 — Datos */}
        {step === 2 && (
          <div className="cob-body">
            <h3 className="cob-title">{covered ? "¡Casi listo! Deja tus datos" : "Déjanos tus datos"}</h3>
            <p className="cob-sub">{[prov, ciudad, sector].filter(Boolean).join(" · ") || geoPlace || manualAddr || "Tu ubicación"}</p>
            <div className="cob-field"><label>Nombre completo *</label>
              <input value={form.nombre} onChange={(e) => setForm({ ...form, nombre: e.target.value })} style={inputStyle} placeholder="Tu nombre" /></div>
            <div className="cob-field"><label>Teléfono / WhatsApp *</label>
              <input value={form.telefono}
                onChange={(e) => setForm({ ...form, telefono: e.target.value.replace(/\D/g, "").slice(0, 10) })}
                style={inputStyle} placeholder="8095550000" inputMode="numeric" maxLength={10} autoComplete="tel-national" /></div>
            <div className="cob-field"><label>Cédula (opcional)</label>
              <input value={form.cedula} onChange={(e) => setForm({ ...form, cedula: e.target.value })} style={inputStyle} placeholder="000-0000000-0" /></div>
            <div className="cob-field"><label>Plan de interés</label>
              <select value={planMbps} onChange={(e) => setPlanMbps(e.target.value)} style={selStyle}>
                <option value="">Aún no lo decido</option>
                {COB_PLANS.map((p) => (
                  <option key={p.mbps} value={p.mbps}>{speedLabel(p)} Mbps — RD${fmtRD(p.price / 2)}/mes (50% × 12m)</option>
                ))}
              </select>
            </div>
            {err && <div className="cob-note err">{err}</div>}
            <button onClick={submitLead} disabled={loading === "lead"} className="btn" style={{ width: "100%", justifyContent: "center", marginTop: 6, background: CLARO_RED, color: "#fff", border: 0 }}>
              {loading === "lead" ? <><Spinner light /> Enviando…</> : "Enviar solicitud"}
            </button>
            <button onClick={() => { setStep(1); setErr(""); }} style={{ background: "transparent", border: 0, color: "var(--ink-3)", fontSize: 13, cursor: "pointer", marginTop: 12, fontFamily: "inherit" }}>← Volver</button>
          </div>
        )}

        {/* PASO 3 — Gracias */}
        {step === 3 && (
          <div className="cob-body" style={{ textAlign: "center" }}>
            {/* Pin de éxito con pulso */}
            <div style={{ position: "relative", width: 72, height: 72, margin: "2px auto 0" }}>
              <span style={{ position: "absolute", inset: 0, borderRadius: 999, background: "rgba(16,185,129,0.25)", animation: "cobPulse 1.8s ease-out infinite" }} />
              <span style={{ position: "relative", display: "grid", placeItems: "center", width: 72, height: 72, borderRadius: 999, background: "#d1fae5", boxShadow: "0 0 0 8px #ecfdf5", color: "#059669" }}>{Ic.pin(30)}</span>
            </div>
            <h3 className="cob-title" style={{ marginTop: 16 }}>¡Gracias por confirmar tu ubicación!</h3>
            <p className="cob-sub" style={{ maxWidth: 320, margin: "8px auto 0" }}>Estamos validando tu cobertura, pronto te contactaremos.</p>

            {/* Ubicación confirmada */}
            {(() => {
              const rows = [];
              if (prov) rows.push([Ic.pin, "Provincia", prov]);
              if (ciudad) rows.push([Ic.building, "Ciudad", ciudad]);
              if (sector) rows.push([Ic.nav, "Sector", sector]);
              if (manualAddr && !sector) rows.push([Ic.nav, "Dirección", manualAddr]);
              if (!rows.length) return null;
              return (
                <div style={{ marginTop: 20, textAlign: "left", borderRadius: 18, background: "#fff", border: "1px solid var(--line)", padding: "4px 16px", boxShadow: "0 10px 28px -18px rgba(16,24,40,0.20)" }}>
                  {rows.map(([icon, label, val], i) => (
                    <div key={label} style={{ display: "flex", alignItems: "center", gap: 12, padding: "11px 0", borderTop: i ? "1px solid var(--line-2, #eee)" : "0" }}>
                      <span style={{ width: 34, height: 34, borderRadius: 999, display: "grid", placeItems: "center", background: "rgba(218,41,28,0.08)", color: CLARO_RED, flexShrink: 0 }}>{icon(17)}</span>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontSize: 10.5, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.05em", color: "var(--ink-4)" }}>{label}</div>
                        <div style={{ fontSize: 14.5, fontWeight: 600, color: "var(--ink)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{val}</div>
                      </div>
                      <span style={{ color: "#10b981", flexShrink: 0, display: "inline-flex" }}>{Ic.checkCircle(18)}</span>
                    </div>
                  ))}
                </div>
              );
            })()}

            {/* Tarjeta promocional */}
            <div style={{ marginTop: 14, textAlign: "left", borderRadius: 18, overflow: "hidden", background: "linear-gradient(135deg,#DA291C,#B71C12)", color: "#fff", padding: 16, boxShadow: "0 18px 40px -18px rgba(218,41,28,0.55)" }}>
              <div style={{ display: "flex", alignItems: "center", gap: 14 }}>
                <span style={{ width: 46, height: 46, borderRadius: 14, background: "rgba(255,255,255,0.15)", display: "grid", placeItems: "center", flexShrink: 0, fontSize: 26, lineHeight: 1 }}>🔥</span>
                <div style={{ lineHeight: 1 }}>
                  <span style={{ fontSize: 36, fontWeight: 800, letterSpacing: "-0.02em" }}>50%</span>
                  <div style={{ fontSize: 12.5, marginTop: 4, color: "rgba(255,255,255,0.92)" }}>de descuento en renta por 12 meses</div>
                </div>
              </div>
              <div style={{ marginTop: 14, display: "flex", alignItems: "center", gap: 8, background: "rgba(255,255,255,0.10)", borderRadius: 12, padding: "8px 12px" }}>
                {Ic.calendar(15)}<span style={{ fontSize: 12, color: "rgba(255,255,255,0.9)" }}>Promoción disponible desde el <b>12 de junio de 2026</b>.</span>
              </div>
            </div>

            {/* Botones de acción */}
            <div style={{ marginTop: 18, display: "flex", flexDirection: "column", gap: 10 }}>
              <a href={window.balUrl("internet-hogar.html")} className="btn" style={{ background: CLARO_RED, color: "#fff", border: 0, minHeight: 52, justifyContent: "center" }}>{Ic.tag(17)} Ver más planes</a>
              <a href={window.balUrl("equipos.html")} className="btn" style={{ background: "#fff", color: CLARO_RED, border: "1.5px solid " + CLARO_RED, minHeight: 52, justifyContent: "center" }}>{Ic.phone(17)} Equipos</a>
              <a href={WA} target="_blank" rel="noopener" className="btn" style={{ background: "#22c55e", color: "#fff", border: 0, minHeight: 52, justifyContent: "center" }}>{Ic.wa(18)} Continuar en WhatsApp</a>
            </div>

            <p className="cob-sub" style={{ marginTop: 16, marginBottom: 0, fontSize: 12.5, color: "var(--ink-4)", maxWidth: 320, marginInline: "auto" }}>
              Te ayudaremos a encontrar la mejor opción disponible para tu zona.
            </p>
          </div>
        )}
      </div>
    </div>
  );
}

// ─── Sección: planes simétricos de fibra (Internet Fijo + Bono de voz) ────────
// Oferta portabilidad: 50% de descuento en la renta por 12 meses.
// mbps = bajada (también es la clave/valor del plan); up = subida.
// Simétricos: subida = bajada. Se muestra "bajada/subida" para no confundir
// con fibra normal (misma bajada, distinta subida).
const COB_PLANS = [
  { mbps: 1000, up: 1000, price: 5495, scope: "Distrito Nacional y Santiago" },
  { mbps: 500,  up: 500,  price: 4495, scope: "Distrito Nacional y Santiago" },
  { mbps: 400,  up: 400,  price: 3595, scope: "Distrito Nacional y Santiago" },
  { mbps: 300,  up: 300,  price: 3095, scope: "Distrito Nacional y Santiago" },
  { mbps: 200,  up: 200,  price: 2295, scope: "a nivel nacional" },
  { mbps: 150,  up: 150,  price: 1995, scope: "a nivel nacional" },
];
const fmtRD = (n) => n.toLocaleString("es-DO", { minimumFractionDigits: n % 1 ? 2 : 0, maximumFractionDigits: 2 });
const speedLabel = (p) => `${p.mbps}/${p.up || p.mbps}`;   // ej. "1000/1000"

function PlanesCobertura({ onVerify }) {
  const top = COB_PLANS[0].price;  // el más caro → destacado

  // SEO: datos estructurados de las ofertas (precio con descuento).
  const ld = {
    "@context": "https://schema.org", "@type": "ItemList",
    itemListElement: COB_PLANS.map((p, i) => ({
      "@type": "Offer", position: i + 1,
      name: `Internet Fijo ${speedLabel(p)} Mbps simétrico + Bono de voz`,
      priceCurrency: "DOP", price: (p.price / 2).toFixed(2),
      description: `${speedLabel(p)} Mbps (bajada/subida). 50% de descuento por portabilidad durante 12 meses. Disponible ${p.scope}.`,
    })),
  };

  return (
    <section className="cob-plans-sec">
      <div className="container">
        <div style={{ textAlign: "center", maxWidth: 620, margin: "0 auto 44px" }}>
          <div className="eyebrow" style={{ marginBottom: 10, color: CLARO_RED }}>Planes de fibra simétrica</div>
          <h2 className="h2" style={{ margin: 0 }}>Internet Fijo + Bono de voz</h2>
          <p className="lead" style={{ marginTop: 14, color: "var(--ink-3)" }}>
            Subida y bajada iguales. <strong style={{ color: CLARO_RED }}>50% de descuento</strong> por portabilidad durante 12 meses.
          </p>
        </div>

        <div className="cob-plans">
          {COB_PLANS.map((p) => {
            const featured = p.price === top;
            const promo = p.price / 2;
            return (
              <article key={p.mbps} className={"cob-plan" + (featured ? " is-pop" : "")}>
                {featured && <span className="cob-plan-badge">★ Más rápido</span>}

                <div className="cob-plan-head">
                  <div className="cob-plan-speed">{speedLabel(p)}<small>Mbps</small></div>
                  <div className="cob-plan-sym">{Ic.signal(13)} Subida y bajada iguales</div>
                  <div className="cob-plan-name">Internet Fijo + Bono de voz</div>
                  <div className="cob-plan-scope">{Ic.pin(12)} Disponible {p.scope}</div>
                </div>

                <div className="cob-plan-bono">{Ic.phone(15)} Bono de voz <b>100 minutos</b></div>

                <div className="cob-plan-priceblock">
                  <div className="cob-plan-old">Regular <s>RD${fmtRD(p.price)}</s></div>
                  <div className="cob-plan-now">RD${fmtRD(promo)}<small>/mes</small></div>
                  <div className="cob-plan-note">primeros 12 meses con portabilidad · impuestos incluidos</div>
                  <span className="cob-plan-off">🔥 50% OFF · 12 meses</span>
                </div>

                <button onClick={() => onVerify(p)} className={"btn cob-cta-glow" + (featured ? "" : " ghost")}>Solicitar</button>
              </article>
            );
          })}
        </div>
      </div>
      <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(ld) }} />
    </section>
  );
}

// ─── Página ──────────────────────────────────────────────────────────────────
function App() {
  const [open, setOpen] = useState(false);
  const [planSel, setPlanSel] = useState("");  // mbps del plan preseleccionado ("" = ninguno)
  const touchedRef = useRef(false);            // ¿el usuario ya interactuó?
  // verify(plan?) — desde una card pasa el plan; desde un botón normal llega el evento (sin .mbps).
  const verify = (plan) => {
    touchedRef.current = true;
    setPlanSel(plan && plan.mbps ? String(plan.mbps) : "");
    setOpen(true);
  };

  // Permite abrir el modal desde cualquier botón con [data-baldera-cobertura]
  // y lo abre automáticamente a los 5s si el usuario no lo hizo antes.
  useEffect(() => {
    const h = (e) => { if (e.target.closest("[data-baldera-cobertura]")) { e.preventDefault(); touchedRef.current = true; setOpen(true); } };
    document.addEventListener("click", h);
    if (/[?&]demo=gracias/.test(location.search)) { touchedRef.current = true; setOpen(true); }  // vista previa del paso 3

    const autoOpen = setTimeout(() => {
      if (!touchedRef.current) { touchedRef.current = true; setOpen(true); }
    }, 5000);

    return () => { document.removeEventListener("click", h); clearTimeout(autoOpen); };
  }, []);

  return (
    <window.PageScaffold active="cobertura">
      <CoberturaStyles />

      {/* HERO */}
      <section className="cob-hero">
        <div className="cob-hero-threads" aria-hidden="true">{[...Array(9)].map((_, i) => <span key={i} style={{ left: `${8 + i * 11}%`, animationDelay: `${i * 0.4}s` }} />)}</div>
        <div className="container" style={{ position: "relative", textAlign: "center", maxWidth: 820 }}>
          <span className="cob-pill"><ClaroMark /> Distribuidor autorizado Claro</span>
          <h1 className="cob-h1">
            Cámbiate al internet <span className="cob-grad">más estable</span> del país.
          </h1>
          <p className="cob-lead">
            Internet de alta velocidad por fibra óptica. Verifica si ya llega a tu zona en segundos y te conectamos con un representante de Claro.
          </p>
          <div className="cob-cta-row">
            <button onClick={verify} className="btn cob-cta-main cob-cta-glow cob-cta-hero">
              {Ic.signal()} Verificar mi cobertura
              <span className="cob-cta-arrow" aria-hidden="true">
                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.6" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14M13 6l6 6-6 6" /></svg>
              </span>
            </button>
          </div>
        </div>
      </section>

      {/* PLANES — 3 simétricos */}
      <PlanesCobertura onVerify={verify} />

      {/* BANDA CTA */}
      <section style={{ background: CLARO_RED, color: "#fff" }}>
        <div className="container" style={{ textAlign: "center", maxWidth: 640 }}>
          <h2 className="h1" style={{ margin: 0, color: "#fff" }}>¿Listo para la fibra?</h2>
          <p className="lead" style={{ color: "rgba(255,255,255,0.9)", marginTop: 18 }}>Verifica tu zona en segundos. Si hay cobertura, te conectamos hoy mismo.</p>
          <div style={{ display: "flex", gap: 12, justifyContent: "center", flexWrap: "wrap", marginTop: 28 }}>
            <button onClick={verify} className="btn cob-cta-glow" style={{ background: "#fff", color: CLARO_RED, fontWeight: 700 }}>{Ic.signal()} Verificar mi cobertura</button>
            <a href={WA} target="_blank" rel="noopener" className="btn" style={{ background: "rgba(255,255,255,0.15)", color: "#fff", border: "1px solid rgba(255,255,255,0.3)" }}>{Ic.chat()} WhatsApp</a>
          </div>
        </div>
      </section>

      {open && <CoverageModal onClose={() => setOpen(false)} initialPlan={planSel} />}
    </window.PageScaffold>
  );
}

function CoberturaStyles() {
  return (
    <style>{`
      @keyframes cobSpin { to { transform: rotate(360deg); } }
      @keyframes cobPulse { 0% { transform: scale(0.9); opacity: 0.7; } 70% { transform: scale(1.5); opacity: 0; } 100% { opacity: 0; } }
      .cob-hero {
        position: relative; overflow: hidden; color: #fff;
        padding: 150px 0 90px;
        background:
          radial-gradient(900px 500px at 70% -10%, rgba(218,41,28,0.32), transparent 60%),
          radial-gradient(700px 500px at 10% 110%, rgba(218,41,28,0.16), transparent 65%),
          #0C0C11;
      }
      .cob-hero-threads { position: absolute; inset: 0; pointer-events: none; overflow: hidden;
        mask-image: linear-gradient(180deg, transparent, #000 30%, #000 70%, transparent);
        -webkit-mask-image: linear-gradient(180deg, transparent, #000 30%, #000 70%, transparent); }
      .cob-hero-threads span {
        position: absolute; top: 0; width: 1px; height: 28%;
        background: linear-gradient(180deg, transparent, rgba(218,41,28,0.55), rgba(218,41,28,0.12), transparent);
        filter: blur(0.3px);
        animation: cobThread 6s linear infinite;
      }
      @keyframes cobThread { 0% { transform: translateY(-40%); opacity: 0; } 15% { opacity: 0.7; } 85% { opacity: 0.7; } 100% { transform: translateY(380%); opacity: 0; } }
      .cob-pill { display: inline-flex; align-items: center; gap: 9px; padding: 8px 18px 8px 10px; border-radius: 999px; background: rgba(255,255,255,0.10); border: 1px solid rgba(255,255,255,0.18); font-size: 14px; font-weight: 600; margin-bottom: 26px; color: #fff; }
      .cob-h1 { font-size: clamp(40px, 7.5vw, 76px); font-weight: 800; letter-spacing: -0.035em; line-height: 1.05; margin: 0; color: #fff; }
      .cob-grad { background: linear-gradient(100deg, #FF5747, #DA291C); -webkit-background-clip: text; background-clip: text; color: transparent; }
      .cob-lead { font-size: clamp(17px, 2.4vw, 22px); color: rgba(255,255,255,0.72); margin: 26px auto 0; max-width: 620px; line-height: 1.55; }
      .cob-cta-row { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; margin-top: 40px; }
      .cob-cta-main { padding: 18px 40px; font-size: 18px; border-radius: 14px; gap: 10px; box-shadow: 0 14px 34px -10px rgba(0,0,0,0.45); }
      .cob-cta-main svg { width: 22px; height: 22px; }

      /* ── CTA con animación + efecto de luz (motion-design) ───────────────
         3 capas: glow ambiental (atrae), barrido de luz (shine), feedback. */
      .cob-cta-glow { position: relative; overflow: hidden; isolation: isolate;
        transition: transform 180ms cubic-bezier(0.22,1,0.36,1), box-shadow 180ms ease; }
      .cob-cta-glow::after {                 /* barrido de luz diagonal */
        content: ""; position: absolute; top: -20%; left: -75%;
        width: 45%; height: 140%; z-index: 2; pointer-events: none;
        transform: skewX(-20deg);
        background: linear-gradient(100deg, transparent, rgba(255,255,255,0.75), transparent);
        animation: cobShine 3.6s ease-in-out infinite; }
      .cob-cta-glow:hover { transform: translateY(-2px) scale(1.015); }
      .cob-cta-glow:hover::after { animation-duration: 1.2s; }
      .cob-cta-glow:active { transform: translateY(0) scale(0.97); transition-duration: 90ms; }

      /* glow rojo pulsante */
      .cob-cta-main { animation: cobGlow 2.6s ease-in-out infinite; }

      /* CTA principal del hero — gradiente rojo vibrante, texto blanco, flecha animada */
      .cob-cta-hero {
        background: linear-gradient(135deg, #FF5043 0%, #DA291C 55%, #B0160C 100%) !important;
        color: #fff !important; border: 0 !important; font-weight: 800;
        letter-spacing: -0.01em;
      }
      .cob-cta-hero svg { color: #fff; }
      .cob-cta-hero::after { background: linear-gradient(100deg, transparent, rgba(255,255,255,0.85), transparent); } /* shine blanco sobre rojo */
      .cob-cta-arrow { display: inline-flex; transition: transform 240ms cubic-bezier(0.22,1,0.36,1); }
      .cob-cta-hero:hover .cob-cta-arrow { transform: translateX(5px); }
      @keyframes cobShine {
        0%        { left: -75%; }
        22%, 100% { left: 130%; }
      }
      @keyframes cobGlow {
        0%, 100% { box-shadow: 0 14px 34px -10px rgba(0,0,0,0.45), 0 6px 26px -6px rgba(218,41,28,0.30), 0 0 0 0 rgba(218,41,28,0.0); }
        50%      { box-shadow: 0 16px 40px -10px rgba(0,0,0,0.50), 0 10px 44px -4px rgba(218,41,28,0.60), 0 0 0 6px rgba(218,41,28,0.10); }
      }

      /* Entrada escalonada del hero (decelerate easing, principio de entrada). */
      .cob-hero .cob-pill,
      .cob-hero .cob-h1,
      .cob-hero .cob-lead,
      .cob-hero .cob-cta-row { animation: cobHeroIn 640ms cubic-bezier(0.22,1,0.36,1) both; }
      .cob-hero .cob-pill       { animation-delay: 60ms; }
      .cob-hero .cob-h1         { animation-delay: 150ms; }
      .cob-hero .cob-lead       { animation-delay: 250ms; }
      .cob-hero .cob-cta-row    { animation-delay: 350ms; }
      @keyframes cobHeroIn { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }

      /* Sección de planes simétricos */
      .cob-plans-sec { background: var(--paper-2, #f6f3f5); }
      .cob-plans { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; align-items: stretch; max-width: 1040px; margin: 0 auto; }
      .cob-plan { position: relative; display: flex; flex-direction: column; background: #fff;
        border: 1px solid var(--line); border-radius: 22px; overflow: hidden;
        box-shadow: 0 1px 2px rgba(20,17,15,0.04), 0 12px 32px -20px rgba(20,17,15,0.20);
        transition: transform 220ms cubic-bezier(0.22,1,0.36,1), box-shadow 220ms; }
      .cob-plan:hover { transform: translateY(-5px); box-shadow: 0 24px 50px -20px rgba(20,17,15,0.30); }
      .cob-plan.is-pop { border: 2px solid ${CLARO_RED}; transform: scale(1.035); z-index: 2;
        box-shadow: 0 28px 64px -20px rgba(218,41,28,0.45); }
      .cob-plan.is-pop:hover { transform: scale(1.035) translateY(-5px); }
      /* Cabecera con velocidad (gradiente rojo; más intenso en el destacado) */
      .cob-plan-head { position: relative; padding: 34px 20px 18px; text-align: center; color: #fff;
        background: linear-gradient(135deg, #E8392B 0%, #DA291C 60%, #B0160C 100%); }
      .cob-plan.is-pop .cob-plan-head { background: linear-gradient(135deg, #FF5A4D 0%, #DA291C 55%, #9E1209 100%); }
      .cob-plan-badge { position: absolute; top: 12px; left: 50%; transform: translateX(-50%);
        background: #FFD54F; color: #5a3a00; font-size: 10.5px; font-weight: 800; letter-spacing: 0.06em;
        text-transform: uppercase; padding: 4px 14px; border-radius: 999px; white-space: nowrap;
        box-shadow: 0 6px 16px -4px rgba(0,0,0,0.35); }
      .cob-plan-speed { font-size: 34px; font-weight: 800; letter-spacing: -0.02em; line-height: 1; white-space: nowrap; }
      .cob-plan-speed small { font-size: 14px; font-weight: 700; margin-left: 5px; letter-spacing: 0; }
      .cob-plan-sym { font-size: 11.5px; opacity: 0.92; display: inline-flex; gap: 5px; align-items: center; justify-content: center; margin-top: 7px; }
      .cob-plan-name { font-size: 15.5px; font-weight: 700; margin-top: 12px; }
      .cob-plan-scope { font-size: 11.5px; opacity: 0.88; margin-top: 5px; display: inline-flex; gap: 5px; align-items: center; justify-content: center; }
      .cob-plan-bono { display: flex; align-items: center; justify-content: center; gap: 7px; font-size: 13px; color: var(--ink-2); padding: 13px; border-bottom: 1px solid var(--line); }
      .cob-plan-bono b { color: var(--ink); }
      .cob-plan-priceblock { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 18px 16px 6px; text-align: center; }
      .cob-plan-old { font-size: 12px; color: var(--ink-3); }
      .cob-plan-old s { opacity: 0.8; }
      .cob-plan-now { font-size: 38px; font-weight: 800; color: ${CLARO_RED}; letter-spacing: -0.03em; line-height: 1; font-variant-numeric: tabular-nums; }
      .cob-plan-now small { font-size: 14px; font-weight: 600; color: var(--ink-3); letter-spacing: 0; }
      .cob-plan-note { font-size: 10.5px; color: var(--ink-3); line-height: 1.35; max-width: 220px; margin-top: 2px; }
      .cob-plan-off { margin-top: 8px; background: #FEF08A; color: #854d0e; font-size: 11.5px; font-weight: 800; padding: 4px 12px; border-radius: 999px; }
      .cob-plan .btn { margin: 14px 18px 20px; justify-content: center; background: ${CLARO_RED}; color: #fff; border: 0; min-height: 50px; }
      .cob-plan .btn.ghost { background: #fff; color: ${CLARO_RED}; border: 1.5px solid ${CLARO_RED}; }
      @media (max-width: 880px) { .cob-plans { grid-template-columns: 1fr 1fr; max-width: 680px; } }
      @media (max-width: 560px) {
        .cob-plans { grid-template-columns: 1fr; gap: 26px; max-width: 360px; }
        .cob-plan.is-pop { transform: none; } .cob-plan.is-pop:hover { transform: translateY(-5px); }
      }

      /* Modal — tarjeta centrada en desktop, bottom-sheet en móvil */
      .cob-backdrop { position: fixed; inset: 0; z-index: 200; background: rgba(12,12,17,0.6); backdrop-filter: blur(10px) saturate(1.1); -webkit-backdrop-filter: blur(10px) saturate(1.1); display: flex; align-items: center; justify-content: center; padding: clamp(12px,4vh,40px) 16px; animation: cobFade 200ms ease both; }
      @keyframes cobFade { from { opacity: 0; } to { opacity: 1; } }
      .cob-modal {
        position: relative; width: 100%; max-width: 440px;
        max-height: 92vh; max-height: 92dvh;
        display: flex; flex-direction: column; overflow: hidden;
        background: var(--surface, #fff); border-radius: 24px;
        box-shadow: 0 40px 90px -30px rgba(0,0,0,0.5);
        animation: cobIn 340ms cubic-bezier(0.16,1,0.3,1) both;
      }
      @keyframes cobIn { from { opacity: 0; transform: scale(0.96) translateY(10px); } to { opacity: 1; transform: none; } }
      .cob-modal::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 4px; background: linear-gradient(90deg, ${CLARO_RED}, #A81810); z-index: 3; }
      .cob-handle { display: none; }
      .cob-close { position: absolute; top: 14px; right: 14px; width: 34px; height: 34px; border-radius: 999px; background: var(--paper-2, #f1eef0); border: 0; cursor: pointer; display: grid; place-items: center; color: var(--ink); z-index: 4; }
      @keyframes cobSheet { from { transform: translateY(100%); } to { transform: translateY(0); } }
      @media (max-width: 540px) {
        .cob-backdrop { align-items: flex-end; padding: 0; }
        .cob-modal {
          max-width: none; width: 100%;
          max-height: 94vh; max-height: 94dvh;
          border-radius: 22px 22px 0 0;
          padding-bottom: env(safe-area-inset-bottom, 0px);
          animation: cobSheet 360ms cubic-bezier(0.16,1,0.3,1) both;
        }
        .cob-modal::before { display: none; }
        .cob-handle { display: block; position: absolute; top: 9px; left: 50%; transform: translateX(-50%); width: 42px; height: 4px; border-radius: 999px; background: var(--line, #ddd); z-index: 4; }
        .cob-steps { padding-top: 24px; }
        .cob-body { padding: 14px 18px calc(20px + env(safe-area-inset-bottom, 0px)); }
        .cob-title { font-size: 19px; }
      }
      .cob-steps { display: flex; gap: 6px; padding: 18px 24px 0; flex-shrink: 0; }
      .cob-step { flex: 1; display: flex; align-items: center; gap: 7px; font-size: 11.5px; font-weight: 600; color: var(--ink-4, #999); }
      .cob-step-dot { width: 22px; height: 22px; border-radius: 999px; background: var(--paper-2,#eee); color: var(--ink-3); display: grid; place-items: center; font-size: 11px; font-weight: 700; flex-shrink: 0; }
      .cob-step.is-active { color: var(--ink); } .cob-step.is-active .cob-step-dot { background: ${CLARO_RED}; color: #fff; }
      .cob-step.is-done .cob-step-dot { background: ${CLARO_RED}; color: #fff; }
      .cob-body { padding: 14px 22px 20px; flex: 1 1 auto; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }
      .cob-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
      .cob-link { display: inline-block; background: transparent; border: 0; padding: 2px 0 4px; color: var(--ink-3); font-size: 13px; cursor: pointer; font-family: inherit; text-decoration: underline; text-underline-offset: 2px; }
      .cob-link:hover { color: ${CLARO_RED}; }
      .cob-title { font-size: 19px; font-weight: 800; letter-spacing: -0.02em; margin: 6px 0 0; }
      .cob-sub { font-size: 13.5px; color: var(--ink-3); margin: 6px 0 14px; line-height: 1.45; }
      .cob-field { margin-bottom: 10px; } .cob-field label { display: block; font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-3); margin-bottom: 5px; }
      .cob-geo { width: 100%; padding: 11px; border-radius: 12px; border: 1.5px solid rgba(218,41,28,0.35); background: #fff; color: ${CLARO_RED}; font-size: 14px; font-weight: 700; cursor: pointer; font-family: inherit; display: inline-flex; align-items: center; justify-content: center; gap: 8px; margin: 2px 0 12px; transition: background 150ms, border-color 150ms; }
      .cob-geo:hover { background: rgba(218,41,28,0.05); border-color: ${CLARO_RED}; }
      .cob-geo:disabled { opacity: 0.6; cursor: default; }
      .cob-map { position: relative; height: clamp(210px, 36vh, 320px); border-radius: 14px; background: var(--paper-2,#f6f3f5); margin: 0 0 8px; overflow: hidden; border: 1px solid var(--line); }
      .cob-search { display: flex; gap: 8px; align-items: stretch; }
      .cob-search > input { flex: 1 1 auto; min-width: 0; }
      .cob-search-btn { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; padding: 0 16px; border-radius: 12px; border: 0; cursor: pointer; font-family: inherit; font-weight: 700; font-size: 14px; color: #fff; background: linear-gradient(135deg, #FF5043 0%, ${CLARO_RED} 60%, #B0160C 100%); }
      .cob-search-btn:disabled { opacity: 0.5; cursor: default; }
      .cob-search-hint { font-size: 12px; color: var(--ink-soft,#6b6470); margin: 7px 0 14px; line-height: 1.4; }
      .cob-promo-pill { display: inline-flex; align-items: center; gap: 7px; align-self: flex-start; margin: 10px 0 14px; padding: 7px 14px; border-radius: 999px; background: rgba(218,41,28,0.08); color: ${CLARO_RED}; font-size: 13.5px; font-weight: 600; }
      .cob-promo-pill b { font-weight: 800; }
      .cob-confirm { background: linear-gradient(135deg, #FF5043 0%, ${CLARO_RED} 55%, #B0160C 100%) !important; font-weight: 800; box-shadow: 0 12px 30px -10px rgba(218,41,28,0.55); }
      .cob-confirm svg { color: #fff; }
      /* Footer FIJO del modal: el CTA primario nunca se oculta por scroll. */
      .cob-foot { flex: 0 0 auto; z-index: 3; background: var(--surface, #fff);
        padding: 12px 22px max(12px, env(safe-area-inset-bottom, 0px));
        border-top: 1px solid var(--line); box-shadow: 0 -12px 20px -14px rgba(20,17,15,0.18); }
      .cob-map .leaflet-container { width: 100%; height: 100%; background: var(--paper-2,#f6f3f5); font: inherit; }
      .cob-map .leaflet-top, .cob-map .leaflet-bottom { z-index: 400; }   /* bajo el modal */
      .cob-map-hint { display: flex; align-items: center; gap: 8px 14px; flex-wrap: wrap; font-size: 12.5px; color: var(--ink-soft,#6b6470); margin: 0 0 12px; }
      .cob-geo-fields { display: flex; flex-direction: column; gap: 12px; margin: 0 0 14px; }
      .cob-plan-chip { display: inline-flex; align-items: center; gap: 7px; margin: 0 0 14px; padding: 8px 14px; border-radius: 999px; background: rgba(218,41,28,0.08); color: ${CLARO_RED}; font-size: 13px; font-weight: 600; }
      .cob-plan-chip svg { flex-shrink: 0; }
      .cob-map-hint svg { color: var(--claro,#DA291C); flex-shrink: 0; }
      .cob-legend { display: inline-flex; align-items: center; gap: 6px; }
      .cob-legend i { width: 13px; height: 13px; border-radius: 4px; background: rgba(34,197,94,0.18); border: 1px solid rgba(22,163,74,0.55); }
      /* Pin arrastrable (divIcon, sin imagen externa → CSP-safe) */
      .cob-pin span { display: block; width: 18px; height: 18px; border-radius: 50% 50% 50% 0;
        background: #DA291C; border: 2.5px solid #fff; transform: rotate(-45deg);
        box-shadow: 0 3px 8px rgba(0,0,0,0.35); cursor: grab; }
      .cob-pin:active span { cursor: grabbing; }
      .cob-note { font-size: 13px; color: var(--ink-2); background: var(--paper-2,#f6f3f5); border-radius: 10px; padding: 10px 12px; margin-bottom: 14px; }
      .cob-note.err { background: var(--err-bg, #fde8ee); color: var(--err, #B30A33); }
      .cob-checking { display: flex; align-items: center; gap: 10px; font-size: 14px; color: var(--ink-2); padding: 12px 0; }
      .cob-result { border-radius: 14px; padding: 16px; display: flex; flex-direction: column; gap: 4px; }
      .cob-result.ok { background: var(--ok-bg, #e9f7ef); }
      .cob-result.ok strong { color: var(--ok, #1FAA54); font-size: 15px; }
      .cob-result.no { background: var(--paper-2,#f6f3f5); }
      .cob-result.no strong { font-size: 15px; }
      .cob-result span { font-size: 13px; color: var(--ink-3); }
      .cob-check-anim { width: 64px; height: 64px; margin: 0 auto; border-radius: 999px; background: linear-gradient(135deg,#25D366,#128C7E); color: #fff; display: grid; place-items: center; font-size: 32px; box-shadow: 0 18px 40px -10px rgba(37,211,102,0.5); }
      @media (prefers-reduced-motion: reduce) {
        .cob-hero-threads span, .cob-modal, .cob-backdrop,
        .cob-cta-main, .cob-cta-glow::after,
        .cob-hero .cob-pill, .cob-hero .cob-h1, .cob-hero .cob-lead, .cob-hero .cob-cta-row { animation: none !important; }
        .cob-cta-glow::after { display: none; }
        .cob-cta-glow:hover { transform: none; }
        .cob-hero-threads { display: none; }
      }
      @media (max-width: 640px) {
        .grid-4 { grid-template-columns: 1fr 1fr !important; }
        .cob-hero { padding: 116px 0 60px; }
        .cob-cta-main { padding: 16px 30px; font-size: 17px; }
        .cob-cta-row .btn { flex: 1 1 auto; justify-content: center; }
      }
    `}</style>
  );
}

window.PAGES = Object.assign({}, window.PAGES, { cobertura: App });
if (!window.__BAL_ROOT) window.__BAL_ROOT = ReactDOM.createRoot(document.getElementById("root"));
window.__BAL_ROOT.render(React.createElement((window.PAGES && window.PAGES[window.PAGE]) || App));
