// Shared chrome — Navbar, Footer, Reveal, Icons, Marquee
const { useState, useEffect, useRef } = React;

// Path helper:
//   1. Detect base path so the same code works in:
//      - Vercel production at root (`https://baldera.com.do/...`)
//      - XAMPP local in subfolder (`http://localhost/Baldera%20web/project/...`)
//   2. Strip `.html` only in production: Vercel `cleanUrls` rewrites silently,
//      but Apache in XAMPP has no rewrite so `planes` (without .html) 404s.
//
//   Rule for BAL_BASE:
//     - URL with extension (e.g. `/Baldera%20web/project/index.html`)
//       → base is the parent directory of the file.
//     - Pretty URL without extension (e.g. `/equipos/iphone-15`, `/planes`)
//       → app is mounted at root, base is `/`.
window.BAL_BASE = (() => {
  const path = location.pathname;
  const lastSegment = path.replace(/\/$/, "").split("/").pop() || "";
  const hasExtension = /\.[a-z0-9]+$/i.test(lastSegment);
  if (hasExtension) return path.replace(/[^/]+$/, "");   // -> "/Baldera%20web/project/"
  return "/";                                            // pretty URL → root
})();

window.BAL_IS_LOCAL = (
  location.hostname === "localhost" ||
  location.hostname === "127.0.0.1" ||
  location.hostname.endsWith(".local")
);

window.balUrl = (p) => {
  if (!p) return p;
  if (/^(https?:|mailto:|tel:|#|\/|\.\.\/)/i.test(p)) return p;
  let clean = p.replace(/^\.\//, "");
  // In production, strip .html so the URL bar stays clean (Vercel handles it).
  // In localhost XAMPP, keep .html — Apache has no rewrite for extensionless URLs.
  if (!window.BAL_IS_LOCAL) {
    clean = clean.replace(/^index\.html(?=$|[?#])/i, "");
    clean = clean.replace(/\.html(?=$|[?#])/i, "");
  }
  return window.BAL_BASE + clean;
};

window.BAL_NAV = [
{ label: "Planes", href: "internet-hogar.html" },
{ label: "Cobertura", href: "cobertura-fibra-optica.html" },
{ label: "Equipos", href: "equipos.html" },
{ label: "Servicios", href: "servicios.html" },
{ label: "Tiendas", href: "tiendas.html" },
{ label: "Nosotros", href: "nosotros.html" },
{ label: "Blog", href: "blog.html" },
{ label: "Contacto", href: "contacto.html" }];


window.Icon = {
  Arrow: (p) => <svg viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><path d="M3 8h10M9 4l4 4-4 4" strokeLinecap="round" strokeLinejoin="round" /></svg>,
  ArrowUp: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><path d="M4 12L12 4M6 4h6v6" strokeLinecap="round" strokeLinejoin="round" /></svg>,
  Search: (p) => <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="1.7" {...p}><circle cx="10.5" cy="10.5" r="6.5" /><path d="M20 20l-4.6-4.6" strokeLinecap="round" /></svg>,
  Bag: (p) => <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="1.7" {...p}><path d="M5 8h14l-1.2 11.2A2 2 0 0 1 15.8 21H8.2a2 2 0 0 1-2-1.8L5 8z" /><path d="M9 8V6a3 3 0 0 1 6 0v2" strokeLinecap="round" /></svg>,
  Check: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2.2" {...p}><path d="M3 8.5l3.2 3.2L13 4.8" strokeLinecap="round" strokeLinejoin="round" /></svg>,
  Plus: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><path d="M8 3v10M3 8h10" strokeLinecap="round" /></svg>,
  Pin: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="currentColor" {...p}><path d="M8 1.5C5.24 1.5 3 3.74 3 6.5c0 4 5 8 5 8s5-4 5-8c0-2.76-2.24-5-5-5zm0 6.7a1.7 1.7 0 1 1 0-3.4 1.7 1.7 0 0 1 0 3.4z" /></svg>,
  Phone: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.6" {...p}><rect x="4" y="1.5" width="8" height="13" rx="1.6" /><path d="M7 12.5h2" /></svg>,
  Wifi: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.6" {...p}><path d="M2 6c3.5-3 8.5-3 12 0M4 8.5c2.2-1.8 5.8-1.8 8 0M6 11c1.2-0.9 2.8-0.9 4 0" strokeLinecap="round" /><circle cx="8" cy="13" r="0.7" fill="currentColor" /></svg>,
  TV: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.6" {...p}><rect x="2" y="3" width="12" height="8" rx="1" /><path d="M5 13h6" /></svg>,
  Building: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><rect x="3" y="2" width="10" height="12" /><path d="M5.5 5h1M9.5 5h1M5.5 8h1M9.5 8h1M5.5 11h1M9.5 11h1" /></svg>,
  Menu: (p) => <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" {...p}><path d="M4 7h16" /><path d="M9 12h11" /><path d="M4 17h16" /></svg>,
  X: (p) => <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" {...p}><path d="M6 6l12 12M18 6L6 18" /></svg>,
  WA: (p) => <svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor" {...p}><path d="M17.6 14.2c-.3-.1-1.7-.8-2-.9s-.5-.1-.7.1c-.2.3-.7.9-.9 1.1-.2.2-.3.2-.6.1-.3-.1-1.2-.5-2.3-1.4-.9-.7-1.4-1.7-1.6-2-.2-.3 0-.5.1-.6l.5-.5c.1-.2.2-.3.3-.5.1-.2 0-.4-.1-.5l-.7-1.7c-.2-.4-.4-.4-.6-.4h-.5c-.2 0-.5.1-.7.4-.3.3-1 1-1 2.4s1 2.8 1.1 3c.2.2 2 3 4.8 4.2.7.3 1.2.5 1.6.6.7.2 1.3.2 1.8.1.5-.1 1.7-.7 1.9-1.4.2-.7.2-1.2.2-1.4-.1-.1-.3-.1-.6-.3zM12 2C6.5 2 2 6.5 2 12c0 1.7.4 3.4 1.3 4.9L2 22l5.3-1.4c1.4.8 3.1 1.2 4.7 1.2 5.5 0 10-4.5 10-10S17.5 2 12 2zm0 18c-1.6 0-3.1-.4-4.4-1.2l-.3-.2-3.2.8.8-3.1-.2-.3C3.7 14.9 3.3 13.4 3.3 12c0-4.8 3.9-8.7 8.7-8.7s8.7 3.9 8.7 8.7-3.9 8-8.7 8z"/></svg>
};

window.useReveal = function () {
  useEffect(() => {
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {if (e.isIntersecting) {e.target.classList.add("in");io.unobserve(e.target);}});
    }, { threshold: 0.12, rootMargin: "0px 0px -6% 0px" });

    const observe = (root) => {
      const els = (root || document).querySelectorAll
        ? (root || document).querySelectorAll(".reveal")
        : [];
      els.forEach((el) => { if (!el.classList.contains("in")) io.observe(el); });
    };

    observe(document);

    // Watch for async-rendered content (e.g. cards added after fetch)
    const mo = new MutationObserver((muts) => {
      for (const m of muts) {
        for (const node of m.addedNodes) {
          if (node.nodeType !== 1) continue;
          if (node.classList && node.classList.contains("reveal")) io.observe(node);
          observe(node);
        }
      }
    });
    mo.observe(document.body, { childList: true, subtree: true });

    // Safety net: any element still hidden after 1.5s gets revealed
    // (covers edge cases where IO doesn't fire — e.g. zero-height parents)
    const safety = setTimeout(() => {
      document.querySelectorAll(".reveal:not(.in)").forEach((el) => el.classList.add("in"));
    }, 1500);

    return () => { io.disconnect(); mo.disconnect(); clearTimeout(safety); };
  }, []);
};

window.Navbar = function Navbar({ active }) {
  const [scrolled, setScrolled] = useState(false);
  const [open, setOpen] = useState(false);
  useEffect(() => {
    const onS = () => setScrolled(window.scrollY > 8);
    window.addEventListener("scroll", onS, { passive: true });
    return () => window.removeEventListener("scroll", onS);
  }, []);
  useEffect(() => {
    if (open) {
      const prev = document.body.style.overflow;
      document.body.style.overflow = "hidden";
      const onKey = (e) => { if (e.key === "Escape") setOpen(false); };
      window.addEventListener("keydown", onKey);
      return () => { document.body.style.overflow = prev; window.removeEventListener("keydown", onKey); };
    }
  }, [open]);
  return (
    <>
      <nav style={{
        position: "fixed",
        top: scrolled ? 8 : 14,
        left: scrolled ? 12 : 16,
        right: scrolled ? 12 : 16,
        zIndex: 100, display: "flex", justifyContent: "center", pointerEvents: "none",
        transition: "top 240ms cubic-bezier(0.32,0.72,0,1), left 240ms cubic-bezier(0.32,0.72,0,1), right 240ms cubic-bezier(0.32,0.72,0,1)",
      }}>
        <div style={{
          width: "100%", maxWidth: 1240,
          height: scrolled ? 54 : 60,
          pointerEvents: "auto",
          background: scrolled ? "rgba(255,255,255,0.72)" : "rgba(255,255,255,0.94)",
          backdropFilter: "saturate(180%) blur(24px)",
          WebkitBackdropFilter: "saturate(180%) blur(24px)",
          borderRadius: 999,
          border: scrolled ? "1px solid rgba(255,255,255,0.45)" : "1px solid var(--line)",
          boxShadow: scrolled
            ? "0 12px 36px -14px rgba(26,16,36,0.18), inset 0 1px 0 rgba(255,255,255,0.6)"
            : "0 4px 14px -8px rgba(26,16,36,0.08)",
          padding: scrolled ? "0 6px 0 18px" : "0 6px 0 22px",
          display: "flex", alignItems: "center", justifyContent: "space-between", gap: 20,
          transition: "background 240ms cubic-bezier(0.32,0.72,0,1), height 240ms cubic-bezier(0.32,0.72,0,1), padding 240ms, border-color 240ms, box-shadow 240ms",
        }}>
          <a href={window.balUrl("index.html")} style={{
            display: "flex", alignItems: "center", flexShrink: 0,
            height: 44,
          }}>
            <img src={window.balUrl("uploads/Logo%20Baldera%20Menu.png")} alt="Baldera Comunicaciones"
              style={{
                display: "block", objectFit: "contain", objectPosition: "left center",
                height: "100%", width: "auto", maxWidth: 200,
              }} />
          </a>
          <div className="bal-nav-desktop" style={{ display: "flex", gap: 30 }}>
            {window.BAL_NAV.map((it) => {
              const a = (active || "").toLowerCase();
              const slug = it.href.replace(/\.html$/, "").toLowerCase();
              const isActive = a !== "" && (a === it.label.toLowerCase() || a === slug);
              return (
                <a key={it.label} href={window.balUrl(it.href)} style={{
                  fontSize: 14, fontWeight: isActive ? 600 : 500,
                  color: isActive ? "var(--brand)" : "var(--ink-2)",
                  position: "relative", padding: "4px 0",
                  transition: "color 140ms, font-weight 140ms",
                }}>
                  {it.label}
                  {isActive && (
                    <span aria-hidden="true" style={{
                      position: "absolute", left: 0, right: 0, bottom: -4, height: 3,
                      background: "var(--brand)", borderRadius: 999,
                      boxShadow: "0 4px 10px -2px rgba(237,15,68,0.55)",
                    }} />
                  )}
                </a>);

            })}
          </div>
          <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
            <a href={window.balUrl("contacto.html")} className="btn btn-brand bal-nav-cta" style={{
              padding: scrolled ? "8px 16px" : "9px 18px",
              fontSize: 13.5, height: scrolled ? 38 : 42,
              transition: "padding 240ms, height 240ms",
            }}>Solicitar <window.Icon.Arrow /></a>
            <button
              className="bal-nav-mobile-btn"
              onClick={() => setOpen(!open)}
              aria-label={open ? "Cerrar menú" : "Abrir menú"}
              aria-expanded={open}
              style={{
                display: "none",
                width: 42, height: 42,
                borderRadius: 999,
                background: open ? "var(--brand)" : "rgba(237,15,68,0.07)",
                border: `1px solid ${open ? "var(--brand)" : "rgba(237,15,68,0.18)"}`,
                color: open ? "#fff" : "var(--brand)",
                cursor: "pointer",
                alignItems: "center", justifyContent: "center",
                transition: "background 220ms ease, color 220ms ease, border-color 220ms ease, box-shadow 220ms, transform 120ms",
                flexShrink: 0,
                boxShadow: open
                  ? "0 8px 20px -8px rgba(237,15,68,0.5)"
                  : "0 1px 2px rgba(26,16,36,0.04)",
                padding: 0,
              }}
              onMouseDown={(e) => { e.currentTarget.style.transform = "scale(0.92)"; }}
              onMouseUp={(e) => { e.currentTarget.style.transform = "scale(1)"; }}
              onMouseLeave={(e) => { e.currentTarget.style.transform = "scale(1)"; }}
              onTouchStart={(e) => { e.currentTarget.style.transform = "scale(0.92)"; }}
              onTouchEnd={(e) => { e.currentTarget.style.transform = "scale(1)"; }}
            >
              {open ? <window.Icon.X /> : <window.Icon.Menu />}
            </button>
          </div>
        </div>
      </nav>
      {open && (
        <>
          <div
            onClick={() => setOpen(false)}
            style={{
              position: "fixed", inset: 0, zIndex: 98,
              background: "rgba(20,12,30,0.32)",
              backdropFilter: "blur(8px)",
              WebkitBackdropFilter: "blur(8px)",
              animation: "balNavFade 220ms ease both",
            }}
          />
          <div
            role="dialog"
            aria-modal="true"
            style={{
              position: "fixed",
              top: scrolled ? 70 : 82,
              left: 12, right: 12,
              zIndex: 99,
              background: "var(--paper)",
              borderRadius: 24,
              border: "1px solid var(--line)",
              boxShadow: "0 32px 64px -24px rgba(26,16,36,0.32), 0 4px 12px -4px rgba(26,16,36,0.08)",
              padding: 16,
              display: "flex", flexDirection: "column", gap: 4,
              maxHeight: "calc(100vh - 100px)",
              overflowY: "auto",
              animation: "balNavDrawer 320ms cubic-bezier(0.32,0.72,0,1) both",
            }}
          >
            {window.BAL_NAV.map((it, i) => {
              const a = (active || "").toLowerCase();
              const slug = it.href.replace(/\.html$/, "").toLowerCase();
              const isActive = a !== "" && (a === it.label.toLowerCase() || a === slug);
              return (
                <a
                  key={it.label}
                  href={window.balUrl(it.href)}
                  onClick={() => setOpen(false)}
                  style={{
                    fontSize: 22, fontWeight: 700, letterSpacing: "-0.02em",
                    padding: "14px 18px",
                    borderRadius: 14,
                    color: isActive ? "var(--brand)" : "var(--ink)",
                    background: isActive ? "rgba(237,15,68,0.06)" : "transparent",
                    display: "flex", alignItems: "center", justifyContent: "space-between",
                    transition: "background 140ms",
                    animation: `balNavItem 360ms cubic-bezier(0.32,0.72,0,1) both`,
                    animationDelay: `${60 + i * 35}ms`,
                  }}
                >
                  <span>{it.label}</span>
                  {isActive ? (
                    <span aria-hidden="true" style={{
                      width: 8, height: 8, borderRadius: 999, background: "var(--brand)",
                      boxShadow: "0 0 0 4px rgba(237,15,68,0.16)",
                    }} />
                  ) : (
                    <window.Icon.Arrow />
                  )}
                </a>
              );
            })}
            <div style={{ height: 1, background: "var(--line)", margin: "10px 4px" }} />
            <a
              href={window.balUrl("contacto.html")}
              onClick={() => setOpen(false)}
              className="btn btn-brand"
              style={{
                height: 52, fontSize: 15, fontWeight: 600,
                justifyContent: "center", borderRadius: 14,
                animation: "balNavItem 360ms cubic-bezier(0.32,0.72,0,1) both",
                animationDelay: `${60 + window.BAL_NAV.length * 35}ms`,
              }}
            >
              Solicitar <window.Icon.Arrow />
            </a>
            <div style={{
              display: "flex", gap: 8, marginTop: 8,
              animation: "balNavItem 360ms cubic-bezier(0.32,0.72,0,1) both",
              animationDelay: `${60 + (window.BAL_NAV.length + 1) * 35}ms`,
            }}>
              <a
                href="tel:+18492209239"
                onClick={() => setOpen(false)}
                style={{
                  flex: 1, height: 46, borderRadius: 12,
                  border: "1px solid var(--line)",
                  display: "flex", alignItems: "center", justifyContent: "center", gap: 8,
                  fontSize: 13, fontWeight: 600, color: "var(--ink-2)",
                  background: "var(--paper-2)",
                }}
              >
                <window.Icon.Phone /> Llamar
              </a>
              <a
                href="https://wa.me/18492209239"
                target="_blank" rel="noopener noreferrer"
                onClick={() => setOpen(false)}
                style={{
                  flex: 1, height: 46, borderRadius: 12,
                  border: "1px solid rgba(37,211,102,0.28)",
                  background: "rgba(37,211,102,0.08)",
                  display: "flex", alignItems: "center", justifyContent: "center", gap: 8,
                  fontSize: 13, fontWeight: 600, color: "#1faa54",
                }}
              >
                <window.Icon.WA /> WhatsApp
              </a>
            </div>
          </div>
        </>
      )}
      <style>{`
        @media (max-width: 900px) {
          .bal-nav-desktop { display: none !important; }
          .bal-nav-mobile-btn { display: flex !important; }
          .bal-nav-cta { display: none !important; }
        }
        @keyframes balNavFade {
          from { opacity: 0; }
          to { opacity: 1; }
        }
        @keyframes balNavDrawer {
          from { opacity: 0; transform: translateY(-12px) scale(0.98); }
          to { opacity: 1; transform: translateY(0) scale(1); }
        }
        @keyframes balNavItem {
          from { opacity: 0; transform: translateY(-6px); }
          to { opacity: 1; transform: translateY(0); }
        }
      `}</style>
    </>);

};

window.Footer = function Footer() {
  const cols = [
  { h: "Planes",     l: [["Internet Hogar", "internet-hogar.html"], ["Triple Play", "planes.html"], ["Móvil", "planes.html"]] },
  { h: "Equipos",    l: [["iPhone", "equipos.html"], ["Samsung", "equipos.html"], ["Accesorios", "equipos.html"], ["Módems", "equipos.html"]] },
  { h: "Servicios",  l: [["Activación", "servicios.html"], ["Portabilidad", "servicios.html"], ["Internet Hogar", "servicios.html"]] },
  { h: "Baldera",    l: [["Sobre nosotros", "nosotros.html"], ["Tiendas", "tiendas.html"], ["Blog", "blog.html"], ["Contacto", "contacto.html"], ["Únete a nuestro equipo", "unete.html"]] }];

  return (
    <footer style={{ background: "var(--ink)", color: "var(--paper)", padding: "100px 0 40px", marginTop: 0 }}>
      <div className="container">
        <div style={{ display: "grid", gridTemplateColumns: "1.4fr 1fr 1fr 1fr 1fr", gap: 48 }} className="footer-grid">
          <div>
            <div style={{ marginBottom: 28 }}>
              <img src={window.balUrl("uploads/Logo%20Blanco.png")} alt="Baldera Comunicaciones" style={{ height: 44, width: "auto", maxWidth: 200, objectFit: "contain", objectPosition: "left center", display: "block" }} />
            </div>
            <p style={{ fontSize: 14, lineHeight: 1.6, color: "rgba(251,248,244,0.62)", maxWidth: 320, margin: 0 }}>
              Distribuidor autorizado Claro RD desde 2001. Conectamos dominicanos con el mundo.
            </p>
            <div style={{ marginTop: 28, display: "flex", gap: 12 }}>
              <a href="#" style={{ width: 38, height: 38, borderRadius: 999, border: "1px solid rgba(251,248,244,0.2)", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 13 }}>IG</a>
              <a href="#" style={{ width: 38, height: 38, borderRadius: 999, border: "1px solid rgba(251,248,244,0.2)", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 13 }}>FB</a>
              <a href="#" style={{ width: 38, height: 38, borderRadius: 999, border: "1px solid rgba(251,248,244,0.2)", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 13 }}>TT</a>
              <a href="#" style={{ width: 38, height: 38, borderRadius: 999, border: "1px solid rgba(251,248,244,0.2)", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 13 }}>WA</a>
            </div>
          </div>
          {cols.map((c) =>
          <div key={c.h}>
              <div style={{ fontSize: 11, fontWeight: 600, letterSpacing: "0.14em", textTransform: "uppercase", color: "rgba(251,248,244,0.5)", marginBottom: 18 }}>{c.h}</div>
              <ul style={{ listStyle: "none", padding: 0, margin: 0, display: "flex", flexDirection: "column", gap: 12 }}>
                {c.l.map(([li, h]) =>
              <li key={li}><a href={window.balUrl(h)} style={{ fontSize: 14, color: "var(--paper)", opacity: 0.78 }} className="u-hover">{li}</a></li>
              )}
              </ul>
            </div>
          )}
        </div>
        <div style={{ height: 1, background: "rgba(251,248,244,0.14)", margin: "64px 0 28px" }} />
        <div style={{ display: "flex", justifyContent: "space-between", gap: 20, fontSize: 12, color: "rgba(251,248,244,0.5)", flexWrap: "wrap" }}>
          <div>© {new Date().getFullYear()} Baldera Comunicaciones · Distribuidor Autorizado Claro RD</div>
          <div style={{ display: "flex", gap: 18, flexWrap: "wrap", alignItems: "center" }}>
            <a href={window.balUrl("privacidad.html")} target="_blank" rel="noopener" style={{ color: "inherit" }} className="u-hover">Privacidad</a>
            <a href={window.balUrl("terminos.html")}   target="_blank" rel="noopener" style={{ color: "inherit" }} className="u-hover">Términos</a>
            <a href={window.balUrl("cookies.html")}    target="_blank" rel="noopener" style={{ color: "inherit" }} className="u-hover">Cookies</a>
            <a href="#" onClick={(e) => { e.preventDefault(); window.balConsent && window.balConsent.open(); }}
               style={{ color: "inherit" }} className="u-hover">Preferencias</a>
            <span aria-hidden="true" style={{ opacity: 0.35 }}>·</span>
            <a href={window.balUrl("iniciar-sesion.html")} rel="nofollow"
               title="Iniciar sesión — acceso administrativo"
               aria-label="Iniciar sesión"
               style={{ color: "inherit", display: "inline-flex", alignItems: "center", gap: 6, opacity: 0.7 }}
               className="u-hover">
              <svg width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" aria-hidden="true">
                <rect x="3" y="7" width="10" height="7" rx="1.5" />
                <path d="M5.5 7V4.5a2.5 2.5 0 0 1 5 0V7" strokeLinecap="round" />
              </svg>
              Iniciar sesión
            </a>
          </div>
        </div>
      </div>
      <style>{`
        @media (max-width: 900px) {
          .footer-grid { grid-template-columns: 1fr 1fr !important; gap: 32px !important; }
        }
        @media (max-width: 560px) {
          .footer-grid { grid-template-columns: 1fr !important; }
        }
      `}</style>
    </footer>);

};

window.Marquee = function Marquee({ items, speed = 38, color = "var(--ink)" }) {
  return (
    <div className="marquee-track" style={{ background: "transparent", padding: "20px 0", borderTop: "1px solid var(--line)", borderBottom: "1px solid var(--line)" }}>
      <div className="marquee" style={{ animationDuration: `${speed}s` }}>
        {[...items, ...items].map((it, i) =>
        <span key={i} style={{ display: "inline-flex", alignItems: "center", gap: 48, fontSize: "clamp(28px, 4vw, 56px)", fontWeight: 700, letterSpacing: "-0.025em", color }}>
            {it}
            <span style={{ display: "inline-block", width: 14, height: 14, borderRadius: 999, background: "var(--brand)", flexShrink: 0 }} />
          </span>
        )}
      </div>
    </div>);

};

/**
 * Reusable LeadForm modal. Open with window.openLeadForm({ kind, id, name, slug, price, extra, sourceTag })
 * On submit:
 *   1. POST to /admin/api/lead_submit.php with full context
 *   2. On success, opens WhatsApp with a structured message
 *   3. Closes modal
 */
window.LeadFormState = (() => {
  const listeners = new Set();
  let current = null;
  return {
    open(payload) { current = payload; listeners.forEach(fn => fn(current)); },
    close() { current = null; listeners.forEach(fn => fn(null)); },
    subscribe(fn) { listeners.add(fn); return () => listeners.delete(fn); },
    get current() { return current; },
  };
})();

window.openLeadForm = (payload) => window.LeadFormState.open(payload);

window.LeadFormModal = function LeadFormModal() {
  const [ctx, setCtx] = useState(window.LeadFormState.current);
  const [form, setForm] = useState({ name: "", phone: "", email: "", message: "" });
  const [status, setStatus] = useState("idle"); // idle | sending | sent | error
  const [error, setError]   = useState(null);
  const [site, setSite]     = useState(window.SITE || {});

  useEffect(() => {
    const u1 = window.LeadFormState.subscribe((p) => {
      setCtx(p);
      if (p) {
        setForm({ name: "", phone: "", email: "", message: p.defaultMessage || "" });
        setStatus("idle");
        setError(null);
      }
    });
    const u2 = () => setSite(window.SITE || {});
    window.addEventListener("baldera-site-ready", u2);
    const onKey = (e) => { if (e.key === "Escape") window.LeadFormState.close(); };
    window.addEventListener("keydown", onKey);
    return () => { u1(); window.removeEventListener("baldera-site-ready", u2); window.removeEventListener("keydown", onKey); };
  }, []);

  if (!ctx) return null;

  const close = () => window.LeadFormState.close();
  const isPlan = ctx.kind === "plan";

  const submit = async (e) => {
    e.preventDefault();
    setError(null);
    if (!form.name.trim()) { setError("Pon tu nombre."); return; }
    if (!form.phone.trim() && !form.email.trim()) { setError("Necesitamos teléfono o email."); return; }
    setStatus("sending");

    const itemLine = ctx.name + (ctx.extra && Object.keys(ctx.extra).length
      ? ' — ' + Object.values(ctx.extra).filter(Boolean).join(' · ')
      : '');
    const priceLine = ctx.price > 0 ? `RD$${Math.round(ctx.price).toLocaleString()}` : 'a consultar';
    const composed = `[${isPlan ? 'Plan' : 'Equipo'}] ${itemLine} (${priceLine})${form.message.trim() ? ' — ' + form.message.trim() : ''}`;

    try {
      const r = await fetch(window.balUrl("admin/api/lead_submit.php"), {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          name: form.name, phone: form.phone, email: form.email,
          message: composed,
          source: ctx.sourceTag || (isPlan ? 'plan-form' : 'equipo-form'),
          page: location.pathname + location.search,
          item_kind: ctx.kind,
          item_id: ctx.id || 0,
          item_name: ctx.name,
          item_slug: ctx.slug,
          item_price: ctx.price || 0,
          item_meta: ctx.extra || null,
          _hp: form._hp || "",
        }),
      });
      const d = await r.json();
      if (!r.ok || !d.ok) throw new Error(d.error || "fail");

      // Open WhatsApp with formatted message
      const phone = (site.whatsapp || '+18492209239').replace(/[^\d+]/g, '');
      const lines = [
        `*Solicitud nueva — ${isPlan ? 'Plan' : 'Equipo'}*`,
        '',
        `*${ctx.name}*`,
      ];
      if (ctx.extra && Object.keys(ctx.extra).length) {
        for (const [k, v] of Object.entries(ctx.extra)) {
          if (v) lines.push(`${k}: ${v}`);
        }
      }
      if (ctx.price > 0) lines.push(`Precio: RD$${Math.round(ctx.price).toLocaleString()}${isPlan ? '/mes' : ''}`);
      lines.push('');
      lines.push(`Cliente: ${form.name}`);
      if (form.phone) lines.push(`Tel: ${form.phone}`);
      if (form.email) lines.push(`Email: ${form.email}`);
      if (form.message.trim()) { lines.push(''); lines.push(`Nota: ${form.message.trim()}`); }
      lines.push('');
      lines.push(`Lead #${d.id}`);

      const url = `https://wa.me/${phone.replace('+','')}?text=${encodeURIComponent(lines.join('\n'))}`;
      setStatus("sent");
      // Small delay so user sees confirmation before tab opens
      setTimeout(() => window.open(url, '_blank', 'noopener'), 400);
    } catch (err) {
      setStatus("error");
      setError(err.message);
    }
  };

  return (
    <>
      <div onClick={close} style={{
        position: "fixed", inset: 0, background: "rgba(14,14,16,0.55)",
        backdropFilter: "blur(6px)", WebkitBackdropFilter: "blur(6px)", zIndex: 1000,
        animation: "balFade 200ms ease-out",
      }} />
      <div role="dialog" aria-modal="true" aria-label="Formulario de solicitud" style={{
        position: "fixed", top: "50%", left: "50%", transform: "translate(-50%, -50%)",
        width: "min(560px, 92vw)", maxHeight: "90vh", overflowY: "auto",
        background: "var(--paper)", borderRadius: 24, zIndex: 1001,
        boxShadow: "0 40px 100px -32px rgba(20,17,15,0.50), 0 8px 24px -8px rgba(20,17,15,0.18), inset 0 1px 0 rgba(255,255,255,0.8)",
        animation: "balPop 320ms cubic-bezier(0.16,1,0.3,1)",
        overflow: "hidden",
      }}>
        <header style={{
          display: "flex", justifyContent: "space-between", alignItems: "flex-start",
          padding: "22px 26px",
          background: isPlan
            ? "linear-gradient(135deg, #fff 0%, #fff 50%, #FFF7F8 100%)"
            : "linear-gradient(135deg, #fff 0%, #fff 50%, #F5F8FF 100%)",
          borderBottom: "1px solid var(--line)",
          position: "relative",
        }}>
          <div aria-hidden="true" style={{
            position: "absolute", top: 0, right: 0, width: 200, height: 100,
            background: isPlan
              ? "radial-gradient(circle at top right, rgba(237,15,68,0.10), transparent 70%)"
              : "radial-gradient(circle at top right, rgba(27,77,140,0.10), transparent 70%)",
            pointerEvents: "none",
          }} />
          <div style={{ position: "relative" }}>
            <div className="mini" style={{
              color: "var(--brand)", marginBottom: 6,
              display: "inline-flex", alignItems: "center", gap: 6,
            }}>
              <span style={{ width: 6, height: 6, borderRadius: 999, background: "var(--brand)" }} />
              {isPlan ? "Solicitar plan" : "Solicitar equipo"}
            </div>
            <h2 style={{
              margin: 0, fontSize: 20, fontWeight: 700, letterSpacing: "-0.018em",
              maxWidth: 380,
            }}>{ctx.name}</h2>
          </div>
          <button onClick={close} aria-label="Cerrar" style={{
            width: 36, height: 36, borderRadius: 999, background: "var(--paper-2)",
            border: 0, color: "var(--ink-2)", fontSize: 18, cursor: "pointer",
            display: "grid", placeItems: "center",
            transition: "background 140ms, transform 140ms",
            position: "relative",
          }}
            onMouseOver={(e) => { e.currentTarget.style.background = "var(--paper-3)"; e.currentTarget.style.transform = "rotate(90deg)"; }}
            onMouseOut={(e)  => { e.currentTarget.style.background = "var(--paper-2)"; e.currentTarget.style.transform = ""; }}
          >
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.8">
              <path d="M3 3l10 10M13 3L3 13" strokeLinecap="round" />
            </svg>
          </button>
        </header>

        <div style={{ padding: 24 }}>
          {ctx.extra && Object.keys(ctx.extra).length > 0 && (
            <div style={{
              padding: "12px 16px",
              background: "linear-gradient(135deg, var(--paper-2), var(--paper-3))",
              borderRadius: 14, border: "1px solid var(--line)",
              marginBottom: 20, fontSize: 13, color: "var(--ink-2)",
              display: "flex", flexWrap: "wrap", gap: "8px 16px",
              alignItems: "center",
            }}>
              {Object.entries(ctx.extra).map(([k, v]) =>
                v ? (
                  <span key={k} style={{ display: "inline-flex", alignItems: "center", gap: 4 }}>
                    <span style={{ color: "var(--ink-4)", fontSize: 11.5, textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600 }}>{k}</span>
                    <strong style={{ color: "var(--ink)", fontWeight: 600 }}>{v}</strong>
                  </span>
                ) : null
              )}
              {ctx.price > 0 && (
                <span style={{
                  marginLeft: "auto", display: "inline-flex", alignItems: "baseline", gap: 4,
                  color: "var(--brand)", fontWeight: 700, fontSize: 16,
                  fontVariantNumeric: "tabular-nums",
                }}>
                  RD${Math.round(ctx.price).toLocaleString()}
                  <small style={{ fontSize: 11, opacity: 0.7, fontWeight: 500 }}>{isPlan ? "/mes" : ""}</small>
                </span>
              )}
            </div>
          )}

          {status === "sent" ? (
            <div style={{ textAlign: "center", padding: "32px 0", animation: "balFade 400ms ease-out" }}>
              <div aria-hidden="true" style={{
                width: 72, height: 72, margin: "0 auto 18px",
                borderRadius: 999, background: "linear-gradient(135deg, #25D366, #1eb155)",
                display: "grid", placeItems: "center",
                boxShadow: "0 18px 40px -10px rgba(37,211,102,0.55)",
                animation: "balCheckPop 600ms cubic-bezier(0.16,1,0.3,1) both",
              }}>
                <svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
                  <path d="M5 12.5l4.5 4.5L19 7" style={{
                    strokeDasharray: 30, strokeDashoffset: 30,
                    animation: "balDraw 500ms 250ms cubic-bezier(0.16,1,0.3,1) forwards",
                  }} />
                </svg>
              </div>
              <h3 style={{ margin: 0, fontSize: 22, fontWeight: 700, letterSpacing: "-0.018em" }}>
                ¡Solicitud recibida!
              </h3>
              <p style={{ marginTop: 10, color: "var(--ink-3)", fontSize: 14, maxWidth: 360, marginInline: "auto", lineHeight: 1.55 }}>
                Te conectamos con un asesor por WhatsApp para terminar la gestión. Abriendo chat…
              </p>
            </div>
          ) : (
            <form onSubmit={submit} style={{ display: "flex", flexDirection: "column", gap: 14 }} noValidate>
              {/* honeypot — bots fill this; humans don't see it */}
              <input type="text" name="_hp" tabIndex={-1} autoComplete="off" aria-hidden="true"
                     value={form._hp || ""} onChange={(e) => setForm({ ...form, _hp: e.target.value })}
                     style={{ position: "absolute", left: "-9999px", width: 1, height: 1, opacity: 0 }} />
              <Field2 label="Nombre *" required value={form.name} onChange={(v) => setForm({ ...form, name: v })} />
              <Field2 label="Teléfono / WhatsApp" type="tel" value={form.phone} onChange={(v) => setForm({ ...form, phone: v })} placeholder="+1 809..." />
              <Field2 label="Email" type="email" value={form.email} onChange={(v) => setForm({ ...form, email: v })} />
              <div>
                <label className="mini" style={{ color: "var(--ink-3)", display: "block", marginBottom: 6 }}>
                  Comentario (opcional)
                </label>
                <textarea value={form.message} onChange={(e) => setForm({ ...form, message: e.target.value })} rows={3}
                  placeholder={isPlan ? "Dirección de instalación, hora ideal..." : "Forma de pago, otra variante..."}
                  style={{
                    width: "100%", padding: "12px 14px", borderRadius: 12, border: "1px solid var(--line)",
                    fontSize: 14, fontFamily: "inherit", resize: "vertical", background: "#fff",
                  }} />
              </div>

              {error && (
                <div style={{ background: "#FCE7E9", color: "var(--brand-deep)", padding: 10, borderRadius: 10, fontSize: 13 }}>
                  ⚠️ {error}
                </div>
              )}

              <div style={{ display: "flex", gap: 10, justifyContent: "flex-end", marginTop: 6 }}>
                <button type="button" onClick={close} style={{
                  padding: "10px 16px", borderRadius: 999, background: "#fff",
                  border: "1px solid var(--line)", cursor: "pointer", fontFamily: "inherit", fontSize: 13.5,
                }}>Cancelar</button>
                <button type="submit" disabled={status === "sending"} style={{
                  padding: "12px 22px", borderRadius: 999, background: "#25D366",
                  color: "#fff", border: 0, cursor: "pointer", fontFamily: "inherit",
                  fontSize: 14, fontWeight: 600, opacity: status === "sending" ? 0.6 : 1,
                  display: "inline-flex", alignItems: "center", gap: 8,
                }}>
                  {status === "sending" ? "Enviando…" : "Enviar y abrir WhatsApp"}
                  <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
                    <path d="M20.52 3.48A11.94 11.94 0 0 0 12.04 0C5.46 0 .14 5.32.14 11.91c0 2.1.55 4.15 1.6 5.96L0 24l6.27-1.65a11.9 11.9 0 0 0 5.77 1.47h.01c6.58 0 11.9-5.32 11.9-11.91 0-3.18-1.24-6.17-3.43-8.43z"/>
                  </svg>
                </button>
              </div>
              <p className="caption" style={{ margin: 0, fontSize: 12, color: "var(--ink-4)", textAlign: "center" }}>
                Quedará registrado en nuestro sistema. Un asesor te contactará en menos de 5 min hábiles.
              </p>
            </form>
          )}
        </div>
      </div>
      <style>{`
        @keyframes balFade { from { opacity: 0; } to { opacity: 1; } }
        @keyframes balPop  {
          from { opacity: 0; transform: translate(-50%, -46%) scale(0.94); }
          to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
        }
        @keyframes balCheckPop {
          0%   { transform: scale(0); opacity: 0; }
          60%  { transform: scale(1.08); opacity: 1; }
          100% { transform: scale(1); opacity: 1; }
        }
        @keyframes balDraw {
          to { stroke-dashoffset: 0; }
        }
        @media (max-width: 520px) {
          .bal-lead-row { grid-template-columns: 1fr !important; }
        }
        @media (prefers-reduced-motion: reduce) {
          [role="dialog"], [aria-modal="true"] { animation: none !important; }
        }
      `}</style>
    </>
  );
};

function Field2({ label, value, onChange, type = "text", required = false, placeholder = "" }) {
  const [focus, setFocus] = useState(false);
  return (
    <div>
      <label className="mini" style={{
        color: focus ? "var(--brand)" : "var(--ink-3)",
        display: "block", marginBottom: 6,
        transition: "color 140ms",
      }}>{label}</label>
      <input type={type} value={value} onChange={(e) => onChange(e.target.value)}
        required={required} placeholder={placeholder}
        onFocus={() => setFocus(true)} onBlur={() => setFocus(false)}
        style={{
          width: "100%", padding: "12px 14px", borderRadius: 12,
          border: "1px solid " + (focus ? "var(--brand)" : "var(--line)"),
          fontSize: 14, fontFamily: "inherit", background: "#fff",
          boxShadow: focus ? "0 0 0 4px rgba(237,15,68,0.15)" : "none",
          outline: "none", transition: "border-color 140ms, box-shadow 140ms",
        }} />
    </div>
  );
}

window.PageScaffold = function PageScaffold({ active, children }) {
  window.useReveal();
  return (
    <>
      <a href="#bal-main" className="bal-skip-link">Saltar al contenido</a>
      <window.Navbar active={active} />
      <main id="bal-main" tabIndex={-1}>{children}</main>
      <window.Footer />
      <window.WhatsAppFAB />
      <window.LeadFormModal />
    </>);

};

window.CartBadge = function CartBadge() {
  const [n, setN] = useState(window.Cart ? window.Cart.count() : 0);
  useEffect(() => {
    const sync = () => setN(window.Cart ? window.Cart.count() : 0);
    sync();
    window.addEventListener('baldera-cart-change', sync);
    window.addEventListener('storage', sync);
    return () => { window.removeEventListener('baldera-cart-change', sync); window.removeEventListener('storage', sync); };
  }, []);
  if (n <= 0) return null;
  return (
    <a href={window.balUrl("carrito.html")} style={{
      position: "relative", display: "inline-flex", alignItems: "center", justifyContent: "center",
      padding: "8px 12px", borderRadius: 999, background: "var(--paper-2)", color: "var(--ink)",
      fontSize: 13, fontWeight: 600, gap: 6, marginRight: 4,
    }}>
      <window.Icon.Bag />
      <span>{n}</span>
    </a>
  );
};

// Imperative API — other components call window.openContactFAB(prefilledMessage)
// to pop the FAB open with a starter message already in the textarea.
window.ContactFABState = (() => {
  const listeners = new Set();
  return {
    open(message) { listeners.forEach(fn => fn(message || "")); },
    subscribe(fn) { listeners.add(fn); return () => listeners.delete(fn); },
  };
})();
window.openContactFAB = (message) => window.ContactFABState.open(message);

window.WhatsAppFAB = function WhatsAppFAB() {
  const [open, setOpen] = useState(false);
  const [site, setSite] = useState(window.SITE || {});
  const [message, setMessage] = useState("");
  const [activeService, setActiveService] = useState(null);
  const textareaRef = useRef(null);

  useEffect(() => {
    const u = () => setSite(window.SITE || {});
    window.addEventListener('baldera-site-ready', u);
    const onKey = (e) => { if (e.key === "Escape") setOpen(false); };
    window.addEventListener('keydown', onKey);
    // Subscribe to imperative opens (e.g. Servicios "Solicitar" buttons).
    const unsubFab = window.ContactFABState.subscribe((msg) => {
      setOpen(true);
      setMessage(msg || "");
      setActiveService(null);
      // Focus textarea + put caret at end after the dialog mounts.
      requestAnimationFrame(() => {
        const ta = textareaRef.current;
        if (ta) { ta.focus(); ta.setSelectionRange(ta.value.length, ta.value.length); }
      });
    });
    return () => {
      window.removeEventListener('baldera-site-ready', u);
      window.removeEventListener('keydown', onKey);
      unsubFab();
    };
  }, []);

  const phone = (site.whatsapp || '+18492209239');
  const email = (site.email   || 'ventas@baldera.com.do');

  // Services — each inserts a starter message in the textarea. User edits, picks channel.
  const services = [
    { key: "equipo",  label: "Info de un equipo",   icon: "📱", msg: "Hola Baldera, me interesa un equipo. ¿Pueden ayudarme con disponibilidad, precio y financiamiento?" },
    { key: "plan",    label: "Asesoría de plan",    icon: "📡", msg: "Hola, necesito asesoría sobre un plan Claro. Cuéntenme las opciones disponibles para mi caso." },
    { key: "porta",   label: "Pasar mi número",     icon: "🔄", msg: "Hola, quiero hacer portabilidad a Claro. ¿Qué necesito y cuánto demora el proceso?" },
    { key: "soporte", label: "Soporte técnico",     icon: "🛠️", msg: "Hola, necesito soporte técnico con un servicio que ya tengo activo." },
  ];

  function pickService(s) {
    setActiveService(s.key);
    setMessage(s.msg);
    // Focus + put caret at end so user can keep typing immediately
    requestAnimationFrame(() => {
      const ta = textareaRef.current;
      if (ta) { ta.focus(); ta.setSelectionRange(s.msg.length, s.msg.length); }
    });
  }

  const finalMessage = (message || "Hola Baldera, quiero hablar con un asesor.").trim();
  const canSend = message.trim().length > 0;

  function sendWhatsApp() {
    if (typeof window.openWhatsApp === "function") {
      window.openWhatsApp(finalMessage, phone);
    } else {
      const cleanPhone = String(phone).replace(/[^\d+]/g, "").replace(/^\+/, "");
      window.open(`https://wa.me/${cleanPhone}?text=${encodeURIComponent(finalMessage)}`, "_blank", "noopener");
    }
    setOpen(false);
  }
  function sendEmail() {
    const subject = "Solicitud desde la web — Baldera";
    const href = `mailto:${email}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(finalMessage)}`;
    // Use location.href so mailto opens in the same context (better mobile support).
    window.location.href = href;
    setOpen(false);
  }
  function reset() {
    setMessage("");
    setActiveService(null);
    requestAnimationFrame(() => textareaRef.current?.focus());
  }

  return (
    <>
      {open && (
        <div onClick={() => setOpen(false)} style={{
          position: "fixed", inset: 0, background: "rgba(14,14,16,0.42)",
          backdropFilter: "blur(3px)", WebkitBackdropFilter: "blur(3px)", zIndex: 998,
          animation: "balFade 180ms ease-out",
        }} />
      )}

      <div style={{
        position: "fixed", right: 22, bottom: 22, zIndex: 999,
        display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 12,
      }}>
        {open && (
          <div role="dialog" aria-modal="true" aria-labelledby="bal-fab-title" style={{
            width: 360, maxWidth: "calc(100vw - 44px)",
            background: "#fff", borderRadius: 22, overflow: "hidden",
            boxShadow: "0 20px 60px -16px rgba(20,17,15,0.30), 0 4px 12px rgba(20,17,15,0.10)",
            border: "1px solid var(--line)",
            animation: "balFadeUp 240ms cubic-bezier(0.16,1,0.3,1) both",
            display: "flex", flexDirection: "column",
            maxHeight: "calc(100vh - 120px)",
          }}>
            {/* Header */}
            <div style={{ background: "#075E54", color: "#fff", padding: "14px 18px", flexShrink: 0 }}>
              <div style={{ display: "flex", gap: 12, alignItems: "center", justifyContent: "space-between" }}>
                <div style={{ display: "flex", gap: 12, alignItems: "center", minWidth: 0 }}>
                  <div style={{
                    width: 38, height: 38, borderRadius: 999, background: "#25D366",
                    display: "grid", placeItems: "center", fontSize: 18, flexShrink: 0,
                  }}>💬</div>
                  <div style={{ minWidth: 0 }}>
                    <strong id="bal-fab-title" style={{ display: "block", fontSize: 14.5, letterSpacing: "-0.005em" }}>{site.name || "Baldera"}</strong>
                    <small style={{ opacity: 0.85, fontSize: 11.5 }}>● En línea · responde rápido</small>
                  </div>
                </div>
                <button onClick={() => setOpen(false)} aria-label="Cerrar"
                        style={{
                          width: 30, height: 30, borderRadius: 999,
                          background: "rgba(255,255,255,0.12)", color: "#fff",
                          border: 0, cursor: "pointer", display: "grid", placeItems: "center",
                          flexShrink: 0,
                        }}>
                  <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2">
                    <path d="M3 3l10 10M13 3L3 13" strokeLinecap="round" />
                  </svg>
                </button>
              </div>
            </div>

            {/* Body — scrollable middle */}
            <div style={{ padding: 14, background: "#ECE5DD", overflowY: "auto", flex: "1 1 auto" }}>
              <div style={{
                background: "#fff", padding: "10px 14px", borderRadius: 14,
                fontSize: 13.5, color: "#0e0e10", marginBottom: 12,
                boxShadow: "0 1px 0.5px rgba(0,0,0,0.13)", lineHeight: 1.4,
              }}>
                <strong>Hola 👋</strong> Elige un servicio para empezar, edita el mensaje si quieres, y elige cómo enviarlo.
              </div>

              {/* Service chips */}
              <div role="group" aria-label="Servicios" style={{
                display: "flex", flexWrap: "wrap", gap: 6, marginBottom: 12,
              }}>
                {services.map((s) => {
                  const isActive = activeService === s.key;
                  return (
                    <button key={s.key} type="button" onClick={() => pickService(s)}
                            aria-pressed={isActive}
                            style={{
                              display: "inline-flex", alignItems: "center", gap: 6,
                              padding: "7px 11px", borderRadius: 999,
                              background: isActive ? "#0e0e10" : "#fff",
                              color: isActive ? "#fff" : "#0e0e10",
                              border: isActive ? "1px solid #0e0e10" : "1px solid rgba(0,0,0,0.08)",
                              cursor: "pointer", fontFamily: "inherit",
                              fontSize: 12.5, fontWeight: 500,
                              transition: "background 140ms, color 140ms, border-color 140ms",
                            }}
                            onMouseOver={(e) => { if (!isActive) e.currentTarget.style.background = "#DCF8C6"; }}
                            onMouseOut={(e)  => { if (!isActive) e.currentTarget.style.background = "#fff"; }}
                    >
                      <span aria-hidden="true">{s.icon}</span>
                      <span>{s.label}</span>
                    </button>
                  );
                })}
              </div>

              {/* Editable message */}
              <label htmlFor="bal-fab-msg" style={{
                display: "block", fontSize: 11, fontWeight: 600,
                letterSpacing: "0.1em", textTransform: "uppercase",
                color: "rgba(0,0,0,0.55)", margin: "8px 4px 6px",
              }}>Tu mensaje</label>
              <div style={{ position: "relative" }}>
                <textarea id="bal-fab-msg" ref={textareaRef}
                          value={message} onChange={(e) => setMessage(e.target.value)}
                          rows={5}
                          placeholder="Escribe tu mensaje aquí, o elige uno arriba para empezar…"
                          style={{
                            width: "100%", padding: "12px 14px", borderRadius: 14,
                            border: "1px solid rgba(0,0,0,0.08)",
                            background: "#fff", fontFamily: "inherit", fontSize: 13.5,
                            resize: "vertical", minHeight: 96, maxHeight: 200,
                            color: "#0e0e10", lineHeight: 1.45,
                            boxShadow: "0 1px 0.5px rgba(0,0,0,0.13)",
                            outline: "none",
                            transition: "border-color 140ms, box-shadow 140ms",
                          }}
                          onFocus={(e) => { e.currentTarget.style.borderColor = "#25D366"; e.currentTarget.style.boxShadow = "0 0 0 3px rgba(37,211,102,0.18)"; }}
                          onBlur={(e)  => { e.currentTarget.style.borderColor = "rgba(0,0,0,0.08)"; e.currentTarget.style.boxShadow = "0 1px 0.5px rgba(0,0,0,0.13)"; }} />
                {message.trim().length > 0 && (
                  <button type="button" onClick={reset}
                          aria-label="Limpiar mensaje"
                          style={{
                            position: "absolute", top: 8, right: 8,
                            width: 24, height: 24, borderRadius: 999,
                            background: "rgba(0,0,0,0.06)", color: "rgba(0,0,0,0.55)",
                            border: 0, cursor: "pointer", fontFamily: "inherit",
                            fontSize: 14, lineHeight: 1, display: "grid", placeItems: "center",
                          }}
                          title="Limpiar">×</button>
                )}
              </div>
              <p style={{
                margin: "6px 4px 0", fontSize: 11, color: "rgba(0,0,0,0.55)",
                fontVariantNumeric: "tabular-nums",
              }}>
                {message.length} {message.length === 1 ? "carácter" : "caracteres"}
              </p>
            </div>

            {/* Footer — channel actions.
                Flex with `flex:1 1 0` forces a strict 50/50 split regardless
                of intrinsic content width (grid 1fr 1fr was honoring auto
                min-content on the WhatsApp button, leaving it pill-sized). */}
            <div className="bal-fab-actions" style={{
              padding: 12, background: "#fff",
              borderTop: "1px solid var(--line)",
              display: "flex", gap: 8,
              flexShrink: 0, alignItems: "stretch",
            }}>
              <button type="button" onClick={sendWhatsApp}
                      aria-label="Enviar por WhatsApp"
                      style={{
                        flex: "1 1 0", minWidth: 0,
                        display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 6,
                        padding: "12px 10px", borderRadius: 999,
                        background: "#25D366", color: "#fff", border: 0, cursor: "pointer",
                        fontFamily: "inherit", fontSize: 13, fontWeight: 600,
                        whiteSpace: "nowrap", lineHeight: 1.2,
                        boxShadow: "0 6px 16px -6px rgba(37,211,102,0.55)",
                        transition: "transform 120ms, filter 140ms",
                      }}
                      onMouseDown={(e) => e.currentTarget.style.transform = "scale(0.97)"}
                      onMouseUp={(e) => e.currentTarget.style.transform = ""}
                      onMouseLeave={(e) => e.currentTarget.style.transform = ""}>
                <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" style={{ flexShrink: 0 }}>
                  <path d="M20.52 3.48A11.94 11.94 0 0 0 12.04 0C5.46 0 .14 5.32.14 11.91c0 2.1.55 4.15 1.6 5.96L0 24l6.27-1.65a11.9 11.9 0 0 0 5.77 1.47h.01c6.58 0 11.9-5.32 11.9-11.91 0-3.18-1.24-6.17-3.43-8.43z"/>
                </svg>
                <span>WhatsApp</span>
              </button>
              <button type="button" onClick={sendEmail}
                      aria-label="Enviar por email"
                      style={{
                        flex: "1 1 0", minWidth: 0,
                        display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 6,
                        padding: "12px 10px", borderRadius: 999,
                        background: "var(--ink)", color: "#fff", border: 0, cursor: "pointer",
                        fontFamily: "inherit", fontSize: 13, fontWeight: 600,
                        whiteSpace: "nowrap", lineHeight: 1.2,
                        boxShadow: "0 6px 16px -6px rgba(20,17,15,0.30)",
                        transition: "transform 120ms, background 140ms",
                      }}
                      onMouseOver={(e) => e.currentTarget.style.background = "var(--brand)"}
                      onMouseOut={(e)  => e.currentTarget.style.background = "var(--ink)"}
                      onMouseDown={(e) => e.currentTarget.style.transform = "scale(0.97)"}
                      onMouseUp={(e) => e.currentTarget.style.transform = ""}>
                <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.8" aria-hidden="true" style={{ flexShrink: 0 }}>
                  <path d="M2 4h12v8H2z" strokeLinejoin="round" />
                  <path d="M2 4l6 5 6-5" strokeLinejoin="round" />
                </svg>
                <span>Email</span>
              </button>
            </div>
          </div>
        )}

        <button onClick={() => setOpen(!open)}
                aria-label={open ? "Cerrar contacto rápido" : "Abrir contacto rápido"}
                aria-expanded={open}
                style={{
                  width: 60, height: 60, borderRadius: 999, border: 0,
                  background: "#25D366", color: "#fff", cursor: "pointer",
                  boxShadow: "0 12px 28px -8px rgba(37,211,102,0.55), 0 4px 8px rgba(0,0,0,0.12)",
                  display: "grid", placeItems: "center",
                  transition: "transform 200ms cubic-bezier(0.32,0.72,0,1)",
                }}
                onMouseOver={(e) => e.currentTarget.style.transform = "scale(1.06)"}
                onMouseOut={(e) => e.currentTarget.style.transform = "scale(1)"}
        >
          {open ? (
            <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" aria-hidden="true"><path d="M5 5l14 14M19 5L5 19"/></svg>
          ) : (
            <svg width="28" height="28" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
              <path d="M20.52 3.48A11.94 11.94 0 0 0 12.04 0C5.46 0 .14 5.32.14 11.91c0 2.1.55 4.15 1.6 5.96L0 24l6.27-1.65a11.9 11.9 0 0 0 5.77 1.47h.01c6.58 0 11.9-5.32 11.9-11.91 0-3.18-1.24-6.17-3.43-8.43zM12.04 21.79h-.01a9.87 9.87 0 0 1-5.03-1.38l-.36-.21-3.72.97.99-3.62-.23-.37a9.86 9.86 0 0 1-1.51-5.27c0-5.46 4.45-9.91 9.92-9.91 2.65 0 5.14 1.03 7.01 2.9a9.86 9.86 0 0 1 2.9 7.02c0 5.47-4.44 9.92-9.96 9.92zm5.45-7.43c-.3-.15-1.77-.87-2.04-.97-.27-.1-.47-.15-.67.15-.2.3-.77.97-.95 1.17-.17.2-.35.22-.65.07-.3-.15-1.27-.47-2.42-1.5-.9-.8-1.5-1.78-1.67-2.08-.17-.3-.02-.46.13-.61.13-.13.3-.35.45-.52.15-.18.2-.3.3-.5.1-.2.05-.37-.02-.52-.07-.15-.67-1.62-.92-2.22-.24-.58-.49-.5-.67-.51l-.57-.01c-.2 0-.52.07-.8.37s-1.05 1.02-1.05 2.49 1.07 2.89 1.22 3.09c.15.2 2.1 3.21 5.08 4.5.71.3 1.27.5 1.7.64.71.22 1.36.19 1.87.12.57-.08 1.77-.72 2.02-1.42.25-.7.25-1.3.17-1.42-.07-.13-.27-.2-.57-.35z"/>
            </svg>
          )}
        </button>
      </div>

      <style>{`
        @keyframes balFadeUp {
          from { opacity: 0; transform: translateY(8px); }
          to   { opacity: 1; transform: translateY(0); }
        }
        /* Channel buttons: enforce 50/50 with flex:1 1 0 so the WhatsApp
           pill never collapses to icon-only when ext. CSS overrides leak in. */
        .bal-fab-actions { display: flex !important; }
        .bal-fab-actions > button { flex: 1 1 0 !important; min-width: 0 !important; white-space: nowrap !important; }
        .bal-fab-actions > button > svg  { flex-shrink: 0; }
        .bal-fab-actions > button > span { overflow: hidden; text-overflow: ellipsis; }
        @media (max-width: 340px) {
          .bal-fab-actions { flex-direction: column !important; }
        }
        @media (prefers-reduced-motion: reduce) {
          [role="dialog"][aria-modal="true"] { animation: none !important; }
        }
      `}</style>
    </>
  );
};

// Phone mock
window.PhoneMock = function PhoneMock({ width = 280, body = "#1a1410", screen, time = "9:41", label = "5G+" }) {
  const r = width * 0.14;
  const h = width * 2.0;
  return (
    <div style={{
      width, height: h, borderRadius: r,
      background: body === "titanium" ? "linear-gradient(160deg,#48433d,#1f1c19 50%,#332f2a)" : body,
      padding: width * 0.025, position: "relative",
      boxShadow: "0 60px 80px -30px rgba(20,17,15,0.35), 0 24px 40px -16px rgba(20,17,15,0.18), inset 0 0 0 1px rgba(255,255,255,0.05)"
    }}>
      <div style={{ position: "absolute", left: -2, top: "22%", width: 3, height: 36, background: "rgba(0,0,0,0.6)", borderRadius: 2 }} />
      <div style={{ position: "absolute", left: -2, top: "30%", width: 3, height: 50, background: "rgba(0,0,0,0.6)", borderRadius: 2 }} />
      <div style={{ position: "absolute", right: -2, top: "26%", width: 3, height: 70, background: "rgba(0,0,0,0.6)", borderRadius: 2 }} />
      <div style={{
        width: "100%", height: "100%", borderRadius: r - width * 0.025,
        background: screen || "linear-gradient(160deg,#FFB58A 0%,#FF7A4D 35%,#ED0F44 75%,#8B0524 100%)",
        position: "relative", overflow: "hidden"
      }}>
        <div style={{ position: "absolute", top: width * 0.045, left: "50%", transform: "translateX(-50%)", width: width * 0.32, height: width * 0.085, borderRadius: 999, background: "#000", zIndex: 3 }} />
        <div style={{
          position: "absolute", top: width * 0.055, left: 0, right: 0, padding: `0 ${width * 0.09}px`,
          display: "flex", justifyContent: "space-between", color: "#fff",
          fontSize: width * 0.045, fontWeight: 600, mixBlendMode: "difference"
        }}>
          <span>{time}</span>
          <span>{label}</span>
        </div>
      </div>
    </div>);

};

// Decorative red dot grid backdrop
window.DotGrid = function DotGrid({ color = "rgba(237,15,68,0.18)", size = 22 }) {
  return (
    <div aria-hidden style={{
      position: "absolute", inset: 0, pointerEvents: "none",
      backgroundImage: `radial-gradient(${color} 1.4px, transparent 1.4px)`,
      backgroundSize: `${size}px ${size}px`,
      maskImage: "radial-gradient(ellipse at 50% 50%, #000 40%, transparent 75%)",
      WebkitMaskImage: "radial-gradient(ellipse at 50% 50%, #000 40%, transparent 75%)"
    }} />);

};

// =========================================================================
// STORES — global data shared across home page and tiendas page
// =========================================================================
// Each store: name, address, hours, phone (constant), lat/lng del pin
// real, mapsUrl = directions deep-link. Cuando tenemos coords precisas
// usamos esas en la URL (evita ambigüedad de búsqueda por nombre).
// Coords con `// ✓ geocoded` están verificadas vía Nominatim/OSM o
// fuentes públicas. Las marcadas `// ~ approx` son estimación al sector
// pendiente de validar contra el pin real de Google My Business.
(function () {
  const PHONE = "849-220-9239";
  const dirCoords = (lat, lng) => `https://www.google.com/maps/dir/?api=1&destination=${lat},${lng}`;
  const dirText   = (q) => `https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent(q)}`;

  window.STORES = {
    "Santiago": [
      // ✓ geocoded — Farmacia Pujols, Gurabo
      { name: "Gurabo",                                  address: "Carretera Luperón KM 2 1/2, al lado de Farmacia Pujols, Gurabo, Santiago", hours: "Lun-Vie 8:00 AM – 6:00 PM · Sáb 8:30 AM – 1:00 PM", phone: PHONE, lat: 19.474521, lng: -70.676513, photo: null, mapsUrl: dirCoords(19.474521, -70.676513) },
      // ~ approx
      { name: "Supermercado Nacional · Estrella Sadhalá", address: "Av. Estrella Sadhalá, Supermercado Nacional, Santiago",      hours: "Lun-Vie 10:00 AM – 7:00 PM · Sáb 10:00 AM – 4:00 PM", phone: PHONE, lat: 19.4524, lng: -70.6889, photo: null, mapsUrl: dirText("Supermercado Nacional Estrella Sadhalá Santiago") },
      // ~ approx
      { name: "La Sirena · Estrella Sadhalá",             address: "Av. Estrella Sadhalá casi esq. Carretera de Jacagua, Santiago", hours: "Lun-Sáb 8:00 AM – 9:00 PM · Dom 9:00 AM – 1:00 PM", phone: PHONE, lat: 19.4595, lng: -70.6921, photo: null, mapsUrl: dirText("La Sirena Estrella Sadhalá Jacagua Santiago") },
      // ✓ geocoded — Calle San Luis, Santiago
      { name: "San Luis",                                 address: "Calle San Luis No. 53, frente al Encanto, Santiago",         hours: "Lun-Vie 8:00 AM – 7:00 PM · Sáb 8:00 AM – 6:00 PM", phone: PHONE, lat: 19.449926, lng: -70.702956, photo: null, mapsUrl: dirCoords(19.449926, -70.702956) },
      // ~ approx
      { name: "El Encanto",                               address: "Av. Restauración esq. Duarte, Santiago",                     hours: "Lun-Sáb 8:30 AM – 9:00 PM · Dom 9:00 AM – 1:00 PM", phone: PHONE, lat: 19.4520, lng: -70.6960, photo: null, mapsUrl: dirText("El Encanto Av Restauración Duarte Santiago") },
      // ~ approx
      { name: "La Sirena · El Sol",                       address: "Calle El Sol No. 99, Santiago",                              hours: "Lun-Sáb 8:00 AM – 8:00 PM · Dom 9:00 AM – 1:00 PM", phone: PHONE, lat: 19.4533, lng: -70.6969, photo: null, mapsUrl: dirText("La Sirena Calle El Sol Santiago") },
    ],
    "Santo Domingo": [
      // ~ approx
      { name: "La Sirena · Duarte",                       address: "Autopista Duarte Km. 13 1/2, Los Peralejos, Santo Domingo",   hours: "Lun-Vie 8:00 AM – 9:00 PM · Sáb 8:00 AM – 8:00 PM · Dom 10:00 AM – 5:00 PM", phone: PHONE, lat: 18.5232, lng: -69.9840, photo: null, mapsUrl: dirText("La Sirena Autopista Duarte Los Peralejos Santo Domingo") },
      // ~ approx
      { name: "Multicentro Churchill",                    address: "Av. Winston Churchill esq. Ángel Severo Cabral, Santo Domingo", hours: "Lun-Vie 8:00 AM – 9:00 PM · Sáb 8:00 AM – 8:00 PM · Dom 10:00 AM – 5:00 PM", phone: PHONE, lat: 18.4658, lng: -69.9375, photo: null, mapsUrl: dirText("Multicentro Churchill Ángel Severo Cabral Santo Domingo") },
      // ~ approx
      { name: "Jumbo Carretera Mella",                    address: "Carretera Mella casi esq. Charles de Gaulle, Santo Domingo Este", hours: "Lun-Vie 8:00 AM – 9:00 PM · Sáb 8:00 AM – 8:00 PM · Dom 10:00 AM – 4:00 PM", phone: PHONE, lat: 18.5042, lng: -69.8661, photo: null, mapsUrl: dirText("Jumbo Mella Charles de Gaulle Santo Domingo Este") },
      // ~ approx (Nominatim dio Borojol Norte por confusión de nombres)
      { name: "Sirena Autopista San Isidro",              address: "Autopista San Isidro No. 40, Santo Domingo Este",            hours: "Lun-Vie 8:00 AM – 9:00 PM · Sáb 8:00 AM – 8:00 PM · Dom 10:00 AM – 4:00 PM", phone: PHONE, lat: 18.4859, lng: -69.8011, photo: null, mapsUrl: dirText("Autopista San Isidro 40 Santo Domingo Este") },
      // ~ approx (mall conocido)
      { name: "Megacentro módulo Puerta del Sol, frente a Anthonys", address: "Av. San Vicente de Paúl esq. Mella, Santo Domingo Este", hours: "Lun-Vie 9:00 AM – 9:00 PM · Sáb 9:00 AM – 8:00 PM · Dom 10:00 AM – 5:00 PM", phone: PHONE, lat: 18.4870, lng: -69.8645, photo: null, mapsUrl: dirText("Megacentro Mall Santo Domingo Este") },
      // ✓ geocoded — Aeropuerto Las Américas
      { name: "AILA – Aeropuerto Las Américas · Primer piso, Terminal de llegadas", address: "Ruta 66, salida Aeropuerto Las Américas, Santo Domingo", hours: "Todos los días 8:00 AM – 9:00 PM", phone: PHONE, lat: 18.429296, lng: -69.671512, photo: null, mapsUrl: dirCoords(18.429296, -69.671512) },
      // ~ approx
      { name: "Los Alcarrizos",                           address: "Calle Puente Blanco #47, Los Alcarrizos, Santo Domingo Oeste", hours: "Lun-Vie 8:00 AM – 8:00 PM · Sáb 8:00 AM – 7:00 PM · Dom 9:00 AM – 1:00 PM", phone: PHONE, lat: 18.5238, lng: -70.0136, photo: null, mapsUrl: dirText("Calle Puente Blanco 47 Los Alcarrizos Santo Domingo") },
    ],
    "Jarabacoa": [
      // ~ approx
      { name: "Jarabacoa I · Plaza Genao Tour",           address: "Calle Independencia #43, Plaza Genao Tour, Jarabacoa",       hours: "Lun-Vie 8:00 AM – 5:30 PM · Sáb 8:00 AM – 1:00 PM", phone: PHONE, lat: 19.1186, lng: -70.6353, photo: null, mapsUrl: dirText("Plaza Genao Tour Calle Independencia 43 Jarabacoa") },
      // ✓ geocoded — Jarabacoa centro
      { name: "Jarabacoa II · Plaza Don Jorge",           address: "Plaza Don Jorge, módulo 102, Jarabacoa",                     hours: "Lun-Vie 8:00 AM – 8:00 PM · Sáb 9:00 AM – 6:00 PM", phone: PHONE, lat: 19.105926, lng: -70.702334, photo: null, mapsUrl: dirCoords(19.105926, -70.702334) },
      // ✓ geocoded — Calle Duarte, Yerba Buena, Jarabacoa
      { name: "Jarabacoa III · Plaza RC",                 address: "Calle Duarte, Plaza RC módulo 101, Jarabacoa",               hours: "Lun-Vie 8:30 AM – 6:00 PM · Sáb 8:30 AM – 1:00 PM", phone: PHONE, lat: 19.121568, lng: -70.642957, photo: null, mapsUrl: dirCoords(19.121568, -70.642957) },
    ],
    "Moca": [
      // ~ approx
      { name: "La Sirena Moca",                           address: "Carr. Duarte esq. Antonio de la Maza, Moca",                 hours: "Consultar horario por WhatsApp", phone: PHONE, lat: 19.3953, lng: -70.5238, photo: null, mapsUrl: dirText("La Sirena Carr Duarte Antonio de la Maza Moca") },
      // ~ approx
      { name: "Jumbo Moca",                               address: "Avenida Agricultores esq. Antonio de la Maza, Moca",         hours: "Lun-Sáb 8:30 AM – 8:00 PM · Dom 9:00 AM – 1:00 PM", phone: PHONE, lat: 19.3969, lng: -70.5241, photo: null, mapsUrl: dirText("Jumbo Avenida Agricultores Antonio de la Maza Moca") },
    ],
    "Puerto Plata": [
      // ✓ geocoded — Aeropuerto Gregorio Luperón
      { name: "Aeropuerto Gregorio Luperón",              address: "Aeropuerto Gregorio Luperón, Puerto Plata",                  hours: "Consultar horario por WhatsApp", phone: PHONE, lat: 19.757546, lng: -70.568925, photo: null, mapsUrl: dirCoords(19.757546, -70.568925) },
    ],
  };
})();

// =========================================================================
// StoresMap — lazy-loaded Leaflet map (Mapbox tiles if token configured,
// OpenStreetMap fallback otherwise). Used on home and tiendas pages.
// =========================================================================
window.StoresMap = function StoresMap({ stores, activeProv, onSelectProv, height = 460 }) {
  const containerRef = useRef(null);
  const mapRef = useRef(null);
  const markersRef = useRef([]);
  const [ready, setReady] = useState(!!window.L);

  // Lazy-load Leaflet (CSS + JS) once
  useEffect(() => {
    if (window.L) { setReady(true); return; }
    if (!document.querySelector('link[href*="leaflet"]')) {
      const css = document.createElement("link");
      css.rel = "stylesheet";
      css.href = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.css";
      css.integrity = "sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=";
      css.crossOrigin = "";
      document.head.appendChild(css);
    }
    if (!document.querySelector('script[src*="leaflet.js"]')) {
      const js = document.createElement("script");
      js.src = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.js";
      js.integrity = "sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=";
      js.crossOrigin = "";
      js.onload = () => setReady(true);
      document.head.appendChild(js);
    } else {
      // Script tag already in DOM but L not yet defined → wait for it
      const t = setInterval(() => { if (window.L) { clearInterval(t); setReady(true); } }, 80);
      setTimeout(() => clearInterval(t), 5000);
    }
  }, []);

  const makeIcon = (active) => window.L.divIcon({
    className: "bal-store-marker",
    html: `<div style="
      position: relative;
      width: ${active ? 28 : 22}px;
      height: ${active ? 28 : 22}px;
      border-radius: 999px;
      background: #ED0F44;
      box-shadow: 0 0 0 ${active ? 8 : 5}px rgba(237,15,68,0.18), 0 6px 16px -4px rgba(237,15,68,0.55);
      border: 3px solid #fff;
      cursor: pointer;
      transform: translate(-50%, -50%);
    "></div>`,
    iconSize: [0, 0],
  });

  // Initialize map once Leaflet is loaded
  useEffect(() => {
    if (!ready || !containerRef.current || mapRef.current) return;
    const L = window.L;
    const token = (window.SITE && window.SITE.mapbox_token) || "";

    const map = L.map(containerRef.current, {
      center: [19.0, -70.3],
      zoom: 8,
      scrollWheelZoom: false,
      zoomControl: true,
      attributionControl: true,
    });
    mapRef.current = map;

    if (token) {
      L.tileLayer(
        `https://api.mapbox.com/styles/v1/mapbox/streets-v12/tiles/{z}/{x}/{y}@2x?access_token=${token}`,
        {
          tileSize: 512, zoomOffset: -1, maxZoom: 19,
          attribution: '© <a href="https://www.mapbox.com/">Mapbox</a> · © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
        }
      ).addTo(map);
    } else {
      L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
        maxZoom: 19,
        attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
      }).addTo(map);
    }

    Object.entries(stores).forEach(([prov, list]) => {
      list.forEach((s) => {
        if (typeof s.lat !== "number" || typeof s.lng !== "number") return;
        const m = L.marker([s.lat, s.lng], { icon: makeIcon(prov === activeProv) }).addTo(map);
        m.bindPopup(`
          <div style="font-family: 'Britti Sans', sans-serif; min-width: 180px;">
            <div style="font-size: 11px; color: #ED0F44; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;">${prov}</div>
            <div style="font-size: 14px; font-weight: 600; margin-top: 4px; color: #1a1024;">${s.name}</div>
            <div style="font-size: 12px; color: #6b5c5c; margin-top: 6px;">${s.hours}</div>
            <a href="tel:${s.phone}" style="display: inline-block; margin-top: 8px; font-size: 12.5px; font-weight: 600; color: #ED0F44; text-decoration: none;">📞 ${s.phone}</a>
          </div>
        `);
        if (onSelectProv) m.on("click", () => onSelectProv(prov));
        markersRef.current.push({ marker: m, prov, store: s });
      });
    });

    const bounds = L.latLngBounds(markersRef.current.map(({ marker }) => marker.getLatLng()));
    if (bounds.isValid()) map.fitBounds(bounds, { padding: [40, 40] });

    return () => {
      map.remove();
      mapRef.current = null;
      markersRef.current = [];
    };
  }, [ready]);

  // Refresh marker styles + fly to province on filter change
  useEffect(() => {
    if (!mapRef.current || markersRef.current.length === 0) return;
    const L = window.L;
    markersRef.current.forEach(({ marker, prov }) => {
      marker.setIcon(makeIcon(prov === activeProv));
    });
    const provMarkers = markersRef.current.filter(({ prov }) => prov === activeProv);
    if (provMarkers.length === 0) return;
    if (provMarkers.length === 1) {
      mapRef.current.flyTo(provMarkers[0].marker.getLatLng(), 13, { duration: 0.8 });
    } else {
      const bounds = L.latLngBounds(provMarkers.map(({ marker }) => marker.getLatLng()));
      mapRef.current.flyToBounds(bounds, { padding: [60, 60], duration: 0.8 });
    }
  }, [activeProv]);

  return (
    <div style={{ position: "relative", borderRadius: 20, overflow: "hidden", background: "var(--paper-2)" }}>
      <div ref={containerRef} style={{ width: "100%", height: height, borderRadius: 20 }} />
      {!ready && (
        <div style={{
          position: "absolute", inset: 0, display: "grid", placeItems: "center",
          background: "var(--paper-2)", color: "var(--ink-3)", fontSize: 13,
        }}>Cargando mapa…</div>
      )}
      <style>{`
        .leaflet-popup-content-wrapper { border-radius: 12px !important; box-shadow: 0 10px 28px -12px rgba(20,17,15,0.32) !important; }
        .leaflet-popup-content { margin: 14px 16px !important; line-height: 1.4 !important; }
        .leaflet-control-zoom a { border-radius: 8px !important; color: #1a1024 !important; }
        .leaflet-control-attribution { background: rgba(255,255,255,0.85) !important; font-size: 10px !important; }
      `}</style>
    </div>
  );
};