const { Button, IconButton, Card, Icon, Input, Select, Badge, EmptyState } = window.MiradorDesignSystem_4bae5c;

const CUANDO = ['Esta semana', 'Este fin de semana', 'La próxima semana', 'Aún estoy viendo opciones'];
/* Formulario para dejar datos. Modal propio con overlay fijo: el Dialog del
   sistema usa position:absolute y en una página larga se centra respecto al
   documento, no a la ventana. */
function VisitaForm({ p, open, onClose }) {
  const [f, setF] = React.useState({ nombre: '', tel: '', email: '', cuando: CUANDO[0] });
  const [err, setErr] = React.useState({});
  const [busy, setBusy] = React.useState(false);
  const [listo, setListo] = React.useState(false);
  const set = (k) => (e) => { setF(x => ({ ...x, [k]: e.target.value })); setErr(x => ({ ...x, [k]: undefined })); };
  React.useEffect(() => {
    if (!open) return;
    const h = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', h);
    document.body.style.overflow = 'hidden';
    return () => { window.removeEventListener('keydown', h); document.body.style.overflow = ''; };
  }, [open]);
  const enviar = async (e) => {
    e.preventDefault();
    const x = {};
    if (f.nombre.trim().split(/\s+/).filter(Boolean).length < 2) x.nombre = 'Escribe tu nombre y apellido';
    if (f.tel.replace(/\D/g, '').length !== 10) x.tel = 'Ingresa 10 dígitos';
    if (f.email && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(f.email.trim())) x.email = 'Correo inválido';
    setErr(x);
    if (Object.keys(x).length) return;
    setBusy(true);
    try {
      await API.post('contacto', {
        nombre: f.nombre.trim(), tel: f.tel.replace(/\D/g, ''), email: f.email.trim() || 'sin-correo@casamia.ec',
        motivo: 'Visita · ' + p.titulo + ' (' + p.id + ')',
        mensaje: `Quiere ver "${p.titulo}" en ${p.sector}, ${p.ciudad} — ${money(p.precio)}.\nCuándo: ${f.cuando}\n${propertyUrl(p)}`
      });
      setListo(true);
    } catch (e2) { setErr({ form: e2.message }); }
    setBusy(false);
  };
  if (!open) return null;
  return (
    <div className="cm-vf-scrim" onClick={onClose}>
      <div className="cm-vf-modal" role="dialog" aria-modal="true" aria-label="Dejar mis datos" onClick={(e) => e.stopPropagation()}>
        <header className="cm-vf-head">
          <span>{listo ? 'Listo' : 'Dejar mis datos'}</span>
          <button type="button" className="cm-vf-x" aria-label="Cerrar" onClick={onClose}><Icon name="x" size={18} /></button>
        </header>
        {listo ? (
          <div className="cm-vf-ok">
            <span className="cm-vf-mark"><Icon name="check" size={24} /></span>
            <strong>Recibimos tus datos</strong>
            <p>Te escribimos por WhatsApp al {f.tel.replace(/\D/g, '')} para coordinar la visita. Atendemos 24/7.</p>
            <Button block variant="outline" onClick={onClose}>Cerrar</Button>
          </div>
        ) : (
          <form className="cm-vf" onSubmit={enviar} noValidate>
            <div className="cm-vf-body">
            <p className="cm-vf-sub">Te contactamos para coordinar la visita a <strong>{p.titulo}</strong>.</p>
            <div className="cm-vf-f"><span className="cm-vf-l">Nombre completo</span><Input placeholder="María Pérez" value={f.nombre} onChange={set('nombre')} error={err.nombre} /></div>
            <div className="cm-vf-two">
              <div className="cm-vf-f"><span className="cm-vf-l">WhatsApp</span><Input placeholder="0988957054" inputMode="numeric" value={f.tel} onChange={set('tel')} error={err.tel} /></div>
              <div className="cm-vf-f"><span className="cm-vf-l">Correo (opcional)</span><Input placeholder="tucorreo@ejemplo.com" value={f.email} onChange={set('email')} error={err.email} /></div>
            </div>
            <div className="cm-vf-f"><span className="cm-vf-l">¿Cuándo te gustaría verla?</span><Select options={CUANDO} value={f.cuando} onChange={set('cuando')} /></div>
            {err.form ? <span className="cm-vf-err">{err.form}</span> : null}
            </div>
            <div className="cm-vf-foot">
              <Button type="button" variant="outline" onClick={onClose}>Cancelar</Button>
              <Button type="submit" disabled={busy}>{busy ? 'Enviando…' : 'Enviar'}</Button>
            </div>
          </form>
        )}
      </div>
    </div>
  );
}

function Gallery({ fotos, titulo }) {
  const [i, setI] = React.useState(0);
  const [full, setFull] = React.useState(false);
  const step = (d) => setI((i + d + fotos.length) % fotos.length);
  React.useEffect(() => {
    if (!full) return;
    const h = (e) => { if (e.key === 'Escape') setFull(false); if (e.key === 'ArrowRight') step(1); if (e.key === 'ArrowLeft') step(-1); };
    window.addEventListener('keydown', h); return () => window.removeEventListener('keydown', h);
  }, [full, i]);
  /* Móvil: una foto por slide con scroll-snap (swipe nativo, sin librería). */
  const track = React.useRef(null);
  const [slide, setSlide] = React.useState(0);
  const onScroll = () => {
    const el = track.current;
    if (!el) return;
    setSlide(Math.round(el.scrollLeft / el.clientWidth));
  };
  /* En móvil y tablet el header no se muestra: la vuelta al listado es este
     botón sobre la foto. Si no hay historial dentro del sitio, va a /propiedades. */
  const atras = () => {
    if (window.history.length > 1 && document.referrer && document.referrer.indexOf(location.origin) === 0) window.history.back();
    else location.href = BACKEND.links.compra;
  };
  return (
    <React.Fragment>
      <div className="cm-galm">
        <button type="button" className="cm-galm-back" onClick={atras} aria-label="Volver"><Icon name="chevron-left" size={17} />Atrás</button>
        <div className="cm-galm-track" ref={track} onScroll={onScroll}>
          {fotos.map((src, k) => (
            <button key={src} className="cm-galm-slide" onClick={() => { setI(k); setFull(true); }} aria-label={'Ampliar foto ' + (k + 1)}>
              <img src={src} alt={titulo + ' — foto ' + (k + 1)} loading={k ? 'lazy' : 'eager'} />
            </button>
          ))}
        </div>
        <span className="cm-galm-count">{slide + 1} / {fotos.length}</span>
        <div className="cm-galm-dots" aria-hidden="true">
          {fotos.slice(0, 8).map((_, k) => <i key={k} className={k === Math.min(slide, 7) ? 'is-on' : ''}></i>)}
        </div>
      </div>
      <div className="cm-gal">
        <button className="cm-gal-main" onClick={() => setFull(true)} aria-label="Ver fotos">
          <img src={fotos[0]} alt={titulo} />
        </button>
        <div className="cm-gal-side">
          {fotos.slice(1, 5).map((src, k) => (
            <button key={src} onClick={() => { setI(k + 1); setFull(true); }} aria-label={'Foto ' + (k + 2)}><img src={src} alt={titulo + ' — foto ' + (k + 2)} loading="lazy" /></button>
          ))}
        </div>
        <button className="cm-gal-all" onClick={() => setFull(true)}><Icon name="images" size={15} /><span>{'Ver las ' + fotos.length + ' fotos'}</span></button>
      </div>
      {full ? (
        <div className="cm-light" onClick={() => setFull(false)}>
          <button className="cm-light-x" aria-label="Cerrar"><Icon name="x" size={20} /></button>
          <button className="cm-light-nav is-prev" aria-label="Anterior" onClick={(e) => { e.stopPropagation(); step(-1); }}><Icon name="chevron-left" size={22} /></button>
          <img src={fotos[i]} alt={titulo + ' — foto ' + (i + 1)} onClick={(e) => e.stopPropagation()} />
          <button className="cm-light-nav is-next" aria-label="Siguiente" onClick={(e) => { e.stopPropagation(); step(1); }}><Icon name="chevron-right" size={22} /></button>
          <span className="cm-light-n">{i + 1} / {fotos.length}</span>
        </div>) : null}
    </React.Fragment>
  );
}

/* Mapa de la ficha: una imagen estática de Google, no el mapa interactivo.
   Pesa una sola petición, no roba el scroll de la página, no pide gestos con
   dos dedos y no se queda en gris cuando la librería tarda. Para ver la zona
   en grande, el enlace abre Google Maps aparte. */
function DetailMap({ p }) {
  const [falla, setFalla] = React.useState(false);
  const punto = p.lat && p.lng ? p.lat + ',' + p.lng : [p.sector, p.ciudad, 'Ecuador'].filter(Boolean).join(', ');
  React.useEffect(() => { setFalla(false); }, [p.id, p.lat, p.lng, p.sector, p.ciudad]);
  const zoom = p.aprox ? 14 : 16;
  const src = 'https://maps.googleapis.com/maps/api/staticmap?center=' + encodeURIComponent(punto)
    + '&zoom=' + zoom + '&size=640x300&scale=2&maptype=roadmap&language=es&region=EC'
    + '&markers=' + encodeURIComponent('color:0x0A6AFF|' + punto)
    + '&key=' + BACKEND.mapsKey;
  const abrir = 'https://www.google.com/maps/search/?api=1&query=' + encodeURIComponent(punto);
  if (falla) {
    const valid = Number.isFinite(p.lat) && Number.isFinite(p.lng) && Math.abs(p.lat) <= 90 && Math.abs(p.lng) <= 180;
    const bbox = valid ? [p.lng - 0.008, p.lat - 0.004, p.lng + 0.008, p.lat + 0.004].join(',') : '';
    return <div className="cm-dmap-wrap">
      {valid ? <iframe className="cm-dmap-backup" title={'Ubicación de ' + p.titulo} loading="lazy" src={'https://www.openstreetmap.org/export/embed.html?bbox=' + encodeURIComponent(bbox) + '&layer=mapnik&marker=' + encodeURIComponent(p.lat + ',' + p.lng)} /> : <p>Ubicación: {[p.sector, p.ciudad].filter(Boolean).join(', ')}</p>}
      <p className="cm-map-msg">{p.aprox ? 'Zona aproximada.' : 'Ubicación publicada por el anunciante.'}</p>
      <a href={abrir} target="_blank" rel="noopener">Ver ubicación en Google Maps</a>
    </div>;
  }
  return (
    <div className="cm-dmap-wrap">
      <a className="cm-dmap" href={abrir} target="_blank" rel="noopener" aria-label="Ver la zona en Google Maps">
        <img src={src} alt={'Zona aproximada de ' + p.titulo + ' en ' + p.sector} loading="lazy" onError={() => setFalla(true)} />
        <span className="cm-dmap-open"><Icon name="map-pin" size={14} />Ver en Google Maps</span>
      </a>
      <p className="cm-map-msg"><Icon name="map-pin" size={15} />Zona aproximada. La dirección exacta te la damos al agendar la visita.</p>
    </div>
  );
}

/* ---- Financiamiento: qué es cada crédito y cuánto pagarías al mes ----
   Las tasas y plazos son los referenciales de cada programa; el banco confirma
   el cupo y la tasa final de cada persona. */
const PROGRAMAS = [
  {
    clave: 'vip', busca: /vip|inter[eé]s p[uú]blico|casa para todos/i, nombre: 'Crédito VIP',
    tasa: 4.99, plazo: 25, entrada: 5, icono: 'shield-check',
    que: 'Crédito con tasa subsidiada por el Estado para Vivienda de Interés Público: casas y departamentos nuevos de hasta unos $107.000.',
    puntos: ['Tasa fija del 4,99% durante todo el plazo', 'Hasta 25 años y hasta el 95% del valor', 'La vivienda debe ser nueva y la primera a tu nombre', 'Lo dan los bancos privados con fondos de la banca pública']
  },
  {
    clave: 'biess', busca: /biess|iess/i, nombre: 'Crédito BIESS',
    tasa: 8.35, plazo: 25, entrada: 5, icono: 'badge-check',
    que: 'Crédito hipotecario del Banco del IESS para afiliados con al menos 36 aportes, 12 de ellos seguidos.',
    puntos: ['Financia hasta el 100% del avalúo en viviendas de menor valor', 'Hasta 25 años, según tu edad y aportes', 'La cuota no pasa del 40% de tus ingresos netos', 'El avalúo lo hace un perito aprobado por el BIESS']
  },
  {
    clave: 'miti', busca: /miti/i, nombre: 'Miti-Miti', tasa: 4.99, plazo: 20, entrada: 5, icono: 'handshake',
    que: 'Programa del Estado en el que la banca pública y la privada financian la vivienda a medias, con tasa preferencial.',
    puntos: ['Mitad del crédito con banca pública, mitad con banca privada', 'Tasa preferencial cercana al 4,99%', 'Entrada desde el 5% del valor', 'Aplica a vivienda nueva dentro del tope del programa']
  },
  {
    clave: 'credicasa', busca: /credicasa/i, nombre: 'Credicasa', tasa: 2.99, plazo: 20, entrada: 5, icono: 'landmark',
    que: 'Crédito promocional que ofrece la constructora de este proyecto, con tasa por debajo del mercado.',
    puntos: ['Tasa promocional desde el 2,99%', 'Entrada acordada directamente con la constructora', 'Trámite más corto que en la banca tradicional', 'Sujeto a disponibilidad de unidades del proyecto']
  },
  {
    clave: 'banca', busca: /banca|privad|hipotecari/i, nombre: 'Banca privada', tasa: 10.5, plazo: 20, entrada: 20, icono: 'landmark',
    que: 'Crédito hipotecario tradicional de un banco o cooperativa, sin tope de precio de vivienda.',
    puntos: ['Entrada habitual del 20% al 30%', 'Plazos de 15 a 20 años', 'Tasa referencial entre el 9% y el 11%', 'Sirve para vivienda nueva o usada']
  }
];
const cuotaMensual = (monto, tasaAnual, anios) => {
  const i = tasaAnual / 100 / 12, n = anios * 12;
  if (!monto || n <= 0) return 0;
  if (!i) return monto / n;
  return monto * i / (1 - Math.pow(1 + i, -n));
};
const dolares = (n) => '$' + Math.round(n).toLocaleString('es-EC').replace(/,/g, '.');

function Financiamiento({ p }) {
  const aplican = PROGRAMAS.filter(x => x.busca.test(p.financiamiento || ''));
  const lista = aplican.length ? aplican : [PROGRAMAS[PROGRAMAS.length - 1]];
  const [sel, setSel] = React.useState(lista[0].clave);
  const prog = lista.find(x => x.clave === sel) || lista[0];
  const [entrada, setEntrada] = React.useState(prog.entrada);
  const [plazo, setPlazo] = React.useState(prog.plazo);
  const [tasa, setTasa] = React.useState(prog.tasa);
  const elegir = (c) => {
    const n = lista.find(x => x.clave === c);
    setSel(c); setEntrada(n.entrada); setPlazo(n.plazo); setTasa(n.tasa);
  };
  const valorEntrada = p.precio * entrada / 100;
  const monto = p.precio - valorEntrada;
  const cuota = cuotaMensual(monto, tasa, plazo);
  const total = cuota * plazo * 12;
  return (
    <section className="cm-dsec">
      <h2>Financiamiento</h2>
      {lista.length > 1 ? (
        <div className="cm-fin-tabs">
          {lista.map(x => (
            <button key={x.clave} type="button" className={'cm-fin-tab' + (x.clave === sel ? ' is-on' : '')} onClick={() => elegir(x.clave)}>
              <Icon name={x.icono} size={15} />{x.nombre}
            </button>
          ))}
        </div>) : null}
      <div className="cm-fin-box">
        <div className="cm-fin-que">
          <Badge tone={finTone(prog.nombre)} icon={prog.icono}>{prog.nombre}</Badge>
          <p>{prog.que}</p>
          <ul>{prog.puntos.map(t => <li key={t}><Icon name="check" size={14} />{t}</li>)}</ul>
          <a href={BACKEND.links.creditos}>Guía completa de créditos</a>
        </div>
        <div className="cm-sim">
          <span className="cm-sim-t">Simula tu cuota</span>
          <label className="cm-sim-f">
            <span>Entrada <b>{entrada}% · {dolares(valorEntrada)}</b></span>
            <input type="range" min="0" max="40" step="1" value={entrada} onChange={(e) => setEntrada(+e.target.value)} />
          </label>
          <label className="cm-sim-f">
            <span>Plazo <b>{plazo} años</b></span>
            <input type="range" min="5" max="25" step="1" value={plazo} onChange={(e) => setPlazo(+e.target.value)} />
          </label>
          <label className="cm-sim-f">
            <span>Tasa anual <b>{tasa.toFixed(2).replace('.', ',')}%</b></span>
            <input type="range" min="2" max="14" step="0.25" value={tasa} onChange={(e) => setTasa(+e.target.value)} />
          </label>
          <div className="cm-sim-out">
            <span className="cm-sim-cuota">{dolares(cuota)}<i>/mes</i></span>
            <span className="cm-sim-meta">Financias {dolares(monto)} · pagas {dolares(total)} en {plazo} años</span>
          </div>
          <p className="cm-sim-nota">Cálculo referencial con cuota fija. El banco confirma tu cupo, la tasa y los seguros.</p>
        </div>
      </div>
    </section>
  );
}

/* Compartir: primero el menú nativo del sistema (móvil), y si no existe, un
   menú propio con WhatsApp, Facebook, correo y copiar enlace. Siempre se
   comparte la URL canónica, no ficha.html. */
function ShareBtn({ p, url, onToast }) {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h); return () => document.removeEventListener('mousedown', h);
  }, []);
  const texto = `${p.titulo} — ${money(p.precio)} · ${p.sector}, ${p.ciudad}`;
  const copiar = async () => {
    setOpen(false);
    try { await navigator.clipboard.writeText(url); onToast('Enlace copiado'); return; } catch (e) {}
    const ta = document.createElement('textarea');
    ta.value = url; ta.style.position = 'fixed'; ta.style.opacity = '0';
    document.body.appendChild(ta); ta.select();
    try { document.execCommand('copy'); onToast('Enlace copiado'); } catch (e) { onToast('Copia el enlace de la barra del navegador'); }
    ta.remove();
  };
  const abrir = async () => {
    if (navigator.share) {
      try { await navigator.share({ title: p.titulo, text: texto, url }); return; } catch (e) { return; }
    }
    setOpen(o => !o);
  };
  const opcion = (icon, label, href) => (
    <a key={label} href={href} target="_blank" rel="noopener" onClick={() => setOpen(false)}><Icon name={icon} size={16} />{label}</a>
  );
  return (
    <span className="cm-share" ref={ref}>
      <Button variant="outline" size="sm" icon="share-2" onClick={abrir} aria-expanded={open}>Compartir</Button>
      {open ? (
        <div className="cm-share-menu" role="menu">
          {opcion('message-circle', 'WhatsApp', `https://wa.me/?text=${encodeURIComponent(texto + '\n' + url)}`)}
          {opcion('facebook', 'Facebook', `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(url)}`)}
          {opcion('mail', 'Correo', `mailto:?subject=${encodeURIComponent(p.titulo + ' — casamia.ec')}&body=${encodeURIComponent(texto + '\n' + url)}`)}
          <button type="button" onClick={copiar}><Icon name="link" size={16} />Copiar enlace</button>
        </div>) : null}
    </span>
  );
}

function Fact({ icon, label, value }) {
  if (!value && value !== 0) return null;
  return (
    <div className="cm-fact">
      <Icon name={icon} size={19} />
      <span className="cm-fact-v">{value}</span>
      <span className="cm-fact-l">{label}</span>
    </div>
  );
}

function App() {
  const [all, catalogStatus] = useCatalog(true);
  const [saved, toggleSave] = useSaved();
  const [join, setJoin] = React.useState(false);
  const [visita, setVisita] = React.useState(false);
  const [toast, avisarToast] = useToast();
  /* /propiedad/3d7c-casa-en-cumbaya en producción, ficha.html?id=3d7c en el editor.
     Si la ruta llega sin id (redirección del hosting), se usa el id que guardó
     el clic en la tarjeta; nunca se muestra otra propiedad. */
  const id = new URLSearchParams(location.search).get('id')
    || (location.pathname.match(/\/propiedad\/([^/-]+)/) || [])[1]
    || (() => { try { return sessionStorage.getItem('casamia:ultima') || ''; } catch (e) { return ''; } })();
  const normalizedId = id.replace(/-/g, '');
  const matches = all.filter(x => x.id === normalizedId || (normalizedId.length === 4 && x.id.startsWith(normalizedId)));
  // Old short links are accepted only when unambiguous.
  const p = matches.length === 1 ? matches[0] : null;
  const savedCount = Object.values(saved).filter(Boolean).length;
  React.useEffect(() => {
    if (!p) return;
    const desc = propertyDescription(p);
    setSeo({
      title: `${p.titulo} — $${Number(p.precio).toLocaleString('es-EC').replace(/,/g, '.')} · ${p.sector}, ${p.ciudad} | casamia.ec`,
      description: desc, canonical: propertyUrl(p), ogType: 'article',
      image: p.fotos?.[0], imageAlt: p.titulo,
      keywords: [p.tipo.toLowerCase() + ' en ' + p.operacion.toLowerCase(), p.tipo + ' ' + p.sector, p.sector + ' ' + p.ciudad, 'inmobiliaria ' + p.ciudad].join(', ')
    });
    jsonLd('propiedad', propertyLd(p));
    jsonLd('ruta', breadcrumbLd([['Inicio', SEO.paths.inicio], ['Propiedades', SEO.paths.propiedades], [p.sector, SEO.paths.propiedades + '?sector=' + encodeURIComponent(p.sector)], [p.titulo, propertyUrl(p)]]));
    jsonLd('organizacion', orgLd());
  }, [p]);
  if (!p) return (
    <div className="cm-detail" style={{ padding: '72px 20px', minHeight: '46vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
      <EmptyState icon="house"
        title={catalogStatus === 'loading' ? 'Cargando la propiedad…' : matches.length > 1 ? 'Varias propiedades con ese enlace' : 'Esta propiedad no está disponible'}
        body={catalogStatus === 'loading' ? 'Un momento, ya casi está.' : catalogStatus === 'error' ? 'No pudimos actualizar la propiedad. Intenta de nuevo.' : matches.length > 1 ? 'Este enlace antiguo corresponde a varias propiedades. Elige una desde el catálogo.' : 'Puede que ya no esté publicada, esté en revisión, o el enlace esté incompleto.'}
        action={catalogStatus !== 'loading' ? <Button onClick={() => { location.href = BACKEND.links.compra; }}>Ver propiedades</Button> : null} />
    </div>
  );
  const relacionadas = all.filter(x => x.id !== p.id && (x.sector === p.sector || x.tipo === p.tipo)).slice(0, 4);
  const waMsg = `Hola, me interesa la propiedad "${p.titulo}" en ${p.sector}, ${p.ciudad} — ${money(p.precio)} (código ${p.id}). ¿Podemos agendar una visita?
${propertyUrl(p)}`;
  /* Cada CTA avisa al worker antes de salir a WhatsApp o al teléfono: así el
     aviso por Resend llega aunque la persona no escriba nada. */
  const avisar = (accion) => API.lead({
    accion, propiedadId: p.id, titulo: p.titulo, precio: p.precio, sector: p.sector,
    ciudad: p.ciudad, tipo: p.tipo, operacion: p.operacion, url: propertyUrl(p)
  });

  return (
    <div>
      <Header onJoin={() => setJoin(true)} savedCount={savedCount} />
      {catalogStatus === 'error' && <p role="status">No pudimos actualizar esta propiedad. Mostramos la última información recibida.</p>}
      <main className="cm-detail">
        <nav className="cm-crumbs">
          <a href={BACKEND.links.inicio}>Inicio</a><Icon name="chevron-right" size={13} />
          <a href={BACKEND.links.compra}>Propiedades</a><Icon name="chevron-right" size={13} />
          <span>{p.sector}</span>
        </nav>

        <div className="cm-dhead">
          <div>
            <h1>{p.titulo}</h1>
            <p className="cm-dloc"><Icon name="map-pin" size={15} />{p.direccion ? p.direccion + ' · ' : ''}{p.sector}, {p.ciudad}</p>
          </div>
          <div className="cm-dhead-act">
            <Button variant={saved[p.id] ? 'primary' : 'outline'} size="sm" icon="heart"
              onClick={() => {
                if (!saved[p.id]) avisar('guardar');
                toggleSave(p.id);
                avisarToast(saved[p.id] ? 'Quitada de guardadas' : 'Guardada en tus favoritas');
              }}>{saved[p.id] ? 'Guardada' : 'Guardar'}</Button>
            <ShareBtn p={p} url={propertyUrl(p)} onToast={avisarToast} />
          </div>
        </div>

        <Gallery fotos={p.fotos} titulo={p.titulo} />

        <div className="cm-dsplit">
          <div className="cm-dmain">
            <div className="cm-facts">
              <Fact icon="bed-double" label={p.habitaciones === 1 ? 'dormitorio' : 'dormitorios'} value={p.habitaciones || null} />
              <Fact icon="bath" label={p.banos === 1 ? 'baño' : 'baños'} value={p.banos || null} />
              <Fact icon="car" label={p.parqueaderos === 1 ? 'parqueadero' : 'parqueaderos'} value={p.parqueaderos || null} />
              <Fact icon="ruler" label="m² de construcción" value={p.area ? num(p.area) : null} />
              <Fact icon="layers" label={p.plantas === 1 ? 'planta' : 'plantas'} value={p.plantas || null} />
            </div>

            <section className="cm-dsec">
              <h2>Sobre esta propiedad</h2>
              <p className="cm-dtext">{p.descripcion}</p>
            </section>

            {p.amenidades?.length ? (
              <section className="cm-dsec">
                <h2>Amenidades</h2>
                <ul className="cm-amen">
                  {p.amenidades.map(a => <li key={a}><Icon name="check" size={15} />{a}</li>)}
                </ul>
              </section>) : null}

            {p.financiamiento ? <Financiamiento p={p} /> : null}

            <section className="cm-dsec">
              <h2>Ubicación</h2>
              <DetailMap p={p} />
            </section>
          </div>

          <aside className="cm-drail">
            <Card className="cm-dcard">
              <span className="cm-dprice">{money(p.precio)}</span>
              <span className="cm-dop">{p.operacion} · {p.tipo}{p.area ? ' · ' + num(p.area) + ' m²' : ''}</span>
              <div className="cm-dcta">
                <Button block icon="message-circle" onClick={() => { avisar('visita'); go(WA(waMsg)); }}>Agendar visita</Button>
                <Button block variant="outline" icon="user" onClick={() => { avisar('datos'); setVisita(true); }}>Dejar mis datos</Button>
              </div>
              <p className="cm-dnote"><Icon name="badge-check" size={14} />Anuncio verificado por casamia.ec</p>
            </Card>
          </aside>
        </div>

        {relacionadas.length ? (
          <section className="cm-dsec cm-rel">
            <h2>Propiedades similares</h2>
            <div className="cm-rel-grid">
              {relacionadas.map(r => (
                <a key={r.id} className="cm-card" href={detailUrl(r.id, r.titulo)}>
                  <div className="cm-card-photo"><img src={r.fotos[0]} alt={r.titulo} loading="lazy" /></div>
                  <div className="cm-card-body">
                    <span className="cm-card-title">{r.titulo}</span>
                    <span className="cm-card-sub">{r.sector}, {r.ciudad}</span>
                    <span className="cm-card-price">{money(r.precio)}</span>
                  </div>
                </a>
              ))}
            </div>
          </section>) : null}
      </main>

      <div className="cm-dbar">
        <span>
          <span className="cm-dbar-p">{money(p.precio)}</span>
          <span className="cm-dbar-s">{p.tipo} · {p.sector}</span>
        </span>
        <span className="cm-dbar-act">
          <Button size="sm" variant="outline" onClick={() => { avisar('datos'); setVisita(true); }}>Dejar datos</Button>
          <Button size="sm" iconLeft="message-circle" onClick={() => { avisar('visita'); go(WA(waMsg)); }}>Agendar visita</Button>
        </span>
      </div>

      <Footer onJoin={() => setJoin(true)} />
      <JoinDialog open={join} onClose={() => setJoin(false)} />
      <VisitaForm p={p} open={visita} onClose={() => setVisita(false)} />
      <Toast msg={toast} icon={/copiad/i.test(toast || '') ? 'link' : 'heart'} />
    </div>
  );
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
