// =========================================================================
// MOBILE — Comercial workspace (iPhone-style)
// =========================================================================

/**
 * MAPA MÓVIL — el de verdad, no el decorado.
 *
 * Hasta el 27-ago-2026 esta pantalla eran gradientes simulando calles, un "tú
 * estás aquí" clavado al 50%/50% y los 6 primeros inmuebles colocados con
 * `left: (20 + i*12)%`, o sea posicionados por su índice en el array. Justo en
 * el dispositivo con el que se sale a la calle.
 *
 * Reutiliza lo que ya vive en MapCmdNotif.jsx (teselas, consultas al OVC,
 * caché de parcelas y la ficha con su desglose por niveles), que se carga
 * antes que este fichero. Duplicarlo habría sido mantener dos mapas.
 *
 * En móvil el orden de importancia se invierte respecto al escritorio: aquí lo
 * primero es DÓNDE ESTOY, porque la pantalla se usa andando.
 */
const MapaMovil = () => {
  const { DATA, Icons, fmtEUR } = window;

  const contenedor = React.useRef(null);
  const mapa = React.useRef(null);
  const marcadores = React.useRef(new Map());
  const marcaYo = React.useRef(null);
  const marcaParcela = React.useRef(null);
  const vigilante = React.useRef(null);

  const [listo, setListo] = React.useState(false);
  const [capaBase, setCapaBase] = React.useState("mapa");
  const [verCatastro, setVerCatastro] = React.useState(true);
  const [miPos, setMiPos] = React.useState(null);
  // Estado, NO el ref: los refs no repintan. Se leia `vigilante.current` en el
  // render y funcionaba de rebote porque setMiPos forzaba el repintado; pero al
  // denegar el permiso el watch ya habia devuelto un id, asi que el boton decia
  // "Dejar de seguirme" mientras no seguia a nadie.
  const [siguiendo, setSiguiendo] = React.useState(false);
  const [ficha, setFicha] = React.useState(null);      // inmueble nuestro
  const [parcela, setParcela] = React.useState(null);  // parcela del Catastro
  const [busqueda, setBusqueda] = React.useState("");
  const [buscando, setBuscando] = React.useState(false);
  const [sugerencias, setSugerencias] = React.useState([]);

  // Misma fuente que el escritorio: toda la agencia, columnas del mapa.
  const cartera = DATA.INMUEBLES_MAPA || DATA.INMUEBLES;
  const todos = React.useMemo(
    () => cartera.filter((p) => p.lat != null && p.lng != null),
    [cartera],
  );

  // Buscador. En movil no lo habia puesto, y hace MAS falta que en escritorio:
  // aqui no cabe la lista lateral donde uno mira lo que hay.
  const conCoords = React.useMemo(() => {
    const t = busqueda.trim().toLowerCase();
    if (!t) return todos;
    return todos.filter((p) =>
      [p.ref, p.addr, p.town, p.refCatastral, p.refCatastralParcela]
        .filter(Boolean).some((v) => String(v).toLowerCase().includes(t)));
  }, [todos, busqueda]);

  /**
   * Sugerencias de dirección del IGN, las mismas que en escritorio.
   *
   * Faltaban aqui, y hacen mas falta que alla: el buscador del movil solo
   * filtraba NUESTRA cartera, asi que escribir la calle en la que estas
   * parado no devolvia nada Y ADEMAS vaciaba el mapa de puntos. El marcador
   * decia "0 de 9" sobre un mapa sin nada. Justo al reves de lo que se
   * necesita andando por la calle.
   *
   * 250 ms de freno: escribir una calle son quince pulsaciones y no se le
   * piden quince respuestas al IGN. Y la respuesta que llega tarde se
   * descarta, o pisa a la buena.
   */
  React.useEffect(() => {
    const q = busqueda.trim();
    if (q.length < 3) { setSugerencias([]); return; }
    let vigente = true;
    const t = setTimeout(async () => {
      try {
        const r = await sugerirDirecciones(q);
        if (vigente) setSugerencias(r);
      } catch (_) { if (vigente) setSugerencias([]); }
    }, 250);
    return () => { vigente = false; clearTimeout(t); };
  }, [busqueda]);

  /**
   * Ir a una dirección elegida. Al llegar se limpia la búsqueda y se cierra la
   * barra: en un teléfono eso devuelve los puntos de la cartera al mapa y
   * quita el teclado, que ocupa media pantalla.
   */
  const irADireccion = async (c) => {
    setSugerencias([]);
    const m = mapa.current;
    if (!m) return;
    const p = (c.lat && c.lng) ? { lat: c.lat, lng: c.lng } : await coordenadasDe(c.address);
    if (!p) return;
    setBusqueda("");
    setBuscando(false);
    m.easeTo({ center: [p.lng, p.lat], zoom: c.type === "portal" ? 18 : 16, duration: 700 });
  };

  // Distancia en línea recta, para ordenar por cercanía cuando hay posición.
  const metros = React.useCallback((a, b) => {
    const R = 6371000, r = Math.PI / 180;
    const dLat = (b.lat - a.lat) * r, dLng = (b.lng - a.lng) * r;
    const x = Math.sin(dLat / 2) ** 2 +
      Math.cos(a.lat * r) * Math.cos(b.lat * r) * Math.sin(dLng / 2) ** 2;
    return Math.round(2 * R * Math.asin(Math.sqrt(x)));
  }, []);

  const cercanos = React.useMemo(() => {
    if (!miPos || miPos.estado !== "ok") return [];
    return conCoords
      .map((p) => ({ p, d: metros(miPos, p) }))
      .sort((a, b) => a.d - b.d)
      .slice(0, 5);
  }, [conCoords, miPos, metros]);

  // ---- construcción del mapa ----
  React.useEffect(() => {
    if (!contenedor.current || mapa.current || !window.maplibregl) return;
    const M = window.maplibregl;

    const m = new M.Map({
      container: contenedor.current,
      style: {
        version: 8,
        sources: {
          ign: { type: "raster", tiles: [TESELAS_IGN], tileSize: 256, maxzoom: 20, attribution: ATRIB_IGN },
          pnoa: { type: "raster", tiles: [TESELAS_PNOA], tileSize: 256, maxzoom: 20, attribution: ATRIB_IGN },
          catastro: { type: "raster", tiles: [TESELAS_CATASTRO], tileSize: 256, attribution: ATRIB_CAT },
        },
        layers: [
          { id: "base-mapa", type: "raster", source: "ign" },
          { id: "base-orto", type: "raster", source: "pnoa", layout: { visibility: "none" } },
          { id: "parcelario", type: "raster", source: "catastro", paint: { "raster-opacity": 0.75 } },
        ],
      },
      center: [-3.79, 37.77],
      zoom: 12,
      attributionControl: { compact: true },
    });

    if (conCoords.length > 0) {
      const lim = new M.LngLatBounds();
      conCoords.forEach((p) => lim.extend([p.lng, p.lat]));
      m.fitBounds(lim, { padding: 40, maxZoom: 15, animate: false });
    }

    m.on("load", () => setListo(true));

    // Tocar el mapa consulta la parcela. Igual que en escritorio, en dos fases.
    m.on("click", async (e) => {
      const { lng, lat } = e.lngLat;
      setFicha(null);
      setParcela({ estado: "cargando" });
      if (marcaParcela.current) marcaParcela.current.remove();
      const punto = document.createElement("div");
      punto.className = "rv-parcela-punto";
      marcaParcela.current = new M.Marker({ element: punto }).setLngLat([lng, lat]).addTo(m);
      try {
        const rc = await rcPorCoordenada(lng.toFixed(6), lat.toFixed(6));
        const guardada = deCache(rc);
        if (guardada) { setParcela({ estado: "ok", rc, datos: guardada, lng, lat }); return; }
        setParcela({ estado: "desglosando", rc, desde: Date.now(), lng, lat });
        const datos = await datosDeParcela(rc);
        aCache(rc, datos);
        setParcela({ estado: "ok", rc, datos, lng, lat });
      } catch (err) {
        setParcela({ estado: "error", error: err.message });
      }
    });

    const obs = new ResizeObserver(() => m.resize());
    obs.observe(contenedor.current);

    mapa.current = m;
    return () => {
      obs.disconnect();
      if (vigilante.current !== null) navigator.geolocation.clearWatch(vigilante.current);
      m.remove();
      mapa.current = null;
    };
  }, []);

  // ---- marcadores de nuestra cartera ----
  React.useEffect(() => {
    const m = mapa.current;
    if (!m || !window.maplibregl) return;
    const creados = new Map();
    const grupos = new Map();
    conCoords.forEach((p) => {
      const k = p.lng + "," + p.lat;
      if (!grupos.has(k)) grupos.set(k, []);
      grupos.get(k).push(p);
    });

    grupos.forEach((grupo, clave) => {
      const primero = grupo[0];
      const varios = grupo.length > 1;
      const exacto = grupo.every((p) => esExacta(p.geoConfianza));
      const clases = new Set(grupo.map(claseEstado));
      const estado = clases.size === 1 ? [...clases][0] : "otro";

      const el = document.createElement("button");
      el.type = "button";
      el.className = "rv-punto " + estado;
      // Más grande que en escritorio: aquí se toca con el dedo, no con un ratón.
      const lado = varios ? 26 : 20;
      el.style.width = lado + "px";
      el.style.height = lado + "px";
      el.textContent = varios ? String(grupo.length) : "";
      el.setAttribute("aria-label", primero.ref + " · " + primero.addr);
      el.addEventListener("click", (ev) => {
        ev.stopPropagation();
        setParcela(null);
        setFicha({ grupo, i: 0 });
      });
      creados.set(clave, new window.maplibregl.Marker({ element: el, opacity: exacto ? "1" : "0.55" })
        .setLngLat([primero.lng, primero.lat]).addTo(m));
    });
    marcadores.current = creados;
    return () => { creados.forEach((mk) => mk.remove()); marcadores.current = new Map(); };
  }, [conCoords]);

  // ---- capas ----
  React.useEffect(() => {
    const m = mapa.current;
    if (!listo || !m) return;
    m.setLayoutProperty("base-mapa", "visibility", capaBase === "mapa" ? "visible" : "none");
    m.setLayoutProperty("base-orto", "visibility", capaBase === "orto" ? "visible" : "none");
    m.setLayoutProperty("parcelario", "visibility", verCatastro ? "visible" : "none");
    m.setPaintProperty("parcelario", "raster-opacity", capaBase === "ninguna" ? 1 : 0.75);
  }, [capaBase, verCatastro, listo]);

  // ---- posición propia. Nada se guarda ni se comparte. ----
  const alternarSeguimiento = () => {
    if (vigilante.current !== null) {
      navigator.geolocation.clearWatch(vigilante.current);
      vigilante.current = null;
      setSiguiendo(false);
      if (marcaYo.current) { marcaYo.current.remove(); marcaYo.current = null; }
      setMiPos(null);
      return;
    }
    if (!navigator.geolocation) { setMiPos({ estado: "no_disponible" }); return; }
    setMiPos({ estado: "buscando" });
    setSiguiendo(true);
    vigilante.current = navigator.geolocation.watchPosition(
      (pos) => {
        const { longitude: lng, latitude: lat, accuracy } = pos.coords;
        setMiPos({ estado: "ok", lng, lat, precision: Math.round(accuracy) });
        const m = mapa.current;
        if (!m) return;
        if (!marcaYo.current) {
          const el = document.createElement("div");
          el.className = "rv-yo";
          marcaYo.current = new window.maplibregl.Marker({ element: el }).setLngLat([lng, lat]).addTo(m);
          m.easeTo({ center: [lng, lat], zoom: Math.max(m.getZoom(), 17), duration: 700 });
        } else {
          marcaYo.current.setLngLat([lng, lat]);
        }
      },
      (err) => {
        // Un watch que ha fallado no esta siguiendo nada: se cierra y el boton
        // vuelve a ofrecer reintentarlo, en vez de mentir.
        if (vigilante.current !== null) { navigator.geolocation.clearWatch(vigilante.current); vigilante.current = null; }
        setSiguiendo(false);
        setMiPos({ estado: err.code === 1 ? "denegado" : "fallo", mensaje: err.message });
      },
      { enableHighAccuracy: true, timeout: 15000, maximumAge: 5000 },
    );
  };

  /**
   * Captar desde la calle. ESTO si va en movil —al reves que asignar—: crear la
   * captacion del portal delante del que estas es justo para lo que se saca el
   * telefono, y no necesita elegir entre 56 fichas.
   */
  const captarUnidad = async (u) => {
    // La coordenada sale del propio estado de la parcela y NO del marcador:
    // ese marcador solo existe si se llego pinchando el mapa. Llegando por
    // "que hay donde estoy" era null y el boton no hacia nada, en silencio.
    if (!parcela || parcela.estado !== "ok" || parcela.lng == null) return;
    const { lng, lat } = parcela;
    try {
      const sesion = await window.RevinSupabase.getSession();
      if (!sesion) {
        setParcela((p) => ({ ...p, avisoOk: false, aviso: "Estás en datos de prueba: no hay dónde crearla." }));
        return;
      }
      const r = await window.RevinSupabase.crearCaptacionDesdeParcela({
        // u es null cuando se capta la parcela ENTERA, no una unidad suelta.
        rc14: parcela.rc, rc20: u ? u.rc20 : null, unidad: u || null, lng, lat, datos: parcela.datos,
      });
      setParcela((p) => ({
        ...p,
        avisoOk: true,
        aviso: r.creado
          ? "Creada la captación " + r.inmueble.ref + "."
          : "Ya existía como " + r.inmueble.ref + ". No se ha creado otra.",
      }));
    } catch (e) {
      const msg = window.RevinSupabase.errorAmable ? window.RevinSupabase.errorAmable(e) : e.message;
      setParcela((p) => ({ ...p, avisoOk: false, aviso: String(msg) }));
    }
  };

  const queHayAqui = async () => {
    if (!miPos || miPos.estado !== "ok") return;
    setFicha(null);
    const { lng, lat } = miPos;   // la coordenada viaja con la parcela
    setParcela({ estado: "cargando" });
    try {
      const rc = await rcPorCoordenada(lng.toFixed(6), lat.toFixed(6));
      const guardada = deCache(rc);
      if (guardada) { setParcela({ estado: "ok", rc, datos: guardada, lng, lat }); return; }
      setParcela({ estado: "desglosando", rc, desde: Date.now(), lng, lat });
      const datos = await datosDeParcela(rc);
      aCache(rc, datos);
      setParcela({ estado: "ok", rc, datos, lng, lat });
    } catch (err) {
      setParcela({ estado: "error", error: err.message });
    }
  };

  return (
    <>
      <div className="m-top">
        <div>
          <div className="greet">
            {miPos && miPos.estado === "ok" ? "ESTÁS AQUÍ · ±" + miPos.precision + " M" : "CARTERA SOBRE EL CATASTRO"}
          </div>
          <h1>MAPA</h1>
        </div>
        <div style={{display: "flex", gap: 6}}>
          <button
            className={"icon-btn" + (buscando ? " on" : "")}
            onClick={() => { if (buscando) setBusqueda(""); setBuscando((v) => !v); }}
            aria-label={buscando ? "Cerrar la búsqueda" : "Buscar inmueble"}
          >
            <Icons.Search/>
          </button>
          <button
            className={"icon-btn" + (siguiendo ? " on" : "")}
            onClick={alternarSeguimiento}
            aria-label={siguiendo ? "Dejar de seguir mi posición" : "Ver dónde estoy"}
          >
            <Icons.Crosshair/>
          </button>
        </div>
      </div>

      {buscando && (
        <div className="mm-buscar">
          <Icons.Search/>
          <input
            autoFocus
            placeholder="Ref, dirección o referencia catastral"
            value={busqueda}
            onChange={(e) => setBusqueda(e.target.value)}
          />
          <span className="mm-cuenta">
            {conCoords.length}{conCoords.length !== todos.length ? " de " + todos.length : ""}
          </span>
        </div>
      )}

      {buscando && sugerencias.length > 0 && (
        <div className="mm-sugerencias">
          <p className="mm-sug-ojo">Direcciones · IGN</p>
          {sugerencias.map((c, i) => (
            <button key={c.id || i} type="button" onClick={() => irADireccion(c)}>
              <span className="d">{c.address}</span>
              <span className="m">{[c.muni, c.province].filter(Boolean).join(" · ")}</span>
            </button>
          ))}
        </div>
      )}

      <div className="mm-lienzo">
        <div ref={contenedor} className="mm-mapa"/>

        <div className="mm-capas">
          <button type="button" aria-pressed={capaBase === "mapa"} onClick={() => setCapaBase("mapa")}>Mapa</button>
          <button type="button" aria-pressed={capaBase === "orto"} onClick={() => setCapaBase("orto")}>Foto</button>
          <button type="button" aria-pressed={capaBase === "ninguna"} onClick={() => { setCapaBase("ninguna"); setVerCatastro(true); }}>Plano</button>
          <button type="button" aria-pressed={verCatastro} onClick={() => setVerCatastro((v) => !v)}>Catastro</button>
        </div>

        {miPos && miPos.estado !== "ok" && (
          <p className="mm-aviso" role="status">
            {miPos.estado === "buscando" ? "Buscando tu posición…"
              : miPos.estado === "denegado"
                ? "Sin permiso de ubicación. El mapa funciona igual."
                : miPos.estado === "no_disponible" ? "Este móvil no da ubicación."
                  : "No se pudo obtener la posición."}
          </p>
        )}

        {/* La acción principal en la calle. */}
        {miPos && miPos.estado === "ok" && !parcela && !ficha && (
          <button className="mm-accion" onClick={queHayAqui}>
            ¿Qué hay donde estoy?
          </button>
        )}

        {/* Hoja inferior: parcela del Catastro, con su desglose por niveles. */}
        {parcela && (
          <div className="mm-hoja">
            <div className="mm-asa"/>
            <FichaParcela
              parcela={parcela}
              alCerrar={() => {
                setParcela(null);
                if (marcaParcela.current) { marcaParcela.current.remove(); marcaParcela.current = null; }
              }}
              candidatos={cartera}
              alAsignar={async () => setParcela((p) => ({ ...p, avisoOk: false, aviso: "Asignar se hace desde el escritorio: aquí no cabe elegir bien entre 56 fichas." }))}
              alCaptar={captarUnidad}
            />
          </div>
        )}

        {/* Hoja inferior: inmueble nuestro. */}
        {ficha && (
          <div className="mm-hoja baja">
            <div className="mm-asa"/>
            <div className="mm-ficha">
              {ficha.grupo.length > 1 && (
                <p className="mm-sub">{ficha.grupo.length} inmuebles en este portal</p>
              )}
              {ficha.grupo.map((p) => (
                <div key={p.id} className="mm-item">
                  <div className="mm-addr">{p.addr}</div>
                  <div className="mm-meta">
                    {p.type} · {p.town}
                    {p.price > 0 && <> · {fmtEUR(p.price)}</>}
                    {!esExacta(p.geoConfianza) && <> · ubicación aproximada</>}
                  </div>
                  {p.refCatastralParcela && <div className="mm-rc">{p.refCatastralParcela}</div>}
                </div>
              ))}
              <button className="mm-cerrar" onClick={() => setFicha(null)}>Cerrar</button>
            </div>
          </div>
        )}

        {/* Lo más cercano, que es lo que se mira andando. */}
        {miPos && miPos.estado === "ok" && cercanos.length > 0 && !parcela && !ficha && (
          <div className="mm-cerca">
            {cercanos.map(({ p, d }) => (
              <button key={p.id} onClick={() => { setParcela(null); setFicha({ grupo: [p], i: 0 }); }}>
                <span className="mm-d">{d < 1000 ? d + " m" : (d / 1000).toFixed(1) + " km"}</span>
                <span className="mm-a">{p.addr}</span>
              </button>
            ))}
          </div>
        )}
      </div>
    </>
  );
};


const MobileScreen = ({ active, setActive }) => {
  const { Icons, DATA, fmtEUR } = window;

  /**
   * Buscador y filtros de la cartera de clientes.
   *
   * Esta pantalla nacio como maqueta para enseñar el diseño desde un
   * ordenador, y se le noto al usarla de verdad el 28-ago-2026: el campo de
   * buscar era un <span> decorativo, los cinco filtros eran botones sin
   * onClick, y la lista estaba cortada con slice(0, 5) mientras la cabecera
   * contaba TODOS. Con siete clientes de prueba decia "7 CLIENTES" y enseñaba
   * cinco; con la cartera real de la agencia esconderia casi todo.
   *
   * Filtrar por prefijo y no por igualdad: los tipos vienen en femenino y
   * masculino ("Compradora", "Inversor", "Vendedor").
   */
  const [buscaCli, setBuscaCli] = React.useState("");
  const [filtroCli, setFiltroCli] = React.useState("Todos");

  const clientes = React.useMemo(() => {
    const t = buscaCli.trim().toLowerCase();
    return (DATA.CLIENTS || []).filter((c) => {
      if (filtroCli === "En riesgo" && !c.cooling) return false;
      if (filtroCli === "Compradores" && !/^comprador/i.test(c.type || "")) return false;
      if (filtroCli === "Inversores" && !/^inversor/i.test(c.type || "")) return false;
      if (filtroCli === "Vendedores" && !/^vendedor/i.test(c.type || "")) return false;
      if (!t) return true;
      return [c.displayName, c.name, c.phone, c.email, c.zone, c.type]
        .filter(Boolean).some((v) => String(v).toLowerCase().includes(t));
    });
  }, [DATA.CLIENTS, buscaCli, filtroCli]);

  // WhatsApp abre la conversación; no manda nada. El numero viaja con espacios
  // y un "+" que wa.me no admite.
  const abrirWhatsapp = (c) => {
    const n = String(c.phone || "").replace(/[^0-9]/g, "");
    if (n) window.open("https://wa.me/" + n, "_blank", "noopener");
  };

  const renderHoy = () => (
    <>
      <div className="m-top">
        <div>
          <div className="greet">MARTES 18 MAR</div>
          <h1>HOY</h1>
        </div>
        <div className="avas">
          <button className="icon-btn"><Icons.Bell/><span className="badge">3</span></button>
          <div className="ava">EM</div>
        </div>
      </div>
      <div className="m-body">
        {/* B0.b — marca SOLO la cabecera (fecha «18 MAR») y estos dos KPIs, que son
            literales. Los listados de abajo (clientes en riesgo, agenda, inmuebles)
            leen DATA real y NO se marcan: marcarlos sería la regla 10 al revés. */}
        <window.DemoBlockMark bloque="movil-cabecera">Fecha y métricas de ejemplo.</window.DemoBlockMark>
        <div className="m-kpis">
          <div className="m-kpi accent">
            <div className="m-kpi-eye">Objetivo mes</div>
            <div className="m-kpi-val">62%</div>
            <div className="m-kpi-sub" style={{color: "var(--neutral-300)"}}>27.8k / 45k €</div>
          </div>
          <div className="m-kpi">
            <div className="m-kpi-eye">Facturado</div>
            <div className="m-kpi-val fin-in">12.6k</div>
            <div className="m-kpi-sub">+18% MoM</div>
          </div>
        </div>

        <div className="m-alert">
          <div className="h"><Icons.Flame/> Clientes en riesgo<span className="cnt">3</span></div>
          {DATA.COOLING_CLIENTS.slice(0, 2).map(c => (
            <div className="m-cool" key={c.id}>
              <div className="ava">{c.name.split(" ").map(p => p[0]).slice(0,2).join("")}</div>
              <div className="m-cool-body">
                <div className="m-cool-name">{c.name}</div>
                <div className="m-cool-meta">{c.reason}</div>
              </div>
              <div className="days">{c.days}d</div>
            </div>
          ))}
        </div>

        <div className="m-card">
          <div className="m-card-h">
            <h3>Próximas 4 acciones</h3>
            <Icons.ChevronRight size={14}/>
          </div>
          {DATA.TODAY_AGENDA.slice(0, 4).map((t, i) => {
            const IcoMap = { Phone: Icons.Phone, Eye: Icons.Eye, Doc: Icons.Doc, Whatsapp: Icons.Whatsapp, Calendar: Icons.Calendar };
            const I = IcoMap[t.icon] || Icons.Clock;
            return (
              <div className="m-list-item" key={i}>
                <div className="time">{t.time}</div>
                <div className={"ico " + (t.tone === "red" ? "red" : "")}><I/></div>
                <div className="m-list-body">
                  <div className="m-list-title">{t.title}</div>
                  <div className="m-list-sub">{t.sub}</div>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </>
  );

  const renderPipeline = () => {
    // `DATA.INMUEBLES[0]` sin comprobar: con la cartera vacía esto reventaba al
    // leer `sample.phase` y el ErrorBoundary pintaba "Algo falló al mostrar esta
    // sección" — en el móvil, que es donde se usa en la calle y donde menos se
    // puede diagnosticar nada.
    const sample = (DATA.INMUEBLES || [])[0];
    if (!sample) {
      return (
        <>
          <div className="m-top">
            <div><h1>PIPELINE</h1></div>
          </div>
          <div className="m-body">
            <div className="empty" style={{padding: "48px 24px", textAlign: "center"}}>
              <div style={{fontSize: 15, fontWeight: 700, marginBottom: 8}}>Todavía no tienes inmuebles</div>
              <div style={{fontSize: 13, color: "var(--fg-3)", lineHeight: 1.5}}>
                Cuando haya alguno en tu cartera, aquí verás por qué fase va.
              </div>
            </div>
          </div>
        </>
      );
    }
    const activeIdx = DATA.PHASES.findIndex(p => p.key === sample.phase);
    return (
      <>
        <div className="m-top">
          <div>
            {/* Decía "REF-034 · REAL 24, 3ºD" a fuego: la misma referencia y la
                misma dirección para cualquier inmueble que se mirase. */}
            <div className="greet">{[sample.ref, sample.addr].filter(Boolean).join(" · ") || "Sin referencia"}</div>
            <h1>PIPELINE</h1>
          </div>
          <button className="icon-btn"><Icons.MoreH/></button>
        </div>
        <div className="m-body">
          <div className="m-prop">
            <div className="m-prop-photo">
              <span style={{position: "absolute", color: "rgba(255,255,255,.2)", fontFamily: "var(--font-display)", fontSize: 12, top: "50%", left: "50%", transform: "translate(-50%, -50%)"}}>FOTO</span>
              <button className="ph-fav"><Icons.Heart/></button>
              <div className="ph-price">{fmtEUR(sample.price)}</div>
              <div className="pagi"><span className="on"/><span/><span/><span/></div>
            </div>
            <div className="m-prop-body">
              <div className="m-prop-addr">{sample.addr}</div>
              <div className="m-prop-loc">{sample.town} · <Pill tone="warning">FASE 4 · VISITAS</Pill></div>
              <div className="m-prop-specs">
                <span className="spec"><Icons.Bed/>{sample.rooms} dorm</span>
                <span className="spec"><Icons.Bath/>{sample.baths} baño</span>
                <span className="spec"><Icons.Sqm/>{sample.area} m²</span>
              </div>
            </div>
          </div>

          <div className="m-card" style={{padding: "8px 12px"}}>
            <div className="m-pipe">
              {DATA.PHASES.map((ph, i) => (
                <div className={"m-pipe-row" + (i < activeIdx ? " done" : "") + (i === activeIdx ? " active" : "")} key={ph.key}>
                  <div className="m-pipe-num">{i < activeIdx ? "✓" : ph.num}</div>
                  <div className="m-pipe-body">
                    <div className="m-pipe-title">{ph.label}</div>
                    <div className="m-pipe-meta">{i < activeIdx ? "Completada" : i === activeIdx ? "En curso · 12 días" : "Pendiente"}</div>
                  </div>
                </div>
              ))}
            </div>
          </div>

          <button className="btn primary" style={{justifyContent: "center", padding: "12px"}}>
            <Icons.ArrowRight/> Avanzar a fase Reserva
          </button>
        </div>
      </>
    );
  };

  const renderClientes = () => (
    <>
      <div className="m-top">
        <div>
          <div className="greet">
            {clientes.length === (DATA.CLIENTS || []).length
              ? clientes.length + " CLIENTES"
              : clientes.length + " DE " + (DATA.CLIENTS || []).length}
          </div>
          <h1>CARTERA</h1>
        </div>
        <button className="icon-btn"><Icons.Plus/></button>
      </div>
      <div className="m-body">
        <div className="tb-search" style={{width: "100%", marginBottom: 4}}>
          <Icons.Search/>
          <input
            className="m-busca-cli"
            placeholder="Buscar cliente…"
            value={buscaCli}
            onChange={(e) => setBuscaCli(e.target.value)}
          />
        </div>
        <div style={{display: "flex", gap: 6, overflowX: "auto", margin: "4px -16px 8px", padding: "0 16px"}}>
          {["Todos", "Compradores", "Inversores", "En riesgo", "Vendedores"].map((f) => (
            <button key={f} type="button" aria-pressed={filtroCli === f}
              className={"fchip" + (filtroCli === f ? " active" : "")}
              style={{flexShrink: 0}}
              onClick={() => setFiltroCli(f)}>{f}</button>
          ))}
        </div>
        {clientes.length === 0 && (
          <p className="mm-sub" style={{padding: "18px 2px"}}>
            Ningún cliente con ese filtro.
          </p>
        )}
        {clientes.map(c => (
          <div className="m-card" key={c.id} style={{padding: 12, display: "flex", alignItems: "center", gap: 12, cursor: "pointer"}}>
            <div className="ava" style={{width: 40, height: 40, borderRadius: 999, background: c.cooling ? "var(--revin-red)" : "var(--neutral-700)", color: "#fff", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 12, fontWeight: 700, flexShrink: 0}}>
              {(c.displayName || c.name).split(" ").map(p => p[0]).slice(0,2).join("")}
            </div>
            <div style={{flex: 1, minWidth: 0}}>
              <div style={{fontSize: 13, fontWeight: 600}}>{c.displayName || c.name}</div>
              <div style={{fontSize: 11, color: "var(--fg-2)", marginTop: 2, display: "flex", gap: 6, alignItems: "center"}}>
                <Pill tone={c.type === "Compradora" ? "info" : "warning"}>{c.type}</Pill>
                {c.budget > 0 && <span>{fmtEUR(c.budget)}</span>}
              </div>
            </div>
            <button className="btn-wa" style={{width: 36, height: 36}}
              aria-label={"WhatsApp a " + (c.displayName || c.name)}
              disabled={!c.phone}
              onClick={(e) => { e.stopPropagation(); abrirWhatsapp(c); }}>
              <Icons.Whatsapp size={16}/>
            </button>
          </div>
        ))}
      </div>
    </>
  );

  const renderMapa = () => <MapaMovil/>;

  return (
    <>
      <div className="m-status">
        <span>9:41</span>
        <span className="right"><Icons.Sun size={14}/> 100%</span>
      </div>
      {active === "dashboard" && renderHoy()}
      {active === "pipeline" && renderPipeline()}
      {active === "clientes" && renderClientes()}
      {active === "mapa" && renderMapa()}

      <div className="m-tabs">
        <button className={"m-tab" + (active === "dashboard" ? " active" : "")} onClick={() => setActive("dashboard")}>
          <Icons.Dash/><span>Hoy</span>
        </button>
        <button className={"m-tab" + (active === "pipeline" ? " active" : "")} onClick={() => setActive("pipeline")}>
          <Icons.Pipeline/><span>Pipeline</span>
        </button>
        <button className="m-tab fab"><Icons.Plus/></button>
        <button className={"m-tab" + (active === "clientes" ? " active" : "")} onClick={() => setActive("clientes")}>
          <Icons.Users/><span>Clientes</span>
        </button>
        <button className={"m-tab" + (active === "mapa" ? " active" : "")} onClick={() => setActive("mapa")}>
          <Icons.Map/><span>Mapa</span>
        </button>
      </div>
    </>
  );
};

const MobileView = () => {
  const [active, setActive] = React.useState("dashboard");
  const [active2, setActive2] = React.useState("pipeline");

  return (
    <div className="m-app">
      <div className="m-frame">
        <div className="m-screen">
          <MobileScreen active={active} setActive={setActive}/>
        </div>
      </div>
      <div className="m-frame">
        <div className="m-screen">
          <MobileScreen active={active2} setActive={setActive2}/>
        </div>
      </div>
    </div>
  );
};

/**
 * La vista movil DE VERDAD, la que se ve en un telefono.
 *
 * `MobileView` dibuja dos iPhones falsos de 390x844 con muesca: es una maqueta
 * para enseñar el diseño desde un ordenador. En un movil de 375 px eso pinta
 * dos marcos mas anchos que la propia pantalla, partidos por la mitad.
 *
 * Esto es lo mismo por dentro pero sin el marco: `MobileScreen` a pantalla
 * completa. Y sin la barra de estado falsa ("9:41 · 100%"), que en un telefono
 * de verdad compite con la del sistema.
 */
const MobileReal = () => {
  const [active, setActive] = React.useState("dashboard");
  return (
    <div className="m-screen m-real">
      <MobileScreen active={active} setActive={setActive}/>
    </div>
  );
};

window.MobileView = MobileView;
window.MobileReal = MobileReal;
