/* ============================================================================
   Momix — Explorador público (/global). Rediseño "Modo Dual", Vista 2.
   UI clara + mapa oscuro (MapLibre). CSP sin inline: todo el estilo vive acá.
   Colores exactos del handoff (README §Design Tokens).
   ============================================================================ */
@font-face { font-family:'Manrope'; font-weight:200 800; font-style:normal; font-display:swap; src:url('/assets/fonts/manrope-latin.woff2') format('woff2'); }
@font-face { font-family:'Outfit';  font-weight:200 800; font-style:normal; font-display:swap; src:url('/assets/fonts/outfit-latin.woff2') format('woff2'); }

*, *::before, *::after { box-sizing:border-box; }
.ex-body { margin:0; height:100vh; overflow:hidden; background:var(--x-s3); color:var(--x-tx);
  font-family:'Manrope',-apple-system,'Segoe UI',sans-serif; -webkit-font-smoothing:antialiased; }
.ex-body button { font-family:inherit; }
.ex-shell { height:100vh; display:flex; flex-direction:column; }
::selection { background:var(--x-ac-tint); color:#08111F; }

/* ── Tema claro / oscuro del explorador (2026-08-24, pedido de Máximo) ───────────────────────
   El explorador nació con los colores escritos a mano —213 literales—, así que no tenía forma de
   cambiar de tema. Acá se convierten en tokens y se define la paleta oscura una sola vez.

   Qué NO es un token: el verde de marca (#22D3A6) y el navy (#08111F) son los mismos en los dos
   temas — un acento que cambia de color con el tema deja de ser un acento. Y los rgba() de sombras
   y velos tampoco: funcionan sobre cualquier fondo porque son transparencias, no colores.

   El tema se elige con la MISMA llave que la home (localStorage `momix-theme`), así que cruzar de
   una pantalla a la otra no cambia de aspecto. El mapa también cambia: hay un estilo oscuro
   (assets/map/style-dark.json) que estaba en el repo sin usarse. */
.ex-body {
  --x-s:#fff; --x-s2:#F1F5F9; --x-bd:#E2E8F0; --x-s3:#F8FAFC;
  --x-tx:#0F172A; --x-tx2:#475569; --x-tx3:#5E6D82; --x-mut:#CBD5E1;
  --x-ac-tint:#DDFBF5; --x-ac-tint2:#C6F5EA; --x-ac-dk:#0B6E63;
}
.ex-body.dark {
  --x-s:#111D33; --x-s2:#0B1220; --x-bd:#233047;
  --x-tx:#F1F5F9; --x-tx2:#CBD5E1; --x-tx3:#94A3B8; --x-mut:#3E4C63;
  --x-ac-tint:#12362D; --x-ac-tint2:#164034; --x-ac-dk:#7CEAD7; --x-s3:#0B1220;
  background:#0B1220;
}
/* Botón del tema, gemelo del de la home. */
.ex-theme { width:36px; height:36px; border-radius:999px; display:inline-flex; align-items:center;
  justify-content:center; cursor:pointer; border:1px solid var(--x-bd); background:none;
  color:var(--x-tx); transition:background .2s; }
.ex-theme:hover { background:var(--x-s2); }
.ex-theme .ex-sun { display:none; }
.ex-body.dark .ex-theme .ex-moon { display:none; }
.ex-body.dark .ex-theme .ex-sun { display:block; }
@media (pointer:coarse) { .ex-theme { width:44px; height:44px; } }


/* ── Header ── */
.ex-header { flex:none; position:relative; display:flex; align-items:center; gap:12px; padding:8px 20px;
  min-height:68px; background:var(--x-s); border-bottom:1px solid var(--x-bd); z-index:20; flex-wrap:wrap; }
.ex-logo { display:flex; align-items:center; gap:9px; }
.ex-logo img { display:block; }
.ex-zona { position:relative; flex:0 1 340px; min-width:150px; display:flex; align-items:center; gap:9px; height:44px;
  padding:0 16px; border:1px solid var(--x-tx3); border-radius:10px; background:var(--x-s); transition:border-color .2s; overflow:hidden; }
.ex-zona:focus-within { border-color:#22D3A6; }
.ex-zona svg { flex:none; }
.ex-zona input { flex:1; min-width:0; border:none; outline:none; background:none; font:400 14px 'Manrope',sans-serif; color:var(--x-tx); }
.ex-zona-tag { margin-left:auto; flex:none; background:var(--x-ac-tint); color:var(--x-ac-dk); font:600 11px 'Manrope',sans-serif; padding:3px 8px; border-radius:999px; }
.ex-morefilt { position:relative; height:44px; display:inline-flex; align-items:center; gap:7px; padding:0 14px;
  border-radius:10px; background:var(--x-tx); color:var(--x-s2); font:500 13.5px 'Manrope',sans-serif; border:none; cursor:pointer; transition:opacity .2s; }
.ex-morefilt:hover { opacity:.85; }
.ex-morefilt:active { transform:scale(.97); }
.ex-fbadge { position:absolute; top:-6px; right:-6px; min-width:19px; height:19px; border-radius:999px;
  background:#22D3A6; color:#08111F; font:600 11px 'Outfit',sans-serif; display:inline-flex; align-items:center; justify-content:center; padding:0 5px; }
.ex-fbadge[hidden] { display:none; }
/* flex-wrap + min-width:0: con el selector ES/EN sumado, la barra derecha (idioma + sonido +
   alertas + ingresar) no entra a 360px y empujaba scroll horizontal en toda la página. */
.ex-header-right { margin-left:auto; display:flex; align-items:center; gap:8px; flex:0 1 auto;
  flex-wrap:wrap; justify-content:flex-end; min-width:0; }


/* Selector ES/EN — mismo lenguaje visual que el de la home, adaptado al header claro. */
.ex-lang { display:inline-flex; align-items:center; gap:6px; height:36px; padding:0 10px; border-radius:999px;
  border:1px solid var(--x-bd); color:var(--x-tx); text-decoration:none; transition:background .2s; font:600 12.5px 'Outfit',sans-serif;
  letter-spacing:.06em; flex:none; }
.ex-lang:hover { background:var(--x-s2); color:var(--x-ac-dk); }
.ex-lang-t { letter-spacing:.06em; }
/* Sin `opacity` en el idioma inactivo: al mezclarse con el blanco daba 2.88:1 (axe lo marcó). El
   contraste se expresa con el COLOR (terciario, 5:1), que además es lo que el estándar mide. */
.ex-lang .off { color:var(--x-tx3); text-decoration:none; transition:color .2s; }
.ex-lang .off:hover { color:var(--x-ac-dk); }
.ex-lang .sep { opacity:.4; }
/* `color` explícito: el ícono usa `currentColor` desde 2026-08-26 —antes tenía el azul oscuro
   quemado en el SVG y en modo oscuro desaparecía—. Sin declararlo acá dependería de lo que herede
   del body, que es justo la clase de dependencia que hizo falta arreglar. */
.ex-sound { width:44px; height:44px; border-radius:10px; display:inline-flex; align-items:center; justify-content:center;
  border:1px solid var(--x-bd); background:var(--x-s); color:var(--x-tx); cursor:pointer; transition:background .2s; }
.ex-sound:hover { background:var(--x-s2); }
.ex-sound .ex-sound-off { display:none; }
.ex-sound.is-muted .ex-sound-on { display:none; }
.ex-sound.is-muted .ex-sound-off { display:block; }
.ex-ingresar { height:44px; padding:0 16px; border-radius:10px; background:#22D3A6; color:#08111F;
  font:600 13.5px 'Outfit',sans-serif; border:none; cursor:pointer; transition:background .2s; }
.ex-ingresar:hover { background:#14B8A6; }


/* La clase la comparten el <button> "Salir" y el <a> "Mi cuenta": `text-decoration:none` para que
   el link no salga subrayado al lado de un botón que no lo está. */



/* ── Layout principal ── */
.ex-main { flex:1; display:flex; min-height:0; }
/* El ancho del panel vive en una variable porque lo leen DOS cosas: el panel y la costura que va
   pegada a su borde. Con el valor escrito en dos lugares se separan sin que nadie se entere — pasó:
   la línea quedaba a 791px con el panel en 760. Se declara acá, ANTES del media query de 4K que la
   redefine: misma especificidad, gana la última que aparece en el archivo. */
.ex-body { --ex-panelw:min(760px,54vw); }
.ex-panel { width:var(--ex-panelw); flex:none; display:flex; flex-direction:column; min-height:0; background:var(--x-s3); }
.ex-panel-head { flex:none; display:flex; align-items:center; justify-content:space-between; gap:10px; padding:20px 24px 14px; }
.ex-count { font:600 22px 'Outfit',sans-serif; color:var(--x-tx); }
.ex-count-sub { font:400 14px 'Manrope',sans-serif; color:var(--x-tx2); }
/* El botón de ORDEN ya no vive en esta cabecera: se mudó a la del panel de filtros, que es donde lo
   tiene la home. Estaba escrito dos veces, y esa era la duplicación a sacar (2026-08-24). */


.ex-results { flex:1; overflow-y:auto; padding:4px 24px 28px; }
/* `auto-fill` y no `1fr 1fr`: al ampliar el listado sobre el mapa, dos columnas fijas repartían
   todo el ancho nuevo entre las MISMAS dos fichas y quedaban estiradas (reportado por Máximo,
   2026-08-25). Con un mínimo por ficha, el ancho de más se convierte en columnas, que es lo que uno
   espera de una grilla. A 712px —el panel sin ampliar— siguen dando exactamente 2: el layout
   aprobado para 1280–1920 no se mueve. */
.ex-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); gap:18px; }

/* ── Tarjeta ── */
.ex-card { background:var(--x-s); border:1px solid var(--x-bd); border-radius:16px; overflow:hidden; cursor:pointer;
  text-decoration:none; color:inherit; display:block; transition:transform .25s, box-shadow .25s, border-color .2s; animation:excardin .35s ease; }
.ex-card:hover, .ex-card.is-hot { transform:translateY(-4px); box-shadow:0 10px 24px rgba(8,17,31,.14); border-color:#22D3A6; }
.ex-card-media { position:relative; height:196px; overflow:hidden; }
.ex-card-img { width:100%; height:100%; object-fit:cover; transition:transform .5s; }
.ex-card:hover .ex-card-img { transform:scale(1.05); }
.ex-card-noimg { position:absolute; inset:0; background:linear-gradient(150deg,var(--x-bd),var(--x-mut)); }
.ex-badge { position:absolute; top:11px; left:11px; padding:4px 10px; border-radius:999px; font:600 11.5px 'Outfit',sans-serif; }
.ex-badge-venta { background:#22D3A6; color:#08111F; }
.ex-badge-alq { background:rgba(241,245,249,.94); color:var(--x-tx); }
.ex-fav { position:absolute; top:11px; right:11px; width:38px; height:38px; border-radius:999px;
  background:rgba(241,245,249,.92); border:none; display:inline-flex; align-items:center; justify-content:center; cursor:pointer; transition:transform .15s; }
.ex-fav:active { transform:scale(.82); }
.ex-fav svg { fill:rgba(15,23,42,.28); stroke:rgba(15,23,42,.55); stroke-width:1.6; }
.ex-fav.is-fav svg { fill:#EF4444; stroke:#EF4444; }
.ex-card-body { padding:13px 16px 15px; }
.ex-card-top { display:flex; align-items:baseline; justify-content:space-between; gap:8px; }
.ex-price { font:600 20px 'Outfit',sans-serif; color:var(--x-tx); }
/* var(--x-tx3) es el token de "meta SOBRE OSCURO" del handoff; acá la tarjeta es BLANCA, así que va el
   terciario var(--x-tx3). Con el gris claro daba 2.56:1 (axe lo marcaba) y la inmobiliaria — que el
   checklist pide VISIBLE en cada tarjeta — casi no se leía. */
.ex-price .per { font:400 12px 'Manrope',sans-serif; color:var(--x-tx3); }
.ex-broker { font:400 11.5px 'Manrope',sans-serif; color:var(--x-tx3); text-align:right; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:46%; }
.ex-card-title { font:500 14px 'Manrope',sans-serif; color:var(--x-tx); margin-top:3px; }
.ex-card-loc { font:400 12.5px 'Manrope',sans-serif; color:var(--x-tx2); margin-top:2px; }
.ex-card-specs { display:flex; align-items:center; gap:14px; margin-top:9px; color:var(--x-tx2); font:400 12.5px 'Manrope',sans-serif; }
/* Antigüedad: contra el borde derecho y apagada. Es contexto, no titular. */
.ex-card-dias { margin-left:auto; flex:none; color:var(--x-tx3); background:var(--x-s2);
  font:600 11.5px 'Manrope',sans-serif; padding:2px 8px; border-radius:999px; }
.ex-empty { grid-column:1/-1; text-align:center; color:var(--x-tx3); padding:56px 20px; }
.ex-empty svg { color:var(--x-tx3); margin-bottom:8px; }
.ex-empty h2 { font:600 18px 'Outfit',sans-serif; color:var(--x-tx); margin:0 0 4px; }
.ex-empty p { margin:0; font:400 13.5px 'Manrope',sans-serif; }

/* ── Paginación ── */
.ex-pager { display:flex; align-items:center; justify-content:center; gap:8px; margin-top:26px;
  font:500 13.5px 'Manrope',sans-serif; color:var(--x-tx2); }
.ex-pager button { min-width:38px; height:38px; padding:0 14px; display:inline-flex; align-items:center; justify-content:center;
  border-radius:10px; border:1px solid var(--x-bd); background:var(--x-s); cursor:pointer; color:var(--x-tx2); font:inherit; transition:background .2s; }
.ex-pager button:hover { background:var(--x-s2); }
.ex-pager button.on { background:var(--x-tx); border-color:var(--x-tx); color:var(--x-s2); }
.ex-pager button:disabled { opacity:.4; cursor:default; }

/* ── Mapa ── */
.ex-mapwrap { flex:1; position:relative; background:#E8EAED; overflow:hidden; min-width:0;
  transition:flex-basis .35s cubic-bezier(.3,.7,.2,1); }
.ex-mapwrap > .ex-map {
  /* ── EL MAPA EN BLANCO (2026-08-25) ────────────────────────────────────────────────────────
     Con `.ex-map` a secas esta regla PERDÍA. MapLibre trae `.maplibregl-map{position:relative}`
     en su hoja, se la pone al contenedor al inicializarse, y desde que los <script>/<link> de
     MapLibre volvieron a ser fijos su CSS carga DESPUÉS de global.css: misma especificidad
     (0-1-0), gana el último que aparece. El contenedor pasaba a `relative`, `inset:0` dejaba de
     tener efecto, quedaba con 0 de ALTO y MapLibre caía a su canvas por defecto de 300px dentro
     de una caja de altura cero: mapa completamente en blanco, sin un solo error de consola.
     Con el selector descendiente (0-2-0) gana esta, no importa el orden de las hojas — que es lo
     que se quiere: el arreglo no puede depender de en qué línea del <head> quedó cada <link>.
     `height`/`width` explícitos además de `inset` para que el colapso sea imposible de repetir. */
  position:absolute; inset:0; height:100%; width:100%;
}
.ex-map .maplibregl-ctrl-attrib { font-size:10px; }
/* ── Espera del mapa ──
   MapLibre baja BAJO DEMANDA (ver assets/global.js): entre el toque en "Mapa" y el primer tile hay
   un rato de red, y un rectángulo gris quieto no le dice a nadie que el toque entró. El velo tapa
   el recuadro entero mientras tanto y se va solo cuando el mapa terminó de dibujarse.
   El anillo que gira es el mismo recurso que usa el CRM para los botones (`.btn.is-loading`), pero
   escrito acá: la vidriera no carga style.css. */
.ex-maploading { position:absolute; inset:0; z-index:4; display:flex; align-items:center; justify-content:center;
  gap:10px; background:var(--x-s3); color:var(--x-tx2); font:500 13.5px 'Manrope',sans-serif; }
.ex-maploading[hidden] { display:none; }   /* [hidden] pierde por cascada contra un display propio */
.ex-maploading-sp { width:18px; height:18px; flex:none; border:2px solid var(--x-mut); border-top-color:#22D3A6;
  border-radius:50%; animation:exspin .7s linear infinite; }
@keyframes exspin { to { transform:rotate(360deg); } }
.ex-map-count { position:absolute; top:20px; left:76px; background:rgba(15,23,42,.82); backdrop-filter:blur(8px);
  color:#F1F5F9; font:500 13px 'Manrope',sans-serif; padding:9px 15px; border-radius:999px; z-index:2; }
/* ── Ampliar el mapa sobre el listado ──
   Pestaña flotante pegada al borde izquierdo del mapa: la flecha apunta hacia donde va a crecer y
   gira 180° al volver. El texto aparece al hover (en el reposo es solo el ícono, para no tapar
   mapa) y en táctil se muestra siempre, porque ahí no hay hover que lo revele. */
/* El texto "Ampliar mapa" / "Ver listado" se quitó (pedido 2026-08-24): sigue en title y
   aria-label, así que el lector de pantalla lo anuncia igual y el mapa gana ese ancho. */

/* Modo ancho: el panel se corre a la izquierda y el mapa ocupa todo. Se anima el ancho del panel
   (no display:none) para que la salida también sea suave. */
.ex-body.map-wide .ex-panel { width:0; min-width:0; padding:0; overflow:hidden; border:none; }
.ex-body.map-wide .ex-panel * { visibility:hidden; }
.ex-panel { transition:width .35s cubic-bezier(.3,.7,.2,1); }

.ex-zoom { position:absolute; top:20px; right:20px; display:flex; flex-direction:column; gap:8px; z-index:2; }
.ex-zoom button { width:42px; height:42px; background:var(--x-s); border:none; border-radius:10px; color:var(--x-tx);
  font:500 20px 'Manrope',sans-serif; cursor:pointer; box-shadow:0 10px 24px rgba(8,17,31,.3); transition:background .2s; }
.ex-zoom button:hover { background:var(--x-ac-tint); }
.ex-zoom button:active { transform:scale(.94); }
.ex-auto { position:absolute; bottom:20px; right:20px; display:flex; align-items:center; gap:10px;
  background:rgba(15,23,42,.94); backdrop-filter:blur(8px); color:#F1F5F9; font:500 13.5px 'Manrope',sans-serif;
  padding:10px 16px; border-radius:999px; cursor:pointer; z-index:3; border:none; }
.ex-auto-sw { width:34px; height:20px; border-radius:999px; background:#5E6D82; position:relative; transition:background .25s; flex:none; }
.ex-auto[aria-checked="true"] .ex-auto-sw { background:#22D3A6; }
.ex-auto-knob { position:absolute; top:2px; left:2px; width:16px; height:16px; border-radius:999px; background:var(--x-s); transition:left .25s; }
.ex-auto[aria-checked="true"] .ex-auto-knob { left:16px; }

/* Pins (creados por JS como maplibregl.Marker con este elemento) */
.ex-pin { padding:6px 11px; border-radius:999px; font:600 12.5px 'Outfit',sans-serif; background:var(--x-s); color:var(--x-tx);
  border:1px solid rgba(15,23,42,.14); cursor:pointer; box-shadow:0 4px 12px rgba(8,17,31,.22);
  transition:background .18s, color .18s, padding .18s, border-color .18s; white-space:nowrap; }
.ex-pin.is-hot { background:#22D3A6; color:#08111F; border-color:#22D3A6; padding:8px 14px; font-size:13.5px; z-index:4; animation:expinpulse 1.8s infinite; }

/* Preview al click de un pin */
/* bottom:76px y no 22px: abajo-izquierda vive la píldora "← Home" (44px + margen) y el botón
   "Ver ficha" del preview le quedaba justo debajo, tapado. */
.ex-preview { position:absolute; left:22px; bottom:76px; width:270px; background:var(--x-s); border-radius:14px;
  overflow:hidden; box-shadow:0 10px 24px rgba(8,17,31,.45); z-index:6; animation:excardin .3s ease; }
.ex-preview[hidden] { display:none; }
.ex-preview-media { position:relative; height:130px; background:linear-gradient(150deg,#1E293B,var(--x-tx)); }
.ex-preview-media img { width:100%; height:100%; object-fit:cover; display:block; }
.ex-preview-x { position:absolute; top:9px; right:9px; width:30px; height:30px; border-radius:999px;
  background:rgba(15,23,42,.6); color:var(--x-s2); border:none; display:inline-flex; align-items:center; justify-content:center; font:400 15px 'Manrope',sans-serif; cursor:pointer; }
.ex-preview-body { padding:12px 14px 14px; }
.ex-preview-price { font:600 17px 'Outfit',sans-serif; color:var(--x-tx); }
.ex-preview-title { font:500 13px 'Manrope',sans-serif; color:var(--x-tx); margin-top:2px; }
.ex-preview-meta { font:400 12px 'Manrope',sans-serif; color:var(--x-tx2); margin-top:2px; }
.ex-preview-cta { display:inline-flex; align-items:center; justify-content:center; margin-top:10px; height:38px; width:100%;
  background:#22D3A6; color:#08111F; font:600 13px 'Outfit',sans-serif; border-radius:9px; text-decoration:none; transition:background .2s; }
.ex-preview-cta:hover { background:#14B8A6; }

/* ── Drawer de filtros: SU PALETA, no su dibujo ──────────────────────────────────────────────
   El panel se dibuja en assets/filters.css, la MISMA hoja que usa la home (pedido de Máximo,
   2026-08-24: "los filtros tienen que ser iguales porque la lógica debe estar en el mismo lugar").
   Acá solo se le pasa la paleta del explorador por los tokens `--mxf-*`.

   Y alcanza con declararlos UNA vez: cada uno apunta a un `--x-*`, que ya cambia solo en
   `.ex-body.dark`. Sin esto habría que escribir la paleta oscura del drawer aparte, que es
   exactamente el tipo de duplicado que se vino a sacar.
   `.ex-clear` se queda más abajo: lo usa "Reenviar código" del modal de login, que no es el drawer. */
.ex-body {
  /* Menú del usuario del CRM (assets/public-usermenu.css), mismo patrón que --mxf-*. */
  --mxum-card:var(--x-s); --mxum-bord:var(--x-bd); --mxum-tx:var(--x-tx); --mxum-tx2:var(--x-tx3);
  --mxum-hover:var(--x-s2); --mxum-av-bg:#22D3A6; --mxum-av-tx:#08111F;
  /* El explorador es OSCURO: la insignia va sobre fondo oscuro y el rojo tiene que leerse ahí,
     así que no son los mismos valores que en la home — es el mismo rol en otra superficie. */
  /* El explorador es OSCURO: misma insignia, valores que se lean sobre fondo oscuro. */
  --mxum-badge-bg:rgba(34,211,166,.18); --mxum-badge-tx:#34D399; --mxum-danger:#F87171; --mxum-warn-bg:rgba(251,191,36,.16); --mxum-warn-tx:#FCD34D;
  --mxf-surface:var(--x-s);
  --mxf-surface-2:var(--x-s2);
  --mxf-border:var(--x-bd);
  --mxf-border-soft:var(--x-bd);
  --mxf-tx:var(--x-tx);
  --mxf-tx2:var(--x-tx2);
  --mxf-tx3:var(--x-tx3);
  --mxf-on-bg:var(--x-ac-tint);
  --mxf-on-tx:var(--x-ac-dk);
  --mxf-track:var(--x-mut);
  --mxf-thumb-bd:var(--x-s);
  --mxf-shadow:-18px 0 44px rgba(8,17,31,.25);
  --mxf-foot-shadow:0 -8px 20px rgba(8,17,31,.06);
}



/* ── Burbuja del "Ingresar" (misma que la home) ─────────────────────────────────
   Adentro va /login?embed=1. Las clases son las mismas que en home.css a propósito: es LA MISMA
   burbuja en las dos pantallas, y darle dos juegos de estilos es cómo se separan (ya pasó con el
   menú del usuario, 2026-08-26). */
.mx-login-scrim { position:fixed; inset:0; z-index:200; display:flex; align-items:center;
  justify-content:center; padding:20px; background:rgba(8,17,31,.55); backdrop-filter:blur(4px); }
.mx-login-scrim[hidden] { display:none; }
.mx-login-pop { position:relative; width:440px; max-width:calc(100vw - 32px);
  height:min(640px, calc(100vh - 48px));   /* 48 y no 80: con el margen viejo, en una ventana
     de 660 el paso de la contraseña se pasaba por 11px y aparecía la barra de scroll justo
     en el paso que más se usa (Máximo, 02/09). 24px arriba y abajo siguen despegándolo. */ background:var(--x-s); border:1px solid var(--x-bd);
  border-radius:18px; box-shadow:0 28px 70px rgba(8,17,31,.42); overflow:hidden; }
.mx-login-frame { display:block; width:100%; height:100%; border:0; background:transparent; }
.mx-login-x { position:absolute; top:10px; right:10px; z-index:2; width:32px; height:32px;
  display:inline-flex; align-items:center; justify-content:center; border:0; border-radius:999px;
  background:var(--x-s2); color:var(--x-tx3); cursor:pointer; }
.mx-login-x:hover { color:var(--x-tx); }

/* ── Píldora ← Home ── */
.ex-home-pill { position:fixed; bottom:18px; left:18px; z-index:35; display:inline-flex; align-items:center; gap:9px;
  height:44px; padding:0 18px 0 14px; border-radius:999px; background:rgba(15,23,42,.88); backdrop-filter:blur(10px);
  color:#F1F5F9; font:600 13px 'Outfit',sans-serif; text-decoration:none; box-shadow:0 10px 24px rgba(8,17,31,.35); opacity:.85; transition:all .25s; }
.ex-home-pill:hover { opacity:1; background:rgba(8,17,31,.95); color:#F1F5F9; padding-right:22px; }
.ex-home-pill.is-embedded { display:none; }

/* ── Modales (login / alertas) ── */
.ex-modal-scrim { position:fixed; inset:0; background:rgba(8,17,31,.55); backdrop-filter:blur(4px); -webkit-backdrop-filter:blur(4px);
  z-index:40; display:flex; align-items:center; justify-content:center; padding:16px; }
.ex-modal-scrim[hidden] { display:none; }
.ex-modal { background:var(--x-s); border-radius:18px; padding:30px; box-shadow:0 10px 24px rgba(8,17,31,.4); animation:exmodalin .28s ease; }

.ex-modal-alerts { width:480px; max-width:100%; max-height:86vh; overflow-y:auto; }
.ex-modal-h { display:flex; align-items:center; gap:9px; font:600 19px 'Outfit',sans-serif; color:var(--x-tx); margin-bottom:6px; }
.ex-modal-h2 { font:600 20px 'Outfit',sans-serif; color:var(--x-tx); margin-bottom:4px; }
.ex-modal-sub { font:400 13.5px/1.5 'Manrope',sans-serif; color:var(--x-tx2); margin-bottom:20px; }




[data-login-step][hidden] { display:none; }

.ex-btn-primary { width:100%; height:48px; background:#22D3A6; color:#08111F; font:600 15px 'Outfit',sans-serif;
  border:none; border-radius:11px; display:inline-flex; align-items:center; justify-content:center; gap:9px; cursor:pointer; transition:background .2s; }
.ex-btn-primary:hover { background:#14B8A6; }
.ex-btn-primary:active { transform:scale(.98); }
.ex-alabel { font:600 12px 'Outfit',sans-serif; letter-spacing:.1em; color:var(--x-ac-dk); text-transform:uppercase; margin-bottom:8px; }




.ex-alert-create { margin-top:6px; }
.ex-alert-list { margin-top:24px; }
.ex-alert-item { display:flex; align-items:center; gap:10px; background:var(--x-s3); border:1px solid var(--x-bd); border-radius:11px; padding:11px 14px; margin-top:8px; }
.ex-alert-item .ai-txt { min-width:0; flex:1; }
.ex-alert-item .ai-title { font:600 13px 'Manrope',sans-serif; color:var(--x-tx); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.ex-alert-item .ai-meta { font:400 11.5px 'Manrope',sans-serif; color:var(--x-tx3); }
.ex-alert-item .ai-del { width:30px; height:30px; border-radius:999px; border:none; background:none; display:inline-flex;
  align-items:center; justify-content:center; color:var(--x-tx3); cursor:pointer; flex:none; transition:all .2s; }
.ex-alert-item .ai-del:hover { background:#FEE2E2; color:#B91C1C; }

/* ── Toast ── */
.ex-toast { position:fixed; bottom:26px; left:50%; transform:translateX(-50%); background:var(--x-tx); color:var(--x-s2);
  font:500 13.5px 'Manrope',sans-serif; padding:12px 22px; border-radius:999px; box-shadow:0 10px 24px rgba(8,17,31,.35);
  z-index:50; display:none; align-items:center; gap:9px; }
.ex-toast.is-on { display:flex; animation:extoastin .25s ease; }
.ex-toast .dot { width:8px; height:8px; border-radius:999px; background:#22D3A6; flex:none; }

@keyframes expinpulse { 0%{box-shadow:0 0 0 0 rgba(34,211,166,.5)} 70%{box-shadow:0 0 0 14px rgba(34,211,166,0)} 100%{box-shadow:0 0 0 0 rgba(34,211,166,0)} }
@keyframes excardin { from{opacity:0; transform:translateY(14px)} to{opacity:1; transform:none} }
@keyframes exmodalin { from{opacity:0; transform:translateY(18px) scale(.98)} to{opacity:1; transform:none} }
@keyframes extoastin { from{opacity:0; transform:translate(-50%,14px)} to{opacity:1; transform:translate(-50%,0)} }

/* ── Responsive: en mobile, tabs Lista/Mapa apilados ── */
.ex-tabs { display:none; }
/* ── Selector de TIPO (ícono + nombre, despliegue con rebote) ── */
.ex-tipo { position:relative; flex:none; }
.ex-tipo-btn { height:44px; display:inline-flex; align-items:center; gap:8px; padding:0 14px; border-radius:10px;
  border:1px solid var(--x-bd); background:var(--x-s); color:var(--x-tx); font:500 13.5px 'Manrope',sans-serif;
  cursor:pointer; transition:background .2s, border-color .2s; }
.ex-tipo-btn:hover { background:var(--x-s2); }
.ex-tipo-btn.is-set { border-color:#22D3A6; background:var(--x-ac-tint); color:var(--x-ac-dk); font-weight:600; }
.ex-tipo-pop { position:absolute; top:calc(100% + 8px); right:0; z-index:30; min-width:216px; max-height:min(60vh,380px);
  overflow-y:auto; background:var(--x-s); border:1px solid var(--x-bd); border-radius:14px; padding:6px;
  box-shadow:0 18px 40px rgba(8,17,31,.22);
  /* "Rebote": la curva se pasa de largo y vuelve — el desplegable entra con vida, no aparece de golpe. */
  animation:extipoin .34s cubic-bezier(.34,1.56,.64,1); transform-origin:top right; }
.ex-tipo-pop[hidden] { display:none; }
.ex-tipo-item { display:flex; align-items:center; gap:10px; width:100%; height:42px; padding:0 12px; border:none;
  background:none; border-radius:9px; color:var(--x-tx); font:500 13.5px 'Manrope',sans-serif; cursor:pointer;
  text-align:left; transition:background .15s; }
.ex-tipo-item:hover { background:var(--x-s2); }
.ex-tipo-item.on { background:var(--x-ac-tint); color:var(--x-ac-dk); font-weight:600; }
.ex-tipo-item svg { flex:none; color:var(--x-tx3); }
.ex-tipo-item.on svg { color:var(--x-ac-dk); }
@keyframes extipoin { from { opacity:0; transform:scale(.86) translateY(-8px); } to { opacity:1; transform:none; } }

/* Selector de OPERACIÓN de una sola pieza — mismo patrón que el de tipo. En desktop se muestran los
   cuatro chips (hay lugar) y este se oculta; en mobile, al revés. */
/* Antes esto era `display:none` y solo aparecía en mobile; en escritorio salían cuatro chips. Ahora
   es el ÚNICO selector de operación en todos los anchos (pedido 2026-08-24) y va antes del buscador. */
.ex-opsel { position:relative; flex:none; display:inline-flex; }
.ex-opsel-btn { height:44px; display:inline-flex; align-items:center; gap:8px; padding:0 14px; border-radius:10px;
  border:1px solid var(--x-bd); background:var(--x-s); color:var(--x-tx); font:500 13.5px 'Manrope',sans-serif;
  cursor:pointer; transition:background .2s, border-color .2s; }
.ex-opsel-btn:hover { background:var(--x-s2); }
.ex-opsel-btn.is-set { border-color:var(--x-tx); background:var(--x-tx); color:var(--x-s2); font-weight:600; }

/* Kebab + hoja de opciones: SOLO mobile (en desktop los controles viven en el header). */
.ex-kebab { display:none; }
.ex-sheet { display:none; }

/* ══ MOBILE ══
   El mapa venía tapado de controles (header de tres filas + ampliar + contador + zoom + Home +
   tabs + el switch de "buscá al mover", todos encimados). Se reordena así, con el mismo estilo:
     · arriba, UNA fila: logo + buscador + "⋮"
     · flotando a la DERECHA: operación y tipo (el tipo se despliega con rebote)
     · abajo: "Más filtros" a la izquierda y las tabs Lista/Mapa centradas
   Los controles no se duplican: global.js MUEVE los nodos a estos destinos y los devuelve al
   volver a desktop. */
@media (max-width:860px) {
  .ex-main { position:relative; }
  .ex-panel { width:100%; }
  .ex-mapwrap { position:absolute; inset:0; }
  .ex-body.show-map .ex-panel { display:none; }
  .ex-body:not(.show-map) .ex-mapwrap { display:none; }

  /* Header: una sola fila. */
  .ex-header { flex-wrap:nowrap; gap:8px; padding:8px 12px; min-height:60px; }
  /* Orden explícito: logo · buscador · "⋮". Sin esto el kebab quedaba entre el logo y el campo,
     que es justo donde no se lo busca. */
  /* min-width:44px: al ocultar el wordmark el isotipo queda de 33px y no llega al piso táctil. */
  .ex-logo { order:1; flex:none; min-width:44px; justify-content:center; }
  .ex-logo img:last-child { display:none; }        /* solo el isotipo: el wordmark come ancho */
  .ex-zona { order:2; flex:1 1 auto; min-width:0; height:42px; padding:0 12px; }
  .ex-zona-tag { display:none; }                    /* el ícono de lupa ya dice qué es */
  .ex-kebab { order:3; display:inline-flex; align-items:center; justify-content:center; width:44px; height:44px; flex:none;
    border:1px solid var(--x-bd); border-radius:10px; background:var(--x-s); color:var(--x-tx); cursor:pointer; }
  .ex-kebab:hover { background:var(--x-s2); }
  .ex-header-right { display:none; }                /* su contenido se muda a la hoja */
  /* El `display` va UNA sola vez y es `flex`: hasta el 13/09/2026 esta regla abría con
     `display:block` y tres líneas más abajo declaraba `display:flex` en el MISMO bloque, así que el
     `block` estaba muerto desde que se escribió —lo pisaba el de abajo— y encima contradecía al
     `flex-direction`, `gap` y `align-items` que lo acompañan. Lo encontró stylelint. */
  .ex-sheet { position:absolute; top:calc(100% + 6px); right:10px; z-index:40;
    background:var(--x-s); border:1px solid var(--x-bd); border-radius:14px; padding:10px;
    box-shadow:0 18px 40px rgba(8,17,31,.22); display:flex; flex-direction:column; gap:8px; align-items:stretch;
    animation:extipoin .3s cubic-bezier(.34,1.56,.64,1); transform-origin:top right; }
  .ex-sheet[hidden] { display:none; }
  .ex-sheet .ex-ingresar, .ex-sheet .ex-sound{ width:100%; justify-content:center; }
  .ex-sheet .ex-lang { justify-content:center; }

  /* Flotantes a la derecha: operación arriba, tipo abajo. `fixed` y colgando del alto real del
     header, no del mapa: valen para las DOS vistas (en la lista también se filtra por operación y
     tipo). En modo Lista el listado recibe padding arriba para que no le tapen la primera tarjeta. */
  .ex-mfloat { position:fixed; top:calc(var(--ex-headh, 60px) + 12px); right:12px; z-index:33;
    display:flex; flex-direction:column; align-items:flex-end; gap:8px; max-width:calc(100% - 24px); }
  /* En LISTA no pueden flotar sobre el contenido (tapaban el "N propiedades" y el "Crear alerta"):
     se convierten en una barra de filtros pegada bajo el header, que es el patrón habitual en un
     listado. Si no entran, se desplazan de costado — no se apilan ni empujan el contenido. */
  /* OJO: nada de `overflow` acá. El desplegable del tipo/operación es `position:absolute` DENTRO de
     esta barra, así que un `overflow-x:auto` lo recortaba y el menú se abría invisible — se veía
     como si el ícono estuviera trabado y no se pudiera cambiar. Con dos controles de una pieza ya
     entran sin necesidad de desplazamiento. */
  .ex-body:not(.show-map) .ex-mfloat { top:var(--ex-headh, 60px); left:0; right:0; max-width:none;
    flex-direction:row; align-items:center; justify-content:flex-start; gap:8px;
    padding:8px 12px; background:var(--x-s); border-bottom:1px solid var(--x-bd); }
  /* Sin sombra dentro de la barra (ya tiene su propio borde). */
  .ex-body:not(.show-map) .ex-mfloat .ex-opsel-btn,
  .ex-body:not(.show-map) .ex-mfloat .ex-tipo-btn { box-shadow:none; background:var(--x-s); }
  .ex-body:not(.show-map) .ex-mfloat .ex-opsel-btn.is-set { background:var(--x-tx); color:var(--x-s2); }
  .ex-body:not(.show-map) .ex-mfloat .ex-tipo-btn.is-set { background:var(--x-ac-tint); }
  .ex-body:not(.show-map) .ex-panel-head { padding-top:66px; }
  .ex-mfloat .ex-opsel-btn { height:44px; background:rgba(255,255,255,.96); backdrop-filter:blur(8px);
    box-shadow:0 4px 14px rgba(8,17,31,.14); }
  .ex-mfloat .ex-opsel-btn.is-set { background:var(--x-tx); }
  .ex-mfloat .ex-tipo-btn { height:44px; background:rgba(255,255,255,.96); backdrop-filter:blur(8px);
    box-shadow:0 4px 14px rgba(8,17,31,.14); }
  /* Sin tipo elegido es SOLO el ícono (un cuadradito, como pidió el diseño); al elegir uno aparece
     el nombre al lado, para saber qué está filtrando sin abrir nada. */
  .ex-mfloat .ex-tipo-btn:not(.is-set) { width:44px; padding:0; justify-content:center; }
  .ex-mfloat .ex-tipo-btn:not(.is-set) .ex-tipo-txt { display:none; }

  /* Los desplegables (tipo y operación) dejan de colgar del botón: en un teléfono, anclados a la
     derecha del control se salían por el borde IZQUIERDO y las opciones quedaban cortadas
     ("ualquier tipo", "asa"). Pasan a ser un panel de ancho completo bajo el header — no depende de
     dónde esté el botón, así que no puede cortarse. */
  .ex-tipo-pop { position:fixed; top:calc(var(--ex-headh, 60px) + 64px); left:12px; right:12px;
    width:auto; min-width:0; max-width:none; max-height:calc(100vh - var(--ex-headh, 60px) - 150px); }

  /* Abajo, UNA fila con los dos controles: "Más filtros" y las tabs Lista/Mapa. Van en el mismo
     contenedor flex (y no uno absoluto y otro centrado con left:50%) porque así no pueden pisarse
     — medido: se solapaban 6px en un iPhone. */
  .ex-mbottom { position:fixed; left:12px; right:12px; bottom:18px; z-index:34;
    display:flex; gap:10px; align-items:center; justify-content:space-between; pointer-events:none; }
  .ex-mbottom > * { pointer-events:auto; }
  .ex-mbottom .ex-morefilt { height:44px; box-shadow:0 8px 20px rgba(8,17,31,.28); flex:none; }
  /* Embebido en la home (modo dual), la píldora "← Home" la dibuja la PÁGINA DE AFUERA, abajo a la
     izquierda — justo encima de "Más filtros", que quedaba tapado. En vez de apretar los tres
     controles en una sola línea (las tabs se salían 7px por el borde), la fila del explorador sube
     un piso: "← Home" abajo, y arriba [Más filtros] [Lista/Mapa]. La clase la pone global.js
     cuando detecta que está dentro del iframe. */
  .ex-body.is-embedded .ex-mbottom { bottom:76px; }
  .ex-body.is-embedded .ex-preview { bottom:134px; }
  .ex-mbottom .ex-tabs { position:static; transform:none; left:auto; bottom:auto; flex:none; }

  .ex-tabs { display:flex; position:fixed; bottom:18px; left:50%; transform:translateX(-50%); z-index:34;
    background:var(--x-s); border:1px solid var(--x-bd); border-radius:999px; padding:4px; box-shadow:0 10px 24px rgba(8,17,31,.18); }
  .ex-tabs button { height:40px; padding:0 18px; border:none; border-radius:999px; background:none; color:var(--x-tx2);
    font:500 13.5px 'Manrope',sans-serif; cursor:pointer; }
  .ex-tabs button.on { background:var(--x-tx); color:var(--x-s2); font:600 13.5px 'Outfit',sans-serif; }

  /* Lo que en un teléfono estorba más de lo que suma: el mapa ya ocupa todo (no hay listado al
     lado que tapar), el zoom se hace con los dedos, el logo ya vuelve a la home y el contador
     compite con los flotantes. El switch "buscá al mover" pasa a ícono, sin la frase. */
  .ex-zoom, .ex-home-pill, .ex-map-count { display:none; }
  .ex-auto { bottom:76px; right:12px; padding:10px; gap:0; font-size:0; }
  .ex-auto .ex-auto-sw { margin:0; }
  .ex-preview { left:12px; right:12px; bottom:76px; width:auto; }
}
@media (max-width:560px) {
  .ex-grid { grid-template-columns:1fr; }
  .ex-header { gap:8px; padding:8px 14px; }
  /* La zona ocupaba una fila entera al final (order:5) cuando el header tenía tres filas. Con el
     header de UNA fila (logo · buscador · ⋮) esa regla lo mandaba al final y dejaba el "⋮" en el
     medio: ahora comparte el orden del bloque de arriba. */
  .ex-zona { flex:1 1 auto; order:2; }
  /* A 360px la cabecera del listado se iba 26px afuera y generaba scroll horizontal en TODA la
     página (medido por la suite). Que envuelva. */
  .ex-panel-head { flex-wrap:wrap; }

}
/* Mismo motivo que en la home: con letra menor a 16px, iOS Safari zoomea solo al enfocar un campo
   y la página queda corrida de costado. 16px en táctil y el zoom no se dispara. */
@media (pointer:coarse) {
  /* Los campos del panel de filtros ya no están acá: filters.css trae su propia regla táctil. */
  .ex-zona input, input[type="text"], input[type="email"], select, textarea{ font-size:16px; }
}

/* Táctil: 44px mínimo (estándar del proyecto) en todo control operable del explorador. */
@media (pointer:coarse) {
  .ex-opsel-btn, .ex-morefilt, .ex-ingresar, .ex-sound, .ex-pager button, .ex-zoom button, .ex-tabs button, .ex-logo, .ex-auto, .ex-pager button{ min-width:44px; }
  /* EL PISO SON 44px EN LAS DOS DIMENSIONES, y acá faltaba el alto: la lista de arriba fija solo
     `min-width`, así que el ancho se corregía y la altura no. Medido en Chrome real el 13/09/2026
     (mobile-390 y tablet-768, las dos declaradas táctiles): el logo quedaba en 44×28 y los botones
     Lista/Mapa en 66×40 y 71×40. La intención ya estaba escrita en el comentario de arriba desde
     el primer día; quedó a medias.
     NO CAMBIA NADA DE LO QUE SE VE, y está comprobado, no supuesto: se capturó la pantalla antes y
     después de aplicar esta regla y las imágenes salen IDÉNTICAS píxel por píxel (mismo sha256). El
     header ya mide más de 44 y centra su contenido, así que esto agranda el área que toca el dedo
     sin mover un solo píxel del dibujo. */
  .ex-logo, .ex-tabs button { min-height:44px; }
  .ex-lang, .ex-lang .on, .ex-lang .off { min-height:44px; display:inline-flex; align-items:center; justify-content:center; }
  .ex-lang .on, .ex-lang .off { min-width:44px; }
  .ex-sound { width:44px; }
  .ex-zoom button { width:44px; }
  .ex-zona, .ex-zona input{ min-height:44px; }
  /* El corazón mide 38px por diseño; en TÁCTIL manda el piso de 44px del estándar (en mouse
     conserva su tamaño original). Agrandarlo de verdad —y no con un ::after invisible— es lo que
     la suite puede medir, y lo que el dedo realmente toca. */
  .ex-fav { width:44px; height:44px; }
}

@media (prefers-reduced-motion: reduce) {
  .ex-card, .ex-card-img, .ex-pin { transition:none; animation:none; }
  .ex-pin.is-hot { animation:none; }
  .ex-maploading-sp { animation:none; }
}

/* ── Monitores grandes: los pins (2026-08-25) ──────────────────────────────────────────────────
   Los globitos de precio son HTML (`makePinEl` en global.js) con la tipografía en píxeles fijos, o
   sea el mismo problema que tenía la home: en un 4K el mapa mide 3000px y los precios siguen en
   12,5px — quedan como pelusas sobre una pantalla enorme y hay que acercarse a leerlos.

   Se aplica el criterio ya decidido para la vidriera: en un monitor más grande las cosas se hacen
   MÁS GRANDES, no MÁS. Y se usan LOS MISMOS cortes que el bloque "Monitores grandes" de home.css
   (1700px y 2400px), para que la home y el explorador escalen juntos y no haya un ancho donde uno
   ya creció y el otro no.

   Los tramos son `min-width`, así que el teléfono no se entera: ahí el pin sigue midiendo lo mismo
   que hoy, que es lo que corresponde con el espacio que hay. */
@media (min-width:1700px) {
  .ex-pin { padding:8px 14px; font-size:15px; }
  .ex-pin.is-hot { padding:10px 17px; font-size:16px; }
}

/* ── Monitores grandes (≥2400px) ───────────────────────────────────────────────────────────────
   Mismo problema que en la home (ver el bloque gemelo en home.css): en un 27" 4K medido con
   `tests/ux/escala-check.js` el panel de resultados quedaba en 760px —una tira del 20% de la
   pantalla— contra un mapa de 3080px, con las fichas y el header en tamaño de 1440. Acá el panel
   pasa a un porcentaje del ancho (sigue ganando el mapa, pero la lista se lee), las fichas van de
   dos a tres por fila y la tipografía acompaña. Por debajo de 2400px no cambia NADA: el layout
   aprobado para 1280–1920 queda igual. */
@media (min-width:2400px) {
  .ex-pin { padding:10px 18px; font-size:18.5px; }
  .ex-pin.is-hot { padding:12px 22px; font-size:20px; }
  .ex-body { --ex-panelw:min(34vw,1400px); }   /* la costura lo sigue solo */
  .ex-grid { grid-template-columns:repeat(3,minmax(0,1fr)); }
  .ex-card-media { height:230px; }
  .ex-price { font-size:23px; }
  .ex-card-title { font-size:16px; }
  .ex-card-loc, .ex-card-specs { font-size:14px; }
  .ex-broker { font-size:13px; }
  .ex-count { font-size:26px; }
  .ex-count-sub { font-size:16px; }
  .ex-header { min-height:78px; }
  .ex-logo img { height:42px; }
  .ex-zona { flex-basis:460px; height:52px; font-size:17px; }
  .ex-zona input { font-size:17px; }
}

/* ── Sugerencias del buscador de lugares ─────────────────────────────────────────────────────
   Cuelga del campo (que es `position:relative`) y sale HACIA ABAJO: el header está arriba de todo,
   así que hay lugar. z-index por encima del mapa y del panel, que es lo único que puede taparla. */
.ex-sug { position:absolute; top:calc(100% + 6px); left:0; right:0; z-index:40;
  background:var(--x-s); border:1px solid var(--x-bd); border-radius:12px; padding:6px;
  box-shadow:0 12px 30px rgba(8,17,31,.16); max-height:min(60vh,340px); overflow-y:auto; }
.ex-sug[hidden] { display:none; }   /* [hidden] pierde por cascada contra un display propio */
.ex-sug-item { display:flex; align-items:center; gap:10px; width:100%; min-height:44px; padding:0 10px;
  border:none; border-radius:8px; background:none; cursor:pointer; text-align:left;
  font:400 14px 'Manrope',sans-serif; color:var(--x-tx); }
.ex-sug-item:hover, .ex-sug-item.on { background:var(--x-s2); }
.ex-sug-t { font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.ex-sug-s { color:var(--x-tx3); font-size:12.5px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* El contador de avisos va a la derecha: dice de un vistazo cuál de las sugerencias tiene algo. */
.ex-sug-n { margin-left:auto; flex:none; background:var(--x-ac-tint); color:var(--x-ac-dk);
  font:600 11.5px 'Manrope',sans-serif; padding:2px 8px; border-radius:999px; }

/* ── Estilos igualados con la home (pedido 2026-08-24, imágenes 2 y 3) ───────────────────────
   Los controles del header eran de otra familia visual que los de la home: pastilla navy rellena
   para la operación, contorno verde para el tipo, navy para "Más filtros". La home usa una sola
   convención —caja de vidrio con borde suave, y VERDE solo para lo que está elegido— y es la que
   manda, porque es la primera pantalla que ve cualquiera. Acá se traduce esa convención al fondo
   claro del explorador: misma altura, mismo radio, mismo peso, verde solo cuando hay filtro. */
.ex-opsel-btn, .ex-tipo-btn, .ex-morefilt {
  height:44px; border-radius:12px; border:1px solid var(--x-bd); background:var(--x-s);
  color:var(--x-tx); font:500 14px 'Manrope',sans-serif; }
.ex-opsel-btn:hover, .ex-tipo-btn:hover, .ex-morefilt:hover { background:var(--x-s2); }
.ex-opsel-btn.is-set, .ex-tipo-btn.is-set {
  border-color:#22D3A6; background:var(--x-ac-tint); color:var(--x-ac-dk); font-weight:600; }
.ex-morefilt { font-weight:500; }

/* ── Mobile: solo "Más filtros", arriba a la derecha ─────────────────────────────────────────
   En el mapa el ancho es del MAPA. Operación y tipo son dos pastillas flotando encima de lo que
   uno vino a mirar, y las dos están adentro del panel de filtros — que es adonde lleva el botón
   que sí queda. Menos controles encima del mapa, mismo alcance. */
@media (max-width:860px) {
  .ex-mfloat .ex-opsel, .ex-mfloat .ex-tipo { display:none; }
  .ex-mfloat { justify-content:flex-end; }
  .ex-mbottom .ex-morefilt, .ex-mfloat .ex-morefilt { order:-1; }
  /* El botón sube al flotante de arriba a la derecha en vez de quedar abajo con las solapas. */
  .ex-body.show-map .ex-mfloat .ex-morefilt { display:inline-flex; }
}

/* ── "Más filtros" como ícono, arriba a la derecha (pedido 2026-08-24) ───────────────────────
   En escritorio y en mobile. El texto ocupaba ancho para decir lo que el ícono ya dice, y el
   badge —que es el dato que importa: cuántos filtros hay puestos— se lee igual o mejor sobre un
   botón cuadrado. El nombre sigue en title y aria-label, así que un lector de pantalla no pierde
   nada.
   Va PEGADO al buscador, no contra el borde derecho (corrección de Máximo, 2026-08-25). Con
   `margin-left:auto` había DOS elementos reclamando el espacio libre del header (este y
   .ex-header-right), y flexbox lo repartió mitad y mitad: el botón quedaba flotando a 230px del
   buscador, sin pertenecer ni a un grupo ni al otro. Filtrar es la continuación de buscar, así que
   el botón vive al lado de la caja de búsqueda. */
.ex-morefilt { width:44px; padding:0; justify-content:center; }
.ex-morefilt .ex-morefilt-txt { display:none; }
/* El badge se corre a la esquina: sin texto al lado, adentro del botón quedaría encima del ícono. */
.ex-morefilt .ex-fbadge { position:absolute; top:-6px; right:-6px; }


/* ── Ampliar el LISTADO sobre el mapa (espejo de `map-wide`, 2026-08-25) ─────────────────────
   La flecha del mapa ya estiraba el mapa sobre el listado; faltaba la contraria. Los dos modos son
   excluyentes por construcción: el JS saca una clase al poner la otra.
   Se colapsa el ANCHO del contenedor del mapa en vez de esconderlo con `display:none` a propósito:
   MapLibre pierde el contexto si su contenedor desaparece, y al volver habría que reinicializarlo
   entero. Con ancho 0 el mapa sigue vivo y basta un `resize()` al volver. */
.ex-body.list-wide .ex-mapwrap { width:0; min-width:0; padding:0; overflow:hidden; border:none; }
.ex-body.list-wide .ex-mapwrap * { visibility:hidden; }
.ex-body.list-wide .ex-panel { width:100%; }

/* La flecha del listado: misma pieza que la del mapa, espejada contra el borde derecho del panel.
   El panel necesita `position:relative` para anclarla — no lo tenía. */
.ex-panel { position:relative; }
/* Cuando el listado ya está ampliado, la flecha apunta al otro lado (traer el mapa de vuelta). */
/* Con el mapa ampliado el panel no se ve, así que su flecha tampoco tiene que estorbar. */
.ex-body.map-wide @media (max-width:860px) {
  /* En teléfono el listado y el mapa ya se alternan con las solapas de abajo: una tercera forma de
     hacer lo mismo sería ruido sobre una pantalla donde el espacio es lo que falta. */
  }

/* Con el listado ampliado manda el ancho real, no el conteo fijo de columnas del override de 4K. */
.ex-body.list-wide .ex-grid { grid-template-columns:repeat(auto-fill,minmax(320px,1fr)); }

/* ── Solapas del "Ingresar": visitante / inmobiliaria (2026-08-25) ───────────────────────────
   El mismo botón es la puerta de las dos identidades. La solapa del CRM no trae campos: manda a
   /login, que es la pantalla real (bloqueo por intentos, segundo factor, dispositivo nuevo).
   Ver el porqué en components/public_login.php. */










/* Menú del visitante: la forma vive en assets/public-usermenu.css (una sola copia). Acá quedan
   solo los colores, que cada pantalla nombra con sus propias variables de tema. */
.pu-um-panel { background:var(--x-s); border:1px solid var(--x-bd); }
.pu-um-head { border-bottom:1px solid var(--x-bd); }
.pu-um-head strong, .pu-um-item { color:var(--x-tx); }
.pu-um-item:hover, .pu-um-item:focus-visible { background:var(--x-s2); }
.pu-um-sep { border-top:1px solid var(--x-bd); }

/* ══ LA CORTINA — abrir el mapa entero o el listado entero ══════════════════════════════════
   Reemplaza las dos pestañas flotantes de antes (una por lado). Máximo eligió esta forma de cinco
   propuestas (2026-08-25) y pidió expresamente que la línea se vea al medio desde el arranque:
   un control que aparece recién al pasar el mouse no lo descubre nadie.

   Tres estados: mapa entero · mitades · listado entero. La regla que hace que abrir a pantalla
   completa no dé miedo es que lo que se cierra SIEMPRE deja cómo volver — de ahí los botones
   redondos, que salen del lado por el que se fue el panel. */

/* La línea. Cuerpo de 18px para poder agarrarla (44px de alto útil con el area de toque), pero
   se DIBUJA de 3px: la zona de agarre no tiene por qué verse. */
.ex-seam { position:absolute; top:0; bottom:0; left:var(--ex-panelw); z-index:6; width:18px;
  transform:translateX(-50%); display:grid; place-items:center; padding:0; border:none;
  background:none; cursor:ew-resize; touch-action:none; }
.ex-seam[hidden] { display:none; }   /* [hidden] pierde por cascada contra el display de la clase */
.ex-seam i { width:3px; height:100%;
  background:linear-gradient(transparent, rgba(148,163,184,.45) 20%, rgba(148,163,184,.45) 80%, transparent);
  transition:background .22s, box-shadow .22s; }
.ex-seam:hover i, .ex-seam:focus-visible i, .ex-seam.is-drag i {
  background:linear-gradient(transparent, #22D3A6 10%, #22D3A6 90%, transparent);
  box-shadow:0 0 14px rgba(34,211,166,.55); }

/* ── La MANIJA (pedido de Máximo, 2026-08-25: "le falta el efecto que tenía el mockup 5 para que
   se vea que hay algo para hacer clic") ──────────────────────────────────────────────────────
   Una línea sola no dice "tocame": se lee como el borde entre dos paneles, que es lo que parece.
   La pastilla con los dos puntitos es el gesto universal de agarre, y al pasar el mouse crece y
   se pone verde. En reposo queda al 55% para no competir con el contenido — visible, no ruidosa.
   Vive DENTRO del botón (`::after` sobre el mismo <i>) para no sumar un nodo ni un listener: el
   área de agarre y el dibujo son la misma pieza. */
.ex-seam i { position:relative; }
.ex-seam i::after {
  content:""; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
  width:16px; height:52px; border-radius:999px;
  background:var(--x-s); border:1px solid var(--x-bd);
  box-shadow:0 3px 12px rgba(8,17,31,.22);
  opacity:.55; transition:opacity .22s, transform .28s cubic-bezier(.22,1.15,.36,1),
              background .22s, border-color .22s;
  /* Los dos puntitos, pintados con el fondo en vez de dos nodos más. */
  background-image:radial-gradient(circle, var(--x-tx3) 1.6px, transparent 1.7px),
                   radial-gradient(circle, var(--x-tx3) 1.6px, transparent 1.7px);
  background-size:100% 50%; background-position:center 38%, center 62%; background-repeat:no-repeat;
}
.ex-seam:hover i::after, .ex-seam:focus-visible i::after, .ex-seam.is-drag i::after {
  opacity:1; transform:translate(-50%,-50%) scale(1.14);
  border-color:#22D3A6;
  background-image:radial-gradient(circle, #0B6E63 1.6px, transparent 1.7px),
                   radial-gradient(circle, #0B6E63 1.6px, transparent 1.7px);
}
/* Mientras se arrastra, el cursor manda: la pastilla no tiene que verse "clickeable" a mitad del
   gesto, tiene que verse agarrada. */
.ex-seam.is-drag i::after { box-shadow:0 0 0 5px rgba(34,211,166,.18), 0 3px 12px rgba(8,17,31,.22); }
@media (prefers-reduced-motion:reduce) { .ex-seam i::after { transition-duration:.01ms; } }
.ex-seam:focus-visible { outline:none; }

/* Los dos paneles cambian de ancho con la misma curva. El sobrepaso chico es lo que hace que el
   panel "aterrice" en vez de frenar en seco. */
.ex-panel, .ex-mapwrap { transition:width .62s cubic-bezier(.22,1.15,.36,1); }
.ex-body.map-wide  .ex-panel   { width:0; min-width:0; padding:0; overflow:hidden; border:none; }
.ex-body.list-wide .ex-mapwrap { width:0; min-width:0; padding:0; overflow:hidden; border:none; }
/* El contenido se desvanece ANTES de que el panel llegue a cero: si no, se ve el texto
   aplastándose contra el borde durante la transición. */
.ex-panel > *, .ex-mapwrap > * { transition:opacity .26s ease; }
.ex-body.map-wide  .ex-panel > *   { opacity:0; }
.ex-body.list-wide .ex-mapwrap > * { opacity:0; }

/* Los botones de vuelta. Redondos, del lado por el que se fue el panel. */
.ex-back { position:absolute; top:50%; z-index:7; width:46px; height:46px; border-radius:50%;
  border:none; background:var(--x-s); color:var(--x-tx); cursor:pointer; display:grid;
  place-items:center; transform:translateY(-50%); box-shadow:0 8px 26px rgba(8,17,31,.34);
  transition:background .2s, transform .2s; }
.ex-back[hidden] { display:none; }
.ex-back:hover { background:#22D3A6; color:#08111F; transform:translateY(-50%) scale(1.06); }
.ex-back-list { left:16px; }    /* el mapa se comió la lista: el botón la trae desde la izquierda */
.ex-back-map  { right:16px; }
/* Con el listado entero, su botón vive contra el borde derecho del panel, no de la pantalla. */
.ex-body.list-wide .ex-back-map { right:16px; }

@media (max-width:860px) {
  /* En teléfono los dos paneles ya se alternan con las solapas de abajo: una segunda forma de
     hacer lo mismo sería ruido sobre la pantalla donde el espacio es lo que falta. */
  .ex-seam, .ex-back { display:none; }
  .ex-panel, .ex-mapwrap { transition:none; }
}
@media (prefers-reduced-motion:reduce) {
  .ex-panel, .ex-mapwrap, .ex-seam i, .ex-back { transition-duration:.01ms; }
}

/* ── Botones de ícono de la barra: alertas y favoritos (2026-08-25) ──────────────────────────
   Solo el ícono. El nombre vive en title/aria-label — no se pierde para un lector de pantalla y
   deja de comerle ancho a la barra, que es la misma decisión que ya se había tomado con "Más
   filtros" el 24-08. 44px: el mínimo de accesibilidad para todo control operable. */
.ex-iconbtn { position:relative; width:44px; height:44px; display:inline-grid; place-items:center;
  border:1px solid var(--x-bd); border-radius:10px; background:var(--x-s); color:var(--x-tx2);
  cursor:pointer; text-decoration:none; transition:background .18s, color .18s, border-color .18s; }
.ex-iconbtn:hover { background:var(--x-s2); color:var(--x-ac-dk); border-color:#22D3A6; }
.ex-iconbtn:active { transform:scale(.96); }
/* `[hidden]` pierde por cascada contra el display de la clase: sin esto los dos íconos se ven
   SIEMPRE, también sin sesión. Es la trampa que ya mordió tres veces en este proyecto. */
.ex-iconbtn[hidden] { display:none; }

/* ── El botón que se llena mientras se manda el código (2026-08-25) ──────────────────────────
   Gemelo del de home.css. Ver assets/public-login.js para el porqué de los tiempos. */




/* ── El avatar del usuario del CRM en la cara pública ────────────────────────────────────────
   Estas reglas viven en style.css, que es la hoja del CRM — y el explorador NO la carga. Así que
   el avatar del menú compartido salía sin círculo, sin fondo y del tamaño de la letra: 24px contra
   los 36 de la home (lo midió tests/ux/vision-check.js comparando las dos pantallas, 2026-08-25).
   Y `.av-36`, que es el tamaño que pide el componente, no estaba definida en NINGUNA hoja.
   Se replican acá en vez de cargar style.css entero en la vidriera: son ocho líneas contra 60 KB
   de estilos del panel que esta pantalla no usa. */
.av { display:inline-flex; align-items:center; justify-content:center; border-radius:999px;
  overflow:hidden; flex:none; font-weight:500; color:#fff; line-height:1; }
.av img { width:100%; height:100%; object-fit:cover; display:block; }
.av-32 { width:32px; height:32px; font-size:14px; }
.av-36 { width:36px; height:36px; font-size:15px; }
.av-40 { width:40px; height:40px; font-size:16px; }
.av-c0{background:#0EA5E9}.av-c1{background:#8B5CF6}.av-c2{background:#F59E0B}.av-c3{background:#EF4444}
.av-c4{background:#10B981}.av-c5{background:#EC4899}.av-c6{background:#6366F1}.av-c7{background:#14B8A6}


/* ── El logo, por tema (2026-08-26) ──────────────────────────────────────────────────────────
   El explorador emitía solo la variante NAVY: sobre el fondo oscuro quedaba casi invisible y se
   leía como un logo roto. Se emiten las dos y acá se elige. No con `filter:invert()` — el isotipo
   lleva el verde de marca y la inversión lo ensucia; los dos archivos ya existían.
   Las reglas de ancho apuntan al SPAN además de al img, porque ahora hay un nivel de más. */
.ex-logo-claro, .ex-logo-oscuro { display:flex; align-items:center; gap:9px; }
.ex-logo-oscuro { display:none; }
.ex-body.dark .ex-logo-claro { display:none; }
.ex-body.dark .ex-logo-oscuro { display:flex; }

/* ── GUARDA GLOBAL [hidden] (02/09/2026) ────────────────────────────────────────────────────
   Cualquier `.clase{display:…}` le gana por cascada al [hidden] del navegador, y el elemento oculto
   se sigue viendo. Pasó CINCO veces más en el testing de hoy (botones, m² manual, chips, selector de
   inmobiliarias) después de haber pasado tres antes: la solución por caso no escala. Con esto,
   `hidden` significa oculto, siempre. Va al FINAL del archivo y con !important a propósito. */
[hidden]{display:none!important}
