// ============================================================
// LGM REAL ESTATE — Componentes principales
// ============================================================
const { useState, useEffect, useRef, useMemo, useCallback } = React;

// --------- ICONOS (inline SVG) ---------
const Icon = {
  Bed: () => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.2"><path d="M2 12v7M22 12v7M2 17h20M3 12h18v-2a3 3 0 0 0-3-3H6a3 3 0 0 0-3 3v2zM8 7V5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>,
  Bath: () => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.2"><path d="M4 12h16v5a3 3 0 0 1-3 3H7a3 3 0 0 1-3-3v-5zM4 12V6a2 2 0 0 1 2-2h1v3M20 21l-1-1M4 21l1-1"/></svg>,
  Car: () => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.2"><path d="M5 15V9l2-5h10l2 5v6M3 15h18v3a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-1H7v1a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-3zM7 15h10"/><circle cx="7" cy="13" r="1"/><circle cx="17" cy="13" r="1"/></svg>,
  Area: () => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.2"><path d="M4 4h16v16H4z"/><path d="M4 9h5v11M20 15h-5V4"/></svg>,
  Arrow: () => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M5 12h14M13 6l6 6-6 6"/></svg>,
  ArrowLeft: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.2"><path d="M19 12H5M11 6l-6 6 6 6"/></svg>,
  ArrowRight: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.2"><path d="M5 12h14M13 6l6 6-6 6"/></svg>,
  Heart: ({fill}) => <svg width="16" height="16" viewBox="0 0 24 24" fill={fill || "none"} stroke="currentColor" strokeWidth="1.4"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>,
  Close: () => <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.2"><path d="M18 6 6 18M6 6l12 12"/></svg>,
  Share: () => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.3"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="M8.59 13.51 15.42 17.49M15.41 6.51 8.59 10.49"/></svg>,
  WhatsApp: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 0 1-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 0 1-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 0 1 2.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0 0 12.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 0 0 5.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 0 0-3.48-8.413Z"/></svg>,
  Check: () => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><path d="M20 6 9 17l-5-5"/></svg>,
  Search: () => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>,
  Sun: () => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.3"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg>,
  Moon: () => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.3"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>,
  Wave: () => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round"><path d="M2 8c2 0 2-2 4-2s2 2 4 2 2-2 4-2 2 2 4 2 2-2 4-2"/><path d="M2 14c2 0 2-2 4-2s2 2 4 2 2-2 4-2 2 2 4 2 2-2 4-2"/><path d="M2 20c2 0 2-2 4-2s2 2 4 2 2-2 4-2 2 2 4 2 2-2 4-2"/></svg>,
  Phone: () => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg>,
  Mail: () => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><path d="m22 6-10 7L2 6"/></svg>,
  Heart2: () => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>,
  Settings: () => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.3"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>,
  Pin: () => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M20 10c0 7-8 13-8 13s-8-6-8-13a8 8 0 0 1 16 0z"/><circle cx="12" cy="10" r="3"/></svg>,
};

// --------- UTILIDADES ---------
function formatPrice(n, currency = "USD") {
  if (n == null) return "—";
  if (n >= 1_000_000) return `US$${(n/1_000_000).toFixed(2).replace(/\.?0+$/, '')}M`;
  if (n >= 1000) return `US$${(n/1000).toFixed(0)}K`;
  return `US$${n.toLocaleString()}`;
}

function formatRent(n) {
  if (n == null) return "—";
  return `US$${n.toLocaleString()}/mo`;
}

// Price filter ranges (USD). Inclusive-exclusive on upper bound; b5 catches 500K and above.
const BUDGET_RANGES = {
  any: [0, Infinity],
  b1: [0, 100000],
  b2: [100000, 150000],
  b3: [150000, 300000],
  b4: [300000, 500000],
  b5: [500000, Infinity],
};

// --------- HOOK: LocalStorage ---------
function useLocalStorage(key, initial) {
  const [val, setVal] = useState(() => {
    try {
      const raw = localStorage.getItem(key);
      return raw ? JSON.parse(raw) : initial;
    } catch { return initial; }
  });
  useEffect(() => {
    try { localStorage.setItem(key, JSON.stringify(val)); } catch {}
  }, [key, val]);
  return [val, setVal];
}

// --------- HOOK: Reveal on scroll ---------
function useReveal(dep) {
  useEffect(() => {
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          e.target.classList.add("visible");
          io.unobserve(e.target);
        }
      });
    }, { threshold: 0.05, rootMargin: "0px 0px -2% 0px" });

    // Re-observar en cada scan: si IO previo fue desconectado por route change,
    // data-revealObs quedaba stale → elementos no re-observados → desaparecían.
    // IO.observe es idempotente para elemento ya observado en mismo IO.
    const observe = (root = document) => {
      root.querySelectorAll(".reveal, .reveal-text").forEach((el) => {
        if (el.classList.contains("visible")) return;
        io.observe(el);
      });
    };

    // Fuerza visibilidad para elementos ya dentro del viewport ahora mismo
    const revealInViewport = () => {
      document.querySelectorAll(".reveal:not(.visible), .reveal-text:not(.visible)").forEach((el) => {
        const r = el.getBoundingClientRect();
        if (r.top < window.innerHeight * 0.98 && r.bottom > 0) {
          el.classList.add("visible");
        }
      });
    };

    observe();
    revealInViewport();

    // Watch DOM para route changes / lazy renders
    const mo = new MutationObserver(() => { observe(); revealInViewport(); });
    mo.observe(document.body, { childList: true, subtree: true });

    // Multi-frame safety: algunos nodos tardan en mountarse tras PageTransition
    const rafs = [];
    rafs.push(requestAnimationFrame(revealInViewport));
    const t1 = setTimeout(revealInViewport, 200);
    const t2 = setTimeout(revealInViewport, 600);

    // Scroll/resize fallback: si IO falla (Safari bug, wrong ancestor), scroll re-chequea
    const onScroll = () => revealInViewport();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);

    return () => {
      io.disconnect();
      mo.disconnect();
      rafs.forEach(id => cancelAnimationFrame(id));
      clearTimeout(t1); clearTimeout(t2);
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
    };
  }, [dep]);
}

// --------- HOOK: Scroll progress + cursor ---------
function useScrollEnhancements() {
  useEffect(() => {
    const onScroll = () => {
      const h = document.documentElement;
      const progress = h.scrollTop / Math.max(1, h.scrollHeight - h.clientHeight);
      document.documentElement.style.setProperty("--scroll-progress", progress);
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
}

// Custom cursor
function CustomCursor() {
  const dotRef = useRef(null);
  const ringRef = useRef(null);
  useEffect(() => {
    if (window.matchMedia("(hover: none)").matches) return;
    let mx = 0, my = 0, rx = 0, ry = 0;
    const dot = dotRef.current, ring = ringRef.current;
    if (!dot || !ring) return;
    const move = (e) => {
      mx = e.clientX; my = e.clientY;
      dot.style.transform = `translate(${mx}px, ${my}px)`;
    };
    const raf = () => {
      rx += (mx - rx) * 0.18;
      ry += (my - ry) * 0.18;
      ring.style.transform = `translate(${rx}px, ${ry}px)`;
      requestAnimationFrame(raf);
    };
    raf();
    window.addEventListener("mousemove", move);
    const onOver = (e) => {
      if (e.target.closest("a, button, .pcard, .gcard, input, select, textarea, [data-cursor='hover']")) {
        dot.classList.add("hover");
        ring.classList.add("hover");
      }
    };
    const onOut = (e) => {
      if (e.target.closest("a, button, .pcard, .gcard, input, select, textarea, [data-cursor='hover']")) {
        dot.classList.remove("hover");
        ring.classList.remove("hover");
      }
    };
    document.addEventListener("mouseover", onOver);
    document.addEventListener("mouseout", onOut);
    return () => {
      window.removeEventListener("mousemove", move);
      document.removeEventListener("mouseover", onOver);
      document.removeEventListener("mouseout", onOut);
    };
  }, []);
  return (
    <>
      <div ref={dotRef} className="cursor-dot" />
      <div ref={ringRef} className="cursor-ring" />
    </>
  );
}

// ============================================================
// NAV
// ============================================================
function Nav({ t, lang, setLang, theme, setTheme, currentPath, onNav }) {
  const [scrolled, setScrolled] = useState(false);
  const [langOpen, setLangOpen] = useState(false);
  const [mobileOpen, setMobileOpen] = useState(false);
  useEffect(() => {
    const fn = () => setScrolled(window.scrollY > 40);
    fn();
    window.addEventListener("scroll", fn, { passive: true });
    return () => window.removeEventListener("scroll", fn);
  }, []);
  useEffect(() => { setMobileOpen(false); }, [currentPath]);
  useEffect(() => {
    document.body.style.overflow = mobileOpen ? "hidden" : "";
    return () => { document.body.style.overflow = ""; };
  }, [mobileOpen]);
  useEffect(() => {
    if (!mobileOpen) return;
    const onKey = (e) => { if (e.key === "Escape") setMobileOpen(false); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [mobileOpen]);

  const langs = [
    { code: "es", label: "ES · Español" },
    { code: "en", label: "EN · English" },
    { code: "fr", label: "FR · Français" },
    { code: "de", label: "DE · Deutsch" },
    { code: "it", label: "IT · Italiano" },
    { code: "ru", label: "RU · Русский" },
  ];

  const links = [
    { to: "/", label: t.nav.home || "Inicio", match: (p) => p === "/" },
    { to: "/propiedades", label: t.nav.properties, match: (p) => p.startsWith("/propiedades") },
    { to: "/sobre-mi", label: t.nav.about, match: (p) => p === "/sobre-mi" },
    { to: "/servicios", label: t.nav.services, match: (p) => p === "/servicios" },
    { to: "/vender", label: t.nav.sell || "Vender", match: (p) => p === "/vender" },
    { to: "/?section=contact", label: t.nav.contact, match: () => false, scrollTo: "#contact" },
  ];

  const go = (e, l) => {
    e.preventDefault();
    if (l.scrollTo) {
      if (currentPath !== "/") {
        location.hash = "#/";
        setTimeout(() => document.querySelector(l.scrollTo)?.scrollIntoView({ behavior: "smooth" }), 450);
      } else {
        document.querySelector(l.scrollTo)?.scrollIntoView({ behavior: "smooth" });
      }
    } else {
      onNav(l.to);
    }
  };

  return (
    <>
      <nav className={`nav${scrolled ? " scrolled" : ""}${mobileOpen ? " nav--mobile-open" : ""}`}>
        <a href="#/" className="nav__logo" onClick={(e) => { e.preventDefault(); onNav("/"); }}>
          <img src="assets/lgm-logo-cream.png" alt="LGM Real Estate" />
        </a>
        <ul className="nav__links">
          {links.map(l => (
            <li key={l.to}>
              <a href={"#" + l.to} className={l.match(currentPath) ? "active" : ""}
                onClick={(e) => go(e, l)}>
                {l.label}
              </a>
            </li>
          ))}
        </ul>
        <div className="nav__right">
          <div className="nav__theme-group" role="group" aria-label="Tema">
            <button type="button" className={`nav__theme-btn${theme === "light" ? " active" : ""}`} onClick={() => setTheme("light")} aria-label="Claro" title="Claro"><Icon.Sun /></button>
            <button type="button" className={`nav__theme-btn${theme === "dark" ? " active" : ""}`} onClick={() => setTheme("dark")} aria-label="Oscuro" title="Oscuro"><Icon.Moon /></button>
            <button type="button" className={`nav__theme-btn${theme === "coastal" ? " active" : ""}`} onClick={() => setTheme("coastal")} aria-label="Costal" title="Costal"><Icon.Wave /></button>
          </div>
          <div style={{ position: "relative" }} className="nav__lang-wrap">
            <button className="nav__lang" onClick={() => setLangOpen(v => !v)}>
              {lang.toUpperCase()}
            </button>
            <div className={`lang-dropdown${langOpen ? " open" : ""}`}>
              {langs.map(l => (
                <button key={l.code} className={lang === l.code ? "active" : ""}
                  onClick={() => { setLang(l.code); setLangOpen(false); }}>
                  {l.label}
                </button>
              ))}
            </div>
          </div>
          <a href="https://wa.me/18294832697" target="_blank" rel="noopener" className="nav__wa">
            <Icon.WhatsApp /> <span className="nav__wa-label">{t.nav.whatsapp}</span>
          </a>
          <button className={`nav__burger${mobileOpen ? " open" : ""}`}
            onClick={() => setMobileOpen(v => !v)} aria-label="Menu">
            <span></span><span></span><span></span>
          </button>
        </div>
      </nav>

      {/* Mobile drawer */}
      <div className={`mobile-drawer${mobileOpen ? " open" : ""}`}>
        <div className="mobile-drawer__scrim" onClick={() => setMobileOpen(false)} />
        <aside className="mobile-drawer__panel" role="dialog" aria-modal="true" aria-label="Menu">
          <button className="mobile-drawer__close" onClick={() => setMobileOpen(false)} aria-label="Cerrar menu" type="button">
            <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
              <path d="M6 6L18 18M18 6L6 18" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
            </svg>
            <span>Cerrar</span>
          </button>
          <ul className="mobile-drawer__links">
            {links.map(l => (
              <li key={l.to}>
                <a href={"#" + l.to} className={l.match(currentPath) ? "active" : ""}
                  onClick={(e) => go(e, l)}>
                  <span>{l.label}</span>
                  <Icon.Arrow />
                </a>
              </li>
            ))}
          </ul>
          <div className="mobile-drawer__foot">
            <div className="mobile-drawer__theme" role="group" aria-label="Tema">
              <button type="button" className={`mobile-drawer__theme-btn${theme === "light" ? " active" : ""}`} onClick={() => setTheme("light")} aria-label="Claro">
                <Icon.Sun /><span>Claro</span>
              </button>
              <button type="button" className={`mobile-drawer__theme-btn${theme === "dark" ? " active" : ""}`} onClick={() => setTheme("dark")} aria-label="Oscuro">
                <Icon.Moon /><span>Oscuro</span>
              </button>
              <button type="button" className={`mobile-drawer__theme-btn${theme === "coastal" ? " active" : ""}`} onClick={() => setTheme("coastal")} aria-label="Costal">
                <Icon.Wave /><span>Costal</span>
              </button>
            </div>
            <div className="mobile-drawer__langs">
              {langs.map(l => (
                <button key={l.code} className={lang === l.code ? "active" : ""}
                  onClick={() => setLang(l.code)}>{l.code.toUpperCase()}</button>
              ))}
            </div>
            <a href="https://wa.me/18294832697" target="_blank" rel="noopener" className="btn-gold" style={{ width: "100%", justifyContent: "center" }}>
              <Icon.WhatsApp /> {t.nav.whatsapp}
            </a>
          </div>
        </aside>
      </div>
    </>
  );
}

// ============================================================
// HERO
// ============================================================
function Hero({ t, onSearch, allProps = [] }) {
  const bgRef = useRef(null);
  const [searchState, setSearchState] = useState({ op: "any", type: "any", province: "any", city: "any", zone: "any", budget: "any" });

  // Dynamic counts → hide filter options with zero properties
  const heroCounts = useMemo(() => {
    const c = { op: {}, type: {}, province: {}, city: {}, zone: {}, budget: {} };
    for (const p of allProps) {
      if (p.operation) c.op[p.operation] = (c.op[p.operation] || 0) + 1;
      if (p.type) c.type[p.type] = (c.type[p.type] || 0) + 1;
      if (p.province) c.province[p.province] = (c.province[p.province] || 0) + 1;
      if (p.city) c.city[p.city] = (c.city[p.city] || 0) + 1;
      if (p.zone) c.zone[p.zone] = (c.zone[p.zone] || 0) + 1;
      const price = p.price || (p.rent ? p.rent * 100 : 0);
      for (const [k, [min, max]] of Object.entries(BUDGET_RANGES)) {
        if (k === "any") continue;
        // Inclusive min, exclusive max; final bucket (Infinity) catches everything >= min.
        const inRange = max === Infinity ? price >= min : price >= min && price < max;
        if (inRange) c.budget[k] = (c.budget[k] || 0) + 1;
      }
    }
    return c;
  }, [allProps]);

  // Índice de ubicaciones derivado de propiedades reales
  const heroLocIndex = useMemo(() => {
    const provMap = {};   // province → Set<city>
    const cityMap = {};   // city → Set<zone>
    for (const p of allProps) {
      if (p.province) {
        (provMap[p.province] = provMap[p.province] || new Set());
        if (p.city) provMap[p.province].add(p.city);
      }
      if (p.city) {
        (cityMap[p.city] = cityMap[p.city] || new Set());
        if (p.zone) cityMap[p.city].add(p.zone);
      }
    }
    return { provMap, cityMap };
  }, [allProps]);

  const heroOpOptions = [
    { value: "any", label: t.ops.any },
    ...[
      { value: "sale", label: t.ops.sale },
      { value: "rent", label: t.ops.rent },
      { value: "vacation", label: t.ops.vacation },
    ].filter(o => (heroCounts.op[o.value] || 0) > 0),
  ];

  const heroTypeOptions = [
    { value: "any", label: t.types.any },
    ...[
      { value: "villa", label: t.types.villa },
      { value: "apartment", label: t.types.apartment },
      { value: "penthouse", label: t.types.penthouse },
      { value: "house", label: t.types.house },
      { value: "land", label: t.types.land },
      { value: "commercial", label: t.types.commercial },
    ].filter(o => (heroCounts.type[o.value] || 0) > 0),
  ];

  const heroBudgetOptions = [
    { value: "any", label: t.budget.any },
    ...[
      { value: "b1", label: t.budget.b1 },
      { value: "b2", label: t.budget.b2 },
      { value: "b3", label: t.budget.b3 },
      { value: "b4", label: t.budget.b4 },
      { value: "b5", label: t.budget.b5 },
    ].filter(o => (heroCounts.budget[o.value] || 0) > 0),
  ];

  useEffect(() => {
    const onScroll = () => {
      if (!bgRef.current) return;
      const y = window.scrollY;
      if (y < 1000) bgRef.current.style.transform = `translateY(${y * 0.25}px) scale(1.08)`;
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);

  // Provincias con props
  const heroProvinces = Object.keys(heroLocIndex.provMap).sort((a, b) => a.localeCompare(b));

  // Ciudades: si provincia seleccionada → solo cities de esa provincia con props; si no → todas las cities con props
  const heroCities = (() => {
    if (searchState.province !== "any") {
      const set = heroLocIndex.provMap[searchState.province];
      return set ? Array.from(set).sort((a, b) => a.localeCompare(b)) : [];
    }
    return Object.keys(heroCounts.city).sort((a, b) => a.localeCompare(b));
  })();

  // Zonas: filtradas por city → province → any
  const heroZones = (() => {
    if (searchState.city !== "any") {
      const set = heroLocIndex.cityMap[searchState.city];
      return set ? Array.from(set).sort((a, b) => a.localeCompare(b)) : [];
    }
    if (searchState.province !== "any") {
      const citySet = heroLocIndex.provMap[searchState.province];
      if (!citySet) return [];
      const zoneSet = new Set();
      citySet.forEach(c => {
        const zs = heroLocIndex.cityMap[c];
        if (zs) zs.forEach(z => zoneSet.add(z));
      });
      return Array.from(zoneSet).sort((a, b) => a.localeCompare(b));
    }
    return Object.keys(heroCounts.zone).sort((a, b) => a.localeCompare(b));
  })();

  return (
    <section className="hero" id="top">
      <div className="hero__bg-mask">
        <div ref={bgRef} className="hero__bg">
          <img
            src="/lgm/assets/hero.jpg"
            alt=""
            onError={(e) => {
              e.currentTarget.src = "https://images.pexels.com/photos/258154/pexels-photo-258154.jpeg?auto=compress&cs=tinysrgb&w=2400";
            }}
          />
        </div>
      </div>

      <div className="hero__content">
        <div className="hero__main">
          <div className="hero__eyebrow reveal">{t.hero.eyebrow}</div>
          <h1 className="hero__title reveal">
            <span>{t.hero.titleA}</span>
            <span className="italic">{t.hero.titleB}</span>
            <span>{t.hero.titleC}</span>
          </h1>
          <p className="hero__sub reveal">{t.hero.sub}</p>
        </div>
      </div>

      <a href="#/propiedades" className="hero__scroll" aria-label={t.hero.scrollHint}>
        <span className="hero__scroll-line"></span>
        {t.hero.scrollHint}
      </a>

      <div className="search">
        {heroOpOptions.length > 1 && (
          <div className="search__field">
            <div className="search__label">{t.hero.searchOp}</div>
            <Dropdown variant="hero" ariaLabel={t.hero.searchOp}
              value={searchState.op}
              onChange={e => setSearchState(s => ({ ...s, op: e.target.value }))}
              options={heroOpOptions} />
          </div>
        )}
        {heroTypeOptions.length > 1 && (
          <div className="search__field">
            <div className="search__label">{t.hero.searchType}</div>
            <Dropdown variant="hero" ariaLabel={t.hero.searchType}
              value={searchState.type}
              onChange={e => setSearchState(s => ({ ...s, type: e.target.value }))}
              options={heroTypeOptions} />
          </div>
        )}
        {heroProvinces.length > 0 && (
          <div className="search__field">
            <div className="search__label">{t.hero.searchProvince || "Provincia"}</div>
            <Dropdown variant="hero" ariaLabel={t.hero.searchProvince || "Provincia"}
              value={searchState.province}
              onChange={e => setSearchState(s => ({ ...s, province: e.target.value, city: "any", zone: "any" }))}
              options={[
                { value: "any", label: t.types.any },
                ...heroProvinces.map(p => ({ value: p, label: p })),
              ]} />
          </div>
        )}
        {heroCities.length > 0 && (
          <div className="search__field">
            <div className="search__label">{t.hero.searchCity || "Ciudad"}</div>
            <Dropdown variant="hero" ariaLabel={t.hero.searchCity || "Ciudad"}
              value={searchState.city}
              onChange={e => setSearchState(s => ({ ...s, city: e.target.value, zone: "any" }))}
              options={[
                { value: "any", label: t.types.any },
                ...heroCities.map(c => ({ value: c, label: c })),
              ]} />
          </div>
        )}
        {heroBudgetOptions.length > 1 && (
          <div className="search__field">
            <div className="search__label">{t.hero.searchBudget}</div>
            <Dropdown variant="hero" ariaLabel={t.hero.searchBudget}
              value={searchState.budget}
              onChange={e => setSearchState(s => ({ ...s, budget: e.target.value }))}
              options={heroBudgetOptions} />
          </div>
        )}
        <button className="search__btn" onClick={() => onSearch(searchState)}>
          <Icon.Search /> {t.hero.searchBtn}
        </button>
      </div>
    </section>
  );
}

// ============================================================
// TICKER
// ============================================================
function Ticker({ t }) {
  const items = t.marquee;
  const content = [...items, ...items, ...items, ...items];
  return (
    <div className="ticker">
      <div className="ticker__track">
        {content.map((item, i) => (
          <span key={i}>{item}<span className="ticker__dot"></span></span>
        ))}
      </div>
    </div>
  );
}

// ============================================================
// DROPDOWN — custom <select> replacement theme-aware
// ============================================================
function Dropdown({ value, onChange, options, placeholder, className, variant, disabled, ariaLabel, alignRight }) {
  const [open, setOpen] = useState(false);
  const rootRef = useRef(null);

  // Flatten search for current label
  const findLabel = (opts) => {
    for (const o of opts) {
      if (o && o.options) {
        const inner = findLabel(o.options);
        if (inner != null) return inner;
      } else if (o && o.value === value) {
        return o.label;
      }
    }
    return null;
  };
  const currentLabel = findLabel(options);
  const buttonLabel = currentLabel != null ? currentLabel : (placeholder || "—");

  useEffect(() => {
    if (!open) return;
    const onDoc = (e) => {
      if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
    };
    const onKey = (e) => { if (e.key === "Escape") setOpen(false); };
    document.addEventListener("mousedown", onDoc);
    document.addEventListener("keydown", onKey);
    return () => {
      document.removeEventListener("mousedown", onDoc);
      document.removeEventListener("keydown", onKey);
    };
  }, [open]);

  const pick = (v) => {
    if (v !== value) onChange({ target: { value: v } });
    setOpen(false);
  };

  const renderOpt = (o, key) => (
    <div key={key}
      role="option"
      aria-selected={o.value === value}
      className={`lgm-dd__opt ${o.value === value ? "lgm-dd__opt--on" : ""}`}
      onClick={() => pick(o.value)}>
      {o.label}
    </div>
  );

  const cls = ["lgm-dd"];
  if (open) cls.push("lgm-dd--open");
  if (variant) cls.push("lgm-dd--" + variant);
  if (disabled) cls.push("lgm-dd--disabled");
  if (className) cls.push(className);

  return (
    <div className={cls.join(" ")} ref={rootRef}>
      <button type="button"
        className="lgm-dd__btn"
        aria-haspopup="listbox"
        aria-expanded={open}
        aria-label={ariaLabel}
        disabled={disabled}
        onClick={() => !disabled && setOpen(o => !o)}>
        <span className="lgm-dd__label">{buttonLabel}</span>
        <svg className="lgm-dd__arrow" width="10" height="6" viewBox="0 0 10 6" aria-hidden="true">
          <path d="M1 1l4 4 4-4" fill="none" stroke="currentColor" strokeWidth="1.4" />
        </svg>
      </button>
      {open && (
        <div className="lgm-dd__panel" role="listbox">
          {options.map((o, i) => {
            if (o && o.options) {
              return (
                <div key={"g-" + i} className="lgm-dd__group">
                  <div className="lgm-dd__group-label">{o.label}</div>
                  {o.options.map((sub, j) => renderOpt(sub, "g" + i + "-o" + j))}
                </div>
              );
            }
            return renderOpt(o, "o-" + i);
          })}
        </div>
      )}
    </div>
  );
}

// Export all components now
Object.assign(window, { Icon, formatPrice, formatRent, BUDGET_RANGES, useLocalStorage, useReveal, useScrollEnhancements, CustomCursor, Nav, Hero, Ticker, Dropdown });
