/* HaceteDueño — CSS base mobile-first.
   Diseño pensado primero para celulares; media queries solo para tablet y desktop.

   ⚠️ Ese «solo» no es literal: hay un puñado de `@media (max-width: …)` que
   refinan pantallas angostas sobre una base que sí es mobile.

   ⚠️ **Cuántos son NO se anota acá: se mide.** Este bloque publicaba una cifra
   y un `grep -c` para recontarla, y el comando **se contaba a sí mismo**: los
   renglones de prosa que nombran el patrón entraban en el resultado, así que
   daba dos de más (`H-16`). Un grep no puede saltear comentarios; un parser sí,
   y el que mide esto es
   `tests/transversales/test_el_encabezado_de_base_css_no_publica_cifras_vencidas.py`.

   ⚠️ Y acá decía que son DOS los que invierten el sentido —los que tienen el
   estado de escritorio como BASE—: son éstos, y el mismo test exige que la
   lista siga siendo cierta.
     · `.wizard-acciones` .......... fijo al pie sólo bajo 767 px
     · `.ficha-contacto-duplicado` . oculto sólo bajo 767 px
     · `.form-grid-3` ............. tres columnas de base, una sola bajo 720 px
     · `.admin-tabla-usuarios` .... tabla con scroll de base, cards bajo 900 px
   No rompen render y se dejan (K10-05, 2026-09-04); si tocás una de ésas, ojo
   con cuál es el default.

   Variables de diseño centralizadas para mantener consistencia y futuras dark themes. */

/* Tipografía oficial del manual de marca: Raleway (body + titulares), manual
   recibido 2026-06-15. Self-hosted desde 2026-08-18: antes salía de Google
   Fonts — el único tercero en el camino crítico de render, y cada visitante
   le mandaba su IP a Google antes del primer paint, justo antes de inscribir
   la base ante la AAIP. Es la fuente VARIABLE v37, subset latin (el español
   entra entero): un solo archivo cubre los pesos 400–700.
   Licencia SIL OFL 1.1 — ver fonts/OFL.txt. */
@font-face {
  font-family: "Raleway";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  /* Sin cache buster a propósito: este url() no pasa por Jinja, así que la
     versión va EN EL NOMBRE (v37). Si se actualiza la fuente, el archivo
     nuevo se renombra (v38, …) — la misma regla que img/hero.*, vigilada por
     test_el_css_no_referencia_estaticos_nuevos_por_su_cuenta. */
  src: url("../fonts/raleway-v37-latin-variable.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* Paleta oficial del manual de marca HaceteDueño (recibido 2026-06-15). */
  --color-primario: #0c2233;          /* Azul oscuro: navbar, headers, links */
  --color-primario-claro: #1a3a52;    /* Tono medio del azul */
  --color-primario-oscuro: #050f1a;   /* Hover/active del primario */
  --color-acento: #ff5200;            /* Naranja: CTAs principales */
  --color-acento-hover: #e64600;      /* Hover del naranja */
  /* El MISMO naranja, oscurecido, y sólo para cuando lleva TEXTO BLANCO
     encima. `#ff5200` sobre blanco da 3,25:1 y WCAG AA pide 4,5:1 (`K10-15`);
     éste da 4,98:1. **No reemplaza al de marca**: los otros ~40 usos de
     `--color-acento` —bordes, íconos, links, acentos— no llevan texto encima y
     quedan exactamente igual. Decisión de Martín, carril `D` 2026-09-04: se
     oscurece el botón, no la identidad. Lo usan los fondos naranjas con texto
     blanco del archivo; el contador de las pestañas de identidad se había
     escapado del primer barrido (`R3-16`), y ahora los cuenta un guardián que
     mira todo fondo con texto blanco, no sólo el naranja. */
  --color-acento-solido: #c94000;
  --color-acento-solido-hover: #a83600;
  /* Lo mismo con el verde de WhatsApp (`R3-16`): `#25d366` con texto blanco
     da 1,98:1, y es el botón de contacto de la ficha —el más importante del
     sitio—. Éste es el mismo verde oscurecido (4,58:1; el hover, 6,01:1). No es
     nuestra identidad, así que el criterio de `K10-15` aplica sin discusión. */
  --color-whatsapp-solido: #188741;
  --color-whatsapp-solido-hover: #147237;
  --color-petroleo: #065471;          /* Azul petróleo intermedio (manual) */
  --color-cian: #00a1ab;              /* Cian/teal (manual) — acentos secundarios */
  --color-gris-oscuro: #333333;       /* Manual */
  --color-gris-medio: #808080;        /* Manual */
  --color-texto: #1a1a1a;
  --color-texto-suave: #555;
  /* #888 daba 3,54:1 sobre las cards y 3,11:1 sobre el gris de los
     placeholders: por debajo del 4,5:1 de WCAG AA, con este gris pintando
     los h2 de la ficha y el «sin foto» de los placeholders (`AF-11-14`).
     #6d6d6d es el primer valor que cierra sobre los seis fondos donde se
     apoya; lo mide `tests/transversales/test_la_escala_de_grises_llega_al_contraste_minimo.py`. */
  --color-texto-muy-suave: #6d6d6d;
  --color-fondo: #fafafa;
  --color-fondo-card: #ffffff;
  --color-fondo-suave: #f5f7fa;       /* Fondo de secciones internas (diferenciales) */
  /* Fondo de la opción activa/hover de listas (typeahead). Existía sólo como
     `var(--color-fondo-hover, #f6f2ef)` SIN declaración, así que el fallback
     era siempre el valor real (`K10-18`); como indicador de estado no alcanza
     solo (1,11:1 sobre blanco) — el que cumple el 3:1 de SC 1.4.11 es el
     filete lateral que ahora lo acompaña. */
  --color-fondo-hover: #f6f2ef;
  --color-borde: #e5e5e5;
  --color-borde-suave: #f0f0f0;
  --color-corazon: #e63946;
  --color-precio: #0c2233;
  --color-badge-bg: rgba(255, 255, 255, 0.94);
  --color-badge-fg: #0c2233;
  /* Los dos acentos semánticos que la paleta de marca no trae y que la hoja
     igual usaba con `var(--color-alerta, …)` / `var(--color-exito, …)`: eran
     dos custom properties SIN declarar, así que el fallback era siempre el
     valor real (`H-01`, mismo defecto que `K10-18`). Se declaran con el valor
     que ya se estaba pintando —el filete ámbar de `.wizard-faltantes` y el
     verde de `.buscar-interpretado`—, así que declararlas no cambió ni un
     píxel; lo que cambia es que ahora existen y se pueden mover de un lugar.
     No son colores de texto: los dos pintan un `border-left` de 3-4 px. */
  --color-alerta: #f0a500;
  --color-exito: #0a5c44;

  --radio-chico: 6px;
  --radio-medio: 10px;
  --radio-grande: 16px;

  --sombra-card: 0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04);
  --sombra-card-hover: 0 4px 12px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.06);
  --sombra-sticky: 0 -2px 12px rgba(0, 0, 0, 0.08);

  --tamano-tap: 44px;
  --ancho-max: 1200px;
  --gap-feed: 0.75rem;
}

/* ---------- Reset minimal ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  /* Raleway oficial del manual de marca; fallback al stack del sistema. */
  font-family: "Raleway", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  font-size: 16px;
  line-height: 1.5;
  color: var(--color-texto);
  background: var(--color-fondo);
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3 {
  font-family: "Raleway", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-weight: 700;
}
img, video { max-width: 100%; height: auto; display: block; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
a { color: var(--color-primario); text-decoration: none; }
a:hover { text-decoration: underline; }
ul, ol { padding: 0; margin: 0; list-style: none; }

/* El `hidden` del HTML lo aplica el NAVEGADOR con una regla suya —
   `[hidden] { display: none }`— y en la cascada el origen del autor le gana al
   del navegador **sin mirar especificidad**. O sea: cualquier `display` que
   escribamos nosotros lo apaga, y el elemento se queda en pantalla.

   Medido el 2026-09-07 (`T6`): once elementos del árbol salían `hidden` y
   visibles igual. Entre ellos los chips de amenities que no corresponden al
   tipo elegido en /buscar y el bloque de hectáreas que sólo va con «Campo»
   —los dos los tapa y destapa el JS con `.hidden = true/false`, así que el
   defecto le pegaba también a quien tiene JS—, los controles del mapa que eran
   «progressive enhancement», y las flechas y los seis puntitos del carrusel de
   la card, que sin JS ofrecían una navegación que no lleva a ningún lado.

   La hoja ya conocía el problema y lo resolvía de a uno: más abajo hay varias
   `.<clase>[hidden] { display: none }` escritas por incidentes sueltos (cuántas
   no se anota acá — se cuentan, y este comentario ya publicó «cinco» cuando
   eran ocho). Cada una arregla su elemento y ninguna arregla el siguiente. Ésta
   es la general y lleva `!important` a propósito: sin él vuelve a ser una
   carrera de especificidad que gana la próxima clase con `display`. Las
   anteriores quedan porque cada una documenta su incidente; hoy son redundantes.

   Lo sostiene `tests/transversales/test_el_hidden_no_se_lo_come_el_display_del_css.py`, que
   barre las plantillas en vez de llevar una lista escrita a mano. */
[hidden] { display: none !important; }

/* ---------- Layout base ---------- */
.contenedor {
  width: 100%;
  max-width: var(--ancho-max);
  margin: 0 auto;
  padding: 0 1rem;
}

main.contenedor { padding-top: 1rem; padding-bottom: 5rem; }

/* ---------- Navbar (mobile-first con menú hamburguesa via checkbox) ---------- */
.navbar {
  /* Fase 5 marca: fondo azul oscuro como el sitio actual.
     El logo del sitio actual está pensado para fondo oscuro (texto blanco). */
  background: var(--color-primario);
  border-bottom: none;
  position: sticky;
  top: 0;
  z-index: 100;
}
.navbar-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5rem 1rem;
  min-height: 56px;
}
@media (min-width: 768px) {
  .navbar-inner { min-height: 68px; }
}
.navbar-marca {
  font-weight: 700;
  font-size: 1.15rem;
  color: #ffffff;
}

/* Toggle hamburguesa — checkbox-hack puro CSS.

   El control NO se esconde con `display: none` (`CGO-08`). Un control con
   `display: none` **no toma foco y no existe para el lector de pantalla**, y
   como el `<label>` que se toca iba `aria-hidden`, en <768 px el menú entero
   quedaba sin ninguna puerta que no fuera el dedo: «Ingresar» y «Crear cuenta»
   para el anónimo, «Mi cuenta» y «Publicar» para el logueado. Alcanza también a
   quien usa la PC con zoom alto — a 400 % un escritorio de 1280 px reflowa a
   320 px CSS y entra por este lado.

   Se esconde como ya lo hacía `.cuenta-nav-toggle-input` (el toggle del panel de
   la cuenta, más abajo en esta hoja): fuera de la vista pero **enfocable**, o sea
   el mismo patrón sin JS que el proyecto ya había elegido. El menú sigue
   abriendo por `:checked` y no por `:hover`. */
.navbar-toggle { position: absolute; left: -9999px; }
/* El anillo de foco va dibujado SOBRE EL LABEL: el control real está a -9999px,
   así que el anillo que el navegador le pinta a él cae fuera de la pantalla y no
   se ve nada. Mismo patrón que `.radio-cards input:focus-visible + label`.
   Blanco, porque el navbar es azul oscuro. */
.navbar-toggle:focus-visible ~ .navbar-toggle-label {
  outline: 2px solid #ffffff;
  outline-offset: 2px;
}
.navbar-toggle-label {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tamano-tap);
  height: var(--tamano-tap);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.navbar-toggle-label::before {
  content: "";
  width: 24px;
  height: 18px;
  /* Hamburguesa blanca para verse sobre el navbar azul oscuro. */
  background: linear-gradient(
    to bottom,
    #ffffff 0, #ffffff 2px,
    transparent 2px, transparent 8px,
    #ffffff 8px, #ffffff 10px,
    transparent 10px, transparent 16px,
    #ffffff 16px, #ffffff 18px
  );
}

.navbar-menu {
  display: none;
  flex-direction: column;
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  background: var(--color-primario);
  border-bottom: none;
  padding: 0.5rem 1rem;
  gap: 0.25rem;
}
.navbar-toggle:checked ~ .navbar-menu { display: flex; }
.navbar-menu a {
  display: flex;
  align-items: center;
  min-height: var(--tamano-tap);
  padding: 0.5rem 0.25rem;
  color: #ffffff;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}
.navbar-menu a:hover { text-decoration: none; color: var(--color-acento); }
.navbar-menu li:last-child a { border-bottom: none; }

/* CTA naranja destacado dentro del navbar (Publicar, Crear cuenta). */
.navbar-menu a.navbar-cta {
  background: var(--color-acento-solido);
  color: #ffffff;
  border-radius: var(--radio-medio);
  font-weight: 600;
  padding: 0.5rem 1.1rem;
  border-bottom: none;
  text-align: center;
  justify-content: center;
}
.navbar-menu a.navbar-cta:hover {
  background: var(--color-acento-solido-hover);
  color: #ffffff;
}

@media (min-width: 768px) {
  /* En escritorio el menú está siempre desplegado, así que el control no
     controla nada: se saca del orden de tabulación en vez de dejar una parada
     de teclado que no hace nada. Acá `display: none` es lo correcto —lo que
     `CGO-08` prohíbe es esconderlo así **donde el toggle es la única puerta**. */
  .navbar-toggle { display: none; }
  .navbar-toggle-label { display: none; }
  .navbar-menu {
    display: flex !important;
    flex-direction: row;
    position: static;
    padding: 0;
    border: none;
    gap: 1rem;
  }
  .navbar-menu a {
    padding: 0.25rem 0.5rem;
    border-bottom: none;
  }
}
/* La barra de escritorio va compacta sólo con mouse. Con el dedo —una tablet a
   768 o 1024 px— los links conservan los 44 px de arriba: con el `min-height:
   auto` sin condición, «Ingresar» medía 32 px y «Crear cuenta» 40 (medido en
   Edge con táctil emulado, 2026-09-13). */
@media (min-width: 768px) and (hover: hover) and (pointer: fine) {
  .navbar-menu a { min-height: auto; }
}

/* ---------- Flashes ---------- */
.flashes { padding: 0.75rem 1rem; }
.flash {
  padding: 0.75rem 1rem;
  border-radius: var(--radio-chico);
  margin-bottom: 0.5rem;
  font-size: 0.95rem;
}
.flash-info { background: #e7f3ff; color: #0a4d8c; }
.flash-success { background: #e6f7ed; color: #086b3d; }
.flash-warning { background: #fff8e0; color: #7a5800; }
.flash-error { background: #ffe6e6; color: #8b1f24; }

/* ---------- Tipografía de páginas ---------- */
h1 { font-size: 1.6rem; margin: 0.5rem 0 1rem; }
h2 { font-size: 1.3rem; margin: 1.5rem 0 0.75rem; }
h3 { font-size: 1.1rem; margin: 1rem 0 0.5rem; }
@media (min-width: 768px) {
  h1 { font-size: 2rem; }
  h2 { font-size: 1.5rem; }
}

/* ---------- Hero (home) ---------- */
.hero {
  text-align: center;
  padding: 2rem 1rem 1.5rem;
}
.hero h1 { font-size: 1.75rem; margin-bottom: 0.5rem; line-height: 1.2; }
.hero p { color: var(--color-texto-suave); margin-bottom: 1.25rem; }
@media (min-width: 768px) { .hero { padding: 3rem 1rem; } .hero h1 { font-size: 2.5rem; } }

/* Hero con imagen de fondo (home pública) — Fase 5 marca.
   El overlay azul oscuro asegura contraste suficiente para texto blanco
   sobre la foto. La imagen "manos" comunica el posicionamiento del portal.

   `T6·C-05`: esto estaba DOS VECES —acá y 4200 líneas más abajo, bajo el
   título «Hero ajustado»— y la cascada mezclaba los dos bloques, así que lo
   que se veía no era ninguno de los dos. Acá quedan los valores que ganaban:
   del bloque de abajo la imagen (WebP + gradiente más suave), el `padding`, el
   `min-height`, el `justify-content`, el `text-align` y todo el `h1` salvo el
   color; de éste el `position`, el color, el `border-radius`, los márgenes y
   el `display`. Se fusionó en ESTA posición y no en la de abajo a propósito:
   `.hero` y su `@media` viven arriba (líneas 281-287) y tienen la misma
   especificidad, así que el bloque tiene que quedar después de ellos.

   Minimalismo ratificado por Martín el 2026-07-24: se probó una variante con
   subtítulo + CTA y la descartó al verla. No reintroducir sin pedido — la
   regla de `p` es lo que quedó de esa variante y hoy no la usa nadie. */
.hero-con-imagen {
  position: relative;
  /* Imagen del hero generada con Gemini 2026-06-16 (living moderno con
     vista a Córdoba). WebP optimizada con fallback JPG.

     ⚠️ Estas dos URLs son los ÚNICOS estáticos que no pasan por `estatico()`:
     viven adentro del CSS, donde Jinja no llega, y en producción se cachean un
     año igual que el resto. Si alguna vez se cambia la foto del hero, hay que
     **cambiarle el nombre al archivo** (`hero-2.webp`), no pisarlo: pisándolo,
     el que ya visitó el sitio sigue viendo la vieja. Un tercer `url()` en el
     CSS hace fallar `test_los_estaticos_llevan_cache_buster.py` a propósito,
     para que la decisión se tome y no se olvide (`T1-06`). */
  background-image:
    linear-gradient(rgba(12, 34, 51, 0.35), rgba(12, 34, 51, 0.55)),
    image-set(
      url('../img/hero.webp') type('image/webp'),
      url('../img/hero.jpg') type('image/jpeg')
    );
  background-size: cover;
  background-position: center;
  color: #ffffff;
  padding: 3.5rem 1.25rem;
  border-radius: var(--radio-grande);
  margin-top: 0.5rem;
  margin-bottom: 1.5rem;
  min-height: 320px;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  text-align: left;
}
.hero-con-imagen .hero-contenido { max-width: 760px; }
.hero-con-imagen h1 {
  color: #ffffff;
  font-size: 1.9rem;
  font-weight: 700;
  line-height: 1.15;
  margin-bottom: 0;
  letter-spacing: -0.01em;
}
.hero-con-imagen p {
  color: rgba(255, 255, 255, 0.94);
  font-size: 1rem;
  margin-bottom: 1.5rem;
}
@media (min-width: 768px) {
  .hero-con-imagen { padding: 5.5rem 3rem; min-height: 440px; }
  .hero-con-imagen h1 { font-size: 3rem; }
  .hero-con-imagen p { font-size: 1.15rem; }
}

/* Logo en navbar — Fase 5. Alto fijo para mantener línea visual del bar.
   +15% sobre el tamaño inicial (Martín dixit 2026-06-15). */
.navbar-logo {
  height: 44px;
  width: auto;
  display: block;
}
@media (min-width: 768px) { .navbar-logo { height: 52px; } }

/* ---------- Botones ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: var(--tamano-tap);
  padding: 0.625rem 1.25rem;
  border-radius: var(--radio-medio);
  font-weight: 600;
  font-size: 1rem;
  border: 1px solid transparent;
  transition: background 0.15s, border 0.15s;
  -webkit-tap-highlight-color: transparent;
  text-align: center;
}
.btn-primario {
  background: var(--color-acento-solido);
  color: white;
}
.btn-primario:hover { background: var(--color-acento-solido-hover); text-decoration: none; }
.btn-secundario {
  background: white;
  color: var(--color-primario);
  border-color: var(--color-primario);
}
.btn-secundario:hover { background: #f5f8fb; text-decoration: none; }
.btn-bloque { display: flex; width: 100%; }
/* Acción terciaria con cara de link ("Ver descripción completa", "Quitar" del
   radio del mapa). Se usaba en dos templates sin que la clase existiera. */
.btn-texto {
  background: none;
  border: none;
  padding: 0.35rem 0;
  min-height: var(--tamano-tap);
  font: inherit;
  font-weight: 600;
  color: var(--color-primario);
  cursor: pointer;
  text-decoration: underline;
}
.btn-texto:hover { color: var(--color-primario-oscuro); }
/* Un link suelto —sin oración alrededor— que tiene que seguir viéndose como
   link: sólo le da el área táctil, sin tocar color, peso ni subrayado. Para
   cuando `.btn-texto` es demasiado (lo pone en negrita y en el color primario)
   y el link de la línea queda en 19 px. Nació con «Quiero dar de baja mi
   cuenta», que es discreto a propósito (R11, 2026-09-12). */
.enlace-suelto {
  display: inline-flex;
  align-items: center;
  min-height: var(--tamano-tap);
}

/* ---------- Feed de avisos ---------- */
.feed {
  display: grid;
  gap: var(--gap-feed);
  grid-template-columns: 1fr;
  margin-top: 1rem;
}
@media (min-width: 540px) { .feed { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 900px) { .feed { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1200px) { .feed { grid-template-columns: repeat(4, 1fr); } }

/* ---------- Card del aviso ---------- */
.aviso-card {
  position: relative;
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  overflow: hidden;
  box-shadow: var(--sombra-card);
  transition: box-shadow 0.18s, transform 0.18s;
}
.aviso-card:hover { box-shadow: var(--sombra-card-hover); transform: translateY(-1px); }

.aviso-card-foto {
  position: relative;
  width: 100%;
  aspect-ratio: 4/5;            /* portada vertical 4:5 */
  background: #efefef;
  overflow: hidden;
}
.aviso-card-foto img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ---- Carrusel de la card (2026-08-05) ----
   Scroll horizontal con snap: el swipe es nativo del navegador, no hay JS
   arrastrando nada. z-index 3 para quedar POR ENCIMA del `.aviso-card-link`
   (z-index 1) que cubre toda la card — si el link quedara arriba, el dedo
   arrastraría sobre él y el navegador scrollearía la página en vez del
   carrusel. Cada slide es su propio link, así el tap sigue abriendo el aviso. */
.aviso-card-carrusel {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  /* El navegador maneja el gesto horizontal y deja el vertical para la página:
     sin esto, un swipe diagonal se traba. */
  touch-action: pan-x pan-y;
  overscroll-behavior-x: contain;
}
.aviso-card-carrusel::-webkit-scrollbar { display: none; }
.aviso-card-slide {
  flex: 0 0 100%;
  scroll-snap-align: center;
  scroll-snap-stop: always;   /* una foto por swipe, no tres de un saque */
  height: 100%;
}

.aviso-card-dots {
  position: absolute;
  z-index: 4;
  left: 0;
  right: 0;
  bottom: 0.55rem;
  display: flex;
  justify-content: center;
  gap: 4px;
  pointer-events: none;
}
.aviso-card-dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.55);
  box-shadow: 0 0 2px rgba(12, 34, 51, 0.5);
  transition: background 0.15s, transform 0.15s;
}
.aviso-card-dot.activo { background: #fff; transform: scale(1.25); }

/* Flechas: son para mouse. En touch molestan más de lo que ayudan (el dedo ya
   pasa las fotos), así que sólo aparecen donde hay hover real. */
.aviso-card-flecha {
  position: absolute;
  z-index: 4;
  top: 50%;
  transform: translateY(-50%);
  display: none;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.9);
  color: var(--color-primario);
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s;
}
.aviso-card-flecha--prev { left: 0.4rem; }
.aviso-card-flecha--next { right: 0.4rem; }
.aviso-card-flecha.esta-inactiva { opacity: 0 !important; pointer-events: none; }
@media (hover: hover) and (pointer: fine) {
  .aviso-card-flecha { display: inline-flex; }
  .aviso-card:hover .aviso-card-flecha { opacity: 1; }
}
.aviso-card-foto-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%; height: 100%;
  color: var(--color-texto-muy-suave);
  font-size: 0.85rem;
}

.aviso-card-badges {
  position: absolute;
  top: 0.5rem;
  left: 0.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  max-width: calc(100% - 4rem);
  /* Por encima del carrusel (z-index 3), que a su vez está sobre el link (1).
     Venía suelto abajo de `.aviso-card-link`, o sea un segundo bloque del
     mismo selector (`T6·C-05`); no se pisaba con nada de acá, así que la
     fusión es de cero píxeles. */
  z-index: 4;
  pointer-events: none;
}
.badge {
  background: var(--color-badge-bg);
  color: var(--color-badge-fg);
  font-size: 0.7rem;
  font-weight: 700;
  /* Fix definitivo 2026-07-06: el pill heredaba line-height 1.5 del body y
     con padding 0.25rem quedaba al doble de alto que el texto ("el fondo
     gigante" que Martín veía en viewports intermedios: celular apaisado,
     tablet, ventana angosta >640px — los fixes del 04/07 solo cubrían
     ≤640px). line-height propio + padding en EM: el fondo escala con el
     texto y la proporción pill/texto es la misma en TODOS los viewports. */
  line-height: 1.15;
  letter-spacing: 0.01em;
  padding: 0.2em 0.6em;
  border-radius: 999px;
  backdrop-filter: blur(4px);
}
/* En mobile los badges tapaban la foto porque con 5+ badges apilados
   ocupaban ~50% del alto de la card 4:5. Achicamos la fuente (el padding
   em acompaña solo) y ocultamos del 4to en adelante — el orden del DOM
   prioriza los editoriales (badges_de_aviso: precio bajó / destacado /
   nuevo / dueño directo) sobre los técnicos (a_estrenar / pozo /
   apto_credito), así que los 3 más importantes siempre se ven. */
@media (max-width: 640px) {
  .aviso-card-badges {
    gap: 0.2rem;
  }
  .aviso-card-badges .badge {
    font-size: 0.64rem;
  }
  .aviso-card-badges .badge:nth-child(n+4) {
    display: none;
  }
}
/* Paleta 2026-07-06 (decisión Martín): color SOLO para los badges
   editoriales — rojo urgencia, dorado destacado, verde nuevo, azul vivo
   dueño directo (la propuesta de valor, despegado del azul del navbar).
   Los técnicos (A estrenar / En pozo / Apto crédito) quedan en el blanco
   translúcido base: informan sin competir. */
.badge-destacado { background: #f0a500; color: #3d2a00; }
.badge-dueno { background: #0468bf; color: white; }
.badge-nuevo { background: #086b3d; color: white; }
.badge-precio-bajo {
  background: #c0392b;
  color: white;
  /* Flechita "↓" antes del texto sin sumar nodos en el template. */
}
.badge-precio-bajo::before { content: "↓ "; }

.aviso-card-corazon {
  position: absolute;
  top: 0.4rem;
  right: 0.4rem;
  background: rgba(255, 255, 255, 0.94);
  border: none;
  width: var(--tamano-tap);
  height: var(--tamano-tap);
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1.4rem;
  color: var(--color-texto-suave);
  line-height: 1;
  backdrop-filter: blur(4px);
  transition: transform 0.12s ease;
  -webkit-tap-highlight-color: transparent;
  /* Por encima de los badges (4) y del carrusel (3): es el único de los tres
     que se toca. Mismo caso que `.aviso-card-badges` (`T6·C-05`). */
  z-index: 5;
}
.aviso-card-corazon:hover { transform: scale(1.06); }
.aviso-card-corazon:active { transform: scale(0.94); }
.aviso-card-corazon.es-favorito { color: var(--color-corazon); }
.aviso-card-corazon::before { content: "♡"; }
.aviso-card-corazon.es-favorito::before { content: "♥"; }

.aviso-card-cuerpo { padding: 0.75rem; }
.aviso-card-precio {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--color-precio);
}
.aviso-card-titulo {
  font-size: 0.95rem;
  color: var(--color-texto);
  margin: 0.1rem 0 0.25rem;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.aviso-card-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1rem;
  font-size: 0.82rem;
  color: var(--color-texto-suave);
}
/* Cada spec = ícono line-style + número (dorm/baños/coch/m²). El ícono abre y
   el número cierra (Martín 2026-08-05); el hueco ENTRE specs (1rem, arriba) es
   mucho mayor que el de adentro para que cada par se lea como una unidad. */
.aviso-card-spec {
  display: inline-flex;
  align-items: center;
  gap: 0.25em;
  font-weight: 600;
}
.aviso-card-spec svg {
  width: 1.15em;
  height: 1.15em;
  flex: none;
}
.aviso-card-ubicacion {
  font-size: 0.8rem;
  color: var(--color-texto-muy-suave);
  margin-top: 0.35rem;
}

/* Card 2026-07-26 (benchmark Zonaprop/Argenprop/ML): eyebrow tipo+operación,
   expensas bajo el precio, y contador de fotos sobre la foto. */
.aviso-card-tipo-op {
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-texto-muy-suave);
  margin-bottom: 0.15rem;
}
.aviso-card-expensas {
  font-size: 0.78rem;
  color: var(--color-texto-suave);
  margin-top: 0.05rem;
}
.aviso-card-fotos-count {
  position: absolute;
  bottom: 0.5rem;
  right: 0.5rem;
  z-index: 4;
  background: rgba(12, 34, 51, 0.72);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 600;
  padding: 0.15em 0.6em;
  border-radius: 999px;
  backdrop-filter: blur(2px);
  pointer-events: none;
}

.aviso-card-link {
  position: absolute;
  inset: 0;
  z-index: 1;
}
/* Los `z-index` del corazón (5) y de los badges (4) vivían acá, sueltos, como
   un segundo bloque de cada selector; están fusionados con el primero de cada
   uno, más arriba (`T6·C-05`). */

/* ---------- Sticky bottom CTA (ficha del aviso) ---------- */
.sticky-cta {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  background: white;
  border-top: 1px solid var(--color-borde);
  padding: 0.6rem 1rem calc(0.6rem + env(safe-area-inset-bottom));
  display: flex;
  gap: 0.5rem;
  box-shadow: var(--sombra-sticky);
  z-index: 50;
}
.sticky-cta .btn { flex: 1; }
@media (min-width: 768px) { .sticky-cta { position: static; box-shadow: none; border: none; } }

/* ---------- Formularios ---------- */
.form-contenedor {
  max-width: 460px;
  margin: 1.5rem auto;
  background: white;
  padding: 1.5rem;
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-grande);
}
.form-contenedor h1 { margin-top: 0; }
.form-grupo { margin-bottom: 1rem; }
.form-label {
  display: block;
  margin-bottom: 0.25rem;
  font-weight: 600;
  font-size: 0.9rem;
}
/* Los rótulos de GRUPO van en `fieldset` + `legend` y no en un `<label>` suelto
   (`H-13`): un `<label>` sin `for` y sin control adentro no nombra nada. El
   `fieldset` acá agrupa semánticamente, no dibuja una caja, así que el borde y
   el padding que le pone el navegador se apagan.
   ⚠️ El `min-width: 0` no es cosmético: la hoja del navegador le da al
   `fieldset` un `min-width: min-content` que no se puede achicar, y los tres
   que usan esto viven adentro de un grid o un flex. */
fieldset.form-grupo {
  border: 0;
  padding: 0;
  margin: 0 0 1rem;
  min-width: 0;
}
fieldset.form-grupo > legend.form-label {
  padding: 0;
}
.form-input {
  width: 100%;
  padding: 0.7rem 0.75rem;
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-medio);
  font-size: 1rem;
  background: white;
  min-height: var(--tamano-tap);
}
.form-input:focus {
  outline: none;
  border-color: var(--color-primario);
  box-shadow: 0 0 0 3px rgba(10, 92, 68, 0.18);
}
.form-input-error {
  border-color: #c22b3b;
  background: #fffafa;
}
/* Al enfocarlo gana el foco: el usuario ya sabe cuál está mal —lo está
   arreglando— y el anillo rojo encima del azul se lee como que sigue fallando. */
.form-input-error:focus {
  border-color: var(--color-primario);
}
.form-check { margin-right: 0.5rem; transform: scale(1.15); }
/* `flex-start` y no `center`: con el texto en dos renglones —el checkbox de
   términos en 390px— el cuadradito quedaba flotando a media altura. */
.form-check-label {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  cursor: pointer;
  min-height: var(--tamano-tap);
  line-height: 1.45;
}
.form-check-label input[type="checkbox"] { margin-top: 0.35rem; flex: 0 0 auto; }
/* Los errores llevan negrita para despegarse de la ayuda del campo, que va
   justo arriba, en el mismo cuerpo y del mismo tamaño: en el registro con el
   form vacío se leían seis renglones chiquitos donde no se distinguía cuál era
   la instrucción y cuál el reproche (2026-08-16). */
.form-errores {
  margin: 0.35rem 0 0;
  padding-left: 0;
  list-style: none;
  color: #c22b3b;
  font-size: 0.85rem;
  font-weight: 600;
}
/* Variante de un error suelto (`<p class="form-error">`). La usan 44 lugares
   —todo el panel admin, el de anunciante y la verificación de identidad— y
   hasta el 2026-08-16 no existía: el mensaje salía en negro, con el mismo peso
   que la ayuda del campo. */
.form-error {
  margin: 0.35rem 0 0;
  color: #c22b3b;
  font-size: 0.85rem;
  font-weight: 600;
}

/* ---------- Footer (Fase 5 marca M3.7 — 3 columnas, fondo oscuro) ---------- */
.footer {
  background: var(--color-primario);
  color: rgba(255, 255, 255, 0.85);
  padding: 2.5rem 0 calc(2rem + env(safe-area-inset-bottom));
  margin-top: 3rem;
  font-size: 0.9rem;
}
.footer-grid {
  display: grid;
  gap: 2rem;
  grid-template-columns: 1fr;
}
@media (min-width: 768px) {
  .footer-grid {
    grid-template-columns: 1.2fr 1fr 1.1fr;
    gap: 3rem;
    align-items: start;
  }
}
.footer-col h2 {
  /* Era un `h4` hasta el 2026-09-13 y heredaba la fuente del cuerpo; el
     `h1, h2, h3` de arriba le cambiaría la lista de respaldo. */
  font-family: inherit;
  color: #ffffff;
  font-size: 1rem;
  font-weight: 700;
  margin: 0 0 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* Sin `gap` desde que cada link mide 44 px (R11, 2026-09-12): la fila ya
   separa, y el `gap` de 8 px le sumaba otros 56 a la lista de ocho links. */
.footer-col ul { display: flex; flex-direction: column; gap: 0; }
/* El alto táctil va acá y no sólo en `.footer-contacto-lista a`: los ocho
   links de «Navegación» medían 22 px en cada página del sitio (R11,
   2026-09-12, a 375 px con táctil emulado), apilados de a uno y justo en la
   franja que el pulgar alcanza peor. Errarle a uno te lleva a otra página. El
   ancho mínimo es por «FAQs», que con el alto arreglado quedaba en 35 px. */
.footer-col a {
  color: rgba(255, 255, 255, 0.85);
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: var(--tamano-tap);
  min-width: var(--tamano-tap);
}
.footer-col a:hover {
  color: #ffffff;
  text-decoration: none;
}
.footer-col svg {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}
.footer-logo {
  height: 56px;
  width: auto;
  margin-bottom: 0.85rem;
  display: block;
}
.footer-tagline {
  margin: 0 0 1rem;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.9);
}
.footer-copy {
  margin: 0;
  font-size: 0.8rem;
  color: rgba(255, 255, 255, 0.55);
}
/* Fila de copyright al fondo del footer, siempre al final tanto en
   desktop como en mobile. Antes vivía dentro de `.footer-col-marca` y
   en mobile quedaba arriba de "Información" (fix 2026-07-04). */
.footer-final {
  margin-top: 2rem;
  padding-top: 1.25rem;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  text-align: center;
}
.footer-final .footer-copy {
  text-align: center;
}
.footer-contacto-lista { margin-bottom: 1rem; }
/* Los tres renglones de «Contacto» son destinos táctiles y valen los 44 px que
   pide `CLAUDE.md`. El del medio ya los declaraba —`.footer-cta-ayuda`, que es
   un <button> y por eso no lo alcanza `.footer-col a`— y los dos <a> del mismo
   <ul>, el mail y el canal de WhatsApp, se habían quedado sin ellos: `.footer-col a`
   es `inline-flex` sin alto, así que medían el alto de su línea (~21 px).
   El barrido de 44 px no podía verlo —una regla que no declara dimensión queda
   fuera de su alcance, está dicho en su docstring— y la clase del canal
   (`footer-cta-canal`) no tenía ninguna regla donde mirarlo. `T6`, 2026-09-07. */
.footer-contacto-lista a { min-height: var(--tamano-tap); }
.footer-redes {
  display: flex;
  gap: 0.6rem;
  margin-top: 0.5rem;
}
/* 38×38 hasta el 2026-09-07: los links a las redes del footer son un destino
   táctil como cualquier otro y el footer es lo último que se toca con el
   pulgar, en la parte más incómoda de la pantalla. El círculo de fondo crece
   con el área; el ícono sigue en 18px. */
.footer-redes a {
  width: var(--tamano-tap);
  height: var(--tamano-tap);
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.08);
  color: #ffffff;
  transition: background 0.15s;
}
.footer-redes a:hover { background: var(--color-acento); }
.footer-redes svg { width: 18px; height: 18px; }

/* ---------- Helpers ---------- */
.solo-mobile { display: initial; }
.solo-desktop { display: none; }
@media (min-width: 768px) {
  .solo-mobile { display: none; }
  .solo-desktop { display: initial; }
}
/* Excepción dialog (bug detectado 2026-07-24): `.solo-mobile` pisaba el
   `display: none` que el navegador le da a un <dialog> CERRADO, y el
   bottom-sheet de filtros de /buscar quedaba renderizado "fantasma" al pie
   de la página en celular. `revert` devuelve el display al stylesheet del
   navegador (block abierto / none cerrado); en desktop no existe nunca. */
dialog.solo-mobile { display: revert; }
@media (min-width: 768px) {
  dialog.solo-mobile { display: none; }
}

/* ---------- Placeholder genérico (durante desarrollo) ---------- */
.placeholder { text-align: center; color: var(--color-texto-muy-suave); padding: 2rem 0; }

/* ---------- Wizard de publicación (Fase 1 #5.3.b) ---------- */
/* Layout full-screen mobile, columna centrada en desktop. Espacio cómodo para
   tap targets ≥ 44px. Sticky CTA al pie en mobile. */

.publicar-landing {
  max-width: 560px;
  margin: 1rem auto 4rem;
  padding: 1rem;
  text-align: center;
}
.publicar-landing .btn-grande {
  width: 100%;
  margin-top: 1rem;
  padding: 1rem;
  font-size: 1.05rem;
}
.lista-borradores {
  margin-top: 1rem;
  text-align: left;
}
.borrador-item {
  padding: 0.75rem;
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-medio);
  margin-bottom: 0.5rem;
  background: white;
}

.wizard {
  max-width: 640px;
  margin: 0 auto;
  padding: 0 1rem 6rem;
}
.wizard-header { margin: 1rem 0 1.25rem; }
.wizard-header h1 { font-size: 1.4rem; margin-top: 0.75rem; line-height: 1.25; }

/* Barra clickeable de pasos (2026-07-17, fix "Continuar"). Reemplaza la
   barra de progreso pasiva anterior. Mobile: chips cuadrados con solo el
   número + scroll horizontal si no entran. Desktop: chips con número + label. */
.wizard-nav {
  margin: 0 -1rem 0.5rem;
  padding: 0 1rem;
  overflow-x: auto;
  scrollbar-width: none;
}
.wizard-nav::-webkit-scrollbar { display: none; }
.wizard-nav-lista {
  display: flex;
  gap: 0.4rem;
  list-style: none;
  margin: 0;
  padding: 0.25rem 0;
}
.wizard-nav-item { flex: 0 0 auto; }
.wizard-nav-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0 0.3rem;
  border: 1px solid var(--color-borde);
  border-radius: 22px;
  background: white;
  color: var(--color-texto-suave);
  text-decoration: none;
  font-size: 0.9rem;
  font-weight: 600;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.wizard-nav-chip:hover,
.wizard-nav-chip:focus-visible {
  border-color: var(--color-primario);
  color: var(--color-primario);
  outline: none;
}
.wizard-nav-item.es-actual .wizard-nav-chip {
  background: var(--color-primario);
  border-color: var(--color-primario);
  color: white;
}
/* El chip del paso ACTUAL es justo el que más se enfoca —es donde el usuario
   está parado— y la regla de arriba, más específica y posterior, le pisaba el
   borde y el color con los que la regla de foco compensa su `outline: none`:
   quedaba sin ningún indicador (`K10-10`). Doble anillo (separador blanco +
   primario), visible sobre el chip azul y sobre el fondo claro de la barra —
   mismo espíritu que el anillo de `.form-input:focus`. */
.wizard-nav-item.es-actual .wizard-nav-chip:focus-visible {
  box-shadow: 0 0 0 2px white, 0 0 0 4px var(--color-primario);
}
/* Mobile: solo el número en el chip. El label del paso actual va grande
   en el <h1> del header — ya existente. */
.wizard-nav-label { display: none; }
@media (min-width: 640px) {
  /* Tablet+: chip lleva número + label. */
  .wizard-nav-chip {
    padding: 0 0.85rem;
    gap: 0.4rem;
  }
  .wizard-nav-label { display: inline; font-weight: 500; }
}

.wizard-form fieldset { border: none; padding: 0; margin: 0 0 1.25rem; }
.wizard-form legend {
  font-weight: 600;
  font-size: 0.95rem;
  margin-bottom: 0.5rem;
  color: var(--color-texto);
}

.wizard-acciones {
  display: flex;
  gap: 0.5rem;
  margin-top: 1.5rem;
}
.wizard-acciones .btn { flex: 1; }
/* Invierte el mobile-first: el estado BASE es el de escritorio y el fijo al pie
   entra sólo bajo 767 px (K10-05). */
@media (max-width: 767px) {
  .wizard-acciones {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    padding: 0.75rem 1rem calc(0.75rem + env(safe-area-inset-bottom));
    background: white;
    border-top: 1px solid var(--color-borde);
    box-shadow: var(--sombra-sticky);
    margin: 0;
    z-index: 10;
  }
}

/* ---- Radio cards (paso 1) ---- */
.radio-cards {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.6rem;
  margin-bottom: 1.5rem;
}
@media (min-width: 540px) { .radio-cards { grid-template-columns: repeat(3, 1fr); } }
.radio-cards input[type="radio"] {
  position: absolute; opacity: 0; pointer-events: none;
}
.radio-cards label {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  min-height: 80px;
  padding: 0.75rem 0.5rem;
  background: white;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio-medio);
  font-weight: 600;
  font-size: 0.95rem;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: border-color 0.12s, background 0.12s;
}
.radio-cards input[type="radio"]:checked + label {
  border-color: var(--color-primario);
  background: #eef2f7;
  color: var(--color-primario-oscuro);
}
.radio-cards input[type="radio"]:focus-visible + label {
  outline: 2px solid var(--color-primario);
  outline-offset: 2px;
}

/* ---- Secciones colapsables (paso 3) ---- */
.wizard-acordeon {
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-medio);
  margin-bottom: 0.75rem;
  background: white;
  overflow: hidden;
}
.wizard-acordeon > summary {
  list-style: none;
  cursor: pointer;
  padding: 0.85rem 1rem;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: var(--tamano-tap);
}
.wizard-acordeon > summary::-webkit-details-marker { display: none; }
.wizard-acordeon > summary::after {
  content: "▾";
  transition: transform 0.18s;
  color: var(--color-texto-muy-suave);
}
.wizard-acordeon[open] > summary::after { transform: rotate(180deg); }
.wizard-acordeon > .acordeon-cuerpo {
  padding: 0 1rem 1rem;
  border-top: 1px solid var(--color-borde-suave);
}
/* Estado del geocoding automático (paso 2, 2026-07-18). Vive arriba del mapa;
   colores suaves para no distraer del formulario. */
.wizard-geocode-estado {
  min-height: 1.4rem;
  margin: 0.35rem 0 0.5rem;
  font-size: 0.85rem;
}
.wizard-geocode-estado.info { color: var(--color-texto-muy-suave); }
.wizard-geocode-estado.ok { color: var(--color-primario-oscuro, #2b7a2b); }
.wizard-geocode-estado.warn { color: var(--color-acento, #ff5200); }

/* Bloque de ambientes en paso 3: grid en vez de acordeón (2026-07-18).
   Mobile 2 cols, tablet 3 cols. Los inputs son cortos (números 0-50). */
.wizard-grid-ambientes {
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-medio);
  padding: 0.85rem 1rem 1rem;
  margin-bottom: 0.75rem;
  background: white;
}
.wizard-grid-ambientes > legend {
  padding: 0 0.35rem;
  font-weight: 600;
}
.wizard-grid-ambientes .grid-ambientes {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.6rem 0.9rem;
}
@media (min-width: 640px) {
  .wizard-grid-ambientes .grid-ambientes {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
.wizard-grid-ambientes .grid-ambientes .form-grupo { margin-bottom: 0; }

/* Marca visual de campo obligatorio: asterisco naranja al lado del label
   (wizard adaptativo por tipo, 2026-07-12). Discreto pero legible. */
.oblig-marca {
  color: var(--color-acento, #ff5200);
  font-weight: 700;
  margin-left: 0.15rem;
}
/* Miniatura de portada en el listado de avisos del admin (thumb 48×48). */
.col-thumb { width: 56px; }
.admin-aviso-thumb {
  display: inline-block;
  width: 48px;
  height: 48px;
  border-radius: var(--radio-chico);
  object-fit: cover;
  vertical-align: middle;
  background: #f0f0f0;
}
.admin-aviso-thumb-vacio {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1.2rem;
  color: var(--color-texto-muy-suave);
  background: #f5f5f5;
}
/* Chip que muestra el filtro pineado desde un card del dashboard admin
   (p. ej. "Pendientes de completar el alta"). Trae X para quitarlo. */
.admin-filtro-activo {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  background: rgba(255, 82, 0, 0.10);
  color: var(--color-acento, #ff5200);
  font-size: 0.82rem;
  font-weight: 600;
  margin-left: 0.35rem;
}
.admin-filtro-activo a {
  color: inherit;
  text-decoration: none;
  font-weight: 700;
  padding-left: 0.15rem;
}
.admin-filtro-activo a:hover { opacity: 0.75; }
/* Ayuda contextual debajo de un input del wizard (p. ej. leyenda anti-precios
   de prueba). Menos vertical que un párrafo suelto — pega al input. */
.form-hint {
  margin-top: 0.25rem;
  margin-bottom: 0.5rem;
  line-height: 1.4;
}
/* Fila de checkboxes en línea (forma de pago junto al precio en paso 3). */
.form-flags-inline {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.25rem;
  margin-top: 0.35rem;
}
.form-flags-inline .form-grupo { margin: 0; }
/* Texto explicativo dentro del cuerpo del acordeón, antes de los inputs. */
.wizard-consejo {
  margin: 0 0 0.85rem;
  padding: 0.7rem 0.9rem;
  background: #f2f8f4;
  border-left: 3px solid #2f8a5f;
  border-radius: 0 6px 6px 0;
  color: var(--color-texto-suave);
  font-size: 0.92rem;
  line-height: 1.5;
}

/* ---- Grid de amenities (paso 4) — chips seleccionables ---- */
.amenities-grupo { margin-bottom: 1.25rem; }
.amenities-grupo > h3 {
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-texto-muy-suave);
  margin: 0 0 0.5rem;
}
.amenities-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.5rem;
}
@media (min-width: 540px) { .amenities-grid { grid-template-columns: repeat(3, 1fr); } }

/* Los MISMOS amenities, de sólo lectura, en la landing del emprendimiento
   («El edificio» y «Formas de pago»). Hasta el 2026-09-07 el <ul> salía sin
   ninguna regla: el reset de arriba le saca la viñeta y la sangría a todo
   `ul`, así que la lista quedaba en una sola columna, sin marca y sin
   separación — tres amenities se leían como un párrafo cortado. La misma
   grilla que la ficha, para que las dos superficies cuenten lo mismo; el
   margen hace de `.amenities-grupo`, que en la landing no existe porque cada
   categoría es un <h3> suelto seguido de su lista. */
.amenities-lista {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.5rem;
  margin-bottom: 1.25rem;
}
@media (min-width: 540px) { .amenities-lista { grid-template-columns: repeat(3, 1fr); } }

.amenity-chip {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.6rem 0.75rem;
  background: white;
  border: 1.5px solid var(--color-borde);
  border-radius: 999px;
  font-size: 0.9rem;
  cursor: pointer;
  min-height: var(--tamano-tap);
  -webkit-tap-highlight-color: transparent;
  transition: border-color 0.12s, background 0.12s;
}
/* Oculta el input nativo (checkbox de amenities y radio de los chips-num):
   el estado visible lo da `:has(input:checked)`. Antes solo apuntaba a
   checkbox y el círculo del radio quedaba visible dentro del pill. */
.amenity-chip input[type="checkbox"],
.amenity-chip input[type="radio"] {
  position: absolute; opacity: 0; pointer-events: none;
}
/* Y como el input es transparente, su foco tampoco se ve: con Tab no había
   forma de saber en qué chip estabas, en los filtros de /buscar y en los
   pasos 1 y 4 del wizard (R11, 2026-09-12). El anillo va en el chip, igual
   que el de `.radio-cards` va en su label. */
.amenity-chip:has(input:focus-visible) {
  outline: 2px solid var(--color-primario);
  outline-offset: 2px;
}
.amenity-chip input:checked + .amenity-chip-texto::before {
  content: "✓ ";
  font-weight: 700;
  color: var(--color-primario);
}
.amenity-chip:has(input:checked) {
  border-color: var(--color-primario);
  background: #eef2f7;
  color: var(--color-primario-oscuro);
}
/* Acá vivían `.amenity-chip-conteo` y `.amenity-chip.chip-sin-stock` (tres
   reglas): el badge con el conteo de resultados por opción de filtro y el
   apagado del tipo sin stock, del quick win #2 del 2026-07-28. Los conteos
   salieron del panel de filtros y las reglas quedaron muertas; sobrevivían
   porque sus **únicas** menciones en todo el árbol eran cuatro
   `assert "amenity-chip-conteo" not in html` de `tests/public/`, y el guardián
   de clases muertas contaba cualquier mención como uso (`CGO-01`).
   Los asserts se quedan: siguen midiendo que el panel no volvió a emitirlos. */

/* ---- Contador de caracteres (paso 7) ---- */
.contador-caracteres {
  display: block;
  text-align: right;
  font-size: 0.78rem;
  color: var(--color-texto-muy-suave);
  margin-top: 0.25rem;
}
.contador-caracteres.al-limite { color: #c22b3b; }

/* ---- Caja centrada (pasos sin form: 5/6/8, email no verificado) ---- */
.caja-centrada {
  max-width: 520px;
  margin: 2rem auto;
  padding: 1.5rem;
  background: white;
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-grande);
  text-align: center;
}
.caja-centrada .form-inline { margin-top: 1rem; }

/* ---- Form inline (botón solo) ---- */
.form-inline { display: inline-block; }

/* ---- Grids cortos dentro de formularios ---- */
.form-grid-2 { display: grid; grid-template-columns: 2fr 1fr; gap: 0.5rem; align-items: end; }
.form-grid-2 .form-grupo { margin-bottom: 0.5rem; }
.form-grid-3 {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 0.5rem;
  align-items: end;
}
.form-grid-3 .form-grupo { margin-bottom: 0.5rem; }
@media (max-width: 720px) {
  .form-grid-3 { grid-template-columns: 1fr; }
}

/* Guía de fotos (P3.7.d): tips colapsables arriba del dropzone. */
.fotos-guia {
  background: var(--color-fondo-card);
  border-radius: var(--radio-chico);
  padding: 0.6rem 0.9rem;
  margin: 0 0 0.75rem;
  font-size: 0.9rem;
}
/* Medía 28 px de alto (R11, 2026-09-12), en la pantalla más táctil del
   wizard. El marcador nativo ya está oculto, así que el `flex` no pierde nada. */
.fotos-guia summary {
  cursor: pointer;
  font-weight: 600;
  padding: 0.2rem 0;
  list-style: none;
  user-select: none;
  display: flex;
  align-items: center;
  min-height: var(--tamano-tap);
}
.fotos-guia summary::-webkit-details-marker { display: none; }
.fotos-guia[open] summary { margin-bottom: 0.5rem; }
.fotos-guia ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.fotos-guia li {
  padding: 0.3rem 0;
  color: var(--color-texto-suave);
  border-top: 1px solid #eee;
}
.fotos-guia li:first-child { border-top: none; }
.fotos-guia strong { color: var(--color-texto); }

/* ---- Dropzone de fotos (paso 5) ---- */
.dropzone {
  border: 2px dashed var(--color-borde);
  border-radius: var(--radio-medio);
  padding: 1.25rem 1rem;
  text-align: center;
  margin-bottom: 1rem;
  transition: border-color 0.15s, background 0.15s;
}
.dropzone.activa { border-color: var(--color-primario); background: #eef2f7; }
.dropzone-hint { color: var(--color-texto-muy-suave); margin: 0.5rem 0 0; font-size: 0.9rem; }
.dropzone-progreso {
  margin: 0.5rem 0 0;
  font-size: 0.9rem;
  color: var(--color-texto-suave);
}
.dropzone-errores {
  margin: 0.75rem 0 0;
  padding: 0.5rem 0.75rem;
  background: #ffe6e6;
  color: #8b1f24;
  border-radius: var(--radio-chico);
  font-size: 0.85rem;
  text-align: left;
}
.dropzone-errores li { list-style: disc inside; }
.dropzone-avisos {
  margin: 0.75rem 0 0;
  padding: 0.5rem 0.75rem;
  background: #e8f4ec;
  color: #1f5f3a;
  border-radius: var(--radio-chico);
  font-size: 0.85rem;
  text-align: left;
}

/* ---- Grid de fotos (4:5) ---- */
.fotos-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.5rem;
  margin-bottom: 1rem;
}
@media (min-width: 540px) { .fotos-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 900px) { .fotos-grid { grid-template-columns: repeat(4, 1fr); } }

.foto-card {
  position: relative;
  aspect-ratio: 4/5;
  background: #efefef;
  border-radius: var(--radio-medio);
  overflow: hidden;
  box-shadow: var(--sombra-card);
  cursor: grab;
  touch-action: none; /* evitamos scroll al arrastrar en mobile */
  -webkit-user-select: none;
  user-select: none;
}
.foto-card img {
  width: 100%; height: 100%;
  object-fit: cover;
  pointer-events: none; /* eventos pasan al card */
}
.foto-card.es-portada { outline: 3px solid var(--color-primario); outline-offset: -3px; }
.foto-card.arrastrando {
  cursor: grabbing;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
  transform: scale(1.02);
  transition: transform 0.1s;
}
.foto-card-placeholder {
  background: repeating-linear-gradient(
    45deg, #f4f4f4, #f4f4f4 8px, #ececec 8px, #ececec 16px
  );
  border: 2px dashed var(--color-borde);
  box-shadow: none;
}

.foto-badge-portada {
  position: absolute;
  top: 0.4rem; left: 0.4rem;
  background: var(--color-primario);
  color: white;
  font-size: 0.7rem;
  font-weight: 600;
  padding: 0.2rem 0.5rem;
  border-radius: 999px;
}
/* 34×34 hasta el 2026-09-07. Son los tres controles de la pantalla más táctil
   del sitio —el editor de fotos del paso 5—, superpuestos sobre la foto y a
   milímetros del gesto de arrastre que reordena: errarle a «borrar» y darle a
   la card es exactamente el accidente que los 44 px evitan. En 2 columnas de
   390 px la card mide ~175, así que los dos de arriba dejan 87 px libres. */
.foto-btn-portada,
.foto-btn-borrar,
.foto-btn-editar {
  position: absolute;
  width: var(--tamano-tap); height: var(--tamano-tap);
  border: none;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.94);
  color: var(--color-texto);
  font-size: 1.1rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  backdrop-filter: blur(3px);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.foto-btn-portada { top: 0.4rem; left: 0.4rem; color: #b8860b; }
.foto-btn-borrar { top: 0.4rem; right: 0.4rem; color: #8b1f24; }
.foto-btn-editar { bottom: 0.4rem; right: 0.4rem; color: var(--color-primario); font-size: 16px; }
.foto-card.es-portada .foto-btn-portada { display: none; }
/* Abajo a la izquierda, que es la única esquina libre: arriba van la ★ y la ×,
   abajo a la derecha el ✎. No es un botón: no se toca, sólo avisa. El ancho
   máximo le deja lugar al ✎: en una tarjeta de 320 px de pantalla el texto
   entero se le metía abajo, y ahí se corta con «…». */
.foto-chip-luz {
  position: absolute;
  bottom: 0.4rem; left: 0.4rem;
  max-width: calc(100% - var(--tamano-tap) - 1.2rem);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  background: rgba(0, 0, 0, 0.6);
  color: white;
  font-size: 0.7rem;
  font-weight: 600;
  padding: 0.2rem 0.5rem;
  border-radius: 999px;
  pointer-events: none;
}

/* Editor de fotos (modal, P3.2). */
.editor-foto {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: 1rem;
}
.editor-foto[hidden] { display: none; }
.editor-foto-caja {
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  padding: 1rem;
  max-width: 700px;
  width: 100%;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  overflow: auto;
}
.editor-foto-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.editor-foto-header h2 { margin: 0; font-size: 1.2rem; }
/* `H-06`. Medía ~32×31 px por `padding` + `font-size`, sin declarar ninguna
   dimensión — en la pantalla más táctil del sitio, el editor de fotos del
   wizard. Declarar el tamaño además lo vuelve medible por el barrido de la
   hoja, que a los que no declaran nada no los ve. */
.editor-foto-cerrar {
  min-width: var(--tamano-tap);
  min-height: var(--tamano-tap);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  font-size: 1.5rem;
  cursor: pointer;
  line-height: 1;
  padding: 0.25rem 0.5rem;
}
.editor-foto-canvas-wrap {
  background: #000;
  border-radius: var(--radio-chico);
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 200px;
}
.editor-foto-canvas-wrap canvas {
  max-width: 100%;
  max-height: 50vh;
  display: block;
}
.editor-foto-controles {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.editor-foto-controles label {
  display: grid;
  grid-template-columns: 100px 1fr 60px;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.9rem;
}
.editor-foto-valor {
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--color-texto-suave);
}
.editor-foto-acciones {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.foto-drag-hint {
  position: absolute;
  bottom: 0.3rem; right: 0.4rem;
  font-size: 0.95rem;
  color: rgba(255, 255, 255, 0.85);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
  pointer-events: none;
}

/* ---- Reel (paso 6) ---- */
.reel-zona {
  border: 2px dashed var(--color-borde);
  border-radius: var(--radio-medio);
  padding: 0.75rem 1.25rem;
  text-align: center;
  margin-bottom: 1rem;
}
.reel-preview {
  width: 100%;
  max-width: 320px;
  aspect-ratio: 9/16;
  background: black;
  border-radius: var(--radio-medio);
  margin: 0 auto 0.75rem;
  display: block;
}
.reel-hint { color: var(--color-texto-muy-suave); font-size: 0.85rem; margin: 0.5rem 0 0; }
/* Mientras el servidor convierte el video (2026-09-15). Verde como los avisos
   buenos y no gris como el progreso: no es una espera trabada, es un paso que
   terminó bien y sigue solo. */
.reel-preparando {
  margin: 0.5rem 0 0.75rem;
  padding: 0.5rem 0.75rem;
  background: #e8f4ec;
  color: #1f5f3a;
  border-radius: var(--radio-chico);
  font-size: 0.9rem;
}
/* Lo que `reel_sin_imagen.js` pone en lugar del video cuando el navegador no
   lo puede decodificar (`R6-10`), en la ficha y en la vista previa del paso 6. */
.reel-sin-imagen {
  max-width: 320px;
  margin: 0 auto 0.75rem;
  padding: 1rem;
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-medio);
  background: var(--color-fondo-suave);
  color: var(--color-texto);
  font-size: 0.95rem;
  text-align: center;
}

/* ---- Preview del aviso (paso 8) ---- */
.caja-alerta {
  background: #fff8e0;
  border: 1px solid #f0c14b;
  border-radius: var(--radio-medio);
  padding: 1rem;
  margin-bottom: 1rem;
  color: #7a5800;
}
.caja-alerta ul { margin: 0.5rem 0 0.5rem 1.25rem; list-style: disc; }
.caja-alerta li { padding: 0.15rem 0; }

.preview-aviso {
  background: white;
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-grande);
  padding: 1rem;
  margin-bottom: 1rem;
}
.preview-fotos {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.4rem;
  margin-bottom: 1rem;
}
@media (min-width: 540px) { .preview-fotos { grid-template-columns: repeat(3, 1fr); } }
.preview-foto {
  position: relative;
  aspect-ratio: 4/5;
  background: #efefef;
  border-radius: var(--radio-medio);
  overflow: hidden;
}
.preview-foto img { width: 100%; height: 100%; object-fit: cover; }
.preview-foto.es-portada { outline: 3px solid var(--color-primario); outline-offset: -3px; }
.preview-foto-placeholder {
  display: flex; align-items: center; justify-content: center;
  color: var(--color-texto-muy-suave);
  aspect-ratio: 4/5;
}
.preview-reel video {
  width: 100%;
  max-width: 280px;
  aspect-ratio: 9/16;
  background: black;
  border-radius: var(--radio-medio);
  margin: 0 auto 1rem;
  display: block;
}

/* Enlaces externos (video / tour 360) en el paso 8 y en el detalle del admin.
   En la ficha pública usan `.ficha-enlaces-multimedia`, más abajo. */
.preview-enlaces-multimedia {
  margin: 0 0 1rem;
  padding: 0.75rem 1rem;
  background: var(--color-fondo-suave, #f6f7f8);
  border-radius: var(--radio-medio);
}
.preview-enlaces-multimedia h3 {
  font-size: 0.95rem;
  margin: 0 0 0.5rem;
}
.preview-enlaces-multimedia p { margin: 0.35rem 0; }
.preview-enlaces-multimedia a { word-break: break-all; }
.preview-enlaces-multimedia code {
  word-break: break-all;
  font-size: 0.85em;
}
/* Enlace guardado que NO es http(s): el moderador tiene que verlo marcado,
   porque la ficha no lo va a renderizar. */
.chip-alerta {
  display: inline-block;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  background: #ffe9a8;
  color: #6b5300;
  font-size: 0.8rem;
  font-weight: 600;
}

/* Plano en el paso 8 y en el detalle del admin. */
.preview-plano { margin: 0 0 1rem; }
.preview-plano h3 { font-size: 0.95rem; margin: 0 0 0.5rem; }
.preview-plano-img {
  max-width: 100%;
  height: auto;
  border-radius: var(--radio-medio);
  display: block;
}

.preview-encabezado { margin-bottom: 1rem; }
.preview-precio {
  font-size: 1.6rem;
  font-weight: 700;
  color: var(--color-precio);
  margin: 0 0 0.25rem;
}
.preview-titulo { font-size: 1.2rem; margin: 0.25rem 0; line-height: 1.3; }
.preview-ubicacion { color: var(--color-texto-suave); margin: 0.25rem 0; font-size: 0.95rem; }
.preview-tipo { color: var(--color-texto-muy-suave); font-size: 0.85rem; margin: 0; }

.preview-ficha h3,
.preview-amenities h3,
.preview-descripcion h3 {
  font-size: 0.9rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-texto-muy-suave);
  margin: 1.25rem 0 0.5rem;
}
.preview-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  font-size: 0.9rem;
}
.preview-meta li {
  background: #eef2f7;
  color: var(--color-primario-oscuro);
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
}
.preview-banderas {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.5rem;
  font-size: 0.8rem;
}
.preview-banderas li {
  background: var(--color-primario);
  color: white;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  font-weight: 600;
}
.preview-amenity-grupo { margin: 0.35rem 0; font-size: 0.9rem; }
.preview-descripcion p { color: var(--color-texto); white-space: pre-line; }

/* ---- Página de éxito post-envío ---- */
.check-grande {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 64px; height: 64px;
  border-radius: 999px;
  background: var(--color-primario);
  color: white;
  font-size: 2.2rem;
  margin: 0.5rem auto 1rem;
}
.aviso-resumen {
  background: #f5f8fb;
  border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio-medio);
  padding: 0.75rem 1rem;
  margin: 1rem 0;
  text-align: left;
}
.acciones-centradas {
  display: flex;
  gap: 0.5rem;
  justify-content: center;
  margin-top: 1rem;
  flex-wrap: wrap;
}

/* ---------- Panel admin ---------- */
/* El panel se piensa para escritorio —Martín y Marcelo procesan muchos avisos
   desde PC— pero NO tiene shell propia: `admin/base_admin.html` monta sobre
   `.cuenta-layout`, la misma del panel del publicador, que es mobile-first.
   `C-10`, 2026-09-08.

   Acá vivían `.admin-shell` y `.admin-sidebar` —la sidebar fija de la shell
   desktop-first original—, con sus reglas y sus dos `@media`, y **ninguna
   plantilla las usaba**. Se borraron en vez de reconstruir la shell: la
   decisión fue aceptar la realidad, no volver atrás. Si alguien las
   «restaura», lo que está restaurando es CSS muerto — el guardián
   `tests/transversales/test_el_panel_admin_no_tiene_shell_propia.py` lo dice y lo mide. */
/* `T6·C-05`: `.admin-header` estaba también 2400 líneas más abajo, en «Panel
   admin de publicidad», y ese bloque ganaba el `margin-bottom` (1rem, no
   1.5rem) y aportaba el flex. Acá está lo que se veía: el flex de allá con el
   margen de allá, que es el que gana por venir después. */
.admin-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}
.admin-header h1 { margin: 0 0 0.25rem; }

.admin-metricas {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.75rem;
}
@media (min-width: 900px) {
  .admin-metricas { grid-template-columns: repeat(4, 1fr); }
}
.admin-metrica {
  background: white;
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-medio);
  padding: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.admin-metrica-numero {
  font-size: 2rem;
  font-weight: 700;
  color: var(--color-primario);
  line-height: 1;
}
.admin-metrica-label {
  font-size: 0.85rem;
  color: var(--color-texto-suave);
}

/* === Dashboard admin estratégico (Fase A — 2026-06-22) =================
   Bloques con título + grilla de cards (operativas, financieras,
   actividad). Variante visual de `.admin-metrica` con más respiración y
   semántica clara de destacado/peligro/ingresos. */
.admin-bloque {
  margin: 1.75rem 0;
}
.admin-bloque-titulo {
  font-size: 0.85rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-texto-suave);
  margin: 0 0 0.75rem;
}

.admin-cards-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.85rem;
}
@media (min-width: 720px) {
  .admin-cards-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 1100px) {
  .admin-cards-grid { grid-template-columns: repeat(4, 1fr); }
}

/* Fase C: gráficos temporales, top anunciantes, export CSV. */
.admin-graficos-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.85rem;
}
@media (min-width: 720px) {
  .admin-graficos-grid { grid-template-columns: repeat(2, 1fr); }
}
.admin-grafico-card {
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  padding: 0.9rem 1rem 0.6rem;
}
.admin-grafico-card > header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.5rem;
  margin-bottom: 0.4rem;
}
.admin-grafico-card h3 {
  margin: 0;
  font-size: 0.95rem;
  color: var(--color-texto);
}
.admin-grafico {
  width: 100%;
}
.admin-grafico-svg {
  width: 100%;
  height: auto;
  display: block;
}
.admin-grafico-eje {
  display: flex;
  justify-content: space-between;
  font-size: 0.7rem;
  color: var(--color-texto-muy-suave);
  padding: 0.2rem 0.3rem 0;
}

.admin-export-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.5rem;
}
@media (min-width: 720px) {
  .admin-export-grid { grid-template-columns: repeat(2, 1fr); }
}
.admin-export-link {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.7rem 0.9rem;
  background: var(--color-fondo-card);
  border-radius: var(--radio-medio);
  border: 1px solid var(--color-borde-suave);
  color: var(--color-texto);
  text-decoration: none;
}
.admin-export-link:hover {
  background: #f5f8fb;
  border-color: var(--color-primario);
}
.admin-export-link strong { color: var(--color-primario); }

.admin-card {
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  padding: 1.1rem 1.2rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-height: 110px;
}
.admin-card-numero {
  font-size: 2rem;
  font-weight: 700;
  color: var(--color-primario);
  line-height: 1;
}
.admin-card-label {
  font-size: 0.9rem;
  color: var(--color-texto);
  font-weight: 500;
}
.admin-card-sub {
  font-size: 0.82rem;
  color: var(--color-texto-suave);
  margin-top: auto;
}
.admin-card-cta { margin: auto 0 0; }

/* Variantes */
.admin-card-destacada {
  border-left: 4px solid var(--color-acento);
  background: #fff8f3;
}
.admin-card-destacada .admin-card-numero { color: var(--color-acento); }

.admin-card-peligro {
  border-left: 4px solid #c0392b;
  background: #fff5f3;
}
.admin-card-peligro .admin-card-numero { color: #c0392b; }

/* Card de ingresos: fondo azul (identidad de finanzas) pero mismo tamaño
   que el resto de los cards del bloque (decisión Martín 2026-07-13 —
   antes ocupaba span 2 con font-size reducido, ahora va a la par). */
.admin-card-ingresos {
  background: linear-gradient(135deg, var(--color-primario) 0%, var(--color-primario-claro) 100%);
  color: #fff;
}
.admin-card-ingresos .admin-card-numero { color: #fff; }
.admin-card-ingresos .admin-card-label { color: rgba(255, 255, 255, 0.92); }
.admin-card-ingresos .admin-card-sub { color: rgba(255, 255, 255, 0.78); }

/* Pill de variación mes a mes (verde si subió, rojo si bajó) */
.admin-variacion {
  display: inline-block;
  padding: 0.05rem 0.45rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 700;
  margin-left: 0.35rem;
}
.admin-variacion-up {
  background: rgba(46, 204, 113, 0.18);
  color: #6fd398;
}
.admin-variacion-down {
  background: rgba(231, 76, 60, 0.20);
  color: #ff9b8c;
}

/* === Sub-nav interno del módulo Finanzas (Fase B — 2026-06-22) ========= */
/* Estilo más sutil que `.admin-tabs` principal — se usa dentro del panel
   admin para alternar entre sub-secciones (Gastos | Recordatorios). */
.admin-tabs--subnav {
  margin: 0 0 1rem;
}

/* === Recordatorios admin (Fase B) ====================================== */
.recordatorios-lista {
  list-style: none;
  padding: 0;
  margin: 0 0 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
}
.recordatorio-item {
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  padding: 1rem 1.2rem;
  border-left: 4px solid var(--color-borde);
}
.recordatorio-cabecera {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 0.75rem;
  margin: 0 0 0.4rem;
}
.recordatorio-titulo {
  margin: 0;
  font-size: 1.05rem;
  color: var(--color-primario);
}
.recordatorio-fecha {
  margin: 0 0 0.5rem;
  font-size: 0.9rem;
  color: var(--color-texto-suave);
}
.recordatorio-descripcion {
  margin: 0 0 0.6rem;
  font-size: 0.92rem;
}
.recordatorio-acciones {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

/* Estados visuales por urgencia */
.recordatorio-urgente { border-left-color: #d68900; background: #fffaf0; }
.recordatorio-vencido { border-left-color: #c0392b; background: #fff5f3; }
.recordatorio-vencido .recordatorio-titulo { color: #c0392b; }
.recordatorio-completado {
  opacity: 0.65;
  border-left-color: #2ecc71;
}
.recordatorio-completado .recordatorio-titulo {
  text-decoration: line-through;
  color: var(--color-texto-suave);
}

/* Pills de categoría — colores discretos por tipo */
.pill-categoria-renovacion { background: #e0eaff; color: #2c4d99; }
.pill-categoria-pago       { background: #fff4d6; color: #8a5a00; }
.pill-categoria-tramite    { background: #e6f5ee; color: #1a7548; }
.pill-categoria-nota       { background: #eef2f7; color: var(--color-texto-suave); }

/* Alineación numérica en las tablas del panel (montos de gastos, avisos por
   barrio). Dos selectores a propósito: el suelto sirve fuera de la tabla, pero
   adentro pesa (0,1,0) y `.admin-tabla th, .admin-tabla td { text-align: left }`,
   con (0,1,1), le ganaba en todas las celdas (medido el 2026-09-12). El que
   gana ahí es el segundo. Lo vigila
   `test_la_clase_de_una_celda_no_pierde_contra_la_tabla.py`. */
.admin-tabla-num,
.admin-tabla .admin-tabla-num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* === Cards del dashboard: gastos y net (Fase B.3 — 2026-06-22) ========= */
.admin-card-gastos {
  background: #fff5f3;
  border-left: 4px solid #c0392b;
}
.admin-card-gastos .admin-card-numero { color: #c0392b; }

.admin-card-net {
  background: #e6f5ec;
  border-left: 4px solid #1a7548;
}
.admin-card-net .admin-card-numero { color: #1a7548; }

.admin-card-net-negativo {
  background: #fff5f3;
  border-left-color: #c0392b;
}
.admin-card-net-negativo .admin-card-numero { color: #c0392b; }

/* === Card expandida de vencimientos próximos en el dashboard ==========
   Lista compacta de hasta 6 recordatorios pendientes. Visible para admin
   y moderador (info operativa, no financiera sensible). */
.admin-vencimientos {
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  padding: 1rem 1.2rem;
  margin-top: 0.85rem;
}
.admin-vencimientos-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin: 0 0 0.6rem;
}
.admin-vencimientos-header h3 {
  margin: 0;
  font-size: 1rem;
  color: var(--color-primario);
}
.admin-vencimientos-todos {
  font-size: 0.85rem;
  color: var(--color-acento);
  font-weight: 600;
}
.admin-vencimientos-lista {
  list-style: none;
  padding: 0;
  margin: 0;
}
.admin-vencimiento {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--color-borde-suave);
}
.admin-vencimiento:last-child { border-bottom: 0; }
.admin-vencimiento-info {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.admin-vencimiento-categoria {
  font-size: 0.78rem;
  color: var(--color-texto-suave);
  text-transform: capitalize;
}
.admin-vencimiento-fecha {
  font-size: 0.88rem;
  color: var(--color-texto-suave);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.admin-vencimiento-urgente .admin-vencimiento-fecha {
  color: #8a5a00;
  font-weight: 600;
}
.admin-vencimiento-vencido {
  background: #fff5f3;
  margin: 0 -1.2rem;
  padding-left: 1.2rem;
  padding-right: 1.2rem;
}
.admin-vencimiento-vencido .admin-vencimiento-fecha {
  color: #c0392b;
  font-weight: 700;
}

/* ---- Botones chicos (panel admin) ---- */
.btn-chico {
  padding: 0.35rem 0.75rem;
  font-size: 0.85rem;
  border-radius: var(--radio-chico);
}
/* El piso de 44 px de `.btn` se saca sólo con mouse: en la PC el panel conserva
   su densidad. Acá decía `min-height: auto` sin condición, y con el dedo el
   botón medía 33,6 px —el «Crear cuenta» de los dos carteles del favorito, en
   el celular, y los del panel en una tablet—. Lo encontró la verificación de
   DeepSeek (`A3`) y se midió en Edge el 2026-09-13. */
@media (hover: hover) and (pointer: fine) {
  .btn-chico { min-height: auto; }
}

/* ---- Tabla del panel ----
   `C-05`: esto estaba DOS VECES —acá y 2000 líneas más abajo— con valores en
   conflicto: `white` vs `--color-fondo-card`, borde vs sombra, `--radio-medio`
   vs `--radio-grande`, y padding, font-size y hover distintos. La cascada
   mezclaba las dos y ganaba la de abajo en todo lo que se pisaba, así que lo
   que se veía no era ninguno de los dos bloques enteros. Tan asumido estaba
   que el comentario de `anunciante/pagos_historial.html:40` hablaba de «las
   dos definiciones» como de un dato conocido.

   Este bloque es UNO y conserva lo que se venía viendo: el que ganaba en cada
   propiedad pisada, más el borde que sólo declaraba el de arriba y la sombra
   que sólo declaraba el de abajo. Dos unificaciones deliberadas y visualmente
   nulas: el filete de las filas pasa de `#eee` al token
   `--color-borde-suave` (#f0f0f0), y los `th` fuera de `thead` —que quedaban
   en .8rem/.04em— toman la misma medida que los de adentro. */
.admin-tabla {
  width: 100%;
  border-collapse: collapse;
  margin: 1rem 0;
  background: var(--color-fondo-card);
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  overflow: hidden;
}
.admin-tabla th,
.admin-tabla td {
  text-align: left;
  padding: 0.6rem 0.85rem;
  border-bottom: 1px solid var(--color-borde-suave);
  vertical-align: top;
  font-size: 0.92rem;
}
.admin-tabla th {
  font-weight: 600;
  color: var(--color-texto-suave);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.admin-tabla thead th { background: #f5f5f5; }
.admin-tabla tbody tr:hover { background: #fafafa; }
.admin-tabla tbody tr:last-child td { border-bottom: none; }

/* Celda de texto libre dentro de una tabla admin (el mensaje de un lead
   comercial). Sin el tope, un párrafo largo se lleva el ancho de la tabla y
   deja las otras columnas en una letra por línea. */
.admin-lead-mensaje {
  max-width: 34ch;
  white-space: pre-line;
  overflow-wrap: anywhere;
}

/* El segundo selector, por lo mismo que `.admin-tabla-num`: puesta en una
   celda, la clase suelta perdía el `font-size` contra `.admin-tabla td` y la
   IP de Contactos y el origen de Consultas salían en 0.92rem (2026-09-12). */
.texto-chico,
.admin-tabla .texto-chico { font-size: 0.78rem; }

/* `.texto-secundario` es la clase más usada del proyecto —222 veces en 84
   plantillas— y hasta el 2026-09-07 **no tenía regla propia**: la única que la
   nombraba era `.softwall-banner-inner .texto-secundario`, que la pinta blanca
   adentro del banner oscuro. O sea que en las otras 221 el párrafo salía del
   color del cuerpo y no se distinguía en nada del texto principal. Es el mismo
   defecto que `H-02`, con la diferencia de que acá la clase sí figuraba en la
   hoja y por eso ningún barrido la marcaba como muerta.
   La regla del banner tiene más especificidad (0,2,0), así que sigue ganando
   donde tiene que ganar; medido, es el único contenedor oscuro que la usa. */
.texto-secundario { color: var(--color-texto-suave); }

/* Fila de botones al pie de un formulario. Cuatro plantillas la escriben y
   tampoco tenía regla: los botones quedaban pegados al texto de arriba y
   separados sólo por el espacio en blanco del HTML. */
.form-acciones {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin-top: 1.25rem;
}

.admin-paginacion {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  margin: 1.5rem 0;
  font-size: 0.9rem;
  color: var(--color-texto-suave);
}

/* Pendientes de moderación (Admin A): filtros + lista con preview + semáforo.

   `T6·C-05`: `.admin-filtros` y `.admin-filtro` estaban también en «Admin:
   gestión de usuarios», 2700 líneas abajo. Ese segundo `.admin-filtros`
   creía no tener fondo ni padding —no los declaraba— pero los heredaba de
   acá, y a cambio le ganaba el `margin`; o sea que ninguna de las dos
   pantallas se veía como decía su bloque. Lo que queda es lo que se ve: el
   fondo/padding/radio de acá con el `margin` de allá. El segundo
   `.admin-filtro` no aportaba una sola declaración nueva. */
.admin-filtros {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: end;
  background: var(--color-fondo-card);
  padding: 0.9rem 1rem;
  border-radius: var(--radio-medio);
  margin: 1rem 0 1.5rem;
}
.admin-filtro {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  font-size: 0.85rem;
  color: var(--color-texto-suave);
}
.admin-filtro > span { font-weight: 600; }
.admin-filtro select,
.admin-filtro input[type="search"] {
  padding: 0.35rem 0.55rem;
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-chico);
  font-size: 0.9rem;
  background: white;
  min-height: 36px;
}
.admin-filtro-busqueda { flex: 1 1 220px; min-width: 180px; }
.admin-filtros-acciones {
  display: flex;
  gap: 0.4rem;
  align-items: center;
}
.admin-pendientes-empty {
  background: var(--color-fondo-card);
  padding: 2rem 1rem;
  text-align: center;
  border-radius: var(--radio-medio);
}

.admin-pendientes-lista {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 0.6rem;
}
.admin-pendiente {
  position: relative;
  display: grid;
  grid-template-columns: 96px 1fr auto;
  gap: 0.9rem;
  padding: 0.65rem;
  background: white;
  border: 1px solid var(--color-borde-suave);
  border-left: 4px solid var(--color-borde);
  border-radius: var(--radio-medio);
  align-items: center;
}
.admin-pendiente:hover { background: #fafcff; }
.admin-pendiente--verde    { border-left-color: #21a86b; }
.admin-pendiente--amarillo { border-left-color: #d0a100; }
.admin-pendiente--rojo     { border-left-color: #c0392b; }

.admin-pendiente-link {
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
}
.admin-pendiente-cuerpo,
.admin-pendiente-accion { position: relative; z-index: 2; pointer-events: none; }
.admin-pendiente-accion .btn { pointer-events: auto; }

.admin-pendiente-foto {
  position: relative;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border-radius: var(--radio-chico);
  background: #eef1f5;
}
.admin-pendiente-foto img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.admin-pendiente-foto-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  color: var(--color-texto-muy-suave);
  font-size: 0.75rem;
}
.admin-pendiente-encabezado {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  align-items: center;
  margin-bottom: 0.15rem;
}
.admin-pendiente-id {
  font-family: monospace;
  font-size: 0.8rem;
  color: var(--color-texto-muy-suave);
}
.admin-pendiente-semaforo {
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.05rem 0.5rem;
  border-radius: 999px;
}
.admin-pendiente-semaforo--verde    { background: #d8f3e5; color: #14603d; }
.admin-pendiente-semaforo--amarillo { background: #fbecb8; color: #6b5300; }
.admin-pendiente-semaforo--rojo     { background: #f8d5d0; color: #7a1e17; }
.admin-pendiente-badge {
  font-size: 0.72rem;
  padding: 0.05rem 0.5rem;
  border-radius: 999px;
  font-weight: 600;
}
.admin-pendiente-badge--nuevo { background: var(--color-primario); color: white; }
.admin-pendiente-badge--lock  { background: #fbecb8; color: #6b5300; }

/* Admin B: banner de lock activo en el detalle. */
.admin-lock-alerta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  background: #fff8e1;
  border-left: 4px solid #d0a100;
  padding: 0.75rem 1rem;
  border-radius: var(--radio-chico);
  margin: 0 0 1rem;
  flex-wrap: wrap;
}
.admin-lock-alerta-info { flex: 1 1 260px; }
.admin-lock-alerta strong { color: #6b5300; }
.admin-pendiente-titulo {
  margin: 0;
  font-size: 1rem;
  line-height: 1.25;
  color: var(--color-texto);
}
.admin-pendiente-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  font-size: 0.85rem;
  color: var(--color-texto-suave);
  margin-top: 0.2rem;
}
.admin-pendiente-publicador {
  font-size: 0.85rem;
  margin-top: 0.2rem;
}

/* La cuenta del publicador no está activa (`partials/admin_estado_cuenta.html`).
   Tiene que ganarle visualmente a todo lo demás de la fila: el moderador está
   barriendo una cola y esto es lo único que puede invalidar la aprobación. */
.admin-cuenta-alerta {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  margin: 0.45rem 0 0;
  padding: 0.45rem 0.6rem;
  background: #ffe6e6;
  border-left: 3px solid #c22b3b;
  border-radius: var(--radio-chico);
  font-size: 0.85rem;
  line-height: 1.4;
  color: #8b1f24;
}
.admin-cuenta-alerta strong { color: #6d1319; }
.admin-cuenta-alerta-icono {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.15rem;
  height: 1.15rem;
  border-radius: 999px;
  background: #c22b3b;
  color: #fff;
  font-weight: 700;
  font-size: 0.78rem;
  line-height: 1;
}

/* Mobile: apilar en vez de columnas. La foto queda arriba a la izquierda. */
@media (max-width: 599px) {
  .admin-pendiente {
    grid-template-columns: 72px 1fr;
    grid-template-areas:
      "foto cuerpo"
      "foto accion";
  }
  .admin-pendiente-foto { grid-area: foto; }
  .admin-pendiente-cuerpo { grid-area: cuerpo; }
  .admin-pendiente-accion { grid-area: accion; justify-self: end; }
}

/* ---- Detalle del aviso en el panel ---- */
.admin-detalle {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
}
@media (min-width: 1100px) {
  .admin-detalle { grid-template-columns: 1fr 320px; }
}
.admin-detalle-principal { min-width: 0; }
.admin-detalle-lateral { display: flex; flex-direction: column; gap: 1rem; }

/* Card del panel de detalle de aviso (lateral: publicador, wa link, etc.).
   Se llamaba `.admin-card` pero pisaba el fondo azul del `.admin-card-ingresos`
   del dashboard (misma clase, mismo archivo, gana la última). Renombrada a
   `.admin-detalle-card` el 2026-07-12. */
.admin-detalle-card {
  background: white;
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-medio);
  padding: 1rem;
}
.admin-detalle-card h3 {
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-texto-muy-suave);
  margin: 0 0 0.5rem;
}
.admin-detalle-card .btn { margin-top: 0.4rem; }

.admin-historial { display: flex; flex-direction: column; gap: 0.6rem; }
.admin-historial li {
  padding: 0.4rem 0.6rem;
  background: #f9fdfb;
  border-left: 3px solid var(--color-borde);
  border-radius: var(--radio-chico);
}
.admin-historial li p { margin: 0.25rem 0 0; }

/* `T6·C-05`: `.btn-peligro` y su `:hover` estaban también en «Admin: gestión
   de usuarios», 2500 líneas abajo, con OTRO rojo. Ganaba el de allá, así que
   el botón nunca fue `#c22b3b`: es `#c0392b`, sin borde y con el padding y el
   `font: inherit` de allá —que además anulan a `.btn-chico` donde conviven—.
   El `text-decoration: none` del hover es lo único que sobrevivía de acá. */
.btn-peligro {
  background: #c0392b;
  color: #fff;
  border: 0;
  border-radius: 6px;
  padding: 0.55rem 1rem;
  font: inherit;
  cursor: pointer;
}
.btn-peligro:hover { background: #962d22; text-decoration: none; }
.btn-peligro:disabled,
.btn-primario:disabled,
.btn-secundario:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}
/* El formulario ya salió y espera la respuesta (`js/envio_unico.js`): el botón
   no se deshabilita —un `disabled` no viaja en el form—, se nota y frena el
   segundo envío. */
form[data-enviando] [type="submit"] {
  opacity: 0.6;
  cursor: progress;
}

.admin-form-accion {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--color-borde-suave);
}
.admin-form-accion:first-of-type { border-top: none; padding-top: 0; }
.admin-form-accion textarea { resize: vertical; min-height: 60px; }

.btn-whatsapp {
  background: var(--color-whatsapp-solido);
  color: white;
  border-color: var(--color-whatsapp-solido);
  margin-top: 0.5rem;
}
.btn-whatsapp:hover { background: var(--color-whatsapp-solido-hover); text-decoration: none; }
.btn-whatsapp::before { content: "💬 "; }

/* ---------- Ficha pública del aviso (Fase 1 #7) ---------- */
/* Mobile-first puro: una columna, fotos al inicio, sticky CTA al pie (se suma
   en 7.3). Desktop: layout en columnas. */
.ficha {
  max-width: 980px;
  margin: 0 auto;
  padding-bottom: 5rem;
}
/* Galería: mobile = swipe horizontal con scroll-snap nativo;
   desktop = grilla 3 columnas (portada grande + 2 chicas). */
.ficha-galeria-wrap { position: relative; margin-bottom: 1rem; }

.ficha-galeria {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  gap: 0.25rem;
  margin: 0 -1rem;
  padding: 0;
  list-style: none;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.ficha-galeria::-webkit-scrollbar { display: none; }

.ficha-foto {
  flex: 0 0 100%;
  scroll-snap-align: start;
  position: relative;
  background: #efefef;
  aspect-ratio: 4/5;
  overflow: hidden;
}
.ficha-foto-btn {
  display: block;
  width: 100%; height: 100%;
  padding: 0; border: none;
  background: transparent;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ficha-foto-btn img { width: 100%; height: 100%; object-fit: cover; display: block; }

@media (min-width: 768px) {
  .ficha-galeria {
    display: grid;
    grid-template-columns: 2fr 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    gap: 0.4rem;
    margin: 0 0 1.5rem;
    overflow: visible;
    scroll-snap-type: none;
  }
  .ficha-foto {
    flex: initial;
    border-radius: var(--radio-medio);
    aspect-ratio: 1/1;
  }
  .ficha-foto:first-child {
    grid-row: span 2;
    aspect-ratio: 4/5;
  }
  /* Solo mostramos hasta 5 fotos en desktop (portada + 4); el resto se ven con
     el lightbox. */
  .ficha-foto:nth-child(n+6) { display: none; }
}

/* Indicadores tipo dots (mobile). */
.ficha-galeria-dots {
  display: flex;
  justify-content: center;
  gap: 0.35rem;
  padding: 0.5rem 0;
}
.ficha-galeria-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--color-borde);
  transition: background 0.18s, transform 0.18s;
}
.ficha-galeria-dot.activo {
  background: var(--color-primario);
  transform: scale(1.25);
}

/* Lightbox */
.lightbox {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.92);
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  /* El deslizamiento lo resuelve `ficha_galeria.js` con pointerdown/pointerup,
     y con `touch-action: auto` el navegador se queda con el gesto horizontal:
     manda `pointercancel` y el `pointerup` no llega nunca. Medido con toques
     de verdad (R3-22): sin esto la foto no cambia; con esto, sí. `pinch-zoom`
     deja el zoom de dos dedos sobre la foto, que el JS no toca a propósito. */
  touch-action: pan-y pinch-zoom;
}
.lightbox[hidden] { display: none; }
.lightbox-img {
  max-width: 95vw;
  max-height: 85vh;
  object-fit: contain;
  display: block;
}
.lightbox-cerrar,
.lightbox-flecha {
  position: absolute;
  background: rgba(255, 255, 255, 0.12);
  color: white;
  border: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.lightbox-cerrar {
  top: 1rem; right: 1rem;
  width: 44px; height: 44px;
  font-size: 1.5rem;
}
.lightbox-flecha {
  top: 50%;
  transform: translateY(-50%);
  width: 48px; height: 48px;
  font-size: 2rem;
  line-height: 1;
}
.lightbox-flecha-prev { left: 0.75rem; }
.lightbox-flecha-next { right: 0.75rem; }
.lightbox-cerrar:hover,
.lightbox-flecha:hover { background: rgba(255, 255, 255, 0.22); }
.lightbox-contador {
  position: absolute;
  bottom: 1rem;
  left: 0; right: 0;
  text-align: center;
  color: rgba(255, 255, 255, 0.85);
  font-size: 0.9rem;
  margin: 0;
}

.ficha-reel {
  margin-bottom: 1.5rem;
}
.ficha-reel video {
  width: 100%;
  max-width: 320px;
  aspect-ratio: 9/16;
  background: black;
  border-radius: var(--radio-medio);
  display: block;
  margin: 0 auto;
}

/* Video externo y tour 360. Botones que abren en pestaña nueva — no embebemos
   el reproductor para no abrirle la CSP a terceros ni filtrarles la IP del
   visitante en cada ficha. Full-width en mobile, lado a lado desde 480px. */
.ficha-enlaces-multimedia {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 1.5rem;
}
.ficha-enlace-multimedia {
  flex: 1 1 100%;
  text-align: center;
}
@media (min-width: 480px) {
  .ficha-enlace-multimedia { flex: 1 1 auto; }
}

/* Paso 6 — las dos vías del video, lado a lado en desktop y apiladas en
   mobile, con un "o" en el medio que deja claro que son alternativas y no dos
   cosas que haya que completar. */
.video-opciones {
  display: grid;
  gap: 1rem;
  margin-top: 0.5rem;
}
.video-opcion h3 {
  font-size: 0.95rem;
  margin: 0 0 0.5rem;
}
.video-separador {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  color: var(--color-texto-muy-suave);
  font-size: 0.9rem;
}
.video-separador::before,
.video-separador::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--color-borde);
}
@media (min-width: 720px) {
  .video-opciones {
    grid-template-columns: 1fr auto 1fr;
    align-items: start;
    gap: 1.25rem;
  }
  /* En desktop el separador se pone vertical, entre las dos columnas. */
  .video-separador { flex-direction: column; align-self: stretch; }
  .video-separador::before,
  .video-separador::after { width: 1px; height: auto; flex: 1; }
}

/* Campo del link + botón "Pegar" al lado: en el celular, el long-press para
   pegar es un menú diminuto que mucha gente no encuentra. */
.video-url-fila {
  display: flex;
  gap: 0.5rem;
  align-items: stretch;
}
.video-url-fila .form-input { flex: 1; min-width: 0; }
.video-url-fila .btn { flex: 0 0 auto; }
.video-url-aviso {
  margin: 0.4rem 0 0;
  font-size: 0.9rem;
  color: #8a5a00;
}
.video-url-aviso.es-ok { color: #14532d; }

/* Barra de subida del video. Aparece sólo mientras sube. */
.reel-barra {
  width: 100%;
  height: 8px;
  margin: 0.5rem 0 0.25rem;
  border-radius: 999px;
  overflow: hidden;
}

/* Bandeja "material cambiado" del admin + su aviso en la cola de pendientes. */
.admin-aviso-material {
  margin: 0.5rem 0 0;
  padding: 0.6rem 0.85rem;
  border-radius: var(--radio-medio);
  background: #fff4d6;
  color: #6b5300;
}
.admin-lista-material { list-style: none; margin: 0; padding: 0; }
.admin-material-item {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem 0;
  border-bottom: 1px solid var(--color-borde);
}
.admin-material-datos { flex: 1 1 260px; }
.admin-material-datos p { margin: 0.2rem 0 0; }
.admin-material-acciones {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}

/* Aviso "seguís publicado" en los pasos 5 y 6 cuando el dueño entra a
   cambiar material de un aviso que ya está en la calle. Verde, no amarillo:
   es una tranquilidad, no una advertencia. */
.aviso-edicion-directa {
  margin: 0 0 0.75rem;
  padding: 0.6rem 0.85rem;
  border-radius: var(--radio-medio);
  background: #e7f5ec;
  color: #14532d;
  font-size: 0.95rem;
}

/* Video externo con carga diferida. La fachada es un <button> del ancho de la
   columna con la portada del aviso adentro; al tocarlo el JS la reemplaza por
   el iframe, que ocupa exactamente el mismo espacio (16:9) para que no salte
   el layout. */
.ficha-video {
  margin-bottom: 1.5rem;
}
.ficha-video-fachada,
.ficha-video-marco {
  width: 100%;
  aspect-ratio: 16/9;
  border: 0;
  border-radius: var(--radio-medio);
  display: block;
}
/* El Short de YouTube, en el marco del reel: 9:16 y del mismo ancho que
   `.ficha-reel video` (2026-09-15). */
.ficha-video--vertical .ficha-video-fachada,
.ficha-video--vertical .ficha-video-marco {
  aspect-ratio: 9/16;
  max-width: 320px;
  margin: 0 auto;
}
.ficha-video-fachada {
  position: relative;
  padding: 0;
  overflow: hidden;
  cursor: pointer;
  background: #000;
}
.ficha-video-fachada img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.75;
}
.ficha-video-icono {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.65);
  color: #fff;
  font-size: 1.6rem;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-left: 4px; /* el glifo ▶ tira a la izquierda */
}
.ficha-video-pie {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 0.5rem 0.75rem;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.75));
  color: #fff;
  font-size: 0.85rem;
  text-align: left;
}

/* Plano en la ficha pública. La imagen va a ancho completo y clickeable
   (abre el archivo original, que suele tener más resolución de la que entra
   en la columna). */
.ficha-plano { margin-bottom: 1.5rem; }
.ficha-plano h2 { font-size: 1.1rem; margin: 0 0 0.5rem; }

/* Las hojas del plano. Es un `<ol>` por la semántica —son hojas numeradas— y
   por eso hay que apagarle la lista: sin esto salía con viñeta y sangría, como
   un índice y no como una imagen.

   Una hoja ocupa el ancho entero; dos o más se reparten en dos columnas cuando
   hay lugar. Un plano comercial casi siempre es una sola. */
.ficha-plano-hojas {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
}
.ficha-plano-hojas > li { min-width: 0; }

/* El botón hereda `.ficha-foto-btn`, que está dimensionado para la galería
   (`height: 100%` dentro de una celda con `aspect-ratio`). Acá no hay celda:
   el alto lo pone la imagen, que es apaisada y no se recorta — recortar un
   plano es cortarle ambientes. */
.ficha-plano-hojas .ficha-foto-btn { height: auto; }

.ficha-plano-img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--radio-medio);
  /* Un plano es líneas negras sobre blanco. Sin borde y sin fondo se derrama
     en la página, que también es blanca, y no se ve dónde empieza la hoja. */
  border: 1px solid var(--color-borde);
  background: #fff;
}

@media (min-width: 700px) {
  .ficha-plano-hojas { grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); }
}

/* Paso 6 "Video y planos": tres bloques apilados con aire entre ellos, para
   que en mobile se lean como tres cosas distintas y no como un formulario
   largo. El aire se ajustó el 2026-09-13 (decisión de Martín): el paso medía
   1,72 pantallas en 375×812 contra el «~1,5» del checklist, y con esto —más la
   frase repetida que se fue de la plantilla— mide 1,47. La línea entre
   bloques sigue separándolos. Lo que hereda de reglas globales se acota con
   `.wizard-multimedia`, para no tocar los otros siete pasos. */
.multimedia-bloque {
  margin-bottom: 1rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--color-borde);
}
.multimedia-bloque > p.texto-secundario { margin: 0.5rem 0; }
.wizard-multimedia .wizard-header .texto-secundario { margin: 0.5rem 0; }
.wizard-multimedia .form-grupo { margin-bottom: 0.5rem; }
.multimedia-bloque:last-of-type { border-bottom: 0; }
.multimedia-bloque h2 {
  font-size: 1.1rem;
  margin: 0 0 0.25rem;
}
.plano-zona { margin-top: 0.75rem; }
.plano-preview {
  max-width: 100%;
  height: auto;
  border-radius: var(--radio-medio);
  display: block;
  margin-bottom: 0.75rem;
}
.plano-cargado { margin: 0 0 0.75rem; }

.ficha-encabezado { margin-bottom: 1.5rem; }
.ficha-precio {
  font-size: 1.75rem;
  font-weight: 700;
  color: var(--color-precio);
  margin: 0 0 0.5rem;
  line-height: 1.1;
}
.ficha-expensas {
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--color-texto-suave);
  margin-left: 0.5rem;
}
.ficha-titulo { font-size: 1.4rem; margin: 0 0 0.5rem; line-height: 1.25; }
@media (min-width: 768px) {
  .ficha-precio { font-size: 2.2rem; }
  .ficha-titulo { font-size: 1.8rem; }
}
.ficha-ubicacion {
  color: var(--color-texto-suave);
  margin: 0 0 0.25rem;
  font-size: 1rem;
}
/* Eyebrow del tipo, arriba del precio — mismo tratamiento que en la card del
   listado, así la ficha y el resultado se leen igual. */
.ficha-tipo {
  color: var(--color-texto-muy-suave);
  margin: 0 0 0.3rem;
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* Volver a los resultados: primera línea de la ficha, antes de la galería. */
.ficha-volver { margin: 0 0 0.5rem; }
.volver-link {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-height: var(--tamano-tap);
  font-size: 0.9rem;
  color: var(--color-texto-suave);
  text-decoration: none;
}
.volver-link:hover { color: var(--color-primario); text-decoration: underline; }
.volver-flecha { font-size: 1.15rem; line-height: 1; }

/* Descripción recortada: el clamp lo pone `ficha_descripcion.js` sólo si el
   texto no entra (ver el comentario del JS).

   La landing del proyecto comparte la regla desde `AF-01-25` (2026-09-08): su
   descripción salía entera —hasta 8000 caracteres, el tope del form— arriba del
   mapa y del contacto, que es el mismo defecto que la ficha del aviso resolvió
   el 2026-08-05. Se comparte el selector en vez de copiar el clamp: dos reglas
   para lo mismo es el camino por el que una de las dos se queda vieja. */
.ficha-descripcion p.esta-recortada,
.emprendimiento-descripcion p.esta-recortada {
  display: -webkit-box;
  -webkit-line-clamp: 8;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* El párrafo del proyecto necesita el `pre-line` que la ficha ya tenía: sin él,
   los saltos de línea que escribió el desarrollista se pierden y el recorte
   corta un bloque de texto corrido. */
.emprendimiento-descripcion p { white-space: pre-line; }
.ficha-descripcion-mas { margin-top: 0.5rem; }
.ficha-descripcion-mas[hidden] { display: none; }
.ficha-favorito-aviso { margin: 0.1rem 0 0; }
/* El `a` global va sin subrayado, y adentro de esta oración «creás tu cuenta»
   se distinguía del texto sólo por el color —2,17:1 contra el gris de
   alrededor, y el mínimo es 3:1— (Lighthouse, 2026-09-13; WCAG 1.4.1). */
.ficha-favorito-aviso a { text-decoration: underline; }

/* El CTA fijo al pie ya repite el botón de contacto en mobile. Invierte el
   mobile-first: el estado BASE es visible (escritorio) — K10-05. */
@media (max-width: 767px) {
  .ficha-contacto-duplicado { display: none; }
}

.ficha > section { margin-bottom: 1.75rem; }
.ficha h2 {
  font-size: 0.95rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-texto-muy-suave);
  margin: 0 0 0.75rem;
}
.ficha-descripcion p {
  margin: 0;
  white-space: pre-line;
  font-size: 1rem;
  line-height: 1.6;
}
.ficha-publicador {
  background: #f5f8fb;
  border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio-medio);
  padding: 1rem;
}
.ficha-publicador h2 { margin-top: 0; }
.ficha-publicador p { margin: 0; }

.ficha-acciones {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 1rem;
}
@media (min-width: 768px) {
  .ficha-acciones { flex-direction: row; flex-wrap: wrap; align-items: center; }
  .ficha-acciones .btn { flex: 1; }
  /* Las aclaraciones ("pedimos cuenta para cuidar el teléfono de quien
     publica", "los favoritos se guardan en este dispositivo") son texto, no
     acciones: se llevan su propio renglón en vez de meterse entre los botones
     y aplastarlos a tres palabras por línea. */
  .ficha-acciones > p { flex: 0 0 100%; margin: 0; }
}

/* ---------- Búsqueda / listado público (Fase 1 #8) ---------- */
/* La UI fina (sidebar desktop + bottom-sheet mobile) llega en 8.4. Por ahora,
   layout vertical simple: form arriba, resultados abajo. */
.buscar { max-width: var(--ancho-max); margin: 0 auto; }
.buscar-header { margin-bottom: 1rem; }
.buscar-header h1 { margin: 0 0 0.25rem; }

/* Aviso de búsqueda ampliada (híbrida): la palabra no matcheó dentro de los
   filtros y mostramos el conjunto estructural. Callout suave, no una alarma. */
.buscar-ampliada,
.buscar-interpretado {
  margin: 0.5rem 0 0;
  padding: 0.6rem 0.8rem;
  border-left: 3px solid var(--color-primario, #2563eb);
  background: var(--color-fondo-suave, #f1f5f9);
  border-radius: 6px;
  font-size: 0.9rem;
  line-height: 1.4;
}
/* El aviso de interpretación es "positivo" (entendimos), no una advertencia. */
.buscar-interpretado {
  border-left-color: var(--color-exito);
}

/* Estado "sin resultados" con salidas de recuperación (quick win #3, 2026-07-28). */
.buscar-sin-resultados {
  text-align: center;
  padding: 2.5rem 1rem;
}
.buscar-sin-resultados-titulo {
  font-size: 1.1rem;
  font-weight: 600;
  margin: 0 0 0.35rem;
}

.buscar-form {
  background: white;
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-medio);
  padding: 1rem;
  margin-bottom: 1.5rem;
}
.buscar-form .btn { margin-top: 0.5rem; }

.buscar-tipos {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

/* Chips táctiles de numéricos (dormitorios/cocheras/ambientes/baños/antigüedad,
   relevamiento 2026-07-25 #4). Mismo look que los de tipo, en fila. */
.buscar-chips-num {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
/* Los chips-radio de min son cortos: los apretamos un poco y centramos.
   El `min-width` es la contracara del `min-height` de `.amenity-chip`: la
   etiqueta es de UN carácter, así que sin él el chip queda de 35-43 px de
   ancho (medido a 375px el 2026-08-13, R4-12). Van pegados en fila con 6 px
   de separación, y errarle a "2" selecciona "1" o "3" sin decir nada: cambia
   el filtro y recarga la lista con otro resultado. */
.buscar-chips-num .amenity-chip.chip-radio {
  padding: 0.55rem 0.85rem;
  justify-content: center;
  min-width: var(--tamano-tap);
}
/* Chip suelto (apto crédito): que tome el ancho del contenido, no del bloque. */
.amenity-chip.amenity-chip-solo {
  display: inline-flex;
}

/* Rangos de precio sugeridos (estilo ML, 2026-07-31): atajos-link con contador.
   Reusan el look de .amenity-chip pero son <a> (sin input): el "activo" se
   marca a mano y el hover invita al toque. */
.buscar-rangos-precio {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.amenity-chip.chip-rango {
  text-decoration: none;
  color: inherit;
}
.amenity-chip.chip-rango:hover {
  border-color: var(--color-primario);
}
.amenity-chip.chip-rango.chip-rango-activo {
  border-color: var(--color-primario);
  background: #eef2f7;
  color: var(--color-primario-oscuro);
  font-weight: 600;
}

/* Aclaración chica bajo los m² (la superficie cambia de sentido según el tipo). */
.buscar-hint {
  font-size: 0.8rem;
  margin: -0.25rem 0 0;
}

/* "Más filtros": los secundarios plegados. El summary es tap-target completo. */
.buscar-mas {
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-medio);
  padding: 0.25rem 0.75rem;
  margin-bottom: 0.5rem;
}
.buscar-mas > summary {
  cursor: pointer;
  list-style: none;
  padding: 0.6rem 0;
  min-height: var(--tamano-tap);
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-weight: 600;
  color: var(--color-primario);
  -webkit-tap-highlight-color: transparent;
}
.buscar-mas > summary::-webkit-details-marker {
  display: none;
}
.buscar-mas > summary::after {
  content: "▾";
  margin-left: auto;
  transition: transform 0.15s;
}
.buscar-mas[open] > summary::after {
  transform: rotate(180deg);
}
.buscar-mas-cuerpo {
  padding-top: 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

/* Controles del mapa interactivo (search as I move + radio, 2026-07-28). */
.mapa-controles {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
  margin-bottom: 0.6rem;
  padding: 0.5rem 0.75rem;
  background: var(--color-fondo-suave, #f5f7fa);
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-medio);
  font-size: 0.9rem;
}
.mapa-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  cursor: pointer;
  min-height: var(--tamano-tap);
}
.mapa-radio {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.mapa-radio-btn {
  padding: 0.4rem 0.75rem;
}
.mapa-radio-ctrl {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}
.mapa-radio-valor {
  font-variant-numeric: tabular-nums;
  min-width: 3.2em;
}
.mapa-count {
  margin-left: auto;
  color: var(--color-texto-suave, #4b5563);
  font-variant-numeric: tabular-nums;
}

.buscar-paginacion {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  margin: 2rem 0 3rem;
}
.buscar-paginacion-info {
  color: var(--color-texto-suave);
  font-size: 0.9rem;
}

.buscar-vacio {
  text-align: center;
  padding: 2.5rem 1rem;
  background: white;
  border: 1px dashed var(--color-borde);
  border-radius: var(--radio-medio);
}

/* Barra de ordenamiento sobre el grid de resultados. */
.buscar-orden-bar {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 0.75rem;
}
.buscar-orden-form {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.buscar-orden-form .form-label {
  margin: 0;
  font-size: 0.85rem;
  color: var(--color-texto-suave);
  white-space: nowrap;
}
.buscar-orden-form .form-input {
  min-height: var(--tamano-tap); /* `H-07`: estaba en 38px */
  padding: 0.35rem 0.65rem;
  font-size: 0.9rem;
  width: auto;
}

/* Input de búsqueda libre — un poco más grande que los demás campos. */
.buscar-q {
  font-size: 1.05rem;
  padding: 0.85rem 1rem;
}

/* Búsqueda por voz (F2, 2026-08-03): botón 🎙 a la derecha del input. El wrap
   ciñe input+botón para anclar el mic al input (no al form-grupo, que también
   tiene el label arriba) y de paso deja el typeahead al ancho del input. El
   botón viene `hidden` del server; buscar_voz.js lo revela si hay soporte. */
.buscar-voz-wrap {
  display: block;
  position: relative;
}
.buscar-q.buscar-voz-activo {
  padding-right: 3rem; /* lugar para el mic: el texto no corre por debajo */
}
/* En la home el input tiene una regla más específica
   (`.buscador-home-form input[type="search"]`) que pisaría el padding de arriba;
   subimos la especificidad para reservar el lugar del mic también acá. */
.buscador-home-form .buscar-q.buscar-voz-activo {
  padding-right: 3rem;
}
/* Evita que el ✕ nativo del type=search choque con el mic. */
.buscar-q.buscar-voz-activo::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
}
.buscar-voz {
  position: absolute;
  right: 0.5rem;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--color-primario);
  cursor: pointer;
  z-index: 41;
}
/* El círculo se ve de 2.1rem porque a 44px tapa el input, pero el ÁREA que se
   puede tocar tiene que ser la del proyecto (`--tamano-tap`). Se extiende con
   un pseudo-elemento transparente centrado: cambia dónde se puede tocar, no lo
   que se ve. Medido en 375px el 2026-08-12: era el único de los 8 controles de
   la home que no llegaba al mínimo, y está pegado adentro del campo de texto,
   así que errarle mete el cursor en el input (R4-09). */
.buscar-voz::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--tamano-tap);
  height: var(--tamano-tap);
  transform: translate(-50%, -50%);
}
.buscar-voz[hidden] {
  display: none;
}
.buscar-voz:hover {
  background: var(--color-fondo-suave);
}
.buscar-voz:focus-visible {
  outline: 2px solid var(--color-primario);
  outline-offset: 2px;
}
/* Estado "grabando": rojo con pulso. */
.buscar-voz.escuchando {
  color: #ffffff;
  background: #e5484d;
  animation: buscar-voz-pulso 1.3s ease-in-out infinite;
}
@keyframes buscar-voz-pulso {
  0%,
  100% {
    box-shadow: 0 0 0 0 rgba(229, 72, 77, 0.55);
  }
  50% {
    box-shadow: 0 0 0 7px rgba(229, 72, 77, 0);
  }
}
@media (prefers-reduced-motion: reduce) {
  .buscar-voz.escuchando {
    animation: none;
  }
}
/* El aviso del micrófono negado (`R4-26`): lo crea buscar_voz.js después del
   label del buscador. En la grilla de la home ocupa la fila entera y va al
   final, para no correr el botón «Buscar» de su lugar en escritorio. */
.buscar-voz-aviso {
  margin: 0.4rem 0 0;
  font-size: 0.9rem;
  color: var(--color-texto);
}
.buscador-home-form .buscar-voz-aviso {
  grid-column: 1 / -1;
  order: 1;
  margin: 0;
}

/* Card "¿Te aviso?" del listado (F3, 2026-08-03): ofrece crear una ALERTA con
   los filtros de la búsqueda actual. Reusa el sistema de búsquedas guardadas.
   Se muestra sobre los resultados cuando hay filtros; 3 estados (anónimo /
   logueado sin la alerta / logueado que ya la tiene). */
.buscar-alerta-card {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem 1rem;
  margin: 0.5rem 0 1rem;
  /* El padding derecho despeja la X, y la X mide 44 px desde `H-05`: con los
     2.4rem dimensionados para la de 24 el texto le pasaba por abajo. */
  padding: 0.9rem 3.2rem 0.9rem 1rem;
  background: var(--color-fondo-suave);
  border: 1px solid var(--color-borde);
  border-left: 4px solid var(--color-primario);
  border-radius: var(--radio-medio);
}
.buscar-alerta-card[hidden] {
  display: none;
}
.buscar-alerta-info {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  flex: 1 1 260px;
  min-width: 0;
}
.buscar-alerta-icono {
  font-size: 1.4rem;
  line-height: 1.2;
}
.buscar-alerta-titulo {
  margin: 0;
  font-weight: 600;
}
.buscar-alerta-sub {
  margin: 0.15rem 0 0;
  font-size: 0.9rem;
  color: #5b6b77;
}
.buscar-alerta-form {
  display: flex;
  flex: 1 1 100%;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem 1rem;
}
.buscar-alerta-acciones {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}
.buscar-alerta-frec .form-input {
  min-height: var(--tamano-tap);
}
/* «Ya tengo cuenta» medía 22 px de alto al lado de un botón de 44 (R11,
   2026-09-12): el área crece hasta la fila y el texto no se mueve. */
.buscar-alerta-secundario {
  color: var(--color-primario);
  text-decoration: underline;
  font-size: 0.9rem;
  display: inline-flex;
  align-items: center;
  min-height: var(--tamano-tap);
}
/* `H-05`. Medía ~24×24 px: no declaraba tamaño, así que salía del `padding` +
   `font-size` y ningún barrido estático de la hoja lo veía. Mismo patrón que
   `.softwall-cerrar` — dimensión declarada, que además lo hace medible.
   El color era `#8393a0`, 2,94:1 sobre el `--color-fondo-suave` de la card:
   por debajo del 4,5 de texto y del 3,0 de SC 1.4.11 que el propio repo cita
   en `K10-18`. `--color-texto-muy-suave` da 4,82:1 sobre ese fondo y es el
   gris que ya está medido contra los seis fondos del proyecto. */
.buscar-alerta-cerrar {
  position: absolute;
  top: 0.35rem;
  right: 0.35rem;
  width: var(--tamano-tap);
  height: var(--tamano-tap);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  background: transparent;
  font-size: 1.3rem;
  line-height: 1;
  color: var(--color-texto-muy-suave);
  cursor: pointer;
}
.buscar-alerta-cerrar:hover {
  color: var(--color-primario);
}
@media (min-width: 640px) {
  .buscar-alerta-form {
    flex-wrap: nowrap;
  }
}

/* Typeahead del buscador (Nivel C, 2026-07-12): dropdown que aparece
   pegado al input .buscar-q con sugerencias de ubicación. Mobile-first.
   Flota con position:absolute anclado al padre del input (clase
   `buscar-typeahead-anchor` que agrega el JS) — dentro del flujo
   deformaba la barra de búsqueda de la home (fix 2026-07-14). */
.buscar-typeahead-anchor {
  position: relative;
}
.buscar-typeahead {
  position: absolute;
  top: calc(100% + 0.35rem);
  left: 0;
  right: 0;
  background: white;
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-medio);
  box-shadow: 0 6px 20px rgba(12, 34, 51, 0.16);
  max-height: 320px;
  overflow-y: auto;
  z-index: 40;
}
.buscar-typeahead[hidden] { display: none; }
.buscar-typeahead-item {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas: "tipo nombre" "tipo contexto";
  column-gap: 0.6rem;
  align-items: baseline;
  width: 100%;
  padding: 0.7rem 0.9rem;
  text-align: left;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--color-borde-suave);
  cursor: pointer;
  color: inherit;
  font-family: inherit;
  min-height: var(--tamano-tap);
}
.buscar-typeahead-item:last-child { border-bottom: 0; }
/* El fondo suave solo daba 1,11:1 sobre el blanco del panel: quien baja con
   la flecha no veía cuál opción está seleccionada (`K10-18`, SC 1.4.11 pide
   3:1 para el indicador de estado). El filete lateral del primario es el
   indicador que cumple, independiente del fondo; el fondo suave acompaña. */
.buscar-typeahead-item.activo,
.buscar-typeahead-item:focus-visible {
  background: var(--color-fondo-hover, #f6f2ef);
  box-shadow: inset 4px 0 0 var(--color-primario);
  outline: none;
}
.buscar-typeahead-item .tt-tipo {
  grid-area: tipo;
  align-self: center;
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-acento, #ff5200);
  padding: 0.15rem 0.5rem;
  background: rgba(255, 82, 0, 0.08);
  border-radius: 999px;
  white-space: nowrap;
}
.buscar-typeahead-item .tt-nombre {
  grid-area: nombre;
  font-weight: 600;
  font-size: 0.98rem;
}
.buscar-typeahead-item .tt-contexto {
  grid-area: contexto;
  font-size: 0.82rem;
  color: var(--color-texto-suave);
}

/* ---------- Layout 2-col (sidebar desktop + main) ---------- */
.buscar-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}
/* La columna de resultados se puede achicar por debajo de su contenido: si no,
   la fila de chips del celular, que no se parte, la estiraba al ancho de todos
   los chips juntos y la página entera se desbordaba (2026-09-18). */
.buscar-main {
  min-width: 0;
}
@media (min-width: 900px) {
  .buscar-layout {
    grid-template-columns: 280px 1fr;
    gap: 2rem;
    align-items: start;
  }
}
.buscar-sidebar {
  position: sticky;
  top: 1rem;
  max-height: calc(100vh - 2rem);
  overflow-y: auto;
}
.buscar-sidebar .buscar-form {
  margin-bottom: 0;
}

/* ---------- Botón "Filtros (N)" mobile ---------- */
.buscar-acciones-mobile {
  margin: 0 0 0.75rem;
}
.buscar-acciones-mobile .btn { width: 100%; justify-content: center; }
.buscar-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 22px;
  padding: 0 0.4rem;
  margin-left: 0.5rem;
  background: var(--color-primario);
  color: white;
  font-size: 0.75rem;
  font-weight: 700;
  border-radius: 999px;
}

/* ---------- Chips de filtros activos ---------- */
/* En el celular, una sola fila que se desliza (2026-09-18): con una frase larga
   los chips llenaban la primera pantalla y no se veía ningún aviso. Desde tablet
   se vuelven a partir en renglones. */
.buscar-chips {
  display: flex;
  flex-wrap: nowrap;
  overflow-x: auto;
  gap: 0.4rem;
  margin: 0 0 1rem;
  padding-bottom: 0.25rem;
  scrollbar-width: thin;
}
.buscar-chips > .buscar-chip {
  flex: 0 0 auto;
  white-space: nowrap;
}
@media (min-width: 768px) {
  .buscar-chips {
    flex-wrap: wrap;
    overflow-x: visible;
  }
}
/* Cada chip es un link que SACA un filtro, y medían 34 px de alto (R11,
   2026-09-12): errarle a uno saca otro filtro sin avisar. La píldora crece a
   los 44 del resto de los controles, como pasó con los círculos de las redes
   del footer. */
.buscar-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-height: var(--tamano-tap);
  padding: 0.35rem 0.75rem;
  background: #eef2f7;
  color: var(--color-primario-oscuro);
  border: 1px solid var(--color-borde-suave);
  border-radius: 999px;
  font-size: 0.85rem;
  text-decoration: none;
  transition: background 0.12s;
}
.buscar-chip:hover {
  background: #e0f0e9;
  text-decoration: none;
}
.buscar-chip-label { color: var(--color-texto-suave); }
.buscar-chip-valor { font-weight: 600; }
.buscar-chip-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.08);
  font-size: 0.85rem;
  line-height: 1;
}
.buscar-chip-clear {
  background: white;
  color: var(--color-texto-suave);
  font-weight: 600;
}
.buscar-chip-clear:hover { background: #f5f5f5; }

/* ---------- Bottom-sheet mobile (dialog nativo) ---------- */
.buscar-dialog {
  margin: auto auto 0;             /* aterriza en el bottom */
  width: 100%;
  max-width: 600px;
  max-height: 90vh;
  border: none;
  border-radius: var(--radio-grande) var(--radio-grande) 0 0;
  padding: 0;
  background: white;
  overflow: hidden;
}
.buscar-dialog::backdrop {
  background: rgba(0, 0, 0, 0.55);
}
.buscar-dialog-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--color-borde);
  position: sticky;
  top: 0;
  background: white;
  z-index: 1;
}
.buscar-dialog-header h2 { margin: 0; font-size: 1.1rem; }
.buscar-dialog-cerrar {
  border: none;
  background: transparent;
  font-size: 1.8rem;
  line-height: 1;
  color: var(--color-texto-suave);
  cursor: pointer;
  width: var(--tamano-tap);
  height: var(--tamano-tap);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  -webkit-tap-highlight-color: transparent;
}
.buscar-dialog-cuerpo {
  padding: 1rem;
  overflow-y: auto;
  max-height: calc(90vh - 60px);
}
.buscar-dialog-cuerpo .buscar-form {
  border: none;
  padding: 0;
  margin: 0;
}
/* En el bottom-sheet, "Aplicar filtros" viaja pegado al pie: aunque el panel
   esté plegado hay scroll, y bajar hasta el fondo para aplicar era la mitad
   del problema de navegación en el celu (Martín 2026-08-05). */
.buscar-form-acciones {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.buscar-dialog-cuerpo .buscar-form-acciones {
  position: sticky;
  bottom: 0;
  margin: 0 -1rem -1rem;
  padding: 0.75rem 1rem calc(0.75rem + env(safe-area-inset-bottom));
  background: white;
  border-top: 1px solid var(--color-borde);
  box-shadow: 0 -6px 16px rgba(12, 34, 51, 0.08);
}

/* ---------- Mapas (ficha, /buscar?vista=mapa, wizard paso 2) ----------
   Leaflet exige que el contenedor tenga altura propia: sin esto el mapa se
   inicializa dentro de un div de 0px y queda invisible. Hueco de ORIGEN de
   Fase 4.3 (el commit del mapa nunca trajo CSS; los tests validan HTML, no
   píxeles) — lo detectó Martín el 2026-07-25 recién al mirar la vista mapa
   con datos reales. Alturas con viewport-cap para que el mapa no se coma la
   página en pantallas bajas. */
.buscar-mapa-canvas {
  height: min(62vh, 560px);
  min-height: 320px;
  border-radius: var(--radio-grande);
  border: 1px solid var(--color-borde);
}
.ficha-mapa-canvas {
  height: min(48vh, 420px);
  min-height: 260px;
  border-radius: var(--radio-grande);
  border: 1px solid var(--color-borde);
}
.wizard-mapa {
  height: min(48vh, 400px);
  min-height: 260px;
  border-radius: var(--radio-grande);
  border: 1px solid var(--color-borde);
}

/* Leaflet posiciona sus controles (zoom, atribución, popups) con z-index 800 a
   1000 — o sea, POR ENCIMA del navbar sticky (100) y del CTA fijo de la ficha
   (50): al scrollear, el "+/−" del mapa y la atribución flotaban sobre las
   barras del sitio (lo vio Martín el 2026-08-05). `isolation` + `z-index: 0`
   encierran esos 1000 en un stacking context propio del contenedor, así el
   mapa entero compite con el resto de la página como UN elemento de z-index 0
   y nunca puede taparle nada. `overflow: hidden` además recorta los tiles al
   border-radius. */
.buscar-mapa-canvas,
.ficha-mapa-canvas,
.wizard-mapa {
  position: relative;
  z-index: 0;
  isolation: isolate;
  overflow: hidden;
}

/* Toggle Grilla/Mapa de /buscar: `.btn-activo` marca la vista actual (el
   template lo emitía desde Fase 4.3 pero la clase nunca existió en CSS —
   los dos botones se veían idénticos). */
.buscar-vista-toggle {
  display: flex;
  gap: 0.5rem;
  margin: 0.75rem 0;
}
.btn-activo,
.btn-secundario.btn-activo {
  background: var(--color-primario);
  border-color: var(--color-primario);
  color: #ffffff;
}

/* ---------- Mis avisos (panel del publicador, Fase 1 #9) ---------- */
/* Chips de filtro de "Mis avisos" (panel único 2026-07-14, reemplazan a
   las tabs por estado). Fila con wrap — en mobile caen al renglón
   siguiente en vez de esconderse tras un scroll horizontal. */
.mis-avisos-chips {
  margin: 1rem 0 1.25rem;
}
.mis-avisos-chips ul {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0;
  margin: 0;
}
.mis-avisos-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.45rem 0.9rem;
  border: 1px solid var(--color-borde);
  border-radius: 999px;
  background: white;
  color: var(--color-texto-suave);
  text-decoration: none;
  white-space: nowrap;
  font-size: 0.92rem;
  min-height: var(--tamano-tap); /* `H-08`: estaba en 40px */
}
.mis-avisos-chip:hover {
  color: var(--color-texto);
  border-color: var(--color-primario);
  text-decoration: none;
}
.mis-avisos-chip-activo,
.mis-avisos-chip-activo:hover {
  background: var(--color-primario);
  border-color: var(--color-primario);
  color: white;
  font-weight: 600;
}
.mis-avisos-chip-conteo {
  background: #eef1ef;
  color: var(--color-texto-suave);
  border-radius: 999px;
  padding: 0.05rem 0.5rem;
  font-size: 0.8rem;
  font-weight: 600;
  min-width: 1.4rem;
  text-align: center;
}
.mis-avisos-chip-activo .mis-avisos-chip-conteo {
  background: rgba(255, 255, 255, 0.22);
  color: white;
}
/* "Rechazados" pide atención: conteo en rojo aunque el chip no esté activo. */
.mis-avisos-chip-alerta:not(.mis-avisos-chip-activo) .mis-avisos-chip-conteo {
  background: #fdeaea;
  color: #8b1f24;
}

/* `T6·C-05`: estaba definida otra vez 80 líneas abajo, en «Tarjeta de gestión
   del aviso», idéntica salvo el `gap` — y ganaba el de allá. El valor que
   queda (1rem) es el que se ve. */
.mis-avisos-lista {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 1rem;
}
.mis-avisos-item {
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  padding: 1rem;
}
.mis-avisos-item-titulo {
  font-size: 1.05rem;
  margin: 0 0 0.25rem;
}

.mis-avisos-empty {
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  padding: 2rem 1rem;
  text-align: center;
}
/* Onboarding global de Mis avisos: aparece cuando el usuario nunca publicó
   nada (sum(conteos) == 0). Distinto del empty-state per-tab — le habla al
   recién llegado, no a alguien que ya está dentro del flow. */
.mis-avisos-onboarding {
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  padding: 2rem 1.5rem;
  text-align: center;
}
.mis-avisos-onboarding h2 {
  margin: 0 0 0.75rem;
  font-size: 1.5rem;
  color: var(--color-primario);
}
.mis-avisos-onboarding-bajada {
  color: var(--color-texto-suave);
  margin: 0 auto 1.25rem;
  max-width: 32rem;
}
.mis-avisos-onboarding-pasos {
  list-style: none;
  padding: 0;
  margin: 0 auto 1.5rem;
  max-width: 28rem;
  text-align: left;
}
.mis-avisos-onboarding-pasos li {
  padding: 0.4rem 0;
  border-bottom: 1px solid #eee;
}
.mis-avisos-onboarding-pasos li:last-child { border-bottom: none; }
.mis-avisos-onboarding-pasos strong {
  color: var(--color-primario);
  margin-right: 0.4rem;
}
.mis-avisos-onboarding-cta { display: inline-block; }

.mis-avisos-empty-titulo {
  font-weight: 600;
  margin: 0 0 0.5rem;
}

.paginacion {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin: 1.5rem 0;
  flex-wrap: wrap;
}
.paginacion-info {
  color: var(--color-texto-suave);
  font-size: 0.9rem;
}
/* «← Anterior» y «Siguiente →» se escriben como `<a>` pelados en Mis avisos,
   los pagos del anunciante y los listados del admin, y medían 24 px de alto
   (R11, 2026-09-12). */
.paginacion a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tamano-tap);
}

/* ---------- Tarjeta de gestión del aviso (Mis avisos, 9.2) ---------- */
/* La segunda definición de `.mis-avisos-lista` vivía acá (`T6·C-05`): está
   fusionada con la primera, en «Mis avisos». */
.mis-avisos-lista > li { display: block; }

.card-aviso {
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  overflow: hidden;
  display: grid;
  grid-template-columns: 1fr;
}
@media (min-width: 600px) {
  .card-aviso { grid-template-columns: 200px 1fr; }
}

.card-aviso-foto {
  position: relative;
  aspect-ratio: 4/5;
  background: #f0f0f0;
  overflow: hidden;
}
@media (min-width: 600px) {
  .card-aviso-foto { aspect-ratio: auto; height: 100%; min-height: 200px; }
}
.card-aviso-foto img { width: 100%; height: 100%; object-fit: cover; display: block; }
.card-aviso-foto-placeholder {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  color: var(--color-texto-muy-suave);
  font-size: 0.9rem;
}

.card-aviso-cuerpo { padding: 1rem; display: flex; flex-direction: column; gap: 0.4rem; }

.card-aviso-encabezado {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.card-aviso-fecha { color: var(--color-texto-suave); font-size: 0.85rem; }

.card-aviso-titulo { font-size: 1.05rem; margin: 0; line-height: 1.3; }
.card-aviso-precio { font-weight: 700; color: var(--color-primario); font-size: 1.1rem; }
.card-aviso-ubicacion { color: var(--color-texto-suave); font-size: 0.9rem; }

/* Pill de estado: una por estado. Suaves, no compiten con el contenido.
   La FORMA la comparten las dos pastillas del sitio público; los COLORES no,
   porque los estados del aviso y los de la campaña son dos vocabularios
   distintos y parecidos — ver `AF-08-17`, más abajo.

   El `background` gris de acá es el de última instancia: si mañana entra un
   estado nuevo al modelo y nadie le escribe su regla, la pastilla se ve gris
   pero **se ve**. Sin `background` ni `color` una pastilla no se distingue de la
   de al lado, que es exactamente cómo `AF-08-17` pasó un mes desapercibido. */
.card-aviso-pill,
.card-campania-pill {
  display: inline-block;
  padding: 0.15rem 0.6rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  background: #eef1ef;
  color: var(--color-texto-suave);
}
/* Los seis estados del AVISO — `Inmueble.ESTADOS`, ni uno más ni uno menos. */
.card-aviso-pill--publicado            { background: #e6f7ed; color: #086b3d; }
.card-aviso-pill--pausado              { background: #fff3d6; color: #7a5800; }
.card-aviso-pill--pendiente_moderacion { background: #e7f3ff; color: #0a4d8c; }
.card-aviso-pill--borrador             { background: #eef1ef; color: #555; }
.card-aviso-pill--vendido              { background: #ead8fc; color: #4b1c8c; }
.card-aviso-pill--rechazado            { background: #ffe6e6; color: #8b1f24; }

.card-aviso-metricas {
  list-style: none;
  padding: 0;
  margin: 0.25rem 0 0;
  display: flex;
  gap: 1rem;
  color: var(--color-texto-suave);
  font-size: 0.9rem;
}
.card-aviso-metricas li {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
}
.card-aviso-metricas svg {
  width: 1em;
  height: 1em;
  flex-shrink: 0;
}

/* Pin del mapa: el dibujo sigue midiendo 25×41 —agrandarlo taparía el mapa—
   y lo que crece es el ÁREA que responde al dedo, con un pseudo-elemento
   transparente centrado de `--tamano-tap` (R4-14). Misma técnica que el botón
   de voz en R4-09. `background: none` porque `divIcon` trae fondo propio. */
.mapa-pin {
  position: relative;
  background: none;
  border: 0;
}
.mapa-pin::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--tamano-tap);
  height: var(--tamano-tap);
  transform: translate(-50%, -50%);
}

/* Capa "tocá para mover el mapa" (R4-13). Cubre el mapa entero para que el
   primer gesto no se pierda: mientras está, el dedo scrollea la página; al
   tocarla se va y el mapa toma los gestos. z-index por encima de los panes de
   Leaflet (400-700) y de sus controles (1000). */
.mapa-tocar-para-mover {
  position: absolute;
  inset: 0;
  z-index: 1001;
  width: 100%;
  border: 0;
  background: transparent;
  color: var(--color-texto);
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: 0.9rem;
  -webkit-tap-highlight-color: transparent;
}
/* La leyenda: legible sobre el mapa sin taparlo. El botón es transparente;
   lo único que se ve es esta pastilla. */
.mapa-tocar-para-mover-texto {
  background: rgba(255, 255, 255, 0.92);
  border: 1px solid var(--color-borde);
  border-radius: 999px;
  padding: 0.5rem 0.9rem;
  box-shadow: var(--sombra-card);
}

/* "¡Se publicó!" — la contracara en verde del bloque de motivo (R7-08). Misma
   forma para que se lea como el mismo tipo de mensaje del sistema sobre el
   aviso; el color es lo único que distingue la buena noticia de la mala. */
.card-aviso-novedad {
  background: #f2fbf5;
  border-left: 3px solid #2e9e5b;
  padding: 0.6rem 0.75rem;
  border-radius: var(--radio-chico);
  margin-top: 0.5rem;
  font-size: 0.95rem;
}
.card-aviso-novedad strong {
  display: block;
  font-size: 0.85rem;
  color: #1c6b3c;
  margin-bottom: 0.2rem;
}
.card-aviso-motivo {
  background: #fff5f5;
  border-left: 3px solid #c0392b;
  padding: 0.6rem 0.75rem;
  border-radius: var(--radio-chico);
  margin-top: 0.5rem;
}
.card-aviso-motivo strong { display: block; font-size: 0.85rem; color: #8b1f24; margin-bottom: 0.2rem; }
.card-aviso-motivo p { margin: 0; font-size: 0.95rem; }
.card-aviso-motivo-fecha { font-size: 0.8rem; color: var(--color-texto-muy-suave); }

/* Acciones: fila horizontal scrolleable en mobile, agrupada al final de la card. */
.card-aviso-acciones {
  margin-top: 0.75rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
/* La línea que explica por qué «Cambiar fotos» no baja el aviso (`H-19`). Vivía
   sólo en un `title=`, y el atributo `title` no aparece nunca en un celular —
   que es donde se usa este panel, mobile-first. */
.card-aviso-ayuda {
  margin: 0.5rem 0 0;
  font-size: 0.85rem;
  color: var(--color-texto-suave);
  line-height: 1.4;
}
.card-aviso-accion-form { margin: 0; }
.card-aviso-accion {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: white;
  color: var(--color-texto);
  border: 1px solid #d6d6d6;
  border-radius: var(--radio-chico);
  padding: 0.45rem 0.8rem;
  font-size: 0.9rem;
  text-decoration: none;
  cursor: pointer;
  min-height: 44px;
}
.card-aviso-accion:hover { background: #f5f5f5; text-decoration: none; }
.card-aviso-accion--primaria {
  background: var(--color-primario);
  color: white;
  border-color: var(--color-primario);
}
.card-aviso-accion--primaria:hover { background: var(--color-primario-oscuro); }
/* "Marcar vendido" — verde éxito. Es una acción de peso alto (cierra la
   operación, notifica a favoritos) y debe distinguirse a un vistazo de
   pausar/editar. Decisión Martín 2026-07-13. */
.card-aviso-accion--vendido {
  background: #1a7548;
  color: white;
  border-color: #1a7548;
}
.card-aviso-accion--vendido:hover { background: #14603a; }
.card-aviso-accion--peligro {
  color: #8b1f24;
  border-color: #f0c5c5;
  /* Lo separamos del resto del row para que no se mezcle con las acciones
     "normales" — visualmente queda anclado a la derecha. En wrap (mobile)
     simplemente cae al renglón siguiente, sigue pareciendo "el final". */
  margin-left: auto;
}
.card-aviso-accion--peligro:hover { background: #fff5f5; }

/* Banner "Vista previa" en ficha pública renderizada por el publicador
   (cuenta.vista_previa). Sticky arriba, color marca, no compite con el
   contenido del aviso pero deja claro que no es la URL pública. */
.ficha-vista-previa {
  background: var(--color-primario);
  color: white;
  padding: 0.75rem 1rem;
  border-radius: var(--radio-chico);
  margin: 0 0 1rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  align-items: center;
  justify-content: space-between;
  font-size: 0.95rem;
}
.ficha-vista-previa strong { font-weight: 600; }
.ficha-vista-previa-estado {
  display: inline-block;
  background: rgba(255,255,255,0.18);
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  font-size: 0.85rem;
  margin-left: 0.4rem;
}
/* Medía 23 px de alto, y es la única salida de la vista previa (R11, 2026-09-12). */
.ficha-vista-previa-volver {
  color: white;
  text-decoration: underline;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  min-height: var(--tamano-tap);
}

/* ---------- Banner publicitario (Fase 3.1) ---------- */
/* Decisión 2026-06-13: HaceteDueño manda visualmente; la publicidad acompaña.
   Slots con label "Publicidad" sutil, sin sombra agresiva, sin colores fuertes. */
.banner-slot {
  display: block;
  margin: 1.25rem 0;
  background: var(--color-fondo-card);
  border: 1px solid #e5e5e5;
  border-radius: var(--radio-grande);
  overflow: hidden;
  position: relative;
}
.banner-slot-label {
  position: absolute;
  top: 0.4rem;
  right: 0.6rem;
  font-size: 0.7rem;
  color: var(--color-texto-muy-suave);
  background: rgba(255, 255, 255, 0.85);
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  pointer-events: none;
}
.banner-slot-link {
  display: block;
  line-height: 0;
}
.banner-slot-link img {
  width: 100%;
  height: auto;
  display: block;
}

/* Zócalo horizontal intercalado en el feed de /buscar (2026-07-25): ocupa
   el ancho completo de la grilla — en PC corta cada 2 filas de 4 cards. El
   gap del grid ya separa; sin márgenes propios. */
.feed .banner-slot--feed-full {
  grid-column: 1 / -1;
  margin: 0;
}

/* Los pares del panel de búsqueda son simétricos (precio desde/hasta,
   ambientes/dormitorios, baños/m²). El `2fr 1fr` global de .form-grid-2
   está pensado para pares "campo + moneda" (wizard, admin) y acá dejaba el
   segundo input a la mitad de ancho (reporte de Martín 2026-07-25). */
.buscar-form .form-grid-2 {
  grid-template-columns: 1fr 1fr;
}

/* Subtipos contextuales del panel (2026-07-26): grupo indentado bajo el
   bloque de tipos, con título más discreto que un label normal. */
.grupo-subtipos {
  margin-left: 0.35rem;
  padding-left: 0.65rem;
  border-left: 2px solid var(--color-borde);
}
.grupo-subtipos-titulo {
  display: block;
  font-size: 0.8rem;
  color: var(--color-texto-suave);
}

/* ---------- Panel admin de publicidad (Fase 3.1.E) ---------- */

/* La segunda definición de `.admin-header` vivía acá (`T6·C-05`). Está
   fusionada con la primera, en el bloque del panel admin; el comentario de
   allá cuenta qué ganaba cada una. */

/* La segunda definición de `.admin-tabla` vivía acá (`C-05`). Está fusionada
   con la primera, en «Tabla del panel»; el comentario de allá cuenta qué ganaba
   cada una. No la repongas: dos bloques del mismo selector a 2000 líneas de
   distancia hacen que lo que se ve no sea ninguno de los dos. */

/* Tabs horizontales del panel admin (Inicio/Pendientes/Usuarios/Publicidad)
   y filtros de estado en sub-pantallas (ej: campañas por estado). Estilo
   "tab strip" con underline naranja para el activo que se traga la línea
   gris del contenedor — los inactivos quedan alineados sin saltos. */
.admin-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  border-bottom: 1px solid var(--color-borde-suave);
  margin: 0 0 1.5rem;
}
/* El piso de alto no cambia la densidad: el `padding` + la línea + el borde ya
   daban 43,64 px, un pelo debajo de la regla (R11, 2026-09-12). */
.admin-tabs a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tamano-tap);
  padding: 0.7rem 1.05rem;
  color: var(--color-texto-suave);
  text-decoration: none;
  border-bottom: 3px solid transparent;
  margin-bottom: -1px;
  font-size: 0.95rem;
  font-weight: 500;
  line-height: 1.2;
  white-space: nowrap;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.admin-tabs a:hover {
  color: var(--color-primario);
  border-bottom-color: var(--color-borde);
  text-decoration: none;
}
.admin-tabs a.activo {
  color: var(--color-primario);
  border-bottom-color: var(--color-acento);
  font-weight: 600;
}

.admin-datos {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.4rem 1rem;
  margin: 1rem 0;
  background: var(--color-fondo-card);
  padding: 1rem;
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
}
.admin-datos dt { color: var(--color-texto-suave); font-weight: 600; font-size: 0.88rem; }
.admin-datos dd { margin: 0; }

.admin-acciones-fila {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin: 1rem 0;
}
.admin-form-rechazo {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin: 0;
  min-width: 280px;
}
.admin-form-rechazo textarea { min-height: 60px; }

.admin-acciones-rapidas { list-style: none; padding: 0; }
.admin-acciones-rapidas li { margin: 0.3rem 0; }

.admin-estado-pill {
  display: inline-block;
  padding: 0.2rem 0.7rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  background: #eef1ef;
  color: var(--color-texto-suave);
  text-transform: capitalize;
}
/* ---------- Los ocho estados de CAMPAÑA, escritos una sola vez ---------- */
/* `AF-08-17` (control 2026-09-07). El vocabulario de la campaña
   —`Campania.ESTADOS_CAMPANIA`— **no es el del aviso**, y los dos se parecen lo
   suficiente como para que nadie note que no se tocan: `rechazada` contra
   `rechazado`, `pausada` contra `pausado`. La pastilla del anunciante emitía
   `card-aviso-pill--{{ estado }}` con el slug de la campaña, y las únicas seis
   reglas que existían eran las del aviso: **seis de los ocho estados salían sin
   `background` ni `color`**, o sea que una campaña rechazada se veía idéntica a
   una en curso.

   Por eso los estados de campaña tienen namespace propio —`admin-estado-pill`
   en el panel del moderador, `card-campania-pill` en el del anunciante— y el
   color va acá una sola vez: es **la misma campaña en dos pantallas**, y que
   cambie de color según quién la mire no tiene ningún sentido. Duplicar los
   ocho pares sería `C-14` un piso más abajo.

   Cruzado contra el modelo en las dos direcciones, y contra la clase que el
   macro emite de verdad, por
   `tests/transversales/test_la_pastilla_de_campania_tiene_una_regla_por_estado.py`. */
.admin-estado-pill--borrador,
.card-campania-pill--borrador             { background: #eef1ef; color: #555; }
.admin-estado-pill--pendiente_moderacion,
.card-campania-pill--pendiente_moderacion { background: #e7f3ff; color: #0a4d8c; }
.admin-estado-pill--aprobada,
.card-campania-pill--aprobada             { background: #e6f7ed; color: #086b3d; }
.admin-estado-pill--rechazada,
.card-campania-pill--rechazada            { background: #ffe6e6; color: #8b1f24; }
.admin-estado-pill--pagada,
.card-campania-pill--pagada               { background: #ddf5e8; color: #0a5c44; }
.admin-estado-pill--en_curso,
.card-campania-pill--en_curso             { background: #ddf5e8; color: #0a5c44; }
.admin-estado-pill--finalizada,
.card-campania-pill--finalizada           { background: #ead8fc; color: #4b1c8c; }
.admin-estado-pill--pausada,
.card-campania-pill--pausada              { background: #fff3d6; color: #7a5800; }

/* ---------- Panel anunciante self-service (Fase 3.2) ---------- */
/* Mobile-first como todo el sitio público (no es admin). */
.anunciante-shell {
  max-width: 720px;
  margin: 0 auto;
}

.anunciante-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
}
.anunciante-header h1 { margin: 0 0 0.2rem; }

.anunciante-form {
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  padding: 1.25rem;
  margin: 1rem 0 2rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.anunciante-empty {
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  padding: 2rem 1.25rem;
  text-align: center;
  margin: 1.5rem 0;
}

.anunciante-pagar {
  background: #f0faf6;
  border: 1px solid var(--color-primario-claro);
  border-radius: var(--radio-grande);
  padding: 1.25rem;
  margin: 1.5rem 0;
}
.anunciante-pagar .mis-avisos-empty-titulo { color: var(--color-primario-oscuro); }
.anunciante-pagar form { margin-top: 0.75rem; }

/* === "Mi cuenta": layout con nav lateral y panel de contenido ============ */

.cuenta-layout { margin: 1.5rem 0 2rem; }
.cuenta-header { margin-bottom: 1rem; }
.cuenta-header h1 { margin: 0 0 0.25rem; }

/* Breadcrumb arriba del título: link sutil para volver a la pantalla anterior.
   Sutil de color, no de tamaño: medía 17 px de alto en todas las pantallas del
   panel —del publicador, del anunciante y del admin— y es el único control
   arriba del título (R11, 2026-09-12). Las editoriales ya lo tenían en 44 por
   el macro `volver()` (`AF-01-11`); el panel lo escribe a mano. */
.cuenta-breadcrumb { margin: 0 0 0.4rem; font-size: 0.9rem; }
.cuenta-breadcrumb a {
  color: var(--color-texto-suave);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  min-height: var(--tamano-tap);
}
.cuenta-breadcrumb a:hover { color: var(--color-acento); text-decoration: underline; }

/* Header con título + CTA a la derecha (Mis avisos, Búsquedas guardadas). */
.cuenta-header-fila {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}
.cuenta-header-fila h1 { margin: 0; }

.cuenta-grid {
  display: grid;
  /* minmax(0, 1fr) y no 1fr: el min-width:auto de los grid items dejaba
     que una foto ancha infle el track más allá del viewport (overflow
     horizontal en mobile — detectado en Mis avisos 2026-07-09). */
  grid-template-columns: minmax(0, 1fr);
  gap: 1.25rem;
}
@media (min-width: 720px) {
  .cuenta-grid { grid-template-columns: 220px minmax(0, 1fr); }
}
.cuenta-grid > * { min-width: 0; }

/* Toggle del menú lateral en mobile: patrón checkbox+label (sin JS).
   El input está escondido; el <label> funciona como botón. El sibling
   selector `:checked ~ .cuenta-nav` muestra el nav. En desktop el toggle
   se oculta y el nav se muestra siempre como sidebar fijo. */
.cuenta-nav-wrap { margin: 0; }
.cuenta-nav-toggle-input { position: absolute; left: -9999px; }
.cuenta-nav-toggle {
  display: block;
  cursor: pointer;
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  padding: 0.85rem 1rem;
  font-weight: 600;
  color: var(--color-primario);
  user-select: none;
}
.cuenta-nav-toggle-input:checked ~ .cuenta-nav-toggle {
  border-radius: var(--radio-grande) var(--radio-grande) 0 0;
}
/* Mobile (default): nav colapsado, se muestra al activar el checkbox.

   `T6·C-05`: la caja de este mismo selector —fondo, radio, sombra, padding—
   estaba suelta DESPUÉS del `@media` de abajo. ⚠️ La fusión va acá y NO allá:
   el `@media (min-width: 720px)` de abajo devuelve el nav a `display: block`
   con la misma especificidad, así que un bloque fusionado puesto después del
   media le ganaría con este `display: none` y **el sidebar de escritorio
   desaparecería**. El `border-radius` no cambia nada: el media pinta el mismo
   valor y el `:checked ~` tiene más especificidad en las dos direcciones. */
.cuenta-nav {
  display: none;
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  padding: 0.5rem;
}
.cuenta-nav-toggle-input:checked ~ .cuenta-nav {
  display: block;
  border-radius: 0 0 var(--radio-grande) var(--radio-grande);
}

@media (min-width: 720px) {
  /* Desktop: nav siempre visible como sidebar fijo, toggle oculto. */
  .cuenta-nav-toggle { display: none; }
  .cuenta-nav,
  .cuenta-nav-toggle-input:checked ~ .cuenta-nav {
    display: block;
    border-radius: var(--radio-grande);
  }
}

/* La caja de `.cuenta-nav` vivía acá, como segunda definición del selector
   (`T6·C-05`). Está fusionada arriba, ANTES del `@media`; el comentario de
   allá explica por qué no puede volver a esta posición. */
.cuenta-nav ul { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.15rem; }
.cuenta-nav-item {
  display: block;
  padding: 0.7rem 0.85rem;
  color: var(--color-texto);
  text-decoration: none;
  border-radius: var(--radio-medio, 8px);
  min-height: 44px;
  line-height: 1.6;
}
.cuenta-nav-item:hover { background: #eef2f7; text-decoration: none; }
.cuenta-nav-item-activo { background: var(--color-primario); color: #fff; font-weight: 600; }
.cuenta-nav-item-activo:hover { background: var(--color-primario-claro); color: #fff; }
/* Item "Salir" al pie del menú lateral — separador visual + color sutil.
   Es un <button> dentro de un form GET (para el modal de confirmación),
   así que neutralizamos los estilos de botón del user agent. */
.cuenta-nav-salir { margin-top: 0.5rem; padding-top: 0.5rem; border-top: 1px solid var(--color-borde-suave); }
.cuenta-nav-item-salir { color: var(--color-texto-suave); }
.cuenta-nav-item-salir:hover { color: var(--color-acento); background: #fff5ef; }
button.cuenta-nav-item-salir {
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  font: inherit;
  cursor: pointer;
}

.cuenta-contenido {
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  padding: 1.25rem;
}
.cuenta-contenido h2 { margin-top: 0; }

.cuenta-readonly {
  background: #f5f8fb;
  border-radius: var(--radio-medio, 8px);
  padding: 0.75rem 0.9rem;
  margin: 0.25rem 0 1.25rem;
}
.cuenta-readonly-fila {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
}
.cuenta-readonly-nota {
  margin: 0.4rem 0 0;
  font-size: 0.88rem;
}

/* Pills de verificación: estado del email y del celular en /cuenta/perfil.
   Mismo patrón usable en cualquier otro lado donde haya que mostrar un
   estado verificado/pendiente. */
.pill-verificacion {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.6rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1.4;
}
.pill-verificacion--ok {
  background: #e6f5ec;
  color: #1a7a3e;
}
.pill-verificacion--pendiente {
  background: #fff5d6;
  color: #7a5b00;
}


/* Fieldset agrupador dentro de un form largo (Datos / Contacto / Actividad
   en /cuenta/perfil). Sin border nativo del browser para mantener el estilo
   limpio; usamos un titulito + separación vertical. */
.form-fieldset {
  border: 0;
  padding: 0;
  margin: 0 0 1.5rem;
}
.form-fieldset-titulo {
  display: block;
  font-size: 0.85rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-texto-suave);
  margin: 0 0 0.6rem;
  padding: 0;
}

/* Historial de eventos auth en /cuenta/seguridad. Lista discreta con
   etiqueta del tipo de evento + fecha + IP. El badge de color es por
   tipo (login_fail rojo suave para llamar la atención). */
.historial-eventos {
  list-style: none;
  padding: 0;
  margin: 0;
}
.historial-evento {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  padding: 0.65rem 0;
  border-bottom: 1px solid var(--color-borde-suave);
  font-size: 0.92rem;
}
.historial-evento:last-child { border-bottom: 0; }
.historial-evento-tipo {
  font-weight: 600;
  color: var(--color-primario);
}
.historial-evento--login_fail .historial-evento-tipo {
  color: #b53e1f;
}
.historial-evento-fecha {
  color: var(--color-texto-suave);
  font-variant-numeric: tabular-nums;
}
.historial-evento-ip {
  color: var(--color-texto-muy-suave);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.85rem;
}

/* Línea final del panel con info de la cuenta (miembro desde, etc.). */
.cuenta-meta-cuenta {
  margin: 1.5rem 0 0;
  padding-top: 1rem;
  border-top: 1px solid var(--color-borde-suave);
  font-size: 0.88rem;
  color: var(--color-texto-suave);
}

/* Línea de metadatos debajo de un campo del form (estado, ayuda, link). */
.form-grupo-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: -0.5rem 0 1rem;
  font-size: 0.88rem;
}
.form-grupo-meta-link {
  font-weight: 600;
}

.cuenta-form .form-grupo { margin-bottom: 1rem; }

/* === Identidad del usuario logueado en el navbar ========================= */

.navbar-usuario a {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.4rem 0.7rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
  line-height: 1.2;
}
.navbar-usuario a:hover { background: rgba(255, 255, 255, 0.16); text-decoration: none; }
.navbar-usuario-hola { opacity: 0.75; font-weight: 400; }
.navbar-usuario strong { font-weight: 600; }

/* ===========================================================
   Fase 5 marca M3 — Home rediseñada (banner + buscador +
   diferenciales + pasos + destacados). 2026-06-16.
   =========================================================== */

/* Accesibilidad: ocultar visualmente sin sacar del a11y tree. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* El «Hero ajustado (sin párrafo, sin CTA)» vivía acá: era la SEGUNDA
   definición de `.hero-con-imagen`, `.hero-con-imagen .hero-contenido` y
   `.hero-con-imagen h1` (`T6·C-05`). Está fusionado con el primero, arriba de
   todo, en «Hero con imagen de fondo»; el comentario de allá cuenta qué ganaba
   cada bloque. No lo repongas acá: `.hero` y su `@media` viven arriba y tienen
   la misma especificidad, así que el bloque fusionado tiene que quedar donde
   quedó. */

/* ---------- Banner GRATIS (franja naranja entre hero y buscador) ----------
   Mobile: 2 líneas (titular en strong + claim debajo, sin separador).
   Desktop: una línea con el ·. */
.banner-gratis {
  background: var(--color-acento-solido);
  color: #ffffff;
  text-align: center;
  padding: 0.85rem 1rem;
  margin: -0.5rem -1rem 0;
  border-radius: 0;
  font-size: 0.95rem;
}
.banner-gratis-texto {
  margin: 0;
  line-height: 1.35;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
}
.banner-gratis strong {
  font-weight: 700;
  letter-spacing: 0.02em;
}
.banner-gratis-sep { display: none; }
.banner-gratis-claim {
  opacity: 0.95;
  font-size: 0.92em;
}
@media (min-width: 768px) {
  .banner-gratis { font-size: 1.05rem; padding: 1rem; }
  .banner-gratis-texto {
    flex-direction: row;
    justify-content: center;
    gap: 0;
  }
  .banner-gratis-sep {
    display: inline;
    opacity: 0.6;
    margin: 0 0.6rem;
  }
  .banner-gratis-claim { font-size: 1em; }
}

/* ---------- Buscador home ---------- */
.buscador-home {
  background: #ffffff;
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-grande);
  padding: 1rem;
  margin-top: 1.25rem;
  margin-bottom: 2rem;
  box-shadow: var(--sombra-card);
}
.buscador-home-form {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.65rem;
}
.buscador-home-form select,
.buscador-home-form input[type="search"] {
  width: 100%;
  padding: 0.75rem 0.9rem;
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-medio);
  background: #ffffff;
  font-size: 1rem;
  min-height: var(--tamano-tap);
}
/* Custom chevron del select — la flecha nativa queda pegada al borde. */
.buscador-home-form select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  padding-right: 2.5rem;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230c2233' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat: no-repeat;
  background-position: right 0.85rem center;
  background-size: 16px;
}
.buscador-home-form select:focus,
.buscador-home-form input[type="search"]:focus {
  outline: none;
  border-color: var(--color-primario);
  box-shadow: 0 0 0 3px rgba(12, 34, 51, 0.12);
}
.buscador-home-btn {
  width: 100%;
}
@media (min-width: 768px) {
  .buscador-home-form {
    /* 240px: "¿Qué inmueble buscás?" necesita ~176px de texto + 54px de
       padding (chevron custom incluido) — con 220px se cortaba (fix
       2026-07-14). */
    grid-template-columns: 240px 1fr auto;
    align-items: stretch;
    gap: 1rem;
  }
  .buscador-home-btn { width: auto; padding-left: 2rem; padding-right: 2rem; }
}

/* ---------- Barra de 3 diferenciales ---------- */
.diferenciales {
  background: var(--color-fondo-suave);
  border-radius: var(--radio-grande);
  padding: 2rem 1rem;
  margin: 0 -1rem 0.5rem;
}
.diferenciales-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
  max-width: var(--ancho-max);
  margin: 0 auto;
  padding: 0 1rem;
}
.diferencial {
  text-align: center;
  padding: 0.5rem;
}
.diferencial-icono {
  width: 56px;
  height: 56px;
  margin: 0 auto 0.75rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: #ffffff;
  border-radius: 50%;
  color: var(--color-acento);
  box-shadow: 0 2px 8px rgba(12, 34, 51, 0.08);
}
.diferencial-icono svg { width: 28px; height: 28px; }
.diferencial h3 {
  color: var(--color-primario);
  font-size: 1.1rem;
  margin: 0 0 0.35rem;
  font-weight: 700;
}
.diferencial p {
  margin: 0;
  color: var(--color-texto-suave);
  font-size: 0.95rem;
  line-height: 1.5;
}
@media (min-width: 768px) {
  .diferenciales { padding: 2.5rem 2rem; }
  .diferenciales-grid { grid-template-columns: repeat(3, 1fr); gap: 2.5rem; }
}

/* ---------- En 3 pasos ---------- */
.pasos {
  padding: 1.5rem 0 1.5rem;
  text-align: center;
}
.pasos-titulo {
  color: var(--color-primario);
  font-size: 1.6rem;
  margin: 0 0 1.75rem;
}
.pasos-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2rem;
}
.paso { padding: 0.5rem; position: relative; }
.paso-numero {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--color-acento-solido);
  color: #ffffff;
  font-weight: 700;
  font-size: 1.4rem;
  margin: 0 auto 0.5rem;
}
.paso-icono {
  color: var(--color-petroleo);
  margin-bottom: 0.65rem;
}
.paso-icono svg { width: 36px; height: 36px; }
.paso h3 {
  color: var(--color-primario);
  font-size: 1.15rem;
  margin: 0 0 0.4rem;
  font-weight: 700;
}
.paso p {
  margin: 0;
  color: var(--color-texto-suave);
  font-size: 0.95rem;
  line-height: 1.5;
}
@media (min-width: 768px) {
  .pasos { padding: 3.5rem 0 2rem; }
  .pasos-titulo { font-size: 2rem; margin-bottom: 2.5rem; }
  .pasos-grid { grid-template-columns: repeat(3, 1fr); gap: 3rem; }
}

/* ---------- Propiedades destacadas ---------- */
.destacados { margin: 1rem 0 2rem; }
.destacados-titulo {
  color: var(--color-primario);
  font-size: 1.5rem;
  margin: 0 0 1.25rem;
}
/* Vidriera de 4 unidades: 2 cols mobile (cards más compactas), 4 cols desktop. */
.feed-destacados { grid-template-columns: repeat(2, 1fr); gap: 0.6rem; }
@media (min-width: 540px) { .feed-destacados { grid-template-columns: repeat(2, 1fr); gap: 0.75rem; } }
@media (min-width: 900px) { .feed-destacados { grid-template-columns: repeat(4, 1fr); } }
@media (min-width: 768px) {
  .destacados-titulo { font-size: 1.85rem; margin-bottom: 1.5rem; }
}

/* ---------- Zócalo comercial chico (≈3-4 cm de altura) ----------
   Desktop: la imagen mantiene su aspect ratio original (1200×220 se ve
   completa a ~220px de alto). Mobile, dos casos (decisión 2026-07-06):
   - Banner con creatividad mobile (`--con-mobile`): el <picture> sirve el
     MPU 600×500 y acá lo mostramos centrado a 300px (300×250 efectivo),
     como Zonaprop/Argenprop.
   - Banner solo con creatividad de PC: si dejáramos `height: auto`, la
     imagen ocuparía ~70px de alto — ilegible. Aplicamos `object-fit:
     cover` a 130px recortando los bordes laterales (fallback histórico).
     Anunciantes deberían centrar el CTA. */
.banner-slot--zocalo {
  margin: 0.5rem 0;
}
.banner-slot--zocalo .banner-slot-link img {
  width: 100%;
  height: auto;
  display: block;
}
@media (max-width: 640px) {
  .banner-slot--zocalo .banner-slot-link {
    line-height: 0;
  }
  .banner-slot--zocalo .banner-slot-link img {
    height: 130px;
    object-fit: cover;
    object-position: center;
  }
  /* El slot entero se achica a 300px y se centra — así el borde y el
     fondo de la card abrazan la imagen, sin aire a los costados. */
  .banner-slot--zocalo.banner-slot--con-mobile {
    width: min(300px, 100%);
    margin-left: auto;
    margin-right: auto;
  }
  .banner-slot--zocalo.banner-slot--con-mobile .banner-slot-link img {
    height: auto;
    object-fit: initial;
  }
}

/* ---------- Emprendimientos en la home (2026-09-09) ---------- */
/* Va con la MISMA forma que el zócalo comercial de arriba —misma caja, mismo
   radio, mismos márgenes, misma proporción de banda— porque ocupa ese lugar
   en la página (decisión de Martín al revisar la home el 2026-09-09). Sin el
   label «Publicidad»: es una sección propia, no un anuncio.

   Mobile-first: en celu es una tarjeta apilada; el layout de banda ancha
   entra recién en tablet. El `<a>` envuelve todo, así que el touch target es
   la banda entera y no hay un botón chico que buscar con el dedo. */
.home-emprendimientos {
  margin: 0.5rem 0;
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-grande);
  overflow: hidden;
}
.home-emprendimientos-link {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  padding: 1.25rem 1.15rem;
  /* El gradiente del zócalo demo: azul de marca → petróleo (los dos del
     manual), para que la banda se lea de la misma familia que el banner. */
  background: linear-gradient(135deg, var(--color-primario) 0%, var(--color-petroleo) 100%);
  color: #fff;
  text-decoration: none;
}
.home-emprendimientos-texto {
  display: block;
}
.home-emprendimientos-kicker {
  display: block;
  font-size: 0.72rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--color-cian);
  margin-bottom: 0.2rem;
}
.home-emprendimientos-titulo {
  display: block;
  font-size: 1.4rem;
  font-weight: 700;
  line-height: 1.15;
  margin-bottom: 0.35rem;
}
.home-emprendimientos-bajada {
  display: block;
  font-size: 0.92rem;
  line-height: 1.45;
  /* Blanco al 88%: baja el peso del párrafo frente al título sin perder el
     contraste sobre el azul (queda muy por encima del 4,5:1 de WCAG AA). */
  color: rgba(255, 255, 255, 0.88);
  max-width: 62ch;
}
.home-emprendimientos-cta {
  align-self: flex-start;
  /* Naranja de marca sólido: es el único de la paleta habilitado para llevar
     texto blanco encima (ver `--color-acento-solido` arriba). */
  background: var(--color-acento-solido);
  color: #fff;
  font-weight: 600;
  font-size: 0.95rem;
  padding: 0.7rem 1.2rem;
  border-radius: var(--radio-chico, 6px);
  /* 44px de alto mínimo: touch target táctil, como pide `CLAUDE.md`. */
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  white-space: nowrap;
}
.home-emprendimientos-link:hover .home-emprendimientos-cta,
.home-emprendimientos-link:focus-visible .home-emprendimientos-cta {
  background: var(--color-acento-solido-hover);
}
@media (min-width: 720px) {
  /* Banda ancha y baja, la proporción del zócalo de 1200×220. */
  .home-emprendimientos-link {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 2rem;
    padding: 1.6rem 2rem;
    /* La creatividad del zócalo es 1200×220 (ratio 5,45): a 1168 px de ancho
       —el contenedor de la home— el banner mide ~214 px de alto. Esta banda
       se para a la misma altura para que las dos se lean como una sola
       familia. `min-height` y no `aspect-ratio`: si el texto crece, crece la
       banda en vez de desbordarse. */
    min-height: 214px;
  }
  .home-emprendimientos-titulo {
    font-size: 1.75rem;
  }
  .home-emprendimientos-cta {
    align-self: center;
  }
}

/* ---------- Página /nosotros ---------- */
.nosotros {
  max-width: 880px;
  margin: 0.5rem auto 2rem;
}
.nosotros-foto {
  margin: 0 0 1.5rem;
  border-radius: var(--radio-grande);
  overflow: hidden;
  background: var(--color-fondo-suave);
}
.nosotros-foto img {
  width: 100%;
  height: auto;
  display: block;
  max-height: 360px;
  object-fit: cover;
}
.nosotros-titulo {
  color: var(--color-primario);
  font-size: 1.8rem;
  text-align: center;
  margin: 0 0 1.25rem;
}
.nosotros-cuerpo {
  max-width: 720px;
  margin: 0 auto;
  color: var(--color-texto);
  font-size: 1.05rem;
  line-height: 1.7;
  text-align: justify;
  hyphens: auto;
}
.nosotros-cuerpo p { margin: 0 0 1.1rem; }
.nosotros-cuerpo p:last-child { margin-bottom: 0; }
.nosotros-cuerpo strong { color: var(--color-primario); }
.nosotros-cuerpo a {
  color: var(--color-acento);
  font-weight: 600;
}
.nosotros-cuerpo a:hover { color: var(--color-acento-hover); }
@media (min-width: 768px) {
  .nosotros-titulo { font-size: 2.2rem; margin-bottom: 1.75rem; }
  .nosotros-foto img { max-height: 440px; }
}

/* ---------- Home: acercar el footer al último banner ----------
   La home termina con el banner_pie. El padding-bottom global del main (5rem)
   + margin-top del footer (3rem) la dejan demasiado lejos. En home reducimos
   ambos. Otras páginas (wizard, mis avisos, panel) mantienen su respiro. */
body.home main.contenedor { padding-bottom: 1rem; }
body.home .footer { margin-top: 1rem; }

/* ---------- Footer: hover con color oficial de cada red ----------
   Sobre-escribe el hover naranja genérico de `.footer-redes a:hover`.
   Necesita más especificidad que la regla genérica — por eso se anidan
   `.footer-redes a.<clase>`. */
.footer-redes a.footer-red--ig:hover {
  background: linear-gradient(45deg, #f09433 0%, #e6683c 25%, #dc2743 50%, #cc2366 75%, #bc1888 100%);
}
.footer-redes a.footer-red--fb:hover { background: #1877f2; }
.footer-redes a.footer-red--yt:hover { background: #ff0000; }

/* === Admin: gestión de usuarios ========================================== */

/* `.admin-filtros` y `.admin-filtro` estaban definidas otra vez acá
   (`T6·C-05`). Fusionadas con las de «Pendientes de moderación», que es donde
   está el comentario. Lo que sigue son las reglas de descendiente propias de
   esta pantalla, que sí son de acá. */
.admin-filtro label { font-size: 0.85rem; color: var(--color-texto-suave); }
.admin-filtro input,
.admin-filtro select {
  padding: 0.45rem 0.6rem;
  border: 1px solid #d0d4d9;
  border-radius: 6px;
  font: inherit;
  min-height: 36px;
}

.pill {
  display: inline-block;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
}
.pill-rol-usuario { background: #eef2f7; color: #34495e; }
.pill-rol-moderador { background: #fff4e0; color: #8a5a00; }
.pill-rol-admin { background: #ffe1e1; color: #a01515; }
.pill-ok { background: #e6f5ee; color: #1a7548; }
.pill-warn { background: #fff4e0; color: #8a5a00; }
.pill-suspendido { background: #ffe1e1; color: #a01515; }

.admin-usuario-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  margin: 1rem 0 1.5rem;
}
@media (min-width: 900px) { .admin-usuario-grid { grid-template-columns: 1fr 1fr; } }

.admin-usuario-card {
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  padding: 1.25rem;
}
.admin-usuario-card h2 { margin-top: 0; }

.admin-dl { display: grid; grid-template-columns: max-content 1fr; gap: 0.4rem 1rem; margin: 0; }
.admin-dl dt { font-weight: 600; color: var(--color-texto-suave); }
.admin-dl dd { margin: 0; }

/* Separador entre las distintas acciones admin (suspender / cambiar rol /
   eliminar). Más espacio que antes para que cada bloque se sienta como una
   acción propia. */
.admin-accion-form {
  border-top: 1px solid #eaecef;
  padding-top: 1.5rem;
  margin-top: 1.5rem;
}
.admin-accion-form:first-of-type { border-top: 0; padding-top: 0; margin-top: 0; }
.admin-accion-form h3 {
  margin: 0 0 0.5rem;
  font-size: 1.05rem;
  color: var(--color-primario);
}
.admin-accion-form label { display: block; margin: 0.5rem 0; font-size: 0.9rem; }

/* Fila horizontal de control + botón (select rol + botón "Cambiar"). Permite
   espacio decente entre los dos y wrap en mobile. */
.admin-accion-fila {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: center;
  margin-top: 0.6rem;
}
.admin-accion-fila select {
  flex: 1 1 auto;
  min-width: 160px;
  padding: 0.5rem 0.6rem;
  border: 1px solid #d0d4d9;
  border-radius: 6px;
  font: inherit;
  background: #fff;
}
.admin-accion-form textarea,
.admin-accion-form select {
  display: block;
  width: 100%;
  margin-top: 0.25rem;
  padding: 0.5rem;
  border: 1px solid #d0d4d9;
  border-radius: 6px;
  font: inherit;
}
.admin-accion-peligro { border-top-color: #ffd1d1; }
.admin-accion-peligro h3 { color: #a01515; }

/* La segunda definición de `.btn-peligro` y de `.btn-peligro:hover` vivía acá
   (`T6·C-05`). Están fusionadas con las primeras, en el bloque del panel de
   moderación; el comentario de allá cuenta qué ganaba cada una. */

/* Actividad del usuario (bloque D — 2026-07-02). Tres cards en fila con
   avisos por estado / engagement recibido / últimos logins. Mobile: apilado. */
.admin-usuario-actividad { margin: 1.5rem 0; }
.admin-usuario-actividad h2 { margin: 0 0 0.75rem; }
.admin-actividad-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}
@media (min-width: 900px) { .admin-actividad-grid { grid-template-columns: repeat(3, 1fr); } }
.admin-actividad-card {
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  padding: 1rem 1.25rem;
}
.admin-actividad-card h3 { margin: 0 0 0.6rem; font-size: 1rem; }
.admin-actividad-lista { list-style: none; padding: 0; margin: 0; }
.admin-actividad-lista li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.75rem;
  padding: 0.35rem 0;
  border-bottom: 1px solid #eaecef;
}
.admin-actividad-lista li:last-child { border-bottom: 0; }
.admin-actividad-logins li { font-size: 0.9rem; }

.admin-tabla-id { color: var(--color-texto-suave); font-variant-numeric: tabular-nums; }

/* Wrapper con scroll horizontal para tablas anchas en mobile — bloque F
   (métricas por moderador, 2026-07-02). */
.admin-tabla-scroll { overflow-x: auto; margin-top: 0.5rem; }
.admin-tabla-moderadores td, .admin-tabla-moderadores th { white-space: nowrap; }
.admin-tabla-moderadores td span.texto-chico { display: block; }

/* Listado de usuarios admin — layout responsive de cards en mobile
   (2026-07-05).

   Approach: en desktop la tabla se ve como tabla normal. En mobile
   (< 900px) cada `<tr>` se re-renderiza como una card apilada: la fila
   pasa a `display: block`, cada `<td>` se muestra como par
   "label + valor" leyendo el `data-label="..."` del template.

   Es el patrón estándar de responsive tables (usado por Stripe,
   GitHub, Notion): elimina el problema del scroll horizontal de raíz
   porque en mobile NO HAY tabla ancha — hay cards apiladas verticales.

   El `<thead>` se oculta en mobile (los labels ya salen de data-label
   en cada fila). El botón "Ver" queda a lo ancho de la card al fondo. */
@media (max-width: 900px) {
  /* Sacamos el scroll — ya no hace falta. */
  .admin-tabla-usuarios.admin-tabla-scroll { overflow-x: visible; }
  .admin-tabla-usuarios table,
  .admin-tabla-usuarios thead,
  .admin-tabla-usuarios tbody,
  .admin-tabla-usuarios tr,
  .admin-tabla-usuarios td { display: block; width: 100%; }
  /* Header oculto — los labels los ponemos por celda con data-label. */
  .admin-tabla-usuarios thead { display: none; }
  /* Cada fila = una card. */
  .admin-tabla-usuarios tbody tr {
    margin-bottom: 0.85rem;
    padding: 0.75rem 0.9rem;
    background: var(--color-fondo-card);
    border: 1px solid var(--color-borde, #e5e5e5);
    border-radius: var(--radio-medio, 8px);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
  }
  .admin-tabla-usuarios tbody tr:hover {
    /* Sin efecto de hover fila en mobile — no aplica el patrón. */
    background: var(--color-fondo-card);
  }
  /* Cada celda: label a la izquierda (via data-label), valor a la
     derecha. Layout de definición horizontal compacta. */
  .admin-tabla-usuarios tbody td {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.75rem;
    padding: 0.3rem 0;
    border-bottom: 1px solid #f2f2f2;
    font-size: 0.9rem;
    text-align: right;
  }
  .admin-tabla-usuarios tbody td:last-child {
    border-bottom: none;
  }
  .admin-tabla-usuarios tbody td::before {
    content: attr(data-label);
    font-weight: 600;
    color: var(--color-texto-suave);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    text-align: left;
    flex-shrink: 0;
  }
  /* Botón "Ver" ocupa todo el ancho al final de la card, sin label. */
  .admin-tabla-usuarios tbody td.col-accion {
    justify-content: stretch;
    padding-top: 0.75rem;
    margin-top: 0.5rem;
  }
  .admin-tabla-usuarios tbody td.col-accion::before {
    content: none;
  }
  .admin-tabla-usuarios tbody td.col-accion .btn {
    width: 100%;
    text-align: center;
  }
  /* Emails largos: si no entran, wrappean. */
  .admin-tabla-usuarios tbody td {
    word-break: break-word;
  }
}
/* Desktop: la col de acción alineada a la derecha, sin sticky (no hace
   falta si no hay scroll). */
.admin-tabla-usuarios .col-accion {
  text-align: right;
}

/* Bloque "motivo del rechazo previo" en `subir_dni.html` — visible solo
   cuando el usuario viene de un rechazo previo (fix 2026-07-04). Es lo
   primero que se ve al entrar, así que lleva el peso visual más alto de la
   pantalla: ícono, borde de acento y sombra propia. */
.dni-rechazo {
  display: flex;
  gap: 0.85rem;
  align-items: flex-start;
  background: #fff4ec;
  border: 1px solid #ffd9c2;
  border-left: 4px solid var(--color-acento);
  border-radius: var(--radio-medio);
  padding: 1rem 1.1rem;
  margin: 0 0 1.4rem;
  box-shadow: 0 2px 8px rgba(255, 82, 0, 0.08);
}
.dni-rechazo-icono {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 999px;
  background: var(--color-acento-solido);
  color: #fff;
  font-weight: 700;
  line-height: 1;
}
.dni-rechazo-cuerpo { min-width: 0; }
.dni-rechazo-titulo {
  margin: 0 0 0.35rem;
  font-size: 1.05rem;
  color: var(--color-primario);
}
.dni-rechazo-motivo {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.5;
}

/* Panel de verificación DNI — R5 (2026-07-02). Sub-tabs por estado
   arriba del listado + grid de 2 fotos en el detalle. */
.admin-subtabs {
  display: flex; gap: 0.5rem; flex-wrap: wrap;
  margin: 0.75rem 0 1.25rem;
  border-bottom: 1px solid var(--color-borde, #dfe4ea);
}
.admin-subtabs a {
  padding: 0.55rem 0.9rem;
  color: var(--color-texto-suave);
  text-decoration: none;
  border-bottom: 3px solid transparent;
  font-weight: 500;
}
.admin-subtabs a.activo {
  color: var(--color-primario, #0c2233);
  border-bottom-color: var(--color-acento, #ff5200);
}
.admin-subtab-count {
  display: inline-block; margin-left: 0.35rem;
  padding: 0.05rem 0.5rem; border-radius: 999px;
  background: var(--color-acento-solido, #c94000); color: #fff;
  font-size: 0.75rem; font-weight: 600;
}
.admin-identidad-fotos {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
}
@media (min-width: 720px) {
  .admin-identidad-fotos { grid-template-columns: 1fr 1fr; }
}
.admin-identidad-fotos figure { margin: 0; }
.admin-identidad-fotos img {
  width: 100%; max-width: 400px;
  height: auto;
  border-radius: var(--radio-grande);
  border: 1px solid var(--color-borde, #dfe4ea);
  cursor: zoom-in;
  background: #f5f5f5;
}
.dni-foto-btn {
  display: block;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: zoom-in;
  width: 100%;
  max-width: 400px;
  -webkit-tap-highlight-color: transparent;
}
.dni-foto-btn img { cursor: zoom-in; }
.dni-foto-btn:focus-visible {
  outline: 2px solid var(--color-acento, #ff5200);
  outline-offset: 3px;
  border-radius: var(--radio-grande);
}
.admin-identidad-fotos figcaption {
  margin-top: 0.35rem;
  font-size: 0.9rem;
  color: var(--color-texto-suave);
  text-align: center;
}

/* Empty states enriquecidos anunciante — camino #1 A6 (2026-07-02). */
.anunciante-empty-rico {
  background: var(--color-fondo-card);
  padding: 1.25rem;
  border-radius: var(--radio-grande);
  border: 1px solid var(--color-borde, #dfe4ea);
}
.anunciante-empty-rico h3 { margin: 1rem 0 0.5rem; font-size: 1rem; }
.anunciante-empty-espacios { list-style: none; padding: 0; margin: 0 0 1rem; }
.anunciante-empty-espacios li {
  display: flex; justify-content: space-between; align-items: center;
  gap: 0.75rem;
  padding: 0.55rem 0;
  border-bottom: 1px solid #eef1f5;
}
.anunciante-empty-espacios li:last-child { border-bottom: 0; }
.anunciante-empty-espacio-precio { font-weight: 600; }

.anunciante-banners-empty {
  background: var(--color-fondo-card);
  padding: 0.9rem 1.1rem;
  border-radius: var(--radio-grande);
  border: 1px dashed var(--color-borde, #dfe4ea);
}
.anunciante-banners-empty p { margin: 0.25rem 0; }

/* Grid de gráficos SVG en detalle de campaña — camino #1 A5 (2026-07-02).
   Reutiliza `.admin-grafico-svg` que ya existe; solo agrupa los dos SVGs
   (impresiones + clicks) para que en desktop queden lado a lado. */
.anunciante-graficos-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  margin: 1rem 0;
}
.anunciante-graficos-grid h3 { margin: 0 0 0.35rem; font-size: 0.95rem; }
@media (min-width: 720px) {
  .anunciante-graficos-grid { grid-template-columns: 1fr 1fr; }
}

/* Preview de total en form de campaña — camino #1 A3 (2026-07-02).
   Grid de 3 columnas (precio/día, días, total) que colapsa en mobile. */
.campania-total-preview {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.5rem 1rem;
  padding: 0.9rem 1.1rem;
  margin: 1rem 0;
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  border: 1px solid var(--color-borde, #dfe4ea);
}
.campania-total-preview > div { display: flex; flex-direction: column; }
.campania-total-preview strong { font-size: 1.05rem; }
.campania-total-preview-total strong { color: var(--color-acento, #ff5200); font-size: 1.2rem; }
@media (min-width: 640px) {
  .campania-total-preview { grid-template-columns: 1fr 1fr 1fr; }
}

/* "Quedan X días" en detalle de campaña — camino #1 A2 (2026-07-02).
   Verde suave para reforzar "está bien, está activa". */
.anunciante-dias-restantes {
  background: #f0faf6;
  border-left: 4px solid var(--color-primario, #0c2233);
  border-radius: var(--radio-grande);
  padding: 0.75rem 1rem;
  margin: 1rem 0;
  font-size: 1rem;
}

/* Timeline vertical con hitos completados/actual/futuro — camino #1 A2.
   Puntos alineados a la izquierda con línea vertical de fondo. */
.anunciante-timeline-seccion { margin: 1.5rem 0; }
.anunciante-timeline {
  list-style: none;
  padding: 0.5rem 0 0.5rem 1.25rem;
  margin: 0;
  position: relative;
}
.anunciante-timeline::before {
  content: "";
  position: absolute;
  left: 0.4rem; top: 0; bottom: 0;
  width: 2px;
  background: #dfe4ea;
}
.anunciante-timeline-item {
  position: relative;
  padding: 0.55rem 0 0.55rem 1.4rem;
  display: flex; flex-direction: column; gap: 0.15rem;
}
.anunciante-timeline-punto {
  position: absolute;
  left: -0.9rem; top: 0.85rem;
  width: 0.85rem; height: 0.85rem;
  border-radius: 50%;
  background: #dfe4ea;
  border: 2px solid #fff;
}
.anunciante-timeline-item--completado .anunciante-timeline-punto { background: var(--color-primario, #0c2233); }
.anunciante-timeline-item--actual .anunciante-timeline-punto {
  background: var(--color-acento, #ff5200);
  box-shadow: 0 0 0 3px rgba(255, 82, 0, 0.18);
}
.anunciante-timeline-item--futuro { opacity: 0.6; }
.anunciante-timeline-etiqueta { font-weight: 600; color: var(--color-texto, #1a1a1a); }

/* Callout de pago pendiente en dashboard anunciante — camino #1 A1
   (2026-07-02). Naranja de marca para llamar la atención sin gritar. */
.anunciante-callout-pago {
  background: #fff4ec;
  border-left: 4px solid var(--color-acento, #ff5200);
  border-radius: var(--radio-grande);
  padding: 0.9rem 1.1rem;
  margin: 0 0 1.25rem;
}
.anunciante-callout-pago strong { display: block; margin-bottom: 0.35rem; color: #a03400; }
.anunciante-callout-pago ul { list-style: none; padding: 0; margin: 0; }
.anunciante-callout-pago li { padding: 0.2rem 0; font-size: 0.95rem; }
.anunciante-callout-pago a { color: var(--color-primario); text-decoration: underline; }

/* Modal reusable de confirmación — camino #2 UX (2026-07-02).
   <dialog> nativo con estilos de marca. Sale gratis: focus trap,
   Escape, backdrop. Solo lo esteticamos. */
.modal-marca {
  border: 0;
  padding: 0;
  max-width: 90vw;
  width: 420px;
  border-radius: var(--radio-grande);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
  background: var(--color-fondo-card);
  color: var(--color-texto);
}
.modal-marca::backdrop { background: rgba(12, 34, 51, 0.55); }
.modal-marca[open] {
  animation: modal-marca-in 120ms ease-out;
}
@keyframes modal-marca-in {
  from { transform: translateY(6px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}
.modal-marca-form { display: flex; flex-direction: column; gap: 0.9rem; padding: 1.25rem 1.25rem 1rem; margin: 0; }
.modal-marca-titulo { margin: 0; font-size: 1.15rem; color: var(--color-primario); }
.modal-marca-mensaje { margin: 0; font-size: 0.98rem; line-height: 1.4; color: var(--color-texto); }

/* Denunciar un aviso (R8-09, 2026-08-17). El disparador es un link discreto
   —salida de emergencia, no CTA— pero con área táctil completa. Desde el
   2026-09-18 son dos —la publicación y quien la publica— y van juntos: en el
   celu uno debajo del otro, sin hueco entre las dos áreas táctiles, y desde
   tablet en el mismo renglón. */
.ficha-denunciar-grupo {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
@media (min-width: 768px) {
  .ficha-denunciar-grupo {
    flex: 0 0 100%;             /* su propio renglón entre los botones de la ficha */
    flex-direction: row;
    flex-wrap: wrap;
    column-gap: 1.5rem;
  }
}
.ficha-denunciar {
  /* Flex: en el anónimo son enlaces, y un `<a>` en línea ignora el
     `min-height`; `align-items` centra el texto en los 44px en vez de dejarlo
     pegado arriba. */
  display: inline-flex;
  align-items: center;
  background: none;
  border: none;
  padding: 0.6rem 0;           /* alto táctil ≥44px junto al line-height */
  min-height: 44px;
  cursor: pointer;
  text-decoration: underline;
  text-align: left;
}
/* Las tres líneas de cómo cuidarse, después de una denuncia de plata o de
   estafa: con viñetas y aire entre ellas, que sin eso se leían como un solo
   párrafo. */
.denuncia-consejos {
  display: grid;
  gap: 0.35rem;
  margin: 0.35rem 0 0.75rem;
  padding-left: 1.2rem;
  list-style: disc;
  font-size: 0.98rem;
  line-height: 1.4;
  color: var(--color-texto);
}
.modal-denuncia-motivos {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  border: none;
  padding: 0;
  margin: 0;
}
.modal-denuncia-motivo {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 44px;            /* cada opción es un target táctil entero */
  cursor: pointer;
  font-size: 0.95rem;
}
.modal-denuncia-detalle {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  font-size: 0.92rem;
}
.modal-denuncia-detalle textarea {
  resize: vertical;
  min-height: 4.5rem;
}
/* Nota del paso 5: cuántas fotos hacen falta y qué se gana con 10+. */
.wizard-nota-fotos {
  margin: 0.5rem 0 0;
  padding: 0.6rem 0.8rem;
  border-radius: 8px;
  background: var(--color-borde-suave);
  font-size: 0.92rem;
  line-height: 1.45;
}
/* Nota informativa del wizard: misma caja que la del paso 5, sin acoplarse a
   ese paso. La usa el paso 1 para avisar que el tipo y la operación se
   congelan al publicar (decisión D4, 2026-08-09). */
.wizard-nota {
  margin: 0.9rem 0 0;
  padding: 0.6rem 0.8rem;
  border-radius: 8px;
  background: var(--color-borde-suave);
  font-size: 0.92rem;
  line-height: 1.45;
}
/* Campo que ya no se puede editar: se muestra el valor, no un control. */
.wizard-campo-fijo {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.7rem 0.9rem;
  margin-bottom: 0.7rem;
  border: 1px solid var(--color-borde);
  border-radius: 8px;
  background: var(--color-fondo-suave);
}
.wizard-campo-fijo-etiqueta {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-texto-muy-suave);
}
/* Guía de redacción del paso 7. */
.lista-guia { margin: 0.4rem 0 0.8rem; padding-left: 1.2rem; line-height: 1.5; }
.lista-guia li { margin-bottom: 0.35rem; }

/* Pregunta opcional dentro del modal de confirmación (ej: "¿se vendió?"). */
/* La casilla «¿se vendió?»: de ella depende que una baja se registre como
   venta, así que el label entero es el target táctil (`AF-11-18`). Sin el
   `min-height` medía ~21 px, la mitad de la regla del proyecto. */
.modal-marca-check {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.9rem;
  min-height: var(--tamano-tap);
  font-size: 0.95rem;
  cursor: pointer;
}
/* Y el `display: flex` de arriba le gana al `[hidden]` del navegador —el
   estilo de autor pisa al del user-agent—, así que sin esta guarda la
   casilla se mostraba SIEMPRE, vacía y sin etiqueta, en toda confirmación
   que no pregunta nada. Medido en el navegador el 2026-08-25: con
   `wrap.hidden = true` el label seguía dando 22,8 px de alto y visible.
   Es el mismo patrón que ya usan `.lightbox[hidden]`, `.editor-foto[hidden]`
   y `.buscar-typeahead[hidden]`. */
.modal-marca-check[hidden] { display: none; }
.modal-marca-check input { width: 1.05rem; height: 1.05rem; }
.modal-marca-acciones {
  display: flex; justify-content: flex-end; gap: 0.6rem;
  margin-top: 0.4rem;
}

/* MFA opt-in — bloque G camino #4 (2026-07-02). QR centrado con ancho
   máximo para que no se estire en desktop; secreto en monospace clara. */
.mfa-qr {
  display: flex; justify-content: center;
  margin: 1rem 0;
  padding: 1rem;
  background: #fff;
  border-radius: var(--radio-grande);
  border: 1px solid var(--color-borde);
}
.mfa-qr svg { width: 100%; max-width: 240px; height: auto; }
.mfa-manual { margin-top: 0.75rem; }
.mfa-manual summary { cursor: pointer; color: var(--color-texto-suave); font-size: 0.9rem; }
.mfa-secreto {
  display: inline-block;
  margin: 0.5rem 0;
  padding: 0.4rem 0.6rem;
  background: var(--color-fondo-card);
  border-radius: var(--radio-chico);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 1.05rem;
  letter-spacing: 0.05em;
  word-break: break-all;
}
.mfa-paso {
  display: flex; flex-direction: column; gap: 0.5rem;
  padding: 1rem 0;
}

/* Los ocho códigos de respaldo (`SEG-02`). Una columna en el celu —que es donde
   se van a copiar a mano— y dos cuando hay ancho. `list-style: none` y el número
   propio no: el `<ol>` está por la semántica, pero un "1." delante de un código
   que también empieza con dígitos se lee como parte del código. */
.mfa-codigos {
  list-style: none;
  margin: 1rem 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.5rem;
}
.mfa-codigos code {
  display: block;
  padding: 0.6rem 0.75rem;
  background: var(--color-fondo-suave);
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-chico);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 1.1rem;
  letter-spacing: 0.08em;
  text-align: center;
  /* Que se pueda seleccionar de un doble clic entero, guión incluido. */
  user-select: all;
}
.mfa-sin-codigos { color: var(--color-corazon); font-weight: 600; }

/* Las tres notas de la pantalla de códigos. Sin esto salían sin viñeta y sin
   aire —el reset de listas del sitio se las come— y las tres frases se leían
   como un párrafo corrido, que es justo lo que no puede pasar en la única
   pantalla donde se explica cómo se usan. */
.mfa-notas {
  list-style: disc;
  margin: 0 0 1.25rem;
  padding-left: 1.25rem;
}
.mfa-notas li { margin: 0.4rem 0; }

/* Cada acción del segundo factor, separada de la de al lado: las dos piden un
   código de 6 dígitos y sin el corte no se ve cuál campo va con cuál botón. */
.mfa-accion {
  margin-top: 1.25rem;
  padding-top: 1rem;
  border-top: 1px solid var(--color-borde);
}
.mfa-input-codigo {
  max-width: 9rem;
  letter-spacing: 0.15em;
  text-align: center;
}

@media (min-width: 480px) {
  .mfa-codigos { grid-template-columns: 1fr 1fr; }
}

.admin-usuario-historial {
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  padding: 1.25rem;
}
.admin-usuario-historial h2 { margin-top: 0; }
.admin-historial-lista { list-style: none; padding: 0; margin: 0; }
.admin-historial-item { padding: 0.6rem 0; border-bottom: 1px solid #eaecef; }
.admin-historial-item:last-child { border-bottom: 0; }
.admin-historial-fecha { color: var(--color-texto-suave); font-size: 0.85rem; margin-right: 0.5rem; }
.admin-historial-item p { margin: 0.25rem 0 0; }

.admin-empty {
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  padding: 1.5rem;
  text-align: center;
}

/* ===========================================================
   Páginas guía (Cómo vender / Cómo comprar / FAQs) — 2026-06-19.
   Layout legible centrado, pasos numerados, callouts de tips,
   accordion nativo para FAQs.
   =========================================================== */

.guia {
  max-width: 760px;
  margin: 1.5rem auto 3rem;
  padding: 0 0.25rem;
}

.guia-cabecera {
  text-align: center;
  margin-bottom: 2.5rem;
}
.guia-titulo {
  color: var(--color-primario);
  font-size: 2rem;
  margin: 0 0 0.75rem;
  font-weight: 700;
}
@media (min-width: 768px) {
  .guia-titulo { font-size: 2.5rem; }
}
.guia-lead {
  color: var(--color-texto-suave);
  font-size: 1.05rem;
  line-height: 1.6;
  max-width: 640px;
  margin: 0 auto;
  text-align: justify;
  hyphens: auto;
}

/* ----- Páginas legales (`/terminos`, `/privacidad`, `/normas`) -----
   Modificador de `.guia`: mismo ancho y misma cabecera que las editoriales,
   pero el cuerpo es texto largo para leer, con secciones numeradas. La cabecera
   va alineada a la izquierda —no centrada como en las guías— porque acá lo que
   sigue es un documento, no una landing. */
.legal .guia-cabecera { text-align: left; margin-bottom: 1.5rem; }
.legal .guia-lead { margin-left: 0; text-align: left; }

.legal-aviso {
  display: block;
  background: #fff8e1;
  border: 1px solid #f0d97c;
  border-left: 4px solid #d9a800;
  border-radius: var(--radio-medio);
  padding: 0.9rem 1.1rem;
  margin: 0 0 2rem;
  font-size: 0.95rem;
  line-height: 1.55;
  color: #6b4e00;
}
.legal-aviso strong { color: #4d3800; }

.legal-seccion {
  margin: 0 0 2rem;
  padding-bottom: 0.25rem;
}
.legal-seccion h2 {
  font-size: 1.25rem;
  color: var(--color-primario);
  margin: 0 0 0.75rem;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--color-borde);
}
.legal-seccion h3 {
  font-size: 1.02rem;
  color: var(--color-primario);
  margin: 1.4rem 0 0.5rem;
}
.legal-seccion p {
  margin: 0 0 0.9rem;
  line-height: 1.65;
}
.legal-lista {
  margin: 0 0 1rem;
  padding-left: 1.2rem;
}
.legal-lista li {
  margin: 0 0 0.6rem;
  line-height: 1.6;
}

.legal-pie {
  margin: 2.5rem 0 0;
  padding-top: 1rem;
  border-top: 1px solid var(--color-borde);
  font-size: 0.9rem;
  color: var(--color-texto-suave);
}

/* ----- Pasos numerados ----- */
.guia-pasos {
  list-style: none;
  padding: 0;
  margin: 0 0 2.5rem;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}
.guia-paso {
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: 1rem;
  align-items: start;
  background: var(--color-fondo-card);
  border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio-grande);
  padding: 1.25rem;
  transition: box-shadow 0.15s, transform 0.15s;
}
.guia-paso:hover {
  box-shadow: var(--sombra-card-hover);
  transform: translateY(-1px);
}
.guia-paso-numero {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--color-acento-solido);
  color: #ffffff;
  font-weight: 700;
  font-size: 1.4rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.guia-paso-cuerpo h3 {
  margin: 0 0 0.4rem;
  color: var(--color-primario);
  font-size: 1.15rem;
  font-weight: 700;
}
.guia-paso-cuerpo p {
  margin: 0;
  color: var(--color-texto);
  line-height: 1.6;
  text-align: justify;
  hyphens: auto;
}
@media (min-width: 768px) {
  .guia-paso { padding: 1.5rem 1.75rem; grid-template-columns: 64px 1fr; gap: 1.25rem; }
  .guia-paso-numero { width: 56px; height: 56px; font-size: 1.6rem; }
  .guia-paso-cuerpo h3 { font-size: 1.25rem; }
}

/* ----- Tips (callout) ----- */
.guia-tips {
  background: var(--color-fondo-suave);
  border-left: 4px solid var(--color-acento);
  border-radius: var(--radio-medio);
  padding: 1.25rem 1.5rem;
  margin: 2.5rem 0;
}
.guia-tips h2 {
  margin: 0 0 0.75rem;
  font-size: 1.2rem;
  color: var(--color-primario);
}
.guia-tips ul {
  margin: 0;
  padding: 0 0 0 1.25rem;
  list-style: disc;
}
.guia-tips li {
  margin-bottom: 0.5rem;
  line-height: 1.55;
}
.guia-tips li:last-child { margin-bottom: 0; }
.guia-tips strong { color: var(--color-primario); font-weight: 700; }

/* ----- CTA al pie ----- */
.guia-cta {
  text-align: center;
  margin-top: 2.5rem;
  padding: 1.5rem 1rem;
}
.guia-cta .btn-grande {
  font-size: 1.1rem;
  padding: 0.875rem 2.25rem;
  min-height: 52px;
}
.guia-cta-nota {
  margin: 0.75rem 0 0;
  color: var(--color-texto-suave);
  font-size: 0.9rem;
}

/* ===========================================================
   FAQs — accordion con <details>/<summary> nativos.
   =========================================================== */

.faqs-grupo { margin-bottom: 2.25rem; }
.faqs-grupo-titulo {
  color: var(--color-primario);
  font-size: 1.35rem;
  margin: 0 0 1rem;
  padding-bottom: 0.5rem;
  border-bottom: 2px solid var(--color-fondo-suave);
}
.faq-item {
  background: var(--color-fondo-card);
  border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio-medio);
  margin-bottom: 0.65rem;
  overflow: hidden;
  transition: box-shadow 0.15s;
}
.faq-item[open] {
  box-shadow: var(--sombra-card);
  border-color: var(--color-borde);
}
.faq-item summary {
  cursor: pointer;
  padding: 1rem 3rem 1rem 1.25rem;
  font-weight: 600;
  color: var(--color-primario);
  line-height: 1.4;
  list-style: none;
  position: relative;
  -webkit-tap-highlight-color: transparent;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: "+";
  position: absolute;
  right: 1.25rem;
  top: 50%;
  transform: translateY(-50%);
  font-size: 1.5rem;
  color: var(--color-acento);
  font-weight: 400;
  transition: transform 0.2s;
  line-height: 1;
}
.faq-item[open] summary::after {
  content: "−";
}
.faq-item summary:hover { background: var(--color-fondo-suave); }
.faq-respuesta {
  padding: 0 1.25rem 1.1rem;
  color: var(--color-texto);
  line-height: 1.6;
}
.faq-respuesta p { margin: 0; }
.faq-respuesta p + p { margin-top: 0.6rem; }
.faq-respuesta a {
  color: var(--color-acento);
  text-decoration: underline;
}

/* ===========================================================
   Auth (login / registro) — redesign 2026-06-21.
   `.form-contenedor` se mantiene para recuperar/resetear/cambiar-password
   sin cambios. Acá viven los wrappers nuevos `.auth-*`.
   =========================================================== */

.auth-shell {
  max-width: 460px;
  margin: 1.5rem auto 3rem;
  padding: 0 0.25rem;
}

.auth-cabecera {
  text-align: center;
  margin-bottom: 1.5rem;
}
.auth-cabecera h1 {
  color: var(--color-primario);
  font-size: 1.85rem;
  margin: 0 0 0.5rem;
  font-weight: 700;
}
@media (min-width: 768px) {
  .auth-cabecera h1 { font-size: 2.1rem; }
}
.auth-lead {
  color: var(--color-texto-suave);
  font-size: 0.98rem;
  line-height: 1.55;
  margin: 0;
  max-width: 400px;
  margin-left: auto;
  margin-right: auto;
}

/* ----- Tarjeta del form ----- */
.auth-card {
  background: var(--color-fondo-card);
  border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  padding: 1.5rem;
}
@media (min-width: 768px) {
  .auth-card { padding: 2rem; }
}
.auth-form .btn-bloque { margin-top: 0.5rem; }

/* Campo de código de un solo uso (el segundo factor). Se separa del resto de
   los `.form-input` porque lo que se tipea son seis dígitos y no una palabra:
   centrado y espaciado se leen de un vistazo contra la app del teléfono. */
.input-codigo {
  text-align: center;
  font-size: 1.4rem;
  letter-spacing: 0.3em;
  text-indent: 0.3em; /* compensa el espaciado del último dígito */
  font-variant-numeric: tabular-nums;
}

/* Chip de estado del trámite ("En revisión"). Dice de un vistazo lo que el
   título dice en palabras, y le da a la pantalla un ancla visual que no cuesta
   una línea de texto más. */
.estado-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0 0 0.75rem;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.estado-chip-punto {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 999px;
  background: currentColor;
}
.estado-chip-espera { background: #fff4e0; color: #8a5a00; }
.auth-card-link {
  margin: 1rem 0 0;
  text-align: center;
  font-size: 0.9rem;
}
/* «¿Olvidaste tu contraseña?» medía 17 px de alto (R11, 2026-09-12). */
.auth-card-link a {
  color: var(--color-texto-suave);
  text-decoration: underline;
  display: inline-flex;
  align-items: center;
  min-height: var(--tamano-tap);
}
.auth-card-link a:hover { color: var(--color-acento); }

/* ----- Link cruzado al pie (Ingresá acá / Creala acá) ----- */
.auth-pie-link {
  margin: 1.75rem 0 0;
  text-align: center;
  font-size: 0.98rem;
  color: var(--color-texto-suave);
}
.auth-pie-link a {
  color: var(--color-acento);
  font-weight: 600;
  text-decoration: none;
}
.auth-pie-link a:hover { text-decoration: underline; }

/* ----- Placeholder global más claro y consistente -----
   Algunos browsers ya lo pintan gris suave; forzamos color y opacidad
   para que se vea igual en todos. Se aplica a inputs y textareas. */
input::placeholder,
textarea::placeholder {
  color: var(--color-texto-muy-suave);
  opacity: 1;
  font-style: normal;
}

/* ----- Toggle "ojo" de password (siempre visible) -----
   El JS `password_toggle.js` envuelve cada `input[type="password"]` con
   `.password-wrap` y agrega el botón. Acá vive el layout y el reseteo
   del control nativo de Edge para evitar el doble botón. */
input[type="password"]::-ms-reveal,
input[type="password"]::-ms-clear {
  display: none;
}
.password-wrap {
  position: relative;
}
.password-wrap .form-input {
  /* Espacio a la derecha para que el texto no se solape con el botón. */
  padding-right: 2.75rem;
}
.password-toggle {
  position: absolute;
  right: 0.4rem;
  top: 50%;
  transform: translateY(-50%);
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  color: var(--color-texto-muy-suave);
  cursor: pointer;
  border-radius: var(--radio-chico);
  padding: 0;
  -webkit-tap-highlight-color: transparent;
}
/* El dibujo mide 36 px para entrar adentro del campo; el área tocable llega a
   44 con el mismo recurso que `.buscar-voz::after`. Medía 36×36 y ningún
   barrido de la hoja lo veía: el botón lo crea `password_toggle.js`, no una
   plantilla (R11, 2026-09-12). */
.password-toggle::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--tamano-tap);
  height: var(--tamano-tap);
  transform: translate(-50%, -50%);
}
.password-toggle:hover {
  color: var(--color-primario);
  background: var(--color-fondo-suave);
}
.password-toggle:focus-visible {
  outline: 2px solid var(--color-primario);
  outline-offset: 2px;
}
.password-toggle svg {
  width: 20px;
  height: 20px;
}

/* ----- Links discretos dentro de un label de checkbox -----
   Usado en registro (link a Términos) y por defecto para cualquier
   <a> que viva dentro de un checkbox-label en el resto del sitio.
   Estilo sutil: underline con color del texto (no naranja brillante),
   hover pasa al azul primario. */
.form-check-label a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--color-texto-muy-suave);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  transition: color 0.15s, text-decoration-color 0.15s;
}
.form-check-label a:hover {
  color: var(--color-primario);
  text-decoration-color: var(--color-primario);
}

/* === Separador en el nav de cuenta ====================================== */
.cuenta-nav-separador {
  height: 1px;
  background: var(--color-borde-suave);
  margin: 0.4rem 0.5rem;
  list-style: none;
}

/* === Forms inline dentro de auth-card (botones como link) =============== */
/* Reusado por la pantalla "Revisá tu casilla" (reenviar mail), "Verificá
   tu celular" (reenviar código) y por el banner global. Evita que un POST
   con un solo botón se vea como form al usuario — visualmente es un link. */
.auth-form-inline {
  display: inline;
  margin: 0;
}
.link-boton {
  background: none;
  border: none;
  padding: 0;
  color: var(--color-primario);
  font: inherit;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.link-boton:hover { color: var(--color-acento); }

/* === auth-card: bloques de texto y acciones extras ===================== */
/* Cuando la card necesita un párrafo descriptivo (no es solo form). */
.auth-card-texto {
  margin: 0 0 1rem;
  color: var(--color-texto-suave);
  line-height: 1.5;
}
.auth-card-acciones {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0 0 1.25rem;
}
.auth-card-acciones .btn { flex: 1 1 auto; min-width: 140px; }

/* Bloque separado dentro de la card para un camino alternativo (ej: "te
   rechazamos el DNI, resubilo"). Va con borde superior para que se lea como
   otra cosa y no como continuación del texto de arriba. */
.auth-card-nota {
  margin-top: 1.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--color-borde);
}
.auth-card-nota .titulo-chico {
  margin: 0 0 0.35rem;
  font-size: 1rem;
  font-weight: 600;
}
.auth-card-nota p { margin: 0 0 0.85rem; }

/* === Fila marcada en tablas del admin ================================== */
/* Para señalar sin bloquear: hoy la usa el ranking de contactos cuando una
   cuenta pasa el piso de volumen sospechoso. */
.admin-tabla tr.fila-alerta { background: #fff7ed; }
.badge-alerta {
  display: inline-block;
  margin-left: 0.4rem;
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  background: #fed7aa;
  color: #7c2d12;
  font-size: 0.72rem;
  font-weight: 600;
  white-space: nowrap;
}

/* === Banner persistente de verificación pendiente ====================== */
/* Se renderiza fuera del .contenedor principal: ancho full bleed con un
   inner contenedor para el padding consistente. Color amarillo suave —
   informativo, no alarmante. */
.banner-verificacion {
  background: #fff8e1;
  border-bottom: 1px solid #f0d97c;
  color: #6b4e00;
}
.banner-verificacion-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  /* Sólo arriba y abajo: el `padding: 0.6rem 0` que había pisaba el de
     `.contenedor`, y en el celu el texto quedaba pegado al borde (se vio al
     capturar el cartel de la cuenta en revisión, 2026-09-18). */
  padding-top: 0.6rem;
  padding-bottom: 0.6rem;
}
.banner-verificacion-texto {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.4;
}
.banner-verificacion-acciones {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
/* 44px como cualquier otra cosa que se toca: medía 32 y es el único botón de
   un banner que aparece en todas las pantallas del panel (2026-08-16). */
.banner-verificacion-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tamano-tap);
  background: #6b4e00;
  color: #fff;
  padding: 0.35rem 1rem;
  border-radius: var(--radio-medio, 8px);
  font-size: 0.9rem;
  font-weight: 600;
  border: none;
  cursor: pointer;
  text-decoration: none;
}
.banner-verificacion-cta:hover {
  background: #4d3800;
  color: #fff;
  text-decoration: none;
}
/* Variante identidad (2026-08-05): mismo formato, tono informativo y no
   amarillo-alerta. No es que la cuenta esté a medias — es que todavía no
   puede publicar, y puede pasarse meses así sin problema. */
.banner-verificacion-identidad {
  background: #eef4fb;
  border-bottom-color: #c6d9ee;
  color: #123a5e;
}
.banner-verificacion-identidad .banner-verificacion-cta {
  background: #123a5e;
}
.banner-verificacion-identidad .banner-verificacion-cta:hover {
  background: #0c2233;
}

/* === Dashboard del comprador (/cuenta/) ================================ */
/* Grilla de cards con los resúmenes del usuario. Mobile-first: 1 columna.
   Desktop: 2 columnas, con la card destacada ocupando full width. */
.dashboard-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}
@media (min-width: 720px) {
  .dashboard-cards { grid-template-columns: 1fr 1fr; }
  .dashboard-card-destacada { grid-column: 1 / -1; }
}

.dashboard-card {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  background: var(--color-fondo-card);
  border-radius: var(--radio-grande);
  box-shadow: var(--sombra-card);
  padding: 1.1rem 1.2rem;
  color: var(--color-texto);
  text-decoration: none;
  transition: box-shadow 0.15s, transform 0.15s;
}
.dashboard-card:hover {
  box-shadow: var(--sombra-card-hover);
  transform: translateY(-1px);
  text-decoration: none;
}
.dashboard-card-titulo {
  margin: 0;
  font-size: 1.05rem;
  color: var(--color-primario);
}
.dashboard-card-numero {
  margin: 0;
  font-size: 2.25rem;
  font-weight: 700;
  color: var(--color-primario);
  line-height: 1;
}
.dashboard-card-detalle {
  margin: 0;
  color: var(--color-texto-suave);
  font-size: 0.92rem;
  line-height: 1.4;
}
.dashboard-card-cta {
  margin-top: auto;
  color: var(--color-acento);
  font-weight: 600;
  font-size: 0.95rem;
}
/* La card destacada tiene un fondo de marca para diferenciar del resto. */
.dashboard-card-destacada {
  background: linear-gradient(135deg, var(--color-primario) 0%, var(--color-primario-claro) 100%);
  color: #fff;
}
.dashboard-card-destacada .dashboard-card-titulo,
.dashboard-card-destacada .dashboard-card-detalle { color: #fff; }
.dashboard-card-destacada .dashboard-card-cta { color: #ffb784; }
/* Botonera Buscar / Publicar dentro de la card destacada. */
.dashboard-card-botonera {
  display: flex;
  gap: 0.6rem;
  margin-top: 0.5rem;
  flex-wrap: wrap;
}
.dashboard-card-botonera .btn { flex: 1 1 8rem; text-align: center; }
.dashboard-card-destacada .btn-secundario {
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
  border-color: rgba(255, 255, 255, 0.5);
}

/* Card de anunciante: cuando el usuario todavía no tiene perfil, lo invitamos
   con un acento naranja sutil — se diferencia de la card de búsqueda
   (azul) sin competir visualmente con ella. */
.dashboard-card-anunciar {
  background: #fff5ef;
  border: 1px solid #ffd9c2;
}
.dashboard-card-anunciar .dashboard-card-titulo { color: var(--color-acento); }

.dashboard-publicar-cta {
  margin: 1.25rem 0 0;
  padding: 0.85rem 1rem;
  background: #f5f8fb;
  border-radius: var(--radio-medio, 8px);
  font-size: 0.95rem;
  color: var(--color-texto-suave);
}
.dashboard-publicar-cta a {
  color: var(--color-acento);
  font-weight: 600;
}
.dashboard-publicar-cta a:hover { text-decoration: underline; }

/* Fecha "Guardado el dd/mm/yyyy" debajo de cada card de favorito. */
.favs-item-fecha {
  margin: 0.25rem 0 0;
  font-size: 0.82rem;
  text-align: center;
}

/* Toast de "guardado en este dispositivo" — lo inyecta favoritos.js la primera
   vez que un visitante sin sesión toca el corazón. Abajo (no arriba como el del
   soft-wall) para no chocar con el navbar ni con el toast del soft-wall si los
   dos aparecen. */
.fav-toast {
  position: fixed;
  left: 1rem;
  right: 1rem;
  /* Arriba del CTA fijo de la ficha, que mide ~4rem. */
  bottom: calc(5rem + env(safe-area-inset-bottom));
  z-index: 960;
  max-width: 380px;
  margin: 0 auto;
  /* Mismo caso que `.softwall-toast` y encontrado midiendo aquél
     (`AF-03-09`): `.fav-toast-cerrar` ya medía 44 px y este padding seguía
     dimensionado para uno de 32, así que el botón se comía 8 px del
     párrafo. Medido en el navegador: `elementFromPoint` sobre el borde del
     texto devolvía `fav-toast-cerrar`, no el `<p>`. */
  padding: 0.9rem 3.4rem 0.9rem 1rem;
  background: var(--color-fondo-card);
  border: 1px solid var(--color-borde);
  border-left: 4px solid var(--color-corazon);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(12, 34, 51, 0.18);
}
.fav-toast p { margin: 0 0 0.4rem; }
.fav-toast-cerrar {
  position: absolute;
  top: 0.25rem;
  right: 0.25rem;
  width: var(--tamano-tap);
  height: var(--tamano-tap);
  background: none;
  border: none;
  font-size: 1.3rem;
  line-height: 1;
  color: var(--color-texto-suave);
  cursor: pointer;
}

/* Barra "Compartir mis favoritos" — link público opt-in (restaurado 2026-07-26). */
.favs-compartir {
  margin: 0 0 1.25rem;
  padding: 1rem 1.25rem;
  background: var(--color-fondo-suave, #f5f7fa);
  border: 1px solid var(--color-borde, #e5e5e5);
  border-radius: 12px;
}
.favs-compartir-titulo {
  margin: 0 0 0.75rem;
  font-size: 0.95rem;
  color: var(--color-texto-suave, #555);
}
.favs-compartir-input {
  width: 100%;
  padding: 0.55rem 0.7rem;
  margin: 0 0 0.75rem;
  font-size: 0.9rem;
  color: var(--color-texto, #1a1a1a);
  background: #fff;
  border: 1px solid var(--color-borde, #e5e5e5);
  border-radius: 8px;
}
.favs-compartir-acciones {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: center;
}

/* Botón "Guardar en favoritos" en la ficha — reusa el `data-favorito-toggle`
   de las cards; favoritos.js le pone la clase `es-favorito` al marcar. El
   estado marcado se comunica con el corazón lleno + el texto en rojo corazón. */
.ficha-favorito .fav-guardado { display: none; }
.ficha-favorito.es-favorito .fav-guardar { display: none; }
.ficha-favorito.es-favorito .fav-guardado { display: inline; }
.ficha-favorito.es-favorito { color: var(--color-corazon); }

/* Perfil del publicador en la ficha (Dueño directo / Inmobiliaria / …). */
.ficha-publicador-perfil {
  display: inline-block;
  margin: 0.3rem 0 0;
  padding: 0.15rem 0.7rem;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--color-primario);
  background: var(--color-fondo-suave, #f5f7fa);
  border-radius: 999px;
}

/* Acciones del empty state de Favoritos — alineadas horizontalmente
   (Ir a buscar / Crear primera lista). Reusable para empty states con
   más de un CTA. */
.favs-empty-acciones {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin: 1rem 0 0;
}
/* ---------------------------------------------------------------------------
   Mis alertas — búsquedas guardadas (`H-02`, 2026-09-07)

   Las doce clases `bg-*` de `cuenta/busquedas_guardadas.html` y
   `cuenta/busqueda_guardada_editar.html` se escribieron **sin su CSS**: la
   única que existía era `.bg-form-inline`, y `git log -S'.bg-lista'` sale
   vacío, así que nunca lo tuvieron. El `<ul>` salía con las viñetas y la
   sangría del browser, las cards sin fondo ni borde ni padding, el badge sin
   pill y —lo que de verdad importa— **una alerta pausada se veía igual que una
   activa**, sólo distinta por la palabra. Los botones sí se veían porque
   `.btn*` existe: probablemente por eso nadie lo notó.

   Estas cards viven adentro de `.cuenta-contenido`, que YA es una card blanca
   con sombra: por eso se separan con borde y no con una segunda sombra.
--------------------------------------------------------------------------- */
.bg-lista { list-style: none; padding: 0; margin: 0; display: grid; gap: 1rem; }

.bg-card {
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-medio);
  padding: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
/* Pausada. El color no es la única señal —el badge dice «· pausada» en
   letras—, pero sin esto las dos alertas eran el mismo rectángulo blanco. El
   ámbar es el mismo par de la pill de «pausado» de Mis avisos. */
.bg-card-pausada {
  background: var(--color-fondo-suave);
  border-left: 4px solid #7a5800;
}

.bg-card-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.bg-card-titulo { font-size: 1.05rem; margin: 0; line-height: 1.3; }
.bg-card-badge {
  display: inline-block;
  padding: 0.15rem 0.6rem;
  border-radius: 999px;
  background: #e7f3ff;
  color: #0a4d8c;
  font-size: 0.78rem;
  font-weight: 600;
  white-space: nowrap;
}
.bg-card-pausada .bg-card-badge { background: #fff3d6; color: #7a5800; }

.bg-resumen {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 0.2rem;
  font-size: 0.92rem;
}
.bg-card-meta { margin: 0; font-size: 0.85rem; }
.bg-card-acciones {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.25rem;
}
.bg-form-inline {
  display: inline;
  margin: 0;
}
.bg-empty {
  background: var(--color-fondo-suave);
  border-radius: var(--radio-medio);
  padding: 1.5rem 1.25rem;
  text-align: center;
}
.bg-editar-form {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  max-width: 520px;
}

/* -----------------------------------------------------------------------
   Soft-wall progresivo (Sprint C, 2026-07-04)
   Visitantes anónimos: umbral 3 = toast, 5 = banner. El umbral 10 reabre el
   MISMO banner.

   ⚠️ Este encabezado decía «10 = modal blocking» y hablaba de «los 3
   elementos». El modal bloqueante del umbral 10 se eliminó el 2026-08-05
   (decisión de Martín: no defendía nada y Google penaliza los interstitials en
   mobile) y son dos. La baja estaba anotada cincuenta líneas más abajo, en su
   propio comentario, mientras el encabezado —lo que uno lee al entrar al
   bloque— seguía prometiendo el tercero (`CGO-07`).
   ----------------------------------------------------------------------- */

/* El JS los muestra flipeando el atributo `hidden`. Por default el HTML
   `hidden` mapea a `display: none`, pero cualquier `display: flex/block`
   custom con especificidad ≥ 1 lo overridea silenciosamente. Esta regla
   con specificity (0,1,1) le gana a las de abajo y garantiza que los 2
   elementos empiecen invisibles hasta que el JS los active. */
.softwall-toast[hidden],
.softwall-banner[hidden] {
  display: none;
}

/* Umbral 3 — toast dismissable arriba a la derecha. */
.softwall-toast {
  position: fixed;
  top: 1rem;
  right: 1rem;
  z-index: 950;
  max-width: min(360px, calc(100vw - 2rem));
  background: var(--color-fondo-card);
  border: 1px solid var(--color-borde);
  border-left: 4px solid var(--color-acento);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(12, 34, 51, 0.14);
  /* El padding derecho tiene que despejar la X, y la X mide 44 px desde
     `AF-03-04`: con los 2.5rem (40 px) dimensionados para la de 32, el
     botón se comía 12 px del párrafo — medido en el navegador,
     `elementFromPoint` sobre el borde del texto devolvía `softwall-cerrar`.
     44 px de botón + 0.4rem de aire = 3.4rem. */
  padding: 0.9rem 3.4rem 0.9rem 1rem;
  animation: softwall-slide-in 220ms ease-out;
}
.softwall-toast-cuerpo p {
  margin: 0 0 0.4rem;
}
.softwall-toast-acciones {
  margin-top: 0.6rem;
}
.softwall-toast .btn-chico {
  font-size: 0.9rem;
  padding: 0.4rem 0.9rem;
}

/* Umbral 5 — banner arriba a todo ancho, dismissable. */
.softwall-banner {
  position: sticky;
  top: 0;
  z-index: 940;
  background: linear-gradient(135deg, var(--color-primario) 0%, var(--color-primario-claro) 100%);
  color: #fff;
  padding: 1rem 0;
  box-shadow: 0 4px 14px rgba(12, 34, 51, 0.18);
  animation: softwall-slide-down 260ms ease-out;
}
.softwall-banner-inner {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  flex-wrap: wrap;
  position: relative;
  /* El padding derecho tiene que despejar la X, y la X del BANNER no está
     pegada al borde: `.softwall-cerrar-banner` la corre a `right: 1rem`. O sea
     que hacen falta 1rem + 44px de botón = 3.75rem, y no los 3.4rem con los que
     se arregló el toast —donde la X va a `right: 0.5rem`— ni mucho menos los
     2.5rem (40 px) que había, dimensionados para la X de 32 px de antes de
     `AF-03-04`. Con 2.5rem el botón se comía los últimos 20 px del párrafo a
     375 px: medido en el navegador, `elementFromPoint` sobre el borde del texto
     devolvía `softwall-cerrar` (`CGO-06`). */
  padding-right: 3.75rem;
}
.softwall-banner-titulo {
  margin: 0 0 0.3rem;
  font-size: 1.05rem;
}
.softwall-banner-inner .texto-secundario {
  color: rgba(255, 255, 255, 0.85);
  margin: 0;
}
.softwall-banner-acciones {
  display: flex;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-left: auto;
}
.softwall-banner-acciones .btn-secundario {
  background: transparent;
  color: #fff;
  border-color: rgba(255, 255, 255, 0.5);
}
.softwall-banner-acciones .btn-secundario:hover {
  background: rgba(255, 255, 255, 0.12);
}

/* El modal bloqueante del umbral 10 se eliminó el 2026-08-05 — ver el
   comentario de `soft_wall.js`. El umbral 10 reabre el banner de arriba. */

/* Botón cerrar (X) compartido por toast y banner. */
/* El único control táctil del toast y del banner —el resto son links de
   «Crear cuenta»—, y los dos son mobile-first. Medía 2rem (32 px) contra
   los 44 del proyecto (`AF-03-04`); el banner además se reabre solo al
   umbral 10, así que cerrarlo es un gesto que el visitante repite. Mismo
   patrón que `.fav-toast-cerrar`, que es la X del otro toast. */
.softwall-cerrar {
  position: absolute;
  top: 0.4rem;
  right: 0.5rem;
  width: var(--tamano-tap);
  height: var(--tamano-tap);
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: 50%;
  color: inherit;
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
  opacity: 0.6;
  padding: 0;
}
.softwall-cerrar:hover {
  opacity: 1;
}
.softwall-cerrar-banner {
  top: 50%;
  right: 1rem;
  transform: translateY(-50%);
  color: #fff;
}

/* Animaciones. */
@keyframes softwall-slide-in {
  from { opacity: 0; transform: translateX(20px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes softwall-slide-down {
  from { opacity: 0; transform: translateY(-100%); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Mobile: en pantallas chicas el banner apila mejor. */
@media (max-width: 640px) {
  .softwall-banner-inner {
    /* La misma cuenta que la regla base, y por eso mismo: ésta es la que manda
       a 375 px, así que dejarla en 2.5rem hacía que arreglar sólo la de arriba
       no cambiara nada (`CGO-06`). */
    padding-right: 3.75rem;
  }
  .softwall-banner-acciones {
    margin-left: 0;
    width: 100%;
  }
  .softwall-banner-acciones .btn {
    flex: 1;
  }
}

/* ---------- WhatsApp de ayuda: triage (Sprint WA-1, 2026-08-05) ---------- */
/* El CTA del footer es un <button>, no un <a>: abre el diálogo y el número lo
   entrega el endpoint. Se disfraza de link para no romper la columna. */
.footer-cta-ayuda {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0;
  background: none;
  border: 0;
  color: rgba(255, 255, 255, 0.85);
  font-size: inherit;
  text-align: left;
  cursor: pointer;
  min-height: var(--tamano-tap);
}
.footer-cta-ayuda:hover { color: #ffffff; }

.ayuda-dialog { width: 460px; }
.ayuda-cuerpo { padding: 1.1rem 1.15rem 1.25rem; }
.ayuda-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.9rem;
}
.ayuda-aviso {
  margin: 0.25rem 0 0;
  font-size: 0.85rem;
  color: var(--color-texto-muy-suave);
}
.ayuda-cerrar {
  background: none;
  border: 0;
  font-size: 1.6rem;
  line-height: 1;
  color: var(--color-texto-muy-suave);
  cursor: pointer;
  padding: 0 0.25rem;
  min-width: var(--tamano-tap);
  min-height: var(--tamano-tap);
}
.ayuda-motivos { list-style: none; margin: 0; padding: 0; }
.ayuda-motivo {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  width: 100%;
  min-height: var(--tamano-tap);
  padding: 0.75rem 0.85rem;
  margin-bottom: 0.4rem;
  background: var(--color-fondo-suave);
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-medio);
  color: var(--color-texto);
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
.ayuda-motivo:hover {
  background: #ffffff;
  border-color: var(--color-acento);
}
.ayuda-motivo-chevron { color: var(--color-texto-muy-suave); font-size: 1.2rem; }
.ayuda-volver {
  background: none;
  border: 0;
  padding: 0 0 0.5rem;
  color: var(--color-texto-suave);
  cursor: pointer;
  min-height: var(--tamano-tap);
}
.ayuda-detalle-titulo { margin: 0 0 0.5rem; font-size: 1.05rem; color: var(--color-primario); }
.ayuda-detalle-texto { margin: 0 0 1rem; line-height: 1.5; }
.ayuda-detalle-link { display: inline-block; margin-bottom: 1rem; }
.ayuda-detalle-wa {
  padding-top: 0.9rem;
  border-top: 1px solid var(--color-borde);
}
.ayuda-detalle-nota {
  margin: 0 0 0.7rem;
  font-size: 0.9rem;
  color: var(--color-texto-suave);
}
.ayuda-detalle-wa .btn { width: 100%; }
.ayuda-error {
  margin: 0.8rem 0 0;
  color: var(--color-corazon);
  font-size: 0.9rem;
}

/* ---------------------------------------------------------------------------
   Verificación de identidad: el formulario en bloques numerados
   (2026-08-06). Antes era una lista plana de campos; con la selfie pasaban a
   ser cinco y se volvía difícil de leer. Los bloques hacen visible que es un
   trámite corto con tres pasos, no un formulario largo.
   --------------------------------------------------------------------------- */
.auth-card-ancha { max-width: 640px; }

/* La tarjeta de esta pantalla va sobre fondo tintado y no blanco: los bloques
   numerados son blancos y sin ese contraste no se despegaban de nada — la
   pantalla se veía plana (observación Martín 2026-08-16). */
.dni-card {
  background: var(--color-fondo-suave, #f5f7fa);
  box-shadow: var(--sombra-card-hover);
}
.dni-titulo {
  margin: 0 0 0.5rem;
  font-size: 1.6rem;
  line-height: 1.25;
  color: var(--color-primario);
}
@media (min-width: 768px) {
  .dni-titulo { font-size: 1.9rem; }
}
.dni-bajada {
  margin: 0 0 1.5rem;
  color: var(--color-texto-suave);
  line-height: 1.55;
}

.dni-bloque {
  background: var(--color-fondo-card, #fff);
  border: 1px solid var(--color-borde, #e5e5e5);
  border-radius: var(--radio-medio, 10px);
  padding: 1.1rem 1.2rem 0.4rem;
  margin: 0 0 1.4rem;
  box-shadow: var(--sombra-card);
}
.dni-bloque-titulo {
  display: flex; align-items: center; gap: 0.55rem;
  padding: 0 0.4rem 0 0.2rem;
  font-weight: 600;
  font-size: 1.05rem;
  color: var(--color-primario, #0c2233);
}
.dni-paso-num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.75rem; height: 1.75rem; border-radius: 999px;
  background: var(--color-primario, #0c2233); color: #fff;
  font-size: 0.9rem; font-weight: 700;
  box-shadow: 0 0 0 4px var(--color-fondo-card, #fff);
}
.dni-bloque-bajada {
  margin: 0 0 0.9rem;
  color: var(--color-texto-suave);
  font-size: 0.95rem;
  line-height: 1.5;
}

/* Los consejos de cómo sacar la foto. No son decorativos: la inclinación del
   documento es lo único que rompe la lectura del código de barras (medido
   2026-08-06), así que esta línea es la que más sube la tasa de aprobación
   automática. */
.dni-consejo {
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
  background: #eef5fb;
  border-radius: var(--radio-chico, 6px);
  padding: 0.7rem 0.9rem;
  margin: 0 0 1rem;
  font-size: 0.92rem;
  line-height: 1.45;
}
.dni-consejo-icono { flex: 0 0 auto; font-size: 1.05rem; line-height: 1.35; }

/* Fichas de foto. Reemplazan al `<input type=file>` nativo, que no tenía nada
   de la plataforma y cuyo botón no llegaba a los 44px de alto. El input sigue
   ahí, oculto con `.sr-only` (no `display:none`) para que siga siendo
   enfocable con el teclado; el foco se dibuja sobre la ficha con
   `:focus-within`. Hereda el lenguaje del `.dropzone` del wizard de fotos:
   borde punteado que se vuelve sólido cuando ya hay algo cargado. */
.dni-foto {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem;
  min-height: var(--tamano-tap);
  border: 2px dashed var(--color-borde, #e5e5e5);
  border-radius: var(--radio-medio, 10px);
  background: #fff;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}
.dni-foto:hover { border-color: var(--color-primario); background: #f7f9fc; }
.dni-foto:focus-within {
  border-color: var(--color-primario);
  box-shadow: 0 0 0 3px rgba(12, 34, 51, 0.16);
}
.dni-foto.cargada {
  border-style: solid;
  border-color: var(--color-cian, #00a1ab);
  background: #f2fbfb;
}
.dni-foto-icono {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 46px;
  border-radius: var(--radio-chico, 6px);
  background: #eef5fb;
  font-size: 1.4rem;
  overflow: hidden;
}
.dni-foto.cargada .dni-foto-icono { background: #dff4f4; }
.dni-foto-prev { width: 100%; height: 100%; object-fit: cover; display: block; }
.dni-foto-cuerpo { flex: 1 1 auto; min-width: 0; }
/* Sin wrap: al cargar la foto el botón pasa de "Elegir" a "Cambiar", se roba
   14px y el título se partía en dos renglones. Los títulos son todos cortos,
   así que el recorte no llega a activarse en la práctica. */
.dni-foto-titulo {
  display: block;
  font-weight: 600;
  color: var(--color-primario);
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.dni-foto-hint {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.82rem;
  color: var(--color-texto-suave);
  overflow-wrap: anywhere;
}
/* Cara de botón, aunque sea un `<span>`: el que decide es el `<label>` que lo
   contiene. Un `<button>` acá adentro robaría el click. */
.dni-foto-accion {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--tamano-tap);
  padding: 0 0.8rem;
  border-radius: var(--radio-medio, 10px);
  background: var(--color-primario);
  color: #fff;
  font-weight: 600;
  font-size: 0.875rem;
  white-space: nowrap;
}
.dni-foto:hover .dni-foto-accion { background: var(--color-primario-oscuro); }
.dni-foto.cargada .dni-foto-accion {
  background: #fff;
  color: var(--color-primario);
  border: 1px solid var(--color-borde);
}

.dni-nota-privada {
  display: flex;
  gap: 0.45rem;
  margin: 0.9rem 0 1rem;
  font-size: 0.85rem;
  color: var(--color-texto-suave);
  line-height: 1.45;
}

/* El CTA de la pantalla. Va más alto y con sombra propia: es la única acción
   y compite con tres bloques de contenido arriba. */
.dni-enviar {
  min-height: 52px;
  font-size: 1.05rem;
  box-shadow: 0 6px 16px rgba(255, 82, 0, 0.28);
}

/* LA CONSIGNA de la selfie. Es lo más importante de la pantalla: si no se
   lee, la foto no sirve y hay que pedirla de nuevo. */
.selfie-consigna {
  background: #fff4ec;
  border: 2px dashed var(--color-acento, #ff5200);
  border-radius: var(--radio-medio, 10px);
  padding: 1rem 1.2rem;
  margin: 0 0 1rem;
  text-align: center;
}
.selfie-consigna-titulo {
  margin: 0 0 0.5rem;
  font-weight: 600;
  color: var(--color-texto-suave);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 0.8rem;
}
.selfie-consigna-texto {
  margin: 0 0 0.6rem;
  font-size: 1.15rem;
  line-height: 1.5;
  font-weight: 600;
  color: var(--color-primario, #0c2233);
}
.selfie-consigna-parte { display: inline-block; }
.selfie-consigna-y { font-weight: 400; color: var(--color-texto-suave); margin: 0 0.15rem; }
.selfie-consigna-nota { margin: 0; color: var(--color-texto-suave); }

.form-grupo-check { margin: 1rem 0 1.2rem; }
.check-inline {
  display: flex; align-items: flex-start; gap: 0.6rem;
  font-size: 0.92rem; line-height: 1.5; cursor: pointer;
}
.check-inline input[type="checkbox"] {
  margin-top: 0.2rem;
  flex: 0 0 auto;
  width: 1.15rem;
  height: 1.15rem;
}

.btn-ancho { width: 100%; }

@media (max-width: 480px) {
  .dni-bloque { padding: 0.9rem 0.8rem 0.3rem; }
  .selfie-consigna-texto { font-size: 1.05rem; }
  /* La tarjeta se come 2×1.5rem de los 390px del celular; el aire lo pone el
     bloque, que ya tiene el suyo. */
  .dni-card { padding: 1.1rem 0.9rem; }
}

/* La selfie en el panel del moderador, al lado de las fotos del documento:
   como se pide SIN el documento en la mano (decisión Martín 2026-08-06), la
   comparación es lado a lado y por eso tienen que estar juntas. */
.admin-selfie-consigna {
  background: #eef5fb;
  border-radius: var(--radio-chico, 6px);
  padding: 0.6rem 0.8rem;
  margin: 0.6rem 0 0;
  font-size: 0.9rem;
}

/* Enlaces a las landings por barrio (`partials/enlaces_seo.html`).

   Mobile-first como todo el sitio público: una columna en el celular, dos o
   tres a partir de tablet. Los links son texto y no chips, a propósito — es un
   bloque para leer y para que el crawler lo recorra, no una botonera. */
.enlaces-seo {
  padding: 1.5rem 1rem 2rem;
  border-top: 1px solid var(--color-borde, #e2e6ea);
}
.enlaces-seo-titulo {
  font-size: 1.1rem;
  margin: 0 0 1rem;
  color: var(--color-primario);
}
.enlaces-seo-grupo + .enlaces-seo-grupo { margin-top: 1.5rem; }
.enlaces-seo-grupo h3 {
  font-size: 0.95rem;
  font-weight: 600;
  margin: 0 0 0.5rem;
  color: var(--color-texto-suave);
}
.enlaces-seo-grupo ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.15rem;
}
.enlaces-seo-grupo li {
  min-height: 44px;
  display: flex;
  align-items: center;
  gap: 0.35rem;
}
/* 44px de alto mínimo: la regla táctil del proyecto vale también para una
   lista de links, que es donde más fácil se la olvida. ⚠️ Y va en el `<a>`,
   no sólo en el `<li>`: hasta el 2026-09-12 la fila medía 44 y el link 24
   (R11), así que un dedo en la mitad de arriba de la fila no tocaba nada. */
.enlaces-seo-grupo li > a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tamano-tap);
}
.enlaces-seo-cuantos { color: var(--color-texto-suave); font-size: 0.85rem; }
/* El margen de arriba se fue con el alto táctil: la caja de 44 ya separa el
   «Ver todos» de la lista lo mismo que antes separaba el margen. */
.enlaces-seo-ver-todos {
  display: inline-flex;
  align-items: center;
  min-height: var(--tamano-tap);
  font-weight: 600;
}

@media (min-width: 768px) {
  .enlaces-seo { padding: 2rem 1.5rem 2.5rem; }
  .enlaces-seo-grupo ul { grid-template-columns: repeat(2, 1fr); column-gap: 1.5rem; }
}
@media (min-width: 1024px) {
  .enlaces-seo-grupo ul { grid-template-columns: repeat(3, 1fr); }
}

/* El plazo de borrado en la card del panel (`cuenta/_card_aviso.html`).

   Tono neutro a propósito: no es un error ni un reto, es información. El rojo
   está reservado para el motivo de un rechazo, que sí exige acción. */
.card-aviso-expira {
  background: #f6f7f9;
  border-left: 3px solid var(--color-texto-muy-suave, #8a94a0);
  padding: 0.5rem 0.75rem;
  border-radius: var(--radio-chico);
  margin-top: 0.5rem;
  font-size: 0.9rem;
}
.card-aviso-expira-ayuda {
  display: block;
  font-size: 0.8rem;
  color: var(--color-texto-suave);
}

/* --- Preguntas rápidas del contacto (ficha) --------------------------------
   Los chips que el comprador suma al primer WhatsApp. Vocabulario cerrado: el
   texto que se manda lo arma el servidor, acá solo se elige. */
.ficha-preguntas {
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--color-borde-suave);
}
.ficha-preguntas-titulo {
  margin: 0;
  font-weight: 600;
}
.ficha-preguntas-ayuda,
.ficha-preguntas-tope {
  margin: 0.15rem 0 0;
  font-size: 0.85rem;
  color: var(--color-texto-suave);
}
.ficha-preguntas-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.7rem;
}
.chip-pregunta {
  min-height: var(--tamano-tap);
  padding: 0.5rem 0.9rem;
  background: white;
  border: 1.5px solid var(--color-borde);
  border-radius: 999px;
  font: inherit;
  font-size: 0.9rem;
  color: var(--color-texto);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: border-color 0.12s, background 0.12s;
}
.chip-pregunta:hover:not(:disabled) { border-color: var(--color-primario); }
.chip-pregunta[aria-pressed="true"] {
  border-color: var(--color-primario);
  background: var(--color-primario);
  color: white;
}
.chip-pregunta[aria-pressed="true"]::before { content: "✓ "; }
/* Deshabilitado por tope: se atenúa pero se sigue viendo. Que las opciones
   desaparezcan al llegar al límite se lee como un bug, no como un límite. */
.chip-pregunta:disabled { opacity: 0.45; cursor: default; }

/* ---------- Miga visible al pie de la ficha (`AX-30`, 2026-08-28) ---------- */

/* Hasta el 2026-08-28 la jerarquía «Comprar › Casa › Córdoba › Nueva Córdoba»
   existía sólo adentro del `BreadcrumbList` de JSON-LD: la ficha tenía 8 links
   y ninguno hacia una landing. Este bloque es el mismo dato, visible y
   clickeable.

   Mobile-first, como todo el sitio público. Va afuera del `<article class=
   "ficha">`, que declara `padding-bottom: 5rem` como colchón del sticky CTA.
   **Ese colchón es del sticky y no se toca**: sacarlo deja el CTA fijo
   comiéndose el pie del aviso en mobile. Se cancela desde ACÁ, con el margen
   negativo del bloque nuevo, y por eso son exactamente los mismos 5rem — el
   número no es de gusto. La miga no necesita ese colchón: quien la protege del
   sticky es el `padding-bottom: 5rem` de `main.contenedor`, que queda debajo.

   Sin la compensación quedan 108 px entre el final del aviso y la miga (los 80
   del colchón más los 28 de margen de la última sección) y se ve desprendida.
   Con ella quedan 28, contra los 32 que la home deja antes de `.enlaces-seo`:
   medido en 375 px el 2026-08-28, no elegido a ojo. */
.ficha-migas {
  /* El mismo ancho que `.ficha` (980px) y no `--ancho-max` (1200px): esto es el
     pie del aviso, no una franja del sitio. Si se desalinea, es porque cambió
     el `max-width` de `.ficha` y hay que cambiar los dos. */
  max-width: 980px;
  margin: -5rem auto 0;
  /* Los mismos 24px que `.enlaces-seo` deja debajo de su borde. */
  padding-top: 1.5rem;
  border-top: 1px solid var(--color-borde);
  font-size: 0.9rem;
}
.ficha-migas-lista {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  /* Sólo separación vertical: la horizontal la da el `›`, que además es lo que
     comunica la jerarquía. En 375px la cadena de 5 eslabones baja de renglón y
     necesita ese aire. */
  row-gap: 0.2rem;
}
.ficha-migas-lista li {
  display: flex;
  align-items: center;
  min-width: 0;
}
.ficha-migas-sep {
  color: var(--color-texto-suave);
  padding: 0 0.1rem;
}
.ficha-migas a {
  /* 44px en las DOS direcciones. «Casa» y «PH» son de tres o cuatro caracteres:
     sin piso de ancho quedan en 30-40 px, van pegados en fila y errarle a uno
     te lleva a **otra landing** sin que nada avise. Es el mismo defecto de
     consecuencia silenciosa que `test_area_tactil_chips_numericos.py` documenta
     para los chips del filtro, donde el alto se miró y el ancho no. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tamano-tap);
  min-width: var(--tamano-tap);
  padding: 0 0.3rem;
  color: var(--color-texto-suave);
  text-decoration: none;
}
.ficha-migas a:hover,
.ficha-migas a:focus-visible {
  color: var(--color-acento);
  text-decoration: underline;
}

/* ---------- Pie de la ficha: similares y destacados (C1) ----------

   Van DEBAJO de la miga, y eso no es indiferente: `.ficha-migas` lleva un
   `margin-top: -5rem` calibrado contra el final del aviso (medido a 375px el
   2026-08-28). Metiendo estos bloques ARRIBA de ella, ese -5rem se los comería;
   abajo, la compensación sigue midiendo contra lo mismo que medía y el
   `padding-bottom: 5rem` de `main.contenedor` —el colchón del sticky-cta— queda
   igual debajo de todo. Por eso el orden del template es aviso -> miga ->
   relacionados y no al revés. */
.ficha-relacionados {
  /* El mismo 980px de `.ficha` y `.ficha-migas`: es el pie del aviso, no una
     franja del sitio. Si se desalinea, cambió el `max-width` de `.ficha`. */
  max-width: 980px;
  margin: 2rem auto 0;
}
.ficha-relacionados-titulo {
  /* Los mismos 1.1rem y el mismo `--color-primario` que `.enlaces-seo-titulo`:
     esto y aquello son la misma cosa —una sección de navegación del sitio
     debajo del contenido—, y el precedente ya estaba tomado en la home. No se
     usa `.ficha h2` (0.95rem, mayúsculas, gris) porque ése es el rótulo de una
     sección de ADENTRO del aviso, y estos bloques viven fuera del `<article>`. */
  margin: 0;
  font-size: 1.1rem;
  color: var(--color-primario);
}
/* Dos por fila en el celu, y no una como `.feed`. La ficha ya es larga: ocho
   tarjetas a ancho completo al pie agregan varias pantallas de scroll a la
   página más visitada del sitio, que es justo donde el visitante ya decidió
   seguir mirando o irse. A partir de 900px entran las cuatro en una fila. */
.ficha-relacionados-feed {
  display: grid;
  gap: var(--gap-feed);
  grid-template-columns: repeat(2, 1fr);
  margin-top: 1rem;
}
@media (min-width: 900px) {
  .ficha-relacionados-feed { grid-template-columns: repeat(4, 1fr); }
}

/* ---------------------------------------------------------------------------
   Emprendimientos (2026-08-31) — índice, landing y wizard del proyecto.

   Mobile-first puro, como todo el sitio público: acá está el layout de celu y
   las media queries agrandan. Toques ≥ 44 px.

   ⚠️ La tabla de tipologías NO es una `<table>` con scroll horizontal: es una
   lista de tarjetas que en desktop se acomoda en columnas. Una tabla de 4
   columnas en 390 px obliga a barrer de costado para leer el precio, que es
   justo el dato por el que la persona entró.
--------------------------------------------------------------------------- */

.emprendimiento-kicker {
  text-transform: uppercase;
  letter-spacing: .08em;
  font-size: .75rem;
  color: var(--color-texto-muy-suave);
  margin: 0 0 .25rem;
}

.emprendimiento-datos {
  list-style: none;
  padding: 0;
  margin: 1rem 0 0;
  display: grid;
  gap: .75rem;
}

.emprendimiento-dato-etiqueta {
  display: block;
  font-size: .8rem;
  color: var(--color-texto-muy-suave);
}

.emprendimiento-galeria {
  display: grid;
  gap: .5rem;
  margin: 1.5rem 0;
}

.emprendimiento-galeria img {
  width: 100%;
  height: auto;
  border-radius: var(--radio-medio);
}

.tipologias-lista {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: .5rem;
}

.tipologia-link {
  display: grid;
  gap: .25rem;
  padding: .75rem;
  /* 44 px de alto mínimo: es un destino táctil, no una fila de tabla. */
  min-height: 44px;
  border: 1px solid var(--color-borde, #ddd);
  border-radius: var(--radio-medio);
  text-decoration: none;
  color: inherit;
}

.tipologia-tipo { font-weight: 600; }
.tipologia-specs { font-size: .85rem; color: var(--color-texto-muy-suave); }
.tipologia-precio { font-weight: 700; }

.emprendimientos-grilla {
  list-style: none;
  padding: 0;
  margin: 1.5rem 0 0;
  display: grid;
  gap: 1rem;
}

.emprendimiento-card-link {
  display: block;
  text-decoration: none;
  color: inherit;
  border: 1px solid var(--color-borde, #ddd);
  border-radius: var(--radio-medio);
  overflow: hidden;
}

/* 4:5 vertical, el mismo ratio que la portada de un aviso: la variante ya la
   genera el procesador de fotos, así que no hace falta un recorte nuevo. */
.emprendimiento-card-foto {
  aspect-ratio: 4 / 5;
  background: var(--color-fondo-suave, #f4f4f4);
}

.emprendimiento-card-foto img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.emprendimiento-card-cuerpo { padding: .75rem; }
.emprendimiento-card-nombre { margin: .25rem 0; font-size: 1.05rem; }
.emprendimiento-card-etapa {
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--color-texto-muy-suave);
}
.emprendimiento-card-ubicacion,
.emprendimiento-card-unidades {
  margin: .25rem 0 0;
  font-size: .9rem;
}

.publicar-bifurcacion { margin-top: 1.5rem; }

.wizard-faltantes {
  margin-top: 1rem;
  padding: .75rem;
  border-left: 3px solid var(--color-alerta);
  background: var(--color-fondo-suave, #f4f4f4);
}

.form-fila { display: grid; gap: .5rem; }

.checkbox-grid { display: grid; gap: .5rem; }

.emprendimiento-agregar-unidad { margin-top: 1rem; }

.estado-vacio { margin-top: 2rem; }

/* Acá vivía `.admin-fotos-grid`, la grilla de imágenes del detalle del
   proyecto. Se fue con `AF-07-23` (2026-09-08): esa pantalla pasó a usar
   `preview-fotos` / `preview-foto`, el mismo patrón que el paso 8 del aviso,
   para poder marcar la portada con el badge que ya existe. Dos grillas para lo
   mismo era el motivo por el que una tenía el badge y la otra no. */

@media (min-width: 700px) {
  .emprendimiento-datos { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
  .emprendimiento-galeria { grid-template-columns: repeat(2, 1fr); }
  .emprendimientos-grilla { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
  /* Recién acá la tipología pasa a fila: en desktop entran las tres columnas
     sin apretar. */
  .tipologia-link {
    grid-template-columns: 2fr 2fr 1fr;
    align-items: center;
    gap: 1rem;
  }
  .tipologia-precio { text-align: right; }
  .form-fila { grid-template-columns: 1fr 1fr; }
  .checkbox-grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
}

/* ---------------------------------------------------------------------------
   Card de emprendimiento en la grilla de /buscar (paso 7, 2026-08-31).

   Hereda todo de `.aviso-card` —mismo 4:5, mismo cuerpo, mismo overlay— y sólo
   agrega el distintivo y la línea de unidades. Que se parezcan es a propósito:
   en la grilla conviven, y una card de proyecto que gritara distinto competiría
   con los avisos de los demás publicadores, que es justo lo contrario de lo que
   la sección viene a arreglar.
--------------------------------------------------------------------------- */
.emprendimiento-resultado-badge {
  position: absolute;
  left: .5rem;
  top: .5rem;
  z-index: 4;
  padding: .15rem .5rem;
  border-radius: 999px;
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  background: var(--color-primario, #14304a);
  color: #fff;
}

.emprendimiento-resultado-unidades {
  margin: .25rem 0 0;
  font-weight: 700;
}

/* ---------------------------------------------------------------------------
   Pantalla «Compartir y destacar» (destacados §4, 2026-08-31).

   Mobile-first: es una pantalla que se usa con el celular en la mano, justo
   antes de abrir Instagram. Las placas se ven grandes porque el punto de la
   pantalla es que el publicador VEA lo que va a compartir — si tuviera que
   imaginárselo, no la usaría.
--------------------------------------------------------------------------- */
.compartir-pantalla section { margin-top: 2rem; }
.compartir-pantalla h2 { font-size: 1.05rem; }

.compartir-placas-lista {
  list-style: none;
  padding: 0;
  margin: 1rem 0 0;
  display: grid;
  gap: 1.25rem;
}
.compartir-placa img {
  width: 100%;
  height: auto;
  border-radius: var(--radio-medio);
  border: 1px solid var(--color-borde, #ddd);
}
.compartir-placa .btn { margin-top: .5rem; width: 100%; }

.compartir-texto textarea {
  width: 100%;
  margin: .5rem 0;
  font: inherit;
  padding: .6rem;
  border: 1px solid var(--color-borde, #ddd);
  border-radius: var(--radio-medio);
}

.compartir-redes-botones {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin-top: .75rem;
}
/* 44 px de alto: son destinos táctiles, no links de texto. */
.compartir-redes-botones .btn { min-height: 44px; display: inline-flex; align-items: center; }

@media (min-width: 700px) {
  .compartir-placas-lista { grid-template-columns: repeat(3, 1fr); align-items: end; }
}

/* === Lo que antes iba en `style="…"` (`D7-04`, 2026-09-12) =================
   `style-src` dejó de admitir `'unsafe-inline'`, y con eso el navegador **no
   aplica** un atributo `style` en producción: un nonce se le pone a un
   `<style>`, nunca a un atributo. Cada atributo que había pasó a una clase.

   Casi todas son utilidades chicas —un margen, una alineación—, porque eso
   era lo que decían los atributos. Las que llevan un selector más largo lo
   llevan a propósito: el atributo le ganaba a cualquier regla, y la clase
   sola perdía contra `.cuenta-contenido h2`, `.admin-tabla th` o
   `.leaflet-container a`. Medido en el navegador contra la versión con el
   atributo, propiedad por propiedad.

   Un `style=` nuevo en una plantilla se ve en dev —la CSP está apagada— y en
   producción no: lo frena antes
   `tests/transversales/test_la_csp_ya_no_admite_javascript_en_linea.py`. */
.margen-arriba-1 { margin-top: 1rem; }
.margen-arriba-1-5 { margin-top: 1.5rem; }
.margen-arriba-2 { margin-top: 2rem; }
.margen-vertical-1 { margin: 1rem 0; }
.texto-centrado { text-align: center; }
.en-linea { display: inline; }

/* Un botón que se lee como el texto que lo rodea, subrayado. `.link-boton`
   lo pinta del color primario; éste hereda el del párrafo. */
.boton-como-texto {
  background: none;
  border: 0;
  padding: 0;
  color: inherit;
  text-decoration: underline;
  cursor: pointer;
  font: inherit;
}

/* El «Pago confirmado» del panel del anunciante: la misma caja que el motivo
   de un rechazo, en el color de lo que salió bien. */
.card-aviso-motivo.card-aviso-motivo--ok {
  border-left-color: var(--color-primario);
  background: #f0faf6;
}

/* La cabecera de la baja de notificaciones: una columna angosta, centrada. */
.cuenta-header.cuenta-header--centrado {
  text-align: center;
  max-width: 560px;
  margin: 0 auto;
}

/* Un título de bloque que arranca separado del anterior. La clase sola lo
   separa en cualquier lado; la segunda línea es la que le gana a
   `.cuenta-contenido h2`, que adentro del panel lo pega arriba. Las dos van
   juntas: sin la primera, la clase no pinta nada fuera del panel, que es lo que
   caza `tests/transversales/test_ninguna_pantalla_se_queda_sin_su_css.py`. */
.admin-bloque-titulo--separado,
.cuenta-contenido .admin-bloque-titulo--separado { margin-top: 2rem; }

/* La fila de totales de una tabla del panel, alineada con los montos. Ídem: la
   clase sola, y la que le gana a `.admin-tabla th`. */
.admin-tabla-total,
.admin-tabla th.admin-tabla-total { text-align: right; }

/* Las dos creatividades de un banner en el detalle de campaña del panel, a la
   escala en que el moderador las compara. */
.banner-miniatura { max-width: 120px; height: auto; }
.banner-miniatura-celular { max-width: 60px; height: auto; }

/* El título del aviso en el globo del mapa de resultados (`mapa_buscar.js`). */
.leaflet-container a.mapa-globo-titulo { color: #0a5c44; font-weight: 600; }

/* Los botones «+» y «−» del zoom, a los 44 px del proyecto (`CGO-10`).

   La hoja vendorizada los deja en **26×26**, y **30×30** cuando Leaflet le pone
   `.leaflet-touch` al contenedor, o sea justo en el dedo. Aparecen en las cinco
   pantallas con mapa, y en el paso 2 del wizard es donde el publicador pone el
   pin de su propiedad: el control con el que encuadra el mapa antes de clavarlo.

   ⚠️ **Se sobreescribe acá y no se toca el vendor** —`vendor/leaflet/leaflet.css`
   es una copia byte a byte de Leaflet 1.9.4 y lo sostiene
   `tests/transversales/test_lo_vendorizado_es_lo_que_dice_ser.py`—, así que el
   override tiene que ganar por especificidad y no por orden: `leaflet.css` se
   carga en `head_extra`, o sea **después** de esta hoja. Nombrar además la clase
   del botón sube a (0,3,1) contra los (0,2,1) del vendor. Es el mismo recurso
   que usa `.leaflet-container a.mapa-globo-titulo`, tres líneas arriba, por el
   mismo motivo.

   El `line-height` va con el alto para que el signo siga centrado: el vendor lo
   ataba a su propio 26/30. */
.leaflet-bar a.leaflet-control-zoom-in,
.leaflet-bar a.leaflet-control-zoom-out,
.leaflet-touch .leaflet-bar a.leaflet-control-zoom-in,
.leaflet-touch .leaflet-bar a.leaflet-control-zoom-out {
  width: var(--tamano-tap);
  height: var(--tamano-tap);
  line-height: var(--tamano-tap);
}
