/* Terminal financiero: oscuro, denso en datos, jerarquía clara; sans para UI, mono
   para cifras.

   ROLES DE COLOR (fijos — el color ES dato, no decoración):
   - --accent (cian)  : SOLO interacción — nav activa, links, botón primario, focus,
                        chip activa, barras y estados del pipeline en gestión.
   - semáforo         : dato — --green favorable / --yellow atención / --red riesgo.
   - ámbar #e0b64c    : confianza de fuente (lead no oficial / sin confirmar), siempre
                        acompañado de ⚠.
   ESCALA TIPOGRÁFICA (5 tokens, nada fuera de ellos):
   --fs-xs 11px (th, labels, tags, h3/h4) · --fs-sm 12px (meta, dt, chips) ·
   --fs-md 13px (body, celdas, inputs) · --fs-lg 16px (títulos de página) ·
   --fs-xl 20px (cifras KPI).
   RADIOS: --r-ctl 4px (inputs/botones/chips) · --r-panel 6px (paneles/tablas/diálogos);
   999px queda reservado a las barras. */
/* El gutter del scroll SIEMPRE reservado: sin esto, una pestaña cuyo contenido cabe
   justo en el viewport (títulos) hacía desaparecer la barra y TODO el layout centrado
   saltaba de ancho al cambiar de pestaña (bug UX 2026-09-03). */
html { scrollbar-gutter: stable; }

/* `hidden` VUELVE A SIGNIFICAR OCULTO. Es la regla más aburrida de la hoja y arregló dos
   bugs vivos el 2026-09-07, así que conviene entender el mecanismo antes de tocarla.

   El atributo `hidden` lo apaga la hoja del NAVEGADOR con `[hidden] { display: none }`, y
   en la cascada el origen manda ANTES que la especificidad: cualquier `display` escrito por
   nosotros —por débil que sea su selector— revive el elemento. O sea que un elemento
   pierde su `hidden` no por un error, sino por recibir un `display` cualquiera, meses
   después y desde otra parte del archivo.

   Los dos que se encontraron así, los dos ocultándose bien desde el JS y visibles igual:
     · `.accion-hero[data-visitante]` — la portada le ofrecía CREAR CUENTA a quien ya tenía
       cuenta (`display: inline-flex`);
     · `#cu-reenviar-caja.dlg-actions` en /cuenta — ofrecía REENVIAR EL ENLACE a quien ya
       había confirmado su correo (`display: flex`), justo la acción sin efecto que el
       comentario de esa plantilla dice que no hay que ofrecer.

   Va GLOBAL y con `!important` a propósito, en vez de un `.clase[hidden]` por caso. Con
   parches puntuales el arreglo dura hasta el próximo `display`, y quien lo escriba no tiene
   por qué saber que aquel elemento se esconde desde un JS que vive en otro archivo. Con
   `!important` la posición dentro de la hoja tampoco importa, que es lo que hace que la
   regla siga valiendo cuando alguien añada estilos debajo.
   Es seguro porque en esta app `hidden` significa siempre «no está», nunca «está pero no se
   ve»: no hay una sola regla que enseñe a propósito un elemento con el atributo puesto, y
   `test_hidden.py` falla si alguien escribe la primera. Para esconder algo conservando su
   sitio, se usa `visibility` o una clase — no `hidden`. */
[hidden] { display: none !important; }

:root {
  /* El navegador debe pintar en oscuro TODO lo nativo: la scrollbar de la ventana (la
     única barra, por regla del proyecto), el calendario de los date, los popups de los
     selects y el autofill. Sin esto quedaban blancos sobre el tema. */
  color-scheme: dark;
  --bg: #0b0f14;
  --panel: #121820;
  --panel-2: #1a222d;
  --border: #243140;
  --border-soft: #18212c;
  --text: #d6dee8;
  --muted: #8a99ab;
  --accent: #3dd2f0;
  --accent-dim: #14404d;
  --green: #2fd573;
  --yellow: #f0c419;
  --red: #f0554d;
  --amber: #e0b64c;
  /* Semáforo en DOS intensidades: el tono pleno (arriba) para cápsulas, banners y alertas
     puntuales; estos desaturados para el dato MASIVO en tablas — cuando el 70% de una
     columna es roja, el rojo pleno deja de alarmar. El rol semántico no cambia. */
  --green-data: #4cc584;
  --red-data: #e8746c;
  --yellow-data: #d6b13c;
  /* Placeholder hundido: un '—' no es información y no puede pesar lo mismo que un label. */
  --dim: #566373;
  /* Elevación: luz cenital de 1px + sombra larga que asienta el panel sin aclarar nada. */
  --shadow-1: inset 0 1px 0 rgba(255,255,255,.03), 0 12px 28px -18px rgba(0,0,0,.85);
  /* Hover del botón primario (el brightness() era imperceptible sobre --accent-dim). */
  --accent-dim-2: #1a5568;
  --th-text: #9db0c4;
  --fs-xs: 11px;
  --fs-sm: 12px;
  --fs-md: 13px;
  --fs-lg: 16px;
  --fs-xl: 20px;
  --r-ctl: 4px;
  --r-panel: 6px;
  --topbar-h: 46px;  /* alto de .topbar; lo usa el encabezado fijo de la tabla */
  --radius: 6px; /* alias legado de --r-panel */
  --mono: ui-monospace, "Cascadia Mono", Consolas, "Courier New", monospace;
  --sans: system-ui, "Segoe UI", Roboto, sans-serif;
}
* { box-sizing: border-box; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: var(--fs-md);
  line-height: 1.45;
}
main { padding: 12px 18px 48px; max-width: 1500px; margin: 0 auto; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
h2 { font-weight: 600; font-size: var(--fs-lg); }
h3, h4 { margin: 4px 0 10px; letter-spacing: .08em; font-size: var(--fs-xs); color: var(--muted); font-weight: 600; text-transform: uppercase; }
.muted { color: var(--muted); font-weight: normal; text-transform: none; letter-spacing: 0; }
.num, td.num, th.num { font-family: var(--mono); }

/* Barra superior */
.topbar {
  display: flex; align-items: center; gap: 26px;
  padding: 9px 18px; background: var(--panel);
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 10;
  height: var(--topbar-h);
  box-shadow: 0 8px 16px -14px rgba(0,0,0,.9);
}
/* Logo BETA: lockup tipográfico de dos líneas (marca + tagline) que cabe en la topbar. */
.logo { display: flex; flex-direction: column; align-items: center; line-height: 1.05; text-decoration: none; color: var(--text); }
.logo:hover { text-decoration: none; }
.logo-marca { font-family: var(--mono); font-weight: bold; letter-spacing: 3px; font-size: 15px; }
.logo-accent { color: var(--accent); }
.logo-tagline {
  font-size: 8.5px; letter-spacing: .32em; text-transform: uppercase;
  color: var(--muted); margin-top: 1px;
}
/* LOS BOTONES DEL BANNER MIDEN TODOS LO MISMO (encargo del dueño, 2026-09-07): «si bien
   cada uno tiene cierta cantidad de letras, cada uno de los botones tenga el mismo tamaño
   de encuadrado». Con anchos desiguales —[ADMIN] son 7 caracteres y [VISTA LISTADO] 15— la
   fila se lee como una escalera y el recuadro activo cambia de tamaño al navegar, que es
   justo el elemento que no debería moverse.

   Se hace con GRID y `grid-auto-columns: 1fr`, no con un `min-width` en píxeles. La razón
   es que aquí el ancho no lo decide esta hoja: lo deciden los RÓTULOS, que el dueño ya
   renombró dos veces en dos días ([MAPA]→[VISTA MAPA], [PIPELINE]→[MIS REMATES]). Un número
   fijo se queda corto al primer rótulo más largo —y entonces ese botón desborda su caja y
   los demás quedan con un hueco— o se queda largo y deja aire muerto. Con `1fr` todas las
   columnas valen lo que la MÁS ANCHA, medida por el navegador: el próximo rótulo se ajusta
   solo.

   `[hidden]` sigue funcionando: un enlace oculto sale del grid y no deja columna vacía —lo
   garantiza la regla global de `[hidden]` del principio de la hoja—, así que un visitante
   ve dos botones iguales y un admin cinco iguales, sin huecos.

   En pantalla ANGOSTA se revierte a flex (más abajo): igualar al más ancho multiplica el
   ancho total por el número de botones, y esta barra ya va justa en un teléfono. */
.topbar nav {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 4px;
}
.topbar nav a {
  color: var(--muted); padding: 4px 12px; border-radius: var(--r-ctl);
  font-size: var(--fs-md); letter-spacing: .05em;
  text-align: center; white-space: nowrap;
}
.topbar nav a:hover { color: var(--text); background: var(--panel-2); text-decoration: none; }
.topbar nav a.active { color: var(--accent); background: var(--accent-dim); }
.topbar-right { margin-left: auto; color: var(--muted); font-family: var(--mono); font-size: var(--fs-sm); }

/* Indicadores rápidos: strip de UNA línea (label y cifra en la misma baseline),
   no cards — los 4 números no ganan 90px de alto. */
.stats {
  display: flex; flex-wrap: wrap; margin-bottom: 10px;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--r-panel);
  box-shadow: var(--shadow-1);
}
.stat {
  display: flex; align-items: baseline; gap: 8px;
  padding: 7px 16px; border-left: 1px solid var(--border);
}
.stat:first-child { border-left: none; }
.stat b { font-family: var(--mono); font-size: 17px; font-weight: 600; }
.stat span { color: var(--muted); font-size: var(--fs-xs); letter-spacing: .06em; text-transform: uppercase; }

/* KPI de ficha: mismas .stats pero con cifra grande (es la franja de decisión). */
.stats.kpi .stat b { font-size: var(--fs-xl); }
.stats.kpi .stat { flex-direction: column; align-items: flex-start; gap: 2px; padding: 8px 16px; }

/* Filtros */
.filters {
  display: flex; flex-wrap: wrap; gap: 12px; align-items: end;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--r-panel);
  padding: 8px 12px; margin-bottom: 8px;
  box-shadow: var(--shadow-1);
}
.filters label { display: flex; flex-direction: column; gap: 4px; color: var(--muted); font-size: var(--fs-xs); letter-spacing: .05em; }
.filters .spacer { flex: 1; }
/* EL DESPLEGABLE DE TIPO DE INMUEBLE (encargo del dueno, 2026-09-14). <details> nativo,
   como el arbol de comunas de /config: trae el teclado y el Esc sin inventar un patron de
   interaccion propio. El `summary` se disfraza de control de la barra —mismo alto, mismo
   borde y misma tipografia que los <input> de al lado— porque en una fila de controles un
   triangulo de navegador se lee como algo de otra pantalla.

   EL PANEL VA ABSOLUTO Y POR ENCIMA (`z-index`), no empujando la barra: desplegarlo no
   puede mover de sitio los campos que estan a su derecha. El `position: relative` del
   <details> es lo que lo ancla; sin el se posicionaria contra la pagina. */
.menu-tipos { position: relative; }
.menu-tipos > summary {
  list-style: none; cursor: pointer; user-select: none;
  background: var(--panel-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-ctl);
  padding: 6px 9px; min-width: 10em;
  font-size: var(--fs-md); letter-spacing: normal; text-transform: none;
}
.menu-tipos > summary::-webkit-details-marker { display: none; }
/* EL CARET, y la lección de CÓMO se rompió. En la pantalla se leía «todos BE» —lo cazó
   el dueño el 2026-09-14—, y la causa no estaba en el CSS: al escribir este archivo
   desde Python, el `\25` de `\25BE` (el triángulo ▾) se leyó como un escape OCTAL.
   0o25 es 0x15, un carácter de control invisible, así que en el disco quedó ese byte
   mudo seguido de un «BE» perfectamente visible.
   Va el carácter LITERAL, que es lo que ya hacen las flechas de ordenar del listado: en
   un archivo UTF-8 un escape numérico solo añade una forma de equivocarse — y esta no
   rompe nada, no sale en ningún log y solo se ve MIRANDO la pantalla. */
.menu-tipos > summary::after { content: " ▾"; color: var(--muted); }
.menu-tipos[open] > summary { border-color: var(--accent); }
/* Cuando de verdad acota, el resumen se enciende: un control plegado que esconde filas y
   se ve igual que uno que no esconde nada es como se pierde una fila sin enterarse. Es la
   misma razon por la que el listado dice cuantos oculta el filtro de la cuenta. */
.menu-tipos > summary.acotando { border-color: var(--accent); color: var(--accent); }
.menu-tipos-caja {
  position: absolute; z-index: 30; top: calc(100% + 4px); left: 0;
  min-width: 100%; max-height: 320px; overflow-y: auto;
  display: flex; flex-direction: column; gap: 2px;
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--r-ctl); box-shadow: var(--shadow-1); padding: 8px 10px;
}
.menu-tipos-caja label {
  flex-direction: row; align-items: center; gap: 7px;
  color: var(--text); font-size: var(--fs-sm); letter-spacing: normal;
  white-space: nowrap; cursor: pointer;
}
input, select, textarea, button {
  font-family: inherit; font-size: var(--fs-md);
  background: var(--panel-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-ctl); padding: 6px 9px;
}
input[type=number], input[type=date] { font-family: var(--mono); }
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent); }
/* Un solo anillo de foco, visible de verdad (el anterior era --accent-dim: 1.4:1). */
:is(a, button, select, input, textarea, summary, .chip, .tab):focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
button {
  cursor: pointer; background: var(--accent-dim); color: var(--accent);
  border-color: transparent; letter-spacing: .06em; font-weight: 600;
  transition: filter .12s ease;
}
/* Hover explícito por variante: el brightness(1.25) anterior era imperceptible sobre
   los fondos oscuros. Se excluyen chips y tabs, que definen su propio estado activo. */
button:not(.secondary):not(.chip):not(.tab):hover { background: var(--accent-dim-2); }
button:disabled { opacity: .35; cursor: default; }
button.secondary { background: var(--panel-2); color: var(--text); border-color: var(--border); font-weight: normal; }
button.secondary:hover { background: var(--border-soft); border-color: var(--muted); color: var(--text); }

/* Fila de acceso rápido: chips de cercanía + estado de los datos, en una sola línea. */
.quick { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; flex-wrap: wrap; }
.quick .spacer { flex: 1; }
.chip {
  background: var(--panel-2); color: var(--muted); border: 1px solid var(--border);
  border-radius: var(--r-ctl); padding: 4px 12px; font-size: var(--fs-sm); letter-spacing: .05em;
  cursor: pointer; font-weight: 600;
}
.chip:hover { color: var(--text); }
.chip.active { background: var(--accent-dim); color: var(--accent); border-color: var(--accent); }

/* Tabla principal. border-collapse: separate (no collapse) para que el encabezado
   fijo (sticky) nunca "sangre" las filas al hacer scroll. */
table.grid {
  width: 100%; border-collapse: separate; border-spacing: 0; background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--r-panel); overflow: hidden;
  box-shadow: var(--shadow-1);
}
table.grid th {
  text-align: center; padding: 8px 9px; font-size: var(--fs-xs); color: var(--th-text);
  letter-spacing: .07em; border-bottom: 1px solid var(--border);
  cursor: pointer; white-space: nowrap; user-select: none;
  background: var(--panel-2);
}
table.grid th:hover { color: var(--accent); }

/* Contenedor del listado. NO tiene scroll propio: la tabla crece completa y scrollea la
   ventana del navegador. Dos barras de scroll anidadas obligaban a adivinar cuál mover y
   dejaban la mitad de las filas fuera de un alto fijo.

   Por qué 'overflow: visible' es obligatorio y no un detalle de estilo: cualquier valor de
   overflow distinto de visible —también en el propio <table>, verificado en Chromium—
   convierte al elemento en contenedor de scroll, y entonces el thead fijo se ancla a ÉL en
   vez de al viewport y se va con las filas. Ojo con el atajo 'overflow-x: auto' +
   'overflow-y: visible': la especificación convierte ese 'visible' en 'auto' y vuelven las
   dos barras. Si la tabla no cabe a lo ancho, desborda a la página, que es justamente el
   scroll que se quiere usar.

   El marco (borde y radio) lo dibuja la propia tabla, no este envoltorio: sin overflow el
   envoltorio no recorta nada, así que un borde suyo quedaría más angosto que la tabla y la
   dejaría asomando por fuera en pantallas donde no cabe. Con el marco en la tabla, ambos
   miden siempre lo mismo. */
.grid-wrap { overflow: visible; }
.grid-wrap table.grid { overflow: visible; }
/* El encabezado se fija al viewport, justo bajo la barra superior (que tiene z-index
   mayor, así que gana si se solapan). Antes se fijaba al contenedor y solo cubría su
   alto; ahora acompaña todo el recorrido de la página. */
.grid-wrap thead th {
  position: sticky; top: var(--topbar-h); z-index: 3;
  background: var(--panel-2);
  /* Plano de separación cuando las filas pasan por debajo al scrollear. */
  box-shadow: 0 1px 0 var(--border), 0 8px 14px -10px rgba(0,0,0,.7);
}
/* Sin overflow el contenedor ya no recorta, así que las esquinas redondeadas del borde
   las siguen las propias celdas. */
.grid-wrap thead th:first-child { border-top-left-radius: var(--r-panel); }
.grid-wrap thead th:last-child { border-top-right-radius: var(--r-panel); }
.grid-wrap tbody tr:last-child td:first-child { border-bottom-left-radius: var(--r-panel); }
.grid-wrap tbody tr:last-child td:last-child { border-bottom-right-radius: var(--r-panel); }
table.grid td { padding: 6px 9px; border-bottom: 1px solid var(--border-soft);
                white-space: nowrap; text-align: center; }
table.grid tfoot td, table.grid tfoot th { text-align: center; font-weight: 600; }

/* EL CUADERNO DEL JUICIO (2026-09-13). Encargo del dueño: «toda la línea de un folio se
   debe ver sin hacer textos en dos líneas… reducir significativamente el alto por línea
   para que se vean muchas más en la pantalla sin necesidad de hacer tanto scroll».

   NO usa `tabla-lectura`, y esa es la decisión: aquella existe para ENVOLVER el texto
   largo (`overflow-wrap: anywhere`), que es exactamente lo contrario. Aquí una causa trae
   65 filas y lo que se hace con ellas es RECORRERLAS con la vista: el ritmo regular vale
   más que ver entera la descripción de las cuatro que la tienen larga — el mismo criterio
   con el que la tarjeta del mapa corta su dirección en una línea.

   El recorte lo hace `text-overflow: ellipsis` y NO un contador de caracteres en el JS:
   así se adapta al ancho real: en un portátil corta antes y en un monitor ancho enseña
   más. Un número fijo estaría mal en las dos pantallas. El texto completo va en el
   `title` de la celda, así que no se pierde nada. */
table.grid.tabla-cuaderno { table-layout: fixed; }
table.grid.tabla-cuaderno th { padding: 4px 8px; }
table.grid.tabla-cuaderno td {
  padding: 2px 8px; line-height: 1.35; overflow: hidden; text-overflow: ellipsis;
}
/* Anchos MEDIDOS sobre los 65 trámites de la causa real: folio ≤ 4 caracteres,
   trámite ≤ 18, foja ≤ 2. La DESCRIPCIÓN no lleva ancho: se queda con lo que sobre, que
   es lo único que debe crecer con la pantalla. */
table.grid.tabla-cuaderno th:nth-child(1) { width: 98px; }   /* FECHA   */
table.grid.tabla-cuaderno th:nth-child(2) { width: 62px; }   /* FOLIO   */
table.grid.tabla-cuaderno th:nth-child(3) { width: 136px; }  /* TRÁMITE */
table.grid.tabla-cuaderno th:nth-child(5) { width: 62px; }   /* DOC     */
/* La columna FOJA se retiró el 2026-09-15: es el número de página del expediente en papel
   —sirve para citar un escrito ante el tribunal, no para decidir una compra— y se llevaba
   54 px de una tabla que ya recorta la descripción. Por eso DOC. pasó de la 6ª a la 5ª. */
/* 62 px y no 52: en la celda caben DOS enlaces desde que se bajan los certificados
   de envio (2026-09-13) — el escrito y su comprobante, como los pinta PJUD. Son dos
   iconos de 13 px, su separacion y el relleno de la celda. Con `table-layout: fixed`
   quedarse corto no desborda: recorta el segundo icono en silencio. */
table.grid.tabla-cuaderno td:nth-child(5) { white-space: nowrap; }
/* La DESCRIPCIÓN va a la IZQUIERDA, y es la tercera alineación de la app después del
   centro y de la derecha de las columnas numéricas. El motivo es el recorte: centrada,
   cada fila empieza en una x distinta y la columna deja de poder recorrerse de un
   vistazo, que es justo para lo que se hizo compacta. */
table.grid.tabla-cuaderno td:nth-child(4) { text-align: left; }
/* EL ICONO DEL PDF VA EN ROJO, y es una EXCEPCIÓN DECLARADA a la regla de color de la app
   (encargo del dueño, 2026-09-15: «mejorar la columna doc. al icono de pdf con rojo para
   que sea más sencillo entender que se puede descargar»).
   En esta aplicación el cian significa INTERACCIÓN y el rojo, riesgo — aquí el rojo no
   dice riesgo: dice PDF, que es lo que ese color significa fuera de la app desde que
   Acrobat existe. Ganó el idioma de fuera, que es el que trae puesto quien llega. Se usa
   el rojo PLENO y no el de dato: a 13 px el desaturado se lee marrón.
   Sigue siendo un control y sigue encendiéndose al posarse encima. */
table.grid.tabla-cuaderno a.doc-pdf { color: var(--red); display: inline-flex; }
/* Aire entre el documento y su certificado: pegados se leen como un solo
   dibujo raro y no como dos cosas que se pueden abrir por separado. */
table.grid.tabla-cuaderno a.doc-pdf + a.doc-pdf { margin-left: 6px; }
table.grid.tabla-cuaderno a.doc-pdf:hover { filter: brightness(1.35); }
table.grid tbody tr { transition: background .1s ease; }
/* Zebra un paso MÁS OSCURO que el panel: estructura de lectura para 50 filas de columnas
   de anchos muy distintos, sin competir con el hover (que aclara). Va en los td para
   respetar los radios de la última fila (border-collapse: separate). */
table.grid tbody tr:nth-child(even) td { background: #0f141b; }
table.grid tbody tr:hover { cursor: pointer; }
/* Las tablas de diagnóstico de /admin (pestaña CICLO) son una MIRADA: ninguna fila lleva
   a ninguna parte, así que no muestran la mano del listado. La alineación centrada de
   `table.grid` se conserva intacta — lo único que cambia es el cursor. */
table.grid.tabla-lectura tbody tr:hover { cursor: default; }
table.grid tbody tr:hover td { background: #202b39; }
/* La fila del listado ES un link (navega a la ficha): la quilla cian lo dice. Solo en el
   listado principal — las demás tablas no navegan. */
#tabla tbody tr:hover td:first-child { box-shadow: inset 2px 0 0 var(--accent); }
/* LAS COLUMNAS NUMÉRICAS VUELVEN A LA DERECHA (aprobado por el dueño, 2026-09-11, con la
   maqueta del rediseño delante). REEMPLAZA a la regla del 2026-08-31, que las centraba
   junto con todo lo demás.

   El motivo es de lectura y se ve con dos cifras de la cartera real: «737» y «6.819»
   centradas dejan las unidades, las decenas y los miles en columnas distintas, así que
   comparar dos filas obliga a leer dígito a dígito en vez de mirar el largo. Alineadas a la
   derecha, el orden de magnitud se ve solo — que es justo para lo que existe la
   monoespaciada con cifras tabulares.

   LO DEMÁS NO SE MUEVE: encabezados, texto y totales siguen centrados. Lo que cambia es la
   excepción de las cifras, no el criterio de la tabla. */
th.num, td.num { text-align: right; }
/* La columna Δ COMUNAL se auto-oculta cuando el dataset no tiene ninguna referencia:
   una columna 100% de guiones solo rompe el ritmo de lectura. Reaparece sola. */
table.sin-ref .col-ref { display: none; }

.estado-tag { color: var(--accent); font-size: var(--fs-xs); letter-spacing: .08em; font-family: var(--mono); }
/* Un remate descartado, ganado o perdido es una decisión TOMADA, no una oportunidad en
   gestión: pierde el cian, que en esta interfaz significa "esto está vivo". Son las filas
   que la vista rápida debe saltarse. `.descartado` se conserva por compatibilidad con
   cualquier marcado viejo; el nombre vigente es `.cerrado`. */
.estado-tag.cerrado,
.estado-tag.descartado { color: var(--muted); }
/* Botón + EVALUAR del listado: 50 cajas cian idénticas eran el elemento dominante de la
   tabla ("se marca solo lo que exige atención"). Reposo silencioso; se enciende en cian
   —el rol de interacción— al pasar sobre SU fila o al enfocarlo con teclado. */
button.btn-evaluar {
  padding: 2px 9px; font-size: var(--fs-xs); letter-spacing: .06em;
  background: transparent; color: var(--muted); border: 1px solid var(--border-soft);
}
table.grid tbody tr:hover button.btn-evaluar, button.btn-evaluar:focus-visible {
  background: var(--accent-dim); color: var(--accent); border-color: transparent;
}
/* UF autocontenida dentro de la celda de monto (dato secundario, atenuado). */
.sub-uf { color: var(--muted); font-size: var(--fs-xs); font-family: var(--mono); }
/* Deuda heredada anotada bajo el mínimo: mismo tamaño que .sub-uf, pero en el rojo del
   semáforo — no es una equivalencia informativa, es un costo que se suma al precio. */
/* Subunidades que arrastra el lote (+E estacionamiento, +B bodega): atenuadas, porque
   acompañan al tipo sin competir con él. */
.sub-unidad { color: var(--muted); font-size: var(--fs-xs); font-family: var(--mono); }

.pos { color: var(--green); }
.neg { color: var(--red); }
.warn { color: var(--yellow); }

/* El DESCUENTO en palabras (`descuentoHTML` de common.js — encargo del dueño, 2026-09-06).
   Vive en la columna DESCUENTO del listado y en las cuatro filas de REFERENCIA COMERCIAL
   de la ficha, o sea en el DATO MASIVO: por eso lleva el tono DESATURADO también fuera de
   `table.grid`, donde `.pos`/`.neg` a secas serían pleno. El pleno se reserva para la
   alerta puntual, que es lo que el semáforo de dos intensidades protege.
   `nowrap` porque «4,5% de sobreprecio» partido en dos líneas deja un «de sobreprecio»
   suelto que se lee como otra fila. */
.dcto { white-space: nowrap; }
.dcto.pos { color: var(--green-data); }
.dcto.neg { color: var(--red-data); }

/* Ficha de detalle */
.volver { display: inline-block; margin-bottom: 10px; color: var(--muted); font-size: var(--fs-sm); }
.ficha-head { margin-bottom: 10px; }
.ficha-head h2 {
  margin: 4px 0 2px; font-size: var(--fs-lg); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  max-width: 110ch;
}
.ficha-head .meta { color: var(--muted); font-size: var(--fs-sm); font-family: var(--mono); }
.addr-full summary { font-size: var(--fs-xs); color: var(--muted); cursor: pointer; }
.addr-full p { font-size: var(--fs-sm); color: var(--muted); max-width: 95ch; margin: 6px 0 0; }
.detail-grid {
  display: grid; grid-template-columns: minmax(380px, 1fr) minmax(380px, 1fr);
  gap: 14px; align-items: start;
}
/* Sin panel de títulos (miradas básica y preliminar) el análisis es UNA columna,
   CENTRADA: pegada a la izquierda con 500px de negro muerto leía como página rota. */
.detail-grid.solo { grid-template-columns: minmax(380px, 900px); justify-content: center; }
#ficha:has(.detail-grid.solo) .ficha-head { max-width: 900px; margin-inline: auto; }
@media (max-width: 900px) { .detail-grid { grid-template-columns: 1fr; } }
.panel {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--r-panel); padding: 16px;
  box-shadow: var(--shadow-1);
}
/* Una tabla dentro de un panel no lleva sombra propia: doble sombra anidada ensucia. */
.panel table.grid { box-shadow: none; }
/* Y es DENSA: la muestra trae 9 columnas que deben caber en los ~870px del panel sin
   desbordar el marco ni ganar scroll propio (regla: una sola barra de scroll). El
   padding lateral de la tabla del listado (9px) la hacía más ancha que la ficha. */
.panel table.grid th, .panel table.grid td { padding-inline: 5px; }
.panel textarea { width: 100%; min-height: 64px; resize: vertical; font-size: var(--fs-sm); line-height: 1.4; }
/* ── LA LECTURA DEL EXPEDIENTE (2026-09-15) ────────────────────────────────────────
   Encargo del dueño: el expediente agrupado, «cosa de que no se vean los 56 documentos,
   sino que se pueda desplegar para verlos», con los datos de la causa y los hallazgos
   delante. El <details> es NATIVO, como el filtro de comunas: trae teclado, marcador de
   estado y la búsqueda del navegador sin inventar un patrón nuevo. */
details.plegable-cuaderno { margin-top: 10px; }
details.plegable-cuaderno > summary {
  cursor: pointer; font-size: var(--fs-sm); color: var(--text);
  padding: 5px 0; user-select: none; letter-spacing: .04em;
}
details.plegable-cuaderno > summary:hover { color: var(--accent); }
/* El resumen se enciende al abrirse: dice de un vistazo cuál de los tres está desplegado. */
details.plegable-cuaderno[open] > summary { color: var(--accent); }

/* UN HALLAZGO ES UN DATO, NO UNA ALERTA: el tono va en la quilla izquierda y nunca en el
   fondo. Es la misma decisión de las tarjetas de RENTABILIDAD y de las columnas de MIS
   REMATES — un bloque en rojo pleno gritaría más que el texto, y aquí puede haber trece
   seguidos. */
.hallazgo {
  border-left: 3px solid var(--border); padding: 6px 0 6px 10px; margin: 8px 0;
  font-size: var(--fs-sm); line-height: 1.45;
}
/* El asunto es la PREGUNTA que abre el detalle: se lee como texto normal, no como una
   etiqueta de archivo. Antes iba en versalitas de 11 px y en gris —era el nombre del tema,
   `citacion_acreedor`— y ahora es la frase que decide si vale la pena abrir. */
summary.hallazgo-tema {
  cursor: pointer; font-size: var(--fs-sm); color: var(--text);
  list-style: none; user-select: none;
}
summary.hallazgo-tema::-webkit-details-marker { display: none; }
summary.hallazgo-tema::before { content: "▸ "; color: var(--muted); }
details[open] > summary.hallazgo-tema::before { content: "▾ "; }
summary.hallazgo-tema:hover { color: var(--accent); }
/* La marca de que una fila tiene aclaración: discreta, porque casi nunca hay que abrirla.
   El texto completo va en su `title` y, a la vista, en el desplegable del bloque. */
.nota-marca { color: var(--muted); cursor: help; font-size: var(--fs-xs); }
.nota-marca:hover { color: var(--accent); }
/* Las aclaraciones son PROSA dentro de una rejilla pensada para cifras: sin esto heredan
   la tipografía monoespaciada de `.kv dd` y un párrafo de 200 caracteres se vuelve ilegible. */
.aclaraciones dd { font-family: inherit; color: var(--muted); line-height: 1.45; }
/* Varios hallazgos del MISMO asunto van dentro de un solo bloque: la separación entre
   ellos es un filete tenue, no otra quilla de color — el color ya dijo la severidad del
   grupo y repetirlo por cada línea lo convierte en decoración. */
.hallazgo-item + .hallazgo-item {
  margin-top: 7px; padding-top: 7px; border-top: 1px solid var(--border-soft);
}
/* LA CITA ES LA PRUEBA y se ve como lo que es: el texto del tribunal, no el nuestro. */
.hallazgo-cita {
  font-style: italic; color: var(--muted); margin-top: 4px;
  border-left: 1px solid var(--border-soft); padding-left: 8px;
}
.hallazgo-fuente { font-family: var(--mono); font-size: var(--fs-xs); color: var(--muted); margin-top: 3px; }
/* El rótulo va en el rojo de DATO, no en el pleno de alerta: lo que sigue son hechos del
   expediente, no una advertencia de la aplicación. */
.rotulo-riesgo {
  font-size: var(--fs-xs); letter-spacing: .1em; font-weight: 600;
  color: var(--red-data); margin-top: 10px;
}
.lista-noconsta { margin: 6px 0 0; padding-left: 18px; font-size: var(--fs-sm); color: var(--muted); }
.lista-noconsta li { margin: 3px 0; line-height: 1.4; }

/* ── EXPEDIENTE (F2): la única carga manual autorizada, solo en la mirada TÍTULOS ── */
/* El botón-etiqueta que envuelve el <input type=file> oculto se ve como button. */
label.btn-file {
  display: inline-block; cursor: pointer; background: var(--panel-2); color: var(--text);
  border: 1px solid var(--border); border-radius: 6px; padding: 6px 12px;
  font-size: var(--fs-sm); letter-spacing: .05em; user-select: none;
}
label.btn-file:hover { background: var(--border-soft); border-color: var(--muted); }
/* El ✕ de descartar una pieza: en reposo silencioso, rojo al hover (control repetido). */
button.link-quiet {
  background: none; border: none; padding: 0 4px; color: var(--muted);
  cursor: pointer; font-size: var(--fs-sm);
}
button.link-quiet:hover { color: var(--red); background: none; }
ul.exp-docs { list-style: none; margin: 8px 0 0; padding: 0; }
ul.exp-docs li { padding: 2px 0; }
.exp-form input, .exp-form select, .exp-form textarea { font-size: var(--fs-sm); }
/* La tabla editable de gravámenes usa layout FIJO (colgroup con %): 8 columnas de
   inputs jamás pueden desbordar el panel — regla: sin scroll interno. */
.exp-form table.grid.exp-gravs { table-layout: fixed; }
.exp-form table.grid input, .exp-form table.grid select {
  width: 100%; min-width: 0; box-sizing: border-box;
}

.panel-inner {
  background: var(--panel-2); border: 1px solid var(--border);
  border-radius: var(--r-panel); padding: 12px; margin: 12px 0;
}
.kv { display: grid; grid-template-columns: 180px 1fr; gap: 5px 12px; margin: 0; }
.kv dt { color: var(--muted); font-size: var(--fs-sm); }
.kv dd { margin: 0; font-family: var(--mono); font-size: var(--fs-md); }
.kv dd.destacado { font-weight: 700; }
/* REMATE EN DOS COLUMNAS (encargo del dueño, 2026-09-12: «son varios datos, entonces
   evaluar dejar en dos columnas para no usar tanto alto de pantalla»). Son once filas y
   la mitad son cifras cortas —fecha, mínimo, garantía, modalidad—, así que una columna
   sola gastaba pantalla en aire a la derecha.

   NO todas se emparejan: Causa, Rol de avalúo, Resultado y Partes llevan `.ancho` y
   ocupan la fila entera. Son las que traen frase y no cifra —el rol arrastra la coletilla
   de las subunidades, el resultado explica que falta el acta—, y partirlas a la mitad del
   ancho las envuelve a tres líneas: se perdería justo el alto que este cambio ahorra.

   El `minmax(0, 1fr)` no es adorno: sin él una celda con un texto largo sin espacios
   —una URL de aviso— empuja la columna y descuadra la rejilla entera, porque el mínimo
   por defecto de una pista de grid es `auto`, o sea el contenido.

   Por debajo de 860 px vuelve a UNA columna. Dos columnas de 180 px de etiqueta más su
   valor no caben en un teléfono, y ahí el alto de pantalla ya no es lo escaso. */
.kv-2col { grid-template-columns: 180px minmax(0, 1fr) 180px minmax(0, 1fr); }
.kv-2col dt.ancho { grid-column: 1; }
.kv-2col dd.ancho { grid-column: 2 / -1; }
@media (max-width: 860px) {
  .kv-2col { grid-template-columns: 180px minmax(0, 1fr); }
  .kv-2col dd.ancho { grid-column: 2; }
}
.bar-row { display: grid; grid-template-columns: 180px 1fr 56px; gap: 10px; align-items: center; margin: 6px 0; }
.bar-row .muted { font-size: var(--fs-sm); }
.bar-row .num { text-align: right; }
.bar { height: 8px; background: var(--bg); border-radius: 999px; overflow: hidden; }
.bar > div { height: 100%; background: var(--accent); border-radius: 999px; }
/* 230 px y no 175: desde el 2026-09-15 el rótulo lleva al lado el enlace al formulario del
   Conservador, y «Hipotecas / gravámenes pedirlo ↗» se partía en dos líneas — medido en
   Chromium. El `nowrap` es lo que impide que la flecha se quede sola en el renglón
   siguiente el día que un rótulo crezca: preferimos que se salga a que se corte. */
.legal-row { display: grid; grid-template-columns: 230px 1fr auto; gap: 10px; align-items: center; margin: 8px 0; }
.legal-row .muted { font-size: var(--fs-sm); white-space: nowrap; }
.legal-row select.sel-ok { border-left: 3px solid var(--green); }
.legal-row select.sel-riesgo { border-left: 3px solid var(--red); }
/* CTA compacta: una línea, es un LINK de acción, no un banner. */
.cta {
  display: block; margin-top: 10px; padding: 6px 10px;
  border: 1px dashed var(--yellow); border-radius: var(--r-panel);
  color: var(--yellow); font-size: var(--fs-sm);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
a.cta:hover { text-decoration: underline; filter: brightness(1.2); }
/* Variante de caja para confianza de datos (banner, no acción). */
.cta.warn { background: #3a2a12; border-color: #7a5c1a; color: var(--amber); white-space: normal; }
/* Acciones de estado al pie de la ficha: el estado actual como tag y SOLO los botones
   de transición lógica (avanzar / descartar) que mandó el backend. */
/* LAS DOS DECISIONES DE LA FICHA, y que se vean (encargo del dueño, 2026-09-07:
   «actualmente pasa desapercibido … que sea una decisión natural del usuario»).

   Eran dos botones del tamaño del texto secundario, apretados contra una etiqueta de
   estado y un desplegable, en una franja fina al final de una página larga. La ficha
   entera es un informe; esto es lo único que se PULSA en ella, y no lo parecía.

   Ahora la franja se declara como zona de acción: más aire, el borde de arriba más
   presente y los botones a tamaño de botón. Se centran porque no acompañan a nada — son
   el final del recorrido de lectura.

   MISMO TAMAÑO Y MISMO COLOR, que es lo que el dueño pidió y además es lo correcto: son
   dos respuestas a la misma pregunta —me lo llevo o no— y pintar DESCARTAR como
   secundaria era contestarla por él. El ancho mínimo iguala las dos cajas aunque los
   rótulos midan distinto. */
.acciones-estado {
  display: flex; align-items: center; justify-content: center; gap: 12px; flex-wrap: wrap;
  margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--border);
}
button.accion-pipe {
  min-width: 210px;
  padding: 10px 20px;
  font-size: var(--fs-sm); letter-spacing: .06em;
}
.acciones-estado .pipe-msg { font-size: var(--fs-xs); flex-basis: 100%; text-align: center; }

/* El candado con su llave dentro. El botón va CENTRADO y separado del texto: es lo que se
   viene a hacer desde aquí, no una nota al pie de la explicación. */
.bc-accion { display: flex; justify-content: center; gap: 10px; flex-wrap: wrap; margin-top: 14px; }
.bloque-candado .pipe-msg { display: block; text-align: center; font-size: var(--fs-xs); margin-top: 8px; }

/* Formularios de excepción plegados (corregir avalúo). */
details.excepcion summary { font-size: var(--fs-xs); color: var(--muted); cursor: pointer; margin: 8px 0 4px; }
details.excepcion summary:hover { color: var(--text); }

/* Kanban: tablero de UN viewport — cada columna scrollea por dentro. */
.kanban { display: grid; grid-template-columns: repeat(6, 1fr); gap: 10px; align-items: stretch; height: calc(100vh - 130px); }
/* Drag & drop de tarjetas (2026-09-02): la tarjeta se arrastra a otra columna. */
.kanban .card[draggable] { cursor: grab; }
.kanban .card.arrastrando { opacity: .45; }
.kanban .col.destino-drop { outline: 1px dashed var(--accent); outline-offset: -4px; }
.kanban .col {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--r-panel); padding: 0 8px 8px;
  display: flex; flex-direction: column; overflow-y: auto; min-height: 0;
  box-shadow: var(--shadow-1);
  scrollbar-width: thin; scrollbar-color: var(--border) transparent;
}
.kanban .col h4 {
  position: sticky; top: 0; z-index: 2; background: var(--panel);
  margin: 0 -8px 8px; padding: 9px 10px; border-bottom: 1px solid var(--border);
  display: flex; justify-content: space-between; align-items: baseline;
}
.kanban .col h4 .cnt { font-family: var(--mono); color: var(--text); }
.kanban .col h4 .cnt.cero { color: var(--muted); }
.card {
  background: var(--panel-2); border: 1px solid var(--border);
  border-radius: var(--r-panel); padding: 8px; margin-bottom: 8px; flex-shrink: 0;
}
/* El recorte de la dirección a DOS líneas lo comparten la tarjeta del tablero y la de la
   landing: es el mismo problema —avisos con direcciones de doscientos caracteres que
   descuadran cualquier tarjeta— y se resuelve una sola vez. La regla compartida declara
   solo lo que de verdad coincide; el margen y el color, que cada tarjeta ajusta a su
   composición, se quedan fuera a propósito: dentro de un `:is()` la especificidad la fija
   el argumento más específico, y una de las dos no podría corregirlos después. */
:is(.card .addr, .mapa-card-addr) {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; font-size: var(--fs-sm); line-height: 1.35;
}
.card .addr { margin: 4px 0; }
.card .metricas { font-family: var(--mono); font-size: var(--fs-xs); color: var(--muted); }
.card .movers { display: flex; justify-content: flex-end; gap: 6px; margin-top: 6px; }
.card .movers button { padding: 2px 10px; font-size: var(--fs-md); line-height: 1.2; }

/* ═══ MIS REMATES: TRES COLUMNAS SON TRABAJO Y TRES SON DESENLACE ═══════════════════════
   (rediseño aprobado por el dueño, 2026-09-11). Las seis columnas se veían idénticas, así
   que la vista no distinguía «lo que estoy trabajando» de «lo que ya terminó» — y la mitad
   del tablero es lo segundo. El tablero es una pantalla HECHA de estados; que el estado no
   se vea es el peor sitio donde ahorrarse una señal.

   El TONO va en un filete de 3 px sobre el encabezado y no en el fondo de la columna: una
   columna de color de punta a punta compite con las tarjetas, que es lo que hay que leer.
   Se usan los tonos DESATURADOS de dato (`--*-data`), que es la intensidad que este
   proyecto reserva para lo masivo dentro de una tabla; los plenos son de cápsula y alerta.
   El cian NO aparece: aquí significaría interacción y estas cabeceras no se pulsan. */
.kanban .col h4 { border-top: 3px solid var(--border); }
.kanban .col[data-col="eval_preliminar"] h4,
.kanban .col[data-col="eval_detallada"] h4,
.kanban .col[data-col="participando"] h4 { border-top-color: var(--yellow-data); }
.kanban .col[data-col="ganado"] h4 { border-top-color: var(--green-data); }
.kanban .col[data-col="perdido"] h4 { border-top-color: var(--red-data); }
.kanban .col[data-col="descartado"] h4 { border-top-color: var(--border); }

/* LAS COLUMNAS CERRADAS SE ATENÚAN, no se esconden. Ganado, perdido y descartado ya no
   piden trabajo: bajan a un segundo plano para que la mirada caiga en las tres que sí. Se
   atenúa el CONTENEDOR y se devuelve el tono completo al posarse o al arrastrar encima —
   nadie tiene que pelear con la opacidad para leer lo que archivó. NO se usa `hidden` ni
   `display`: aquí «está pero pesa menos» es exactamente lo que se quiere decir, y para eso
   la app usa `opacity`, nunca el atributo (regla del 2026-09-07). */
.kanban .col[data-col="ganado"],
.kanban .col[data-col="perdido"],
.kanban .col[data-col="descartado"] { opacity: .62; }
.kanban .col[data-col="ganado"]:hover,
.kanban .col[data-col="perdido"]:hover,
.kanban .col[data-col="descartado"]:hover,
.kanban .col.destino-drop { opacity: 1; }

/* LAS DOS CIFRAS DE LA TARJETA, en su propia línea y con jerarquía. El mínimo manda —es lo
   que se paga— y el descuento va detrás, en el semáforo desaturado que ya usa el listado. */
.card .cifras {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  font-family: var(--mono); margin-top: 2px;
}
.card .cifras .minimo { font-size: var(--fs-sm); color: var(--text); }
.card .cifras .dcto { font-size: var(--fs-xs); }
.card .cifras .cuando { font-size: var(--fs-xs); margin-left: auto; }

/* LA LÍNEA DE CUPO: cuántas propiedades quedan este ciclo. Va al pie del tablero, que es
   donde se decide si añadir una más, y NO en la barra: allí competiría con la navegación
   en todas las páginas para decir algo que solo importa aquí. */
.pipe-cupo {
  margin: 8px 0 0; font-size: var(--fs-xs); color: var(--muted);
  display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap;
}
.pipe-cupo b { font-family: var(--mono); color: var(--text); font-weight: 600; }
.pipe-cupo .agotado { color: var(--red-data); }

/* Anclas entre las secciones de la ficha: el mismo patrón de .w-grupo — una línea sutil
   superior que corta el flujo de ~25 filas clave-valor idénticas. */
.h3-sec { margin: 18px 0 10px; padding-top: 12px; border-top: 1px solid var(--border-soft); }
/* EL ENCABEZADO CONTIENE SUS CHIPS (2026-09-14). Los de UF/CLP del margen van flotados a la
   derecha, y un float NO cuenta para el alto de su padre: el `<h3>` medía lo que su texto y
   los botones —26 px— se salían por abajo, montándose sobre la primera fila de la tabla.
   ESE era el «quedan muy pegados al texto» que reportó el dueño; la distancia era el
   síntoma. Con el clearfix el encabezado mide lo que de verdad ocupa y lo de abajo se
   coloca solo. En un `.h3-sec` sin float esto no hace nada: un bloque vacío mide cero. */
.h3-sec::after { content: ""; display: block; clear: both; }
/* Va en una clase y no en un `style=` dentro del JS: un estilo en línea no se puede ajustar
   desde la hoja ni se ve al auditar la cascada, que es donde este proyecto ya tropezó cinco
   veces. */
.h3-chips { float: right; }
/* LA PRIMERA SECCION DE UNA PESTANA NO LLEVA LINEA (encargo del dueno, 2026-09-14:
   «eliminar la linea de separacion que esta antes de comenzar el analisis»). Una linea
   SEPARA dos cosas, y arriba de la primera no hay nada que separar: lo unico que hay es
   el borde de la propia pestana, dos pixeles mas arriba, asi que se leian dos rayas
   paralelas. Afecta a ANALISIS DE PRECIO (Costo efectivo), MARGEN DEL NEGOCIO (los chips
   UF/CLP van dentro de ese h3) y ANALISIS DE TITULOS (Titulos - expediente), que son las
   tres que el dueno nombro.

   Va por :first-child y no por una clase nueva en cada bloque: quien escriba una pestana
   nueva no tiene por que acordarse de una regla que el sitio ya sabe. INFORMACION BASICA
   no se ve afectada —abre con un <h3> pelado, no con .h3-sec— y RENTABILIDAD tampoco,
   que desde el 2026-09-13 abre con su barra de cifras. */
.tab-body > .h3-sec:first-child { margin-top: 0; padding-top: 0; border-top: 0; }

/* Config — panel encuadrado con subpestañas propias. Las pestañas van DENTRO del panel
   y no en .topbar: el banner navega entre secciones de la app; esto navega dentro de una.
   El borde inferior de .tabs es el que "encuadra" y ata la pestaña activa a su contenido. */
.tabs-panel { padding: 0; }
.tabs-panel { box-shadow: var(--shadow-1); }
.tabs {
  display: flex; align-items: center; gap: 2px; flex-wrap: wrap;
  padding: 8px 12px 0; border-bottom: 1px solid var(--border);
  background: var(--panel-2); border-radius: var(--r-panel) var(--r-panel) 0 0;
}
.tabs .spacer { flex: 1; }
.tab {
  background: transparent; color: var(--muted); border: 1px solid transparent;
  border-bottom: none; border-radius: var(--r-ctl) var(--r-ctl) 0 0;
  padding: 7px 14px; font-size: var(--fs-sm); letter-spacing: .07em; font-weight: 600;
  margin-bottom: -1px;  /* tapa el borde del contenedor: la activa se funde con el cuerpo */
}
.tab:hover { color: var(--text); filter: none; }
.tab.active {
  background: var(--panel); color: var(--accent);
  border-color: var(--border); border-bottom: 1px solid var(--panel);
}
.tab-body { padding: 14px 16px 18px; }
/* PESTAÑAS PAREJAS (encargo del dueño, 2026-09-10, para /admin): todas ocupan lo mismo,
   se llame la pestaña CICLO o CONTROL DE CALIDAD. Hoy miden 67, 162 y 171 px —medido— y
   esa desigualdad se lee como descuido en una fila de tres botones.

   Se reparte el contenedor con `flex: 1` en vez de fijar un ancho: el número de pestañas
   lo decide la plantilla, así que una nueva se acomoda sola y ninguna se queda con un
   `min-width` que un rótulo largo desborde. `text-align: center` porque un texto pegado a
   la izquierda dentro de una caja ancha delata que la caja se estiró.

   Va por CLASE y no sobre `.tabs` a secas: en la FICHA y en /config las pestañas siguen
   midiendo lo que su texto, que ahí es lo correcto — son cuatro y una fila estirada dejaría
   huecos enormes entre rótulos cortos. */
.tabs.tabs-parejas .tab { flex: 1; text-align: center; }

/* LAS TABLAS DE SOLO LECTURA NO SE DESBORDAN (mismo encargo: «ajustar pestaña Ciclo, ya
   que está descuadrada la tabla»). El síntoma medido: la tabla QUÉ HIZO ocupaba 2.286 px
   dentro de un panel de 1.387 —900 px fuera— mientras las otras tres medían 1.355. Cuatro
   tablas apiladas con anchos distintos, y una saliéndose de su panel.

   LA CAUSA no era el número de filas sino UNA celda: el motivo del catastro son 228
   caracteres en una línea, y con `table-layout: auto` el `width: 100%` es un mínimo, no un
   máximo — el contenido manda y la tabla crece. `fixed` invierte eso: manda el ancho
   declarado y el texto se reparte dentro.

   `overflow-wrap: anywhere` es la otra mitad: sin él, una cadena larga sin espacios
   —una ruta, un identificador— seguiría empujando la columna aunque la tabla sea `fixed`.

   NO se arregla con scroll horizontal, y eso no es una preferencia: «una sola barra de
   scroll, la de la ventana» es regla del proyecto.

   EL SELECTOR LLEVA `table.grid` DELANTE A PROPÓSITO, y es la tercera vez que este
   proyecto tropieza con lo mismo: `table.grid td` declara `white-space: nowrap` para todas
   las tablas y pesa (0,1,2), así que un `.tabla-lectura td` a secas (0,1,1) PIERDE. El
   síntoma no es un error sino una celda que se sigue cortando después de haberla
   arreglado — medido: `whiteSpace` seguía en `nowrap` con la regla nueva puesta. */
table.grid.tabla-lectura { table-layout: fixed; }
table.grid.tabla-lectura td { overflow-wrap: anywhere; white-space: normal; }
/* La primera columna es el RÓTULO del dato y no necesita más: dejarla al 38% evita que un
   motivo largo de la segunda le robe todo el sitio y deje los nombres en dos líneas. */
table.grid.tabla-lectura th:first-child,
table.grid.tabla-lectura td:first-child { width: 38%; }
/* MARGEN ESPERADO: grilla tipo estado de resultados. Cuatro columnas fijas —
   etiqueta · signo · CAJA (todas las cifras alineadas ahí, mono, derecha) · leyenda —
   para que se lea la hilación: entradas arriba, costos en negativo, márgenes como
   filas-resultado que cierran cada sección. */
/* AIRE ENTRE LOS CHIPS UF/CLP Y LA PRIMERA FILA (encargo del dueno, 2026-09-14:
   «los botones UF / CLP quedan muy pegados al texto "referencia muestra…". Distanciar un
   poco, bajando valor de venta y todo lo siguiente»).

   Los chips van FLOTADOS a la derecha dentro del <h3>, asi que no ocupan alto: la leyenda
   de la primera fila —que vive en la cuarta columna, tambien a la derecha— les quedaba
   pegada debajo. Se resuelve bajando la TABLA entera y no moviendo los chips: son el
   control de la seccion y su sitio es el canto del encabezado. */
.margen-tabla {
  display: grid; grid-template-columns: 190px 14px 150px 1fr;
  gap: 6px 10px; align-items: center; max-width: 100%;
  margin-top: 12px;
}
.mt-label { color: var(--muted); font-size: var(--fs-sm); }
.mt-label.resultado { color: var(--text); font-weight: 600; letter-spacing: .04em; }
.mt-signo { color: var(--red-data); font-family: var(--mono); text-align: center; }
.mt-caja {
  box-sizing: border-box; width: 100%; text-align: right;
  font-family: var(--mono); font-size: var(--fs-sm);
  padding: 5px 9px; border: 1px solid var(--border); border-radius: var(--r-ctl);
  background: var(--panel-2); color: var(--text);
}
/* Las cajas de FÓRMULA (márgenes, autocompletados): mismo tamaño y alineación que las
   editables, pero sin apariencia de input — son resultado, no dato a completar. */
.mt-caja.calc {
  display: inline-block; background: transparent; border-color: var(--border-soft);
}
.mt-label.resultado + .mt-signo { color: var(--muted); }
.mt-label.resultado + .mt-signo + .mt-caja.calc { border-color: var(--accent-dim-2); font-weight: 700; }
/* El color semafórico del RESULTADO gana a la caja (la regla de .mt-caja lo pisaba). */
.mt-caja.calc.pos { color: var(--green-data); }
.mt-caja.calc.neg { color: var(--red); }
/* El % del MARGEN DEL NEGOCIO va EN LA MISMA FILA, destacado como resultado:
   mono semafórico con su propia cápsula sutil. */
.mt-pct {
  font-family: var(--mono); font-size: var(--fs-sm); font-weight: 700;
  padding: 4px 10px; border: 1px solid var(--accent-dim-2); border-radius: 999px;
  white-space: nowrap;
}
.mt-pct.pos { color: var(--green); }
.mt-pct.neg { color: var(--red); }
/* Una cifra que NO se puede afirmar (el payback que no ocurre, un cap rate sin insumo)
   es atención, no riesgo: ámbar pleno —es una cápsula puntual, no dato masivo— y nunca
   rojo, que aquí significaría «el negocio pierde». `neutro` es para la cifra que
   simplemente no existe como número y no reclama nada (2026-09-05). */
.mt-pct.warn { color: var(--yellow); }
.mt-pct.neutro { color: var(--muted); }
/* RETORNO: las métricas van en COLUMNA —cápsula arriba, su explicación debajo— porque el
   encargo del dueño (2026-09-05) es que qué mide cada número se LEA, no que viva en un
   tooltip: en una pantalla táctil un tooltip no existe. El ancho tope en `ch` mantiene la
   línea en una medida legible sin fijar píxeles. */
/* TIR Y PAYBACK EN LA ESQUINA (encargo del dueño, 2026-09-12: «que queden en la esquina
   superior derecha debajo de los botones CLP y UF»). Son los titulares de la pestaña: se
   leen antes que nada y se quedan a la vista mientras se recorre el resto.

   VA PEGADO AL ENCABEZADO y por eso el margen superior es NEGATIVO: los chips UF/CLP
   flotan dentro del `<h3>`, así que el bloque siguiente arranca por debajo de la línea del
   título y no de los chips. Sin el tirón hacia arriba quedaba un hueco donde debería
   leerse como una sola esquina.

   El `clear: both` no es adorno: sin él este bloque se mete AL LADO de los chips flotados
   —o los pisa— según el ancho de la pantalla, y el resultado solo se ve mirando.

   En pantalla angosta se alinea a la IZQUIERDA y sin el tirón: a 520 px no hay «esquina
   superior derecha» que valga, y forzarla dejaría los dos números encima del título. */
/* UNA SOLA FILA: las CIFRAS a la izquierda y los CONTROLES a la derecha (2026-09-13).
   Es el reparto natural de una barra —lo que se lee a un lado, lo que se pulsa al otro— y
   llena una línea que, con los cuatro elementos a la derecha, dejaba media pantalla vacía.

   En pantalla angosta la barra se parte y los chips caen debajo: forzar la fila a 400 px
   comprimiría las dos cifras hasta partir «no ocurre» en dos renglones.

   `align-items: flex-start` — LOS CUATRO BLOQUES COMPARTEN SU BORDE SUPERIOR (encargo del
   dueño, 2026-09-14). Ha tenido las tres alineaciones posibles en dos días y conviene dejar
   por qué manda esta: `center` dejaba la cifra medio renglón bajo el botón; `flex-end`
   alineaba las BASES —la del chip con la de la cifra grande, que era lo pedido el 13—; y
   arriba es lo que pidió el 14, «que queden alineados con el comienzo de las tarjetas».
   Que las tres se hayan podido defender es la señal de que esto se decide MIRÁNDOLO. */
.retorno-barra {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: 16px; flex-wrap: wrap; margin: 2px 0 4px;
}
/* SIN MARGEN, y el `0` es explícito porque hay algo que anular: `.retorno-controles` se usa
   también en MARGEN DEL NEGOCIO, donde lleva su propio margen de bloque. Aquí los chips son
   la cuarta columna de una fila y cualquier tirón —el negativo que los esquinaba hasta el
   2026-09-14 incluido— los saca de la línea de las tarjetas. */
.retorno-barra > .retorno-controles { margin: 0; }
.retorno-titulares { display: flex; gap: 10px; flex-wrap: wrap; }
.retorno-controles { display: flex; gap: 6px; }

/* LAS TARJETAS DE TIR Y PAYBACK (encargo del dueño, 2026-09-13: «que payback y TIR queden
   como tarjetas, de forma más estética»). Eran dos pilas de texto sueltas y se leían como
   principio del párrafo siguiente; con su caja se leen como lo que son, los titulares.

   EL TONO VA EN LA QUILLA IZQUIERDA Y EN EL NÚMERO, nunca en el fondo. El fondo teñido es
   el idioma de las cápsulas semafóricas del listado, y una tarjeta grande en verde pleno
   gritaría más que el dato: aquí el semáforo informa, no alerta. Es la misma decisión que
   el resaltado de la tarjeta del mapa —«el fondo se queda quieto»— y por el mismo motivo.

   `min-width` y no un ancho fijo: «no ocurre» y «mes 24» miden distinto, y dos tarjetas de
   anchos distintos al lado de los chips se ven descuadradas. */
.rt-tarjeta {
  display: flex; flex-direction: column; gap: 2px;
  min-width: 132px; padding: 8px 12px 9px;
  background: var(--panel-2); border: 1px solid var(--border-soft);
  border-left: 3px solid var(--border); border-radius: var(--r-ctl);
}
.rt-tarjeta.pos { border-left-color: var(--green-data); }
.rt-tarjeta.neg { border-left-color: var(--red-data); }
.rt-tarjeta.warn { border-left-color: var(--yellow-data); }
.rt-rotulo {
  font-size: 10px; letter-spacing: .1em; color: var(--muted); font-weight: 600;
}
.rt-valor {
  font-family: var(--mono); font-size: var(--fs-lg); font-weight: 700; line-height: 1.15;
}
/* La segunda línea solo aparece cuando la cifra NO se puede afirmar: se marca solo lo que
   exige atención. Envuelve —no se corta— porque es la única explicación de ese hueco. */
.rt-nota {
  font-size: var(--fs-xs); color: var(--muted); line-height: 1.4; max-width: 34ch;
  margin-top: 2px;
}
/* La tarjeta que SE PULSA: misma caja que las de dato para que la fila se lea pareja, y el
   cian —que en esta app significa interacción y solo eso— reservado para ella. Las otras
   tres son dato y no se encienden. */
a.rt-tarjeta.rt-accion {
  text-decoration: none; cursor: pointer; justify-content: center;
  border-left-color: var(--accent-dim-2);
}
a.rt-tarjeta.rt-accion:hover {
  border-color: var(--accent); border-left-color: var(--accent);
  background: var(--accent-dim);
}
.rt-valor.rt-excel { color: var(--accent); font-size: var(--fs-md); }
/* El RELATO (punto 8 del encargo): un párrafo, no una tabla. Se separa del resto con el
   mismo gesto que un panel hundido —fondo un tono más claro y una guía cian a la
   izquierda— para que se lea como «esto es lo que significa todo lo de arriba».
   Desde el 2026-09-13 es el ÚNICO texto explicativo de la pestaña calculada: los bloques
   «Qué mide / En plata / Contra qué» de cada métrica se retiraron por encargo del dueño, y
   este párrafo dice lo mismo en dos frases y con los números puestos. */
/* A LO ANCHO DE LA FICHA desde el 2026-09-12 (encargo del dueño: «que el cuadro llegue
   hasta el final lateral de la ficha para que quede encuadrado con la ficha»). Tenía el
   tope de 96ch por la medida de lectura, y aquí pierde contra lo mismo que en el cierre del
   margen: es el bloque que CIERRA la pestaña, y una caja que llega a media anchura se lee
   como nota al margen en vez de como la conclusión. Se compensa con más interlínea. */
.retorno-relato {
  margin: 16px 0 0; padding: 13px 16px;
  background: var(--panel-2); border-left: 2px solid var(--accent-dim-2);
  border-radius: var(--r-ctl);
  font-size: var(--fs-md); line-height: 1.7;
}
/* Los supuestos sin dato: son VARIOS a la vez, así que el ámbar va DESATURADO (dos
   intensidades: el pleno se reserva para la alerta puntual). Se listan explicados. */
.retorno-pendientes { margin: 14px 0 0; }
.retorno-pendientes .rp-titulo {
  color: var(--yellow-data); font-size: var(--fs-xs); letter-spacing: .08em; font-weight: 600;
}
.retorno-pendientes ul { margin: 5px 0 0; padding-left: 18px; max-width: 96ch; }
.retorno-pendientes li { font-size: var(--fs-xs); color: var(--muted); line-height: 1.5; }
.retorno-pendientes li b { color: var(--text); font-weight: 600; }
.mt-ley { font-size: var(--fs-xs); color: var(--muted); line-height: 1.35; }

/* ── LA FILA CLAVE DEL MARGEN: el máximo a ofertar (encargo del dueño, 2026-09-12) ──
   «Destacar este dato, ya que es importante que sea sencillo para el usuario entender que
   este es el dato más importante que debe ajustar.»

   Se destaca con TRES cosas y ninguna es el fondo de la fila: la quilla cian a la
   izquierda, el rótulo en el color del texto (las demás etiquetas van en `muted`) y la
   caja con borde pleno. Un fondo teñido competiría con el resultado de abajo, que es lo
   único que la grilla ya destaca, y dos cosas destacadas no destacan ninguna.

   EL CIAN AQUÍ SÍ CORRESPONDE y conviene dejarlo escrito, porque la regla del proyecto es
   que el cian es SOLO interacción: esto es literalmente el control que hay que tocar. No
   se pinta cian el número —eso sería teñir un dato— sino la quilla y el borde de la caja.

   Y va en UNA sola fila a propósito. La grilla no tiene `<tr>`, así que la clase la ponen
   las cuatro celdas hermanas; si mañana se destacan tres filas, no destacará ninguna. */
/* EL VALOR DE COMPRA, SUBIDO DE TONO (segundo encargo del dueño sobre la misma fila,
   2026-09-12: «aún está muy poco destacado, debe ser notorio para el usuario que este es
   el primer dato a ajustar»). La versión anterior —quilla de 3 px y un borde atenuado en
   la caja— era correcta y no se veía.

   LO QUE SE SUBE Y LO QUE NO. El cian sigue viviendo en el CONTROL y no en el dato: la
   quilla pasa a 4 px, la caja gana un borde pleno de 2 px y su número va en semibold,
   y el rótulo estrena una etiqueta que dice literalmente por dónde empezar. **El número
   sigue sin teñirse** y el fondo de la fila sigue sin tocarse — el primero porque el cian
   es interacción y no valor, el segundo porque un fondo aquí chocaría con el idioma de
   las filas-resultado que vienen debajo.

   Y sigue siendo UNA sola fila: si mañana se destacan tres, no destaca ninguna. */
.margen-tabla .mt-label.mt-clave {
  color: var(--text); font-weight: 600;
  box-shadow: inset 4px 0 0 var(--accent); padding-left: 11px;
}
/* La etiqueta «ajústalo primero»: minúscula, en cian, debajo del rótulo. Es la única
   instrucción imperativa de la tabla, y por eso puede permitirse el color de acción. */
.mt-primero {
  display: block; margin-top: 2px;
  font-size: 10px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--accent); font-weight: 700;
}
.margen-tabla input.mt-caja.mt-clave {
  border: 2px solid var(--accent-dim-2); background: var(--panel);
  font-weight: 600;
}
.margen-tabla input.mt-caja.mt-clave:focus {
  border-color: var(--accent);
}
.margen-tabla .mt-ley.mt-clave { color: var(--text); }

/* EL CIERRE DEL MARGEN: «una primera estimación que debe ser revisada y refinada».
   Va en una caja de nota, no en `muted` suelto: es lo último que se lee del bloque y
   tiene que sobrevivir al vistazo. Sin tono de advertencia — no hay nada que corregir,
   hay algo que refinar, y en esta app el ámbar significa lo primero. */
/* A LO ANCHO DE LA FICHA desde el 2026-09-12 (encargo del dueño: «el cuadro que sea
   alargado para cubrir el largo de la ficha»). Tenía `max-width: 68ch` por la regla de
   oro de la medida de lectura, y aquí pierde contra otra cosa: es el CIERRE de una tabla
   que ocupa el panel entero, y una caja que llega a media anchura se lee como una nota al
   margen — justo lo que este texto no es. Se compensa el renglón largo con más interlínea
   y con el mismo cuerpo pequeño. */
.margen-cierre {
  margin: 16px 0 0; padding: 12px 14px;
  font-size: var(--fs-sm); line-height: 1.55; color: var(--text);
  border: 1px solid var(--border-soft); border-radius: var(--r-ctl);
  background: var(--panel-2);
}
.margen-cierre.muted { color: var(--muted); background: transparent; }
/* Los costos editables se leen en NEGATIVO: el signo vive en su columna y el número
   dentro de la caja hereda el rojo de dato. */
.mt-signo:not(:empty) + input.mt-caja { color: var(--red-data); }

/* Subsecciones del MARGEN ESPERADO (costos de inscripción / operacionales). */
.sub-sec {
  margin: 12px 0 2px; color: var(--muted); font-size: var(--fs-xs);
  letter-spacing: .08em; font-weight: 600;
}
.chip.on { background: var(--accent-dim-2); color: var(--accent); border-color: var(--accent); }

/* Publicación EXCLUIDA de la mediana de la muestra: visible pero hundida. */
#ficha tr.fila-excluida td, #ficha tr.fila-excluida td a { color: var(--dim); }
#ficha tr.fila-excluida td { text-decoration: line-through; }
#ficha tr.fila-excluida td:first-child { text-decoration: none; }

/* Checkbox de la muestra DESHABILITADO (2026-09-06, punto 3 del encargo): con cuenta la
   muestra se ve ENTERA, pero excluir una publicación exige tener el remate en el propio
   pipeline. La casilla se pinta apagada y con el cursor de "no" — dice la verdad, que hoy
   no se puede, en vez de dejar que el clic se pierda contra un 409. El texto que explica
   qué la habilita va debajo de la tabla: la señal apagada avisa, la línea explica. */
#ficha .chk-muestra:disabled { opacity: .45; cursor: not-allowed; }

/* Tabla FANTASMA de la muestra (2026-09-06): las columnas de verdad, sin datos, y el
   motivo debajo. Es «siempre se muestran TODOS los campos» llevado al candado de cuenta:
   el dueño pidió que quien no tiene cuenta vea LA TABLA y no un párrafo, porque ver la
   forma de lo que falta es lo que invita a entrar. El ENCABEZADO va a intensidad normal
   —es información real: qué trae la muestra— y el cuerpo hundido, para que no se lea ni
   por un segundo como una tabla con datos. Las celdas van vacías: un número de ejemplo en
   una ficha de inversión no es una ilustración, es un dato falso. */
.tabla-fantasma tbody td { color: var(--dim); }
.tabla-fantasma + .bloque-candado, .tabla-fantasma + p { margin-top: 10px; }

/* Mapa de la propiedad (Información básica): alto fijo, marco del panel. */
.mapa-prop {
  height: 340px; border: 1px solid var(--border); border-radius: var(--r-ctl);
  background: var(--panel-2);
}
/* El VESTIDO OSCURO de los controles de Leaflet es de la app, no de un mapa concreto:
   desde que hay dos mapas (la ficha y la landing) se escribe una vez para los dos. Con
   una copia por mapa, el día que se ajuste un tono quedaría uno de los dos claro — es la
   misma razón por la que el descuento tiene un solo formateador. El selector va por
   DESCENDENCIA para que dé igual si la clase está en el contenedor de Leaflet o en su
   envoltorio: en la ficha `.mapa-prop` ES el contenedor, en la landing puede no serlo. */
:is(.mapa-prop, .mapa-lienzo) .leaflet-control-layers {
  background: var(--panel-2); color: var(--text); border: 1px solid var(--border);
  border-radius: var(--r-ctl); box-shadow: var(--shadow-1);
}
:is(.mapa-prop, .mapa-lienzo) .leaflet-control-layers-toggle { filter: invert(.85); }

:is(.mapa-prop, .mapa-lienzo) .leaflet-control-attribution {
  background: rgba(13, 17, 23, .7); color: var(--muted);
}
:is(.mapa-prop, .mapa-lienzo) .leaflet-control-attribution a { color: var(--muted); }

/* Pestaña BLOQUEADA: visible con su candado — explica qué falta en vez de esconderse.
   Por defecto no navega (la del paso 2 del registro: hasta que la cuenta existe no hay
   nada que mostrar). */
.tab.locked { color: var(--dim); cursor: not-allowed; }
.tab.locked:hover { color: var(--dim); background: transparent; border-color: transparent; }
/* ABRIBLE: la pestaña con candado de la FICHA sí navega, porque su cuerpo ES la
   explicación de qué falta y cómo se abre (auditoría 2026-09-04: en una pantalla táctil
   no hay tooltip que leer, y el motivo vivía solo en el `title`). Sigue hundida —el
   contenido no está— pero se comporta como lo que es: algo en lo que se puede entrar. */
.tab.locked.abrible { cursor: pointer; }
.tab.locked.abrible:hover { color: var(--muted); }
.tab.locked.abrible.active { color: var(--muted); background: var(--panel);
  border-color: var(--border); border-bottom: 1px solid var(--panel); }
/* Notas + acciones viven al PIE de la ficha, fuera de las pestañas: acompañan al
   análisis completo. El borde superior las separa del cuerpo de la pestaña. */
.ficha-pie { padding: 12px 16px 18px; border-top: 1px solid var(--border-soft); }
.tab-intro, .tab-nota { font-size: var(--fs-sm); line-height: 1.5; max-width: 92ch; margin: 0 0 4px; }
.tab-nota { margin: 2px 0 8px; }

/* Casillas del filtro: rejilla de ancho fijo para que las etiquetas queden alineadas
   aunque la ayuda de abajo tenga largos muy distintos. */
.check-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 6px 14px; }
.chk {
  display: grid; grid-template-columns: auto 1fr; gap: 9px; align-items: start;
  padding: 7px 9px; border: 1px solid var(--border-soft); border-radius: var(--r-ctl);
  cursor: pointer;
}
.chk:hover { border-color: var(--border); background: var(--panel-2); }
.chk input { accent-color: var(--accent); margin: 1px 0 0; padding: 0; }
.chk-txt { font-size: var(--fs-sm); letter-spacing: .05em; font-weight: 600; }
.chk-extra { color: var(--muted); font-weight: normal; font-family: var(--mono); font-size: var(--fs-xs); }
.chk-detalle {
  display: block; margin-top: 3px; color: var(--muted);
  font-size: var(--fs-xs); font-weight: normal; letter-spacing: 0; line-height: 1.4;
}
/* EN LA SECCION «DONDE», EL DETALLE VA EN LA MISMA LINEA (encargo del dueno, 2026-09-14:
   «que el texto "la Region Metropolitana fuera del Gran Santiago" quede en la misma linea
   de periferia, que no quede abajo», y lo mismo con los dos de las ramas). En bloque cada
   casilla medía dos renglones, y con veintiuna eso es el doble de pantalla para una
   aclaracion de cinco palabras.

   Se acota a esas dos zonas y NO se cambia `.chk-detalle` entero: en TIPO DE INMUEBLE los
   detalles son mas largos y ahi el renglon propio es lo correcto. Dos usos parecidos, dos
   comportamientos, y por eso el selector es explicito en vez de global. */
.fw-ramas .chk-detalle,
details.fw-grupo > summary > .chk-detalle {
  display: inline; margin-top: 0; margin-left: 8px;
}

@media (max-width: 1100px) { .kanban { grid-template-columns: repeat(3, 1fr); height: auto; } }
#tabla-ref .sel-sector { padding: 3px 6px; font-size: var(--fs-xs); }
/* Títulos de grupo dentro de una pestaña de /config (SECTOR, TIPO DE INMUEBLE). */
.w-grupo { margin: 16px 0 2px; padding-top: 10px; border-top: 1px solid var(--border-soft); }
.w-grupo:first-child { margin-top: 4px; padding-top: 0; border-top: none; }

/* EL ASISTENTE DE AVISOS (encargo del dueño, 2026-09-07): una pregunta por pantalla.
   `.w-grupo` de arriba era el encabezado de cada bloque cuando las cinco preguntas caían
   juntas; sigue en uso en el resto de /config y por eso no se toca.
   Un paso ocupa un alto MÍNIMO para que la barra de navegación no salte de sitio entre
   una pregunta de dos casillas y otra de veinte: un botón que se mueve bajo el cursor se
   pulsa por error. */
.w-cabecera { margin: 2px 0 12px; }
.w-progreso {
  display: block; font-family: var(--mono); font-size: var(--fs-xs);
  letter-spacing: .08em; color: var(--muted);
}
.w-titulo { margin: 2px 0 0; font-size: var(--fs-md); color: var(--text); }
.w-paso { min-height: 190px; }
.w-nav {
  display: flex; gap: 8px; align-items: center;
  margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border-soft);
}
/* ATRÁS es secundario y se ve como tal: el camino natural es hacia adelante. */

/* GUARDADO: el botón en reposo no es un botón apagado por error, es un estado alcanzado.
   Verde de dato (el desaturado, no el pleno: no es una alerta) y sin cursor de mano, para
   que se lea como «ya está» y no como «esto no funciona». */
button.btn-hecho:disabled {
  color: var(--green-data); border-color: var(--green-data); opacity: 1; cursor: default;
}
#av-atras { color: var(--muted); }
#av-atras:hover { color: var(--text); }

.add-ref { display: flex; gap: 8px; margin-top: 12px; align-items: center; flex-wrap: wrap; }
/* Aquí vivía `.add-ref[hidden] { display: none }`, el parche de la auditoría del
   2026-09-04 que escondía el editor a quien no era admin mientras la sección estaba en
   /config. En la F2 (2026-09-05) la sección se mudó a /admin, que es admin entera: ya no
   hay a quién esconderle nada y la regla se fue con el parche. */
/* Los inputs de la tabla en REPOSO son dato, no formulario: sin caja ni borde (100
   rectángulos de chrome ensuciaban la tabla). La caja aparece al pasar por la fila o al
   enfocar. La fila sucia (regla de abajo) sigue ganando: va después en el archivo. */
#tabla-ref td { padding: 4px 9px; }
#tabla-ref input, #tabla-ref .sel-sector {
  background: transparent; border-color: transparent; padding: 3px 8px;
}
#tabla-ref tr:hover input, #tabla-ref tr:hover .sel-sector,
#tabla-ref input:focus, #tabla-ref .sel-sector:focus {
  background: var(--panel-2); border-color: var(--border);
}
/* Guardado por fila solo cuando la fila está sucia (editada y sin guardar). */
#tabla-ref button[data-save] { visibility: hidden; padding: 2px 10px; }
#tabla-ref tr.dirty button[data-save] { visibility: visible; }
#tabla-ref tr.dirty input { border-color: var(--yellow); }

dialog {
  background: var(--panel); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-panel);
  box-shadow: 0 0 0 1px var(--accent-dim), 0 24px 64px rgba(0, 0, 0, .55);
  padding: 18px; min-width: 560px; max-width: 720px;
}
dialog::backdrop { background: rgba(0,0,0,.7); }
dialog h3 { color: var(--text); font-size: var(--fs-md); letter-spacing: .08em; }
dialog textarea, dialog input { width: 100%; margin: 6px 0; }
.dlg-actions { display: flex; gap: 8px; margin-top: 8px; align-items: center; }
pre { white-space: pre-wrap; font-family: var(--mono); }

/* Semáforo con volumen calibrado donde el dato es MASIVO: tono desaturado. Los tonos
   plenos siguen vigentes fuera (cápsulas, banners, .cta) — el rol no cambia.
   No es «dentro de tablas» sino «donde hay muchos»: las TARJETAS del mapa y del tablero
   repiten el mismo dato cientos de veces y lo pintaban pleno, así que el mismo remate se
   leía con dos intensidades según la pantalla. Los tres helpers compartidos
   (`diasCelda`, `descuentoHTML`, `minimoUF`) producen estas clases en las tres, y por eso
   la regla vive en un selector y no en tres. */
:is(table.grid, .card, .mapa-card) .pos { color: var(--green-data); }
:is(table.grid, .card, .mapa-card) .neg { color: var(--red-data); }
:is(table.grid, .card, .mapa-card) .warn { color: var(--yellow-data); }
/* Placeholder hundido: los '—' no pueden pesar lo mismo que un label real. */
.dim { color: var(--dim); }

/* Marca de confianza de fuente (lead no oficial): en la TABLA es solo el glifo ámbar
   (el detalle vive en title); la versión con caja queda para la ficha (.cta.warn). */
.tag-lead {
  font-size: var(--fs-xs);
  color: var(--amber);
  white-space: nowrap;
}

/* Columna FUENTE del listado: compacta, no debe competir con la dirección */
.grid td.fuente { white-space: nowrap; font-size: var(--fs-xs); color: var(--muted); }

/* ══════════════════════ LA PUERTA (F1, 2026-09-04) ══════════════════════════
   Login, registro, recuperación y los bloques con candado. Lo mínimo, sobre los
   tokens que ya existen: el cian sigue siendo SOLO interacción, la escala
   tipográfica sigue cerrada en sus cinco tamaños y nada de esto estrena scroll
   propio — la única barra sigue siendo la de la ventana. */

/* Barra superior: el extremo derecho es solo de la puerta. El RELOJ salió el 2026-09-06
   por encargo del dueño (punto 3): repetía la hora que el sistema operativo ya muestra en
   la misma pantalla, y se llevaba un repintado por segundo en todas las páginas. */
.topbar-right { display: flex; align-items: center; gap: 14px; }
.auth-nav { display: flex; align-items: center; gap: 8px; font-family: var(--sans); }
.auth-nav a { color: var(--muted); letter-spacing: .05em; }
.auth-nav a:hover { color: var(--accent); text-decoration: none; }
/* El nombre de quien está dentro. En REPOSO sigue siendo identidad y no acción —color de
   texto, nunca cian—, pero desde el 2026-09-06 también es el enlace a /cuenta (encargo del
   dueño, punto 4), así que al pasar por encima se enciende como cualquier control de la
   app: cian y subrayado. Es el mismo patrón de los controles repetidos del listado, que
   viven en reposo silencioso y se encienden al hover. */
.auth-quien {
  color: var(--text); font-family: var(--sans); font-size: var(--fs-sm);
  max-width: 22ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.auth-nav a.auth-quien:hover { color: var(--accent); text-decoration: underline; }
/* Estando EN /cuenta, el nombre se pinta activo igual que un enlace del banner en su
   sección: sin esto, /cuenta sería la única página que no dice dónde estoy. */
.auth-nav a.auth-quien.active { color: var(--accent); }
.auth-salir { padding: 3px 10px; font-size: var(--fs-xs); letter-spacing: .06em; }

/* Página /cuenta (2026-09-06): un panel de formularios, angosto y centrado, como el de la
   puerta. Estirado a los 1500px del layout, un campo de una línea obliga a barrer la
   pantalla entera para leerlo. Más ancho que .auth-panel porque aquí hay cuatro bloques
   con su prosa, no dos campos. */
.form-panel { max-width: 720px; margin: 24px auto; }

/* Panel de la puerta: angosto y centrado. Un formulario de dos campos estirado a
   1500px obliga a barrer la pantalla para leer una línea. */
.auth-panel { max-width: 400px; margin: 48px auto; }
.auth-h { font-size: var(--fs-lg); font-weight: 600; letter-spacing: .06em; margin: 0 0 12px; }
.auth-pie { font-size: var(--fs-sm); margin: 14px 0 0; }
.auth-sep { padding: 0 4px; color: var(--dim); }
/* El registro sí es ancho: el paso 2 monta la rejilla de casillas del filtro. */
.auth-registro { max-width: 900px; margin: 24px auto; }

/* Campo de formulario: etiqueta arriba en versal pequeña, como .filters label. */
.campo { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px;
         color: var(--muted); font-size: var(--fs-xs); letter-spacing: .05em; }
.campo input { width: 100%; font-size: var(--fs-md); letter-spacing: 0; }
.campo-ayuda { font-size: var(--fs-xs); letter-spacing: 0; line-height: 1.45; }
.form-registro { max-width: 420px; }
/* CUENTA de /config (F2, 2026-09-05): el mismo ancho que el registro. Un campo de una
   línea estirado a los 1500px del layout obliga a barrer la pantalla para leerlo. */
.form-cuenta { max-width: 420px; }
/* El correo se muestra en SOLO LECTURA: identifica la cuenta y no se cambia desde ahí.
   Se pinta como dato —hundido y sin caja de formulario— pero sigue siendo copiable
   (`readonly`, no `disabled`), porque leerlo y copiarlo es justo para lo que está. */
.campo input[readonly] {
  background: var(--panel-2); color: var(--muted); border-color: var(--border-soft);
}

/* Medidor de la contraseña: mide el LARGO, que es lo que de verdad protege, y no
   una "fuerza" inventada con reglas de tipos de carácter. */
.medidor { display: flex; align-items: center; gap: 8px; margin-top: 2px; }
.medidor-barra { flex: 1; height: 4px; background: var(--bg); border-radius: 999px; overflow: hidden; }
.medidor-barra > div { height: 100%; width: 0; background: var(--muted); border-radius: 999px;
                       transition: width .12s ease; }
.medidor-barra > div.ok { background: var(--green-data); }
.medidor-txt { font-family: var(--mono); font-size: var(--fs-xs); letter-spacing: 0;
               min-width: 12ch; }

/* ══════════════ AVISOS AUTOMÁTICOS (/config, 2026-09-06) ═════════════
   Encargo del dueño, punto 5. Todo sobre los tokens y las clases que ya existían: las
   casillas de SECTOR y TIPO son las `.chk` del filtro (mismo control, misma función que
   las dibuja) y lo único nuevo es lo que de verdad es nuevo —el rango en UF, la cascada de
   comunas y el canal que todavía no existe—. */

/* Rango en UF: dos campos y un guión. Ancho acotado —son números de cuatro cifras— y
   alineados por abajo para que el separador quede a la altura de las cajas. */
.uf-rango { display: flex; align-items: flex-end; gap: 10px; max-width: 380px; }
.uf-rango .campo { flex: 1; margin-bottom: 4px; }
.uf-rango-sep { padding-bottom: 10px; }

/* SE TECLEA LA COMUNA (encargo del dueño, 2026-09-14). Reemplaza a la cascada sector →
   comunas: seis casillas y ~52 chips abriendo el asistente que existe para no colapsar al
   usuario. Con la cascada se fueron `.cascada-grupo`, `.cascada-sector` y `.chip-check`.

   La caja va acotada —un nombre de comuna no pasa de veinte caracteres— y a la izquierda,
   no a lo ancho del panel: una caja de 900 px promete un párrafo. */
.comuna-buscar { display: flex; gap: 8px; max-width: 340px; margin-bottom: 10px; }
.comuna-buscar input { flex: 1; }
.chip-grid { display: flex; flex-wrap: wrap; gap: 6px; }
/* El chip ELEGIDO va en cian —es una elección viva, no un control en reposo— y su ✕ se
   enciende al pasar por encima, que es donde se pulsa. Es un `<button>`, así que hereda el
   `cursor` y el foco visible del resto de la app. */
.chip-quita {
  display: inline-flex; align-items: center; gap: 7px;
  background: var(--accent-dim); color: var(--accent); border-color: var(--accent);
  font-weight: normal; letter-spacing: 0;
}
.chip-quita .chip-x { color: var(--muted); font-size: var(--fs-xs); }
.chip-quita:hover { background: var(--accent-dim-2); }
.chip-quita:hover .chip-x { color: var(--accent); }

/* Canal que TODAVÍA NO EXISTE (WhatsApp). Se ve, y se ve como algo que viene: borde
   punteado —la misma señal de «aquí falta algo» de .bloque-candado y de la tabla
   fantasma—, texto hundido y la palabra PRÓXIMAMENTE. No lleva ámbar ni ⚠: no hay nada
   que corregir, y una alerta por algo que funciona como debe es ruido. */
.chk.futuro { border-style: dashed; cursor: default; }
.chk.futuro:hover { border-color: var(--border-soft); background: transparent; }
.chk.futuro .chk-txt { color: var(--muted); }
.chk.futuro input:disabled { cursor: not-allowed; }

/* Línea explicativa dentro del recuadro del impacto. */
.impacto-nota { margin-top: 6px; font-size: var(--fs-sm); line-height: 1.5; }

/* LAS CIFRAS DEL IMPACTO (rediseño 2026-09-11). Mismo patrón que `.ficha-kpis`: rótulo en
   mono pequeño arriba, número grande debajo. NO se comparte la clase con la ficha aunque el
   dibujo se parezca — son dos composiciones distintas (allí tres cifras mandan la cabecera,
   aquí resumen un panel) y unificarlas ataría el tamaño de una al de la otra. Se comparten
   los TOKENS, que es donde la coherencia de verdad vive. */
.fw-impacto { display: flex; gap: 26px; margin: 0; flex-wrap: wrap; }
.fw-impacto dt {
  margin: 0 0 2px; font-family: var(--mono); font-size: var(--fs-xs);
  letter-spacing: .12em; color: var(--muted);
}
.fw-impacto dd {
  margin: 0; font-family: var(--mono); font-size: var(--fs-lg); color: var(--text);
}
/* El ámbar es el DESATURADO de dato, no el pleno de alerta: son ocultos a propósito por una
   preferencia del usuario, no un error que corregir. */
.fw-impacto dd.warn { color: var(--yellow-data); }
.fw-impacto dd.dim { color: var(--dim); }

/* EL ÁRBOL DEL FILTRO (encargo del dueño, 2026-09-10): Santiago → sectores → comunas y
   Regiones → comunas. Con el país entero dentro son 345 comunas posibles, así que la
   pantalla NACE COLAPSADA: en reposo se ven dos interruptores y 21 casillas de grupo, y
   las comunas solo aparecen si alguien las va a buscar.

   Se usa el `<details>` NATIVO, igual que el expediente de la ficha: trae el teclado, el
   foco y el estado abierto/cerrado sin inventar un patrón de interacción que esta app no
   tiene en ninguna otra parte. Lo único que se le quita es el triángulo del sistema, que
   se reemplaza por uno propio a la DERECHA — a la izquierda chocaría con la casilla. */
/* AIRE ENTRE LOS DOS INTERRUPTORES Y LO QUE DESPLIEGAN (encargo del dueno, 2026-09-14:
   «dejar una separacion entre los botones de la seccion donde y la seleccion que se
   despliega de manera posterior»). Eran 4 px y los sectores parecian la tercera casilla de
   la misma fila en vez de su contenido. La linea fina no es adorno: dice donde acaba la
   pregunta y empieza la respuesta, sin encerrar nada en una caja. */
/* LAS DOS RAMAS, EN DOS COLUMNAS ANCHAS. Heredaban la rejilla de 280 px de `.check-grid`
   y ahi «por sectores de la Region Metropolitana» no cabia en la linea del rotulo: se
   partia en dos, que es justo lo que el dueno pidio evitar. Con la mitad del panel cada
   una entran de sobra y las dos cajas miden lo mismo. */
.fw-ramas { grid-template-columns: 1fr 1fr; margin-bottom: 0; }
.fw-arbol:not([hidden]) {
  margin: 14px 0 0; padding-top: 12px; border-top: 1px solid var(--border-soft);
}
/* Y entre las dos ramas cuando las dos estan abiertas. */
.fw-arbol + .fw-arbol:not([hidden]) { margin-top: 10px; }
details.fw-grupo { border-top: 1px solid var(--border-soft); }
details.fw-grupo:first-child { border-top: none; }
details.fw-grupo > summary {
  display: flex; align-items: center; gap: 8px; cursor: pointer; list-style: none;
}
details.fw-grupo > summary::-webkit-details-marker { display: none; }
/* LA CASILLA YA NO SE ESTIRA. Con `flex: 1` ocupaba la fila entera y empujaba el
   desplegable al extremo derecho, a veinte centimetros del nombre del sector: el dueno lo
   reporto asi el 2026-09-14 —«el boton de desplegar practicamente no se ve y esta muy hacia
   la derecha»—. Con el ancho natural, el control queda pegado a lo que despliega. Lo que se
   pierde es que el resto de la fila deje de marcar la casilla; a cambio ABRE el grupo, que
   es lo que alguien que pincha ahi esta buscando. */
/* EL NOMBRE EN UNA COLUMNA DE ANCHO FIJO. Sin esto los controles quedaban en escalera —
   uno por cada largo de nombre— y una fila de botones desalineados se lee como descuido.
   19,5em sale de MEDIR el más ancho en el navegador y no de estimarlo: «XV ARICA Y
   PARINACOTA 4/4» ocupa 18,9em y le sigue «IX LA ARAUCANÍA 32/32» con 17. Con 16em —el
   primer intento— esos dos desbordaban y sus controles quedaban 14 y 37 px a la derecha de
   los demás, que es la escalera que esto viene a quitar. Lo que sobre en los cortos es
   espacio en blanco, que es justo lo que alinea. Si algún día entra un nombre más largo,
   hay que volver a medirlo: el síntoma no es un error, es una fila descuadrada. */
details.fw-grupo > summary > .chk {
  flex: 0 0 auto; min-width: 19.5em; border-color: transparent;
}
/* EL DESPLEGABLE, CON PALABRAS Y NO UN TRIANGULO SUELTO. Un «▸» de 10 px en gris no se ve
   —eso es lo que el dueno no encontraba— y ademas no dice que hace. El texto lo pone el
   CSS desde el estado del `<details>`, que es el unico sitio donde ese estado vive: no hay
   una clase que mantener sincronizada desde el JS. */
.fw-ver::after {
  content: "ver comunas ▾";
  color: var(--accent); font-size: var(--fs-xs); letter-spacing: .04em;
  border: 1px solid var(--border-soft); border-radius: var(--r-ctl);
  padding: 2px 8px; white-space: nowrap;
}
details.fw-grupo[open] > summary > .fw-ver::after { content: "ocultar ▴"; }
details.fw-grupo > summary:hover > .fw-ver::after { border-color: var(--accent); }
/* Las comunas van indentadas y en columnas más estrechas: son nombres cortos y caben
   muchas por fila, que es lo que evita una lista de 52 elementos en vertical. */
.fw-comunas {
  grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
  padding: 2px 0 12px 22px; gap: 4px 10px;
}
/* LAS REGIONES VAN EN DOS COLUMNAS (encargo del dueno, 2026-09-14: «dejar estas en dos
   hileras para que no se deba hacer tanto scroll hacia abajo»). Quince en vertical eran
   quince filas de scroll para elegir una.

   La que se ABRE ocupa las dos: sus comunas son hasta 54 y en media pantalla saldrian en
   una columna estrecha, que es el problema que esto viene a resolver, no a mudar. El
   `grid-column: 1 / -1` lo hace sin una linea de JS — el estado abierto ya vive en el
   `<details>`.

   Los SECTORES no se tocan: son seis, caben de una mirada, y en dos columnas la primera
   fila quedaria con «oriente» y «centro» separados por medio panel. */
.fw-regiones {
  display: grid; grid-template-columns: 1fr 1fr; gap: 0 18px; align-items: start;
}
.fw-regiones > details.fw-grupo[open] { grid-column: 1 / -1; }
/* Con dos columnas, la linea de arriba tiene que caer en las DOS primeras, no solo en la
   de la izquierda: `:first-child` deja la segunda con una raya sobre nada. */
.fw-regiones > details.fw-grupo:nth-child(2) { border-top: none; }
/* EL NUMERO DE LA REGION, en columna propia y en mono para que los nombres arranquen todos
   en la misma x. Atenuado: identifica, no compite con el nombre. */
.fw-num {
  display: inline-block; min-width: 3.2em; margin-right: 6px;
  font-family: var(--mono); color: var(--muted); font-weight: normal;
}
.fw-comunas .chk { padding: 4px 8px; }
.fw-comunas .chk-txt { font-weight: normal; letter-spacing: .02em; }
/* Marca de estado del grupo: se pinta solo cuando quedó a MEDIAS. «Todas» y «ninguna» ya
   las dice la casilla, y una marca en casi todas las filas es ruido (regla de la app). */
.fw-estado[data-estado="algunas"]::before { content: "parcial"; font-size: var(--fs-xs); }

/* Campo trampa para robots: fuera de la vista y fuera del recorrido de teclado, pero
   presente en el DOM — un autocompletador automático lo rellena. No se usa
   `display:none`: hay robots que descartan lo que está así. */
.trampa {
  position: absolute; left: -9999px; width: 1px; height: 1px;
  overflow: hidden; opacity: 0;
}

/* Enlace con aspecto de botón: se usa donde la acción es NAVEGAR (crear cuenta,
   ingresar), no enviar un formulario. */
.boton-link {
  display: inline-block; cursor: pointer;
  background: var(--accent-dim); color: var(--accent); border: 1px solid transparent;
  border-radius: var(--r-ctl); padding: 6px 14px;
  font-size: var(--fs-sm); letter-spacing: .06em; font-weight: 600;
}
.boton-link:hover { background: var(--accent-dim-2); text-decoration: none; }
.boton-link.secundario {
  background: var(--panel-2); color: var(--text); border-color: var(--border); font-weight: normal;
}
.boton-link.secundario:hover { background: var(--border-soft); border-color: var(--muted); }

/* Bloque CON CANDADO dentro de una sección visible (la muestra de portales para quien
   no tiene cuenta). No es un banner ni una alerta: ocupa el sitio del contenido que
   falta y dice qué hay detrás y cómo se abre — la misma regla de la ficha, "se muestra
   qué falta en vez de esconderlo". El candado no es ámbar: no hay nada que corregir. */
.bloque-candado {
  background: var(--panel-2); border: 1px dashed var(--border);
  border-radius: var(--r-panel); padding: 14px 16px;
  display: flex; flex-direction: column; align-items: flex-start; gap: 8px;
}
.bloque-candado .bc-titulo {
  margin: 0; font-size: var(--fs-sm); font-weight: 600; letter-spacing: .04em; color: var(--text);
}
.bloque-candado p { margin: 0; font-size: var(--fs-sm); line-height: 1.5; max-width: 78ch; }
.bloque-candado .dlg-actions { margin-top: 2px; }

/* «En camino» de la hidratación (F4, 2026-09-05): el remate entró al pipeline pero el
   presupuesto diario de red no alcanzó y el ciclo lo completa. Tono DESATURADO a propósito:
   es información, no una alerta — nada está mal, solo todavía no está. El semáforo pleno se
   reserva para lo que exige atención (CLAUDE.md, dos intensidades). */
.aviso-camino {
  margin: .4rem 0 .2rem;
  padding: .35rem .6rem;
  border-left: 3px solid var(--yellow-data);
  background: color-mix(in srgb, var(--yellow-data) 8%, transparent);
  border-radius: 3px;
}

/* ═════════ LA VISTA MAPA: EL LIENZO, SU BARRA LATERAL Y SUS TARJETAS ═════════
   Este bloque se llamaba «LA LANDING» hasta el 2026-09-07, y se retitula el mismo día en
   que deja de serlo: el mapa se muda a `/mapa` y la portada de `/` pasa a ser la sección
   del final de esta hoja. Dos bloques llamados landing en el mismo archivo es la clase de
   pista falsa que hace tocar el sitio equivocado. La página no cambió ni un píxel; cambió
   su dirección y su papel.
   Encargo del dueño (2026-09-06): replicar el «ver en mapa» de capitalremates —mapa grande
   a la izquierda, tarjetas sincronizadas a la derecha— manteniendo los filtros del usuario,
   y ponerlo de página de entrada (así fue hasta el 2026-09-07).
   Encargo del dueño (2026-09-07): «eliminar el filtro ver (7 días, 15 días, 30 días). El
   buscar, mínimo uf desde y mínimo uf hasta dejarlos en el lado derecho, antes de las
   primeras fichas, así todo el mapa sube un poco y se gana espacio. Lo mismo con este
   aviso, que quede en la barra lateral». Los chips VER se fueron de esta página, y los
   controles y los avisos bajaron a la columna derecha.
   Lo que se gana NO es alto de lienzo —el mapa ya medía una ventana entera— sino que esa
   ventana EMPIEZA arriba del todo: con dos filas de controles encima, el borde inferior del
   mapa caía fuera de la pantalla y había que scrollear cien píxeles para ver el pin más al
   sur. Ese era el «se gana espacio» del encargo, y por eso la altura no se toca.

   Nada de esto estrena una estética. La tarjeta es la misma criatura que la del tablero y
   comparte con ella el recorte de la dirección a dos líneas; el hover repite la quilla cian
   con que una fila del listado dice «esto lleva a alguna parte»; la ausencia se señala con
   el borde punteado que ya usan `.bloque-candado`, la tabla fantasma y el canal que todavía
   no existe. Lo único nuevo es lo que de verdad es nuevo: la relación entre un lienzo que
   se queda quieto y una columna que no. */

/* EL PROBLEMA DE ESTA PÁGINA, Y POR QUÉ NO NECESITA UNA SEGUNDA BARRA DE SCROLL.
   Desde que el listado sigue al mapa (2026-09-07), la columna trae una tarjeta por cada
   remate DENTRO DE LA VISTA — dos si se hizo zoom sobre dos, y las trescientas y pico si se
   aleja hasta ver el Gran Santiago entero. O sea que el caso alto no desapareció: sigue
   siendo una columna de varios miles de píxeles, y la salida cómoda seguiría siendo darle
   `overflow-y: auto` y dejar el mapa fijo al lado. Es exactamente la que el proyecto
   prohíbe: dos barras anidadas obligan a adivinar cuál mover.
   Se invierte el planteamiento y el problema desaparece: la que scrollea es LA PÁGINA, y el
   que se queda quieto es el MAPA, pegado con `position: sticky` justo debajo de la barra
   superior. Se obtiene lo que se quería —el mapa siempre a la vista mientras se recorren
   las tarjetas— sin estrenar ni una barra. Y es lo que hace que
   `card.scrollIntoView({block:"nearest"})` de `mapa.js` funcione: desplaza la ventana, que
   es la que manda.
   La mudanza de los filtros al costado NO cambia esto, y hay que decir por qué porque era
   la duda razonable: los controles viajan DENTRO de la columna derecha, así que crecen con
   ella y se van hacia arriba al scrollear, como cualquier contenido. No se quedan pegados a
   propósito — un bloque fijo de cien píxeles en la columna estrecha se come justo el sitio
   que este encargo venía a recuperar, y con el listado siguiendo al mapa la lista rara vez
   es lo bastante larga como para perder de vista el buscador.
   MATIZ que hay que decir, desde el encargo del dueño del 2026-09-07: el mapa hace zoom con
   la rueda (`scrollWheelZoom: true`), así que ENCIMA DEL LIENZO la rueda no scrollea la
   página. La columna de tarjetas queda fuera del mapa y ahí sí, que es lo que hace vivible
   la combinación — Google Maps se comporta igual. Si el lienzo pasara a ocupar el ancho
   completo, esto habría que volver a mirarlo.
   Dos requisitos que no se pueden tocar, porque al romperlos el sticky no falla — deja de
   hacer nada, en silencio:
     · `align-items: start` en la rejilla — con el `stretch` por defecto la columna del mapa
       ocuparía todo el alto de la fila y no le quedaría recorrido por el que pegarse.
     · `.mapa-layout` sin `overflow` (o sea, `visible`) — un ancestro que recorta anula el
       sticky de sus descendientes. Es la misma trampa que documenta `.grid-wrap` para el
       encabezado fijo de la tabla, con otro disfraz. */
.mapa-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(300px, 380px);
  gap: 12px;
  align-items: start;
}
/* MÓVIL: el lienzo aún no arrastra, y se DICE. Sin este rótulo el mapa se siente muerto
   —el dedo lo empuja y no pasa nada— y esa es peor experiencia que la trampa que evita.
   Desaparece al primer toque, que es cuando el mapa pasa a comportarse como un mapa. */
.mapa-tocar-para-mover::after {
  content: "toca el mapa para moverlo";
  position: absolute; left: 50%; bottom: 10px; transform: translateX(-50%);
  z-index: 500; pointer-events: none;
  padding: 4px 10px; border-radius: 999px;
  background: rgba(13, 17, 23, .82); color: var(--muted);
  font-size: var(--fs-xs); letter-spacing: .02em; white-space: nowrap;
}

/* EL ORDEN VISUAL NO ES EL DEL DOM, y está hecho a propósito: en el marcado la columna va
   PRIMERO para que el tabulador llegue al buscador antes que al widget de Leaflet (que es
   focalizable y cuyas flechas panean el mapa). Aquí se las coloca en su sitio.
   Es la única inversión orden-DOM/orden-visual de la app; se permite porque las dos
   regiones son independientes y ninguna se lee «después» de la otra, que es cuando esta
   técnica desorienta. */
.mapa-layout > .mapa-columna { grid-column: 2; grid-row: 1; }
.mapa-layout > .mapa-vista   { grid-column: 1; grid-row: 1; }

/* La columna izquierda: el lienzo y, debajo, la nota que solo se escribe cuando el mapa no
   puede cumplir lo que promete. Ocupa el alto de la ventana menos la barra, porque en una
   landing el mapa ES la página y no una viñeta que la acompaña.
   `--mapa-hueco` es el aire de arriba y el de abajo, y está en una variable porque el `top`
   del sticky y la altura tienen que sumar lo mismo: si se separan, el mapa asoma por debajo
   del borde inferior y se pierde justo la franja donde el ojo busca el pin más al sur. */
.mapa-vista {
  --mapa-hueco: 12px;
  position: sticky;
  top: calc(var(--topbar-h) + var(--mapa-hueco));
  height: calc(100vh - var(--topbar-h) - var(--mapa-hueco) * 2);
  display: flex; flex-direction: column; gap: 6px; min-width: 0;
}
/* El lienzo se come todo el alto que la nota no use. `min-height: 0` es obligatorio: sin
   él, el mínimo automático de un ítem flex es su contenido y el mapa empujaría la columna
   más allá de la ventana. `overflow: hidden` recorta los tiles al radio del panel — es
   `hidden`, no `auto`: no crea barra ninguna. El fondo es del panel y no el gris claro que
   Leaflet pinta por defecto, que se ve mientras cargan las teselas. */
.mapa-lienzo {
  flex: 1; min-height: 0;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--r-panel);
  overflow: hidden;
  box-shadow: var(--shadow-1);
}
/* La nota del mapa. Va debajo y no encima porque lo que explica es una limitación de lo
   que se está viendo, no un encabezado. Sin `display` propio: el `hidden` del HTML tiene
   que seguir escondiéndola, y darle un display se lo comería. */
.mapa-nota { margin: 0; font-size: var(--fs-xs); line-height: 1.4; }

/* ── LA COLUMNA DERECHA (2026-09-07) ──
   Lleva, en este orden, los filtros, los avisos y las tarjetas, y llega como UN SOLO
   elemento. No es una preferencia de escritura: la rejilla tiene dos columnas, así que un
   tercer hijo suelto caería en la casilla libre de la fila siguiente —debajo del mapa— y
   eso no se ve fallar, solo se ve raro. Un contenedor deja el contrato en un sitio. */
.mapa-columna { display: flex; flex-direction: column; gap: 10px; min-width: 0; }

/* Los filtros son los MISMOS del listado —mismo módulo, mismos `id`, mismos textos— y por
   eso conservan `.filters` entero: el panel, el borde y la sombra son los de esa barra. Lo
   que añade `.mapa-filtros` es lo único que aquí es distinto: caben en 300 px y no en
   1.400. El buscador se queda con una fila entera, porque una dirección no se lee en media
   columna, y los dos extremos del rango comparten la de abajo, que es como se leen de
   verdad: un intervalo, no dos campos sueltos.
   Los anchos van AQUÍ y no en un `style=` de la plantilla, que es lo que había: un ancho
   escrito en línea gana siempre sobre la hoja y deja el campo tan ancho en una columna
   estrecha como en la barra del listado.
   El margen inferior se anula: la separación la pone el `gap` de la columna, y sumar los
   dos deja un hueco que no decidió nadie.
   Los `max-width` son para la OTRA punta: bajo 900 px la columna pasa a ocupar la pantalla
   entera, y sin tope los dos extremos del rango se estirarían a cuatrocientos píxeles cada
   uno. Una caja para cuatro dígitos del ancho de la pantalla no se lee como un campo, se
   lee como un error de maquetación. */
.mapa-filtros { margin: 0; padding: 8px 10px; gap: 8px 10px; }
.mapa-filtros label { flex: 1 1 8em; min-width: 0; max-width: 170px; }
.mapa-filtros label:has(#f-q) { flex-basis: 100%; max-width: 420px; }
.mapa-filtros input { width: 100%; }

/* LOS AVISOS. Cada uno dice algo que la pantalla NO está mostrando: los que esconde el
   filtro de la cuenta, los que no tienen coordenadas, los que quedaron fuera de la vista
   del mapa, los que no se pudieron medir contra el rango, y —solo si algo exige atención—
   el estado de los datos. Van en columna y no en una línea corrida porque son recuentos
   independientes: seguidos, el ojo los suma y entonces ninguno dice lo que dice.
   Ninguna regla les fija el `display` a los hijos para MOSTRARLOS, y es a propósito: cinco
   de los seis nacen con el atributo `hidden` y solo aparecen cuando su número existe, así
   que un `display` propio se lo comería y quedarían los seis visibles y vacíos. Que el
   contenedor sea flex no rompe eso — un hijo en `display: none` no llega a ser ítem, sigue
   sin pintarse.
   El tono lo trae cada aviso con su clase: `muted` para lo que solo informa de una medición
   que no se pudo hacer, `warn` para la única contradicción de la pantalla (un DESDE mayor
   que el HASTA). Es la regla de las dos intensidades: se marca solo lo que exige atención. */
.mapa-avisos {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  font-size: var(--fs-xs); line-height: 1.4;
}
/* `#estado-datos` NO usa `hidden`: `common.js` le escribe el texto o lo deja en blanco. Un
   hijo vacío en una columna flex sigue reservando su hueco, así que sin esto quedaría un
   renglón en blanco que nadie explica. Es la excepción a lo de arriba y va aquí, en la
   única regla que puede apagar un hijo, para que se lea junto al motivo. */
.mapa-avisos > :empty { display: none; }
/* Un aviso que además NAVEGA se ve como tal. `.muted` gana al color de `a` por
   especificidad y el proyecto quita el subrayado a los enlaces, así que sin esto un aviso
   pulsable y uno que no lo es son idénticos en reposo — y en un teléfono no hay hover que
   lo desempate. Punteado y no sólido: sigue siendo un aviso, no una llamada a la acción. */
.aviso-enlace { text-decoration: underline dotted; text-underline-offset: 3px; }
.aviso-enlace:hover { color: var(--text); text-decoration: underline; }

/* Y si NO HAY NINGÚN aviso que dar, se apaga el bloque entero. Ojo: NO es el caso normal
   —con la cartera de hoy siempre hay al menos la línea de «sin ubicación»—, así que esto
   protege el día raro, no el corriente. No es
   cosmética: un contenedor de alto cero sigue cobrando los dos `gap` que lo rodean, y esos
   10 px de más aparecerían justo cuando no hay nada que decir. La condición es literal
   —«ningún hijo que no esté oculto y no esté vacío»— y si el navegador no entendiera
   `:has()` la regla se cae sola y lo único que queda es el hueco. */
.mapa-avisos:not(:has(> :not([hidden]):not(:empty))) { display: none; }

/* La columna de tarjetas. Sin `overflow` y sin alto máximo —crece lo que tenga que crecer—
   porque la barra de la ventana es la que la recorre. `min-width: 0` no es un detalle: sin
   él, una dirección larga empuja el ancho mínimo de la pista del grid y le come sitio al
   mapa. */
.mapa-tarjetas {
  display: flex; flex-direction: column; gap: 8px; min-width: 0;
}

/* EL HUECO DE LA COLUMNA. Que no haya tarjetas significa cuatro cosas distintas —no hay
   remates en esta parte del mapa · ninguno de los que pasan el filtro tiene coordenadas ·
   los filtros de pantalla no dejan ninguno · no llegó nada del servidor— y el texto lo
   escribe `mapa.js`, que es el único que sabe cuál es. Lo que pone esta regla es que las
   cuatro se lean como un hueco EXPLICADO y no como una lista que se quedó en blanco: el
   borde punteado que la app ya usa para lo que todavía no está (`.bloque-candado`, la tabla
   fantasma, el canal que no existe), sin ámbar y sin ⚠, porque no hay nada que corregir.
   Va sobre el `<p>` y no sobre una clase nueva a propósito: la columna solo tiene párrafos
   sueltos en estos casos —las tarjetas son `<a>`—, así que la clase sería un segundo
   contrato que mantener entre esta hoja y `mapa.js`. Alcanza también al «cargando…», que es
   el mismo hueco un segundo antes, y al `<p class="neg">` de un error, que se pinta rojo
   por su clase y encuadrado por ésta. */
.mapa-tarjetas > p {
  margin: 0;
  border: 1px dashed var(--border);
  border-radius: var(--r-panel);
  background: var(--panel-2);
  padding: 12px 14px;
  font-size: var(--fs-sm); line-height: 1.5;
}

/* LA TARJETA. Desde el 2026-09-08 ofrece DOS acciones y por eso son dos controles: el
   cuerpo es un `<button>` que lleva el mapa a la propiedad, y abajo a la derecha un `<a>`
   que abre la ficha (encargo del dueño). Antes la tarjeta entera era un enlace a la ficha,
   así que ver DÓNDE cae un remate obligaba a buscar su pin a ojo entre cientos.
   Van uno AL LADO del otro y no anidados: un `<a>` dentro de un `<button>` es HTML inválido.
   El contenedor solo pinta la caja; quien la rellena es `.mapa-card-ir`.

   SIGUE SIENDO UN `<a>` CUANDO NO HAY MAPA (lo decide `tarjeta()` en mapa.js): si Leaflet
   no cargó, esta columna es la aplicación entera y un botón que promete llevarte en el mapa
   no llevaría a ninguna parte. Por eso los estilos de abajo valen para los dos casos.

   En reposo calla, que es lo que corresponde a una de cientos; al pasar por encima se
   aclara y saca la quilla cian, el mismo gesto de una fila del listado. */
/* EL ALTO LO MARCA LA FOTO (encargo del dueño, 2026-09-09: «que el alto de la foto marque
   el inicio y fin de la tarjeta… todas las tarjetas deben tener el mismo tamaño»).
   `--foto` es la ÚNICA medida: la altura de la tarjeta se deriva de ella y del relleno, así
   que cambiar el tamaño de la miniatura no descuadra nada. Con dos números —uno para la
   imagen y otro para la caja— el día que se toque uno la foto deja de estar centrada.
   TODAS MIDEN LO MISMO, y eso obliga a una consecuencia que hay que aceptar de frente: la
   dirección se corta con puntos suspensivos en UNA línea. Antes envolvía a dos o tres y por
   eso las tarjetas tenían alturas distintas; el texto completo sigue en el `title` y en la
   ficha. Una columna de cientos se recorre con la vista, y para eso el ritmo regular vale
   más que ver la dirección entera en las tres que la tienen larga. */
.mapa-card {
  --foto: 84px;
  position: relative;               /* ancla del «Ver ficha», que va en la esquina */
  display: flex; flex-direction: column;
  height: calc(var(--foto) + 16px); /* 8 px de aire arriba y abajo */
  background: var(--panel-2);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-panel);
  padding: 8px 10px;
  color: var(--text);
  cursor: pointer;
  transition: background .1s ease;
}
.mapa-card:hover {
  background: #202b39;
  box-shadow: inset 2px 0 0 var(--accent);
  text-decoration: none;
}

/* EL CUERPO PULSABLE. Un `<button>` trae de fábrica fondo, borde, fuente y centrado propios
   del sistema operativo: hay que apagarlos todos o la tarjeta deja de parecer una tarjeta.
   Hereda la tipografía en vez de fijarla — así sigue a los tokens de la hoja y no se queda
   con la de un botón nativo.
   `text-align: left` es obligatorio: sin él el navegador centra el contenido y la dirección
   deja de alinearse con la comuna de arriba.
   NO lleva su propio hover: el aclarado es de la tarjeta entera, que es lo que se pulsa. */
/* CON FOTO la tarjeta pasa a DOS COLUMNAS: la miniatura a la izquierda y el texto a la
   derecha (encargo del dueño, 2026-09-09). El botón deja de apilar en columna y apila en
   FILA; el texto sigue siendo una columna, ahora dentro de `.mapa-card-cuerpo`.
   Se hace con `flex-direction: row` y no con una rejilla porque la foto solo la trae la
   tarjeta SELECCIONADA: con `grid-template-columns` habría que declarar la variante sin
   foto para que el texto no quedara indentado por una columna vacía, y con flex el texto
   ocupa todo el ancho cuando la foto no está, sin una regla más. */
.mapa-card-ir {
  /* `align-items: center` es lo que deja la foto a la misma distancia del borde de arriba
     que del de abajo, que es lo que pidió el dueño. El texto se centra con ella. */
  display: flex; flex-direction: row; align-items: center; gap: 10px;
  width: 100%; height: 100%; padding: 0; margin: 0;
  background: none; border: 0; border-radius: 0;
  font: inherit; color: inherit; text-align: left;
  cursor: pointer;
  /* Un control que se pulsa repetidas veces —se recorre la columna probando propiedades—
     no debe seleccionar su propio texto al segundo clic: el resaltado del navegador tapa la
     tarjeta con un bloque de color y parece un fallo. La dirección sigue siendo copiable
     desde la ficha, que es donde se va a leer con calma. */
  user-select: none;
}
/* El foco del teclado se dibuja sobre la TARJETA y no sobre el botón: el botón llega hasta
   los bordes del relleno y su anillo quedaría pegado al texto. Así se ve la misma caja que
   se va a activar. */
.mapa-card:has(.mapa-card-ir:focus-visible) {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.mapa-card-ir:focus-visible { outline: none; }

/* «VER FICHA». Va SIEMPRE VISIBLE (elección del dueño entre mostrarlo siempre o solo al
   pasar el ratón): en una pantalla táctil no existe «pasar el ratón», así que esconderlo
   obligaría a mantener dos comportamientos y en el móvil habría que mostrarlo igual.
   En reposo va en `muted` —es la acción secundaria de la tarjeta— y se enciende en cian al
   hover, como todo control de la app. Se posiciona contra la tarjeta, en su esquina. */
/* El texto de la tarjeta. Es la columna que antes era el botón entero, y `min-width: 0` es
   obligatorio: sin él un hijo flex no baja de su ancho de contenido y la dirección larga
   —que ya viene truncada a 64 caracteres— empujaría la foto fuera de la tarjeta. */
.mapa-card-cuerpo {
  display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0;
  /* `min-width: 0` deja que la dirección se corte en vez de empujar la foto fuera; el
     `overflow` impide que una línea de más rompa el alto fijo de la tarjeta. */
  overflow: hidden;
  /* Sitio para que el «Ver ficha» no se monte sobre la última línea. VA AQUÍ Y NO EN EL
     BOTÓN, y la diferencia se ve a simple vista (encargo del dueño, 2026-09-09: «la foto
     tiene que estar centrada considerando el límite superior y el inferior»). En el botón
     este relleno encogía el área que `align-items: center` reparte, así que el eje de
     centrado subía 7 px y la foto quedaba a 1 px del borde de arriba y a 15 del de abajo
     —medido—. Puesto en el TEXTO, que es lo único que el «Ver ficha» puede tapar, la foto
     se centra contra la caja entera y el hueco de abajo sigue reservado. */
  padding-bottom: 14px;
}

/* LA MINIATURA DE STREET VIEW. Cuadrada y pequeña: es una PRIMERA IMPRESIÓN, no una foto
   para estudiar —para eso está la ficha—, y en una columna de cientos de tarjetas cada
   píxel de alto se paga en scroll.

   VA EN TODAS LAS TARJETAS Y SIEMPRE (encargo del dueño, 2026-09-09). El `<img>` se pinta
   SIN `src` y `mapa.js` lo rellena cuando la tarjeta se asoma a la pantalla; el ancho y el
   alto están fijados aquí para que el hueco ocupe su sitio desde el primer momento. Sin
   eso, cada foto que llega empujaría el texto y la columna daría saltos mientras se
   scrollea, que es peor que no tener foto.
   Un `<img>` sin `src` no pinta nada ni pide nada: el hueco se ve como un rectángulo del
   color del panel, que es exactamente lo que se quiere mientras llega.
   `flex: 0 0 auto` para que no se encoja cuando la dirección es larga, y el `border-radius`
   pequeño para que se lea como parte de la tarjeta y no como un adorno pegado encima. */
.mapa-card-foto {
  flex: 0 0 auto;
  width: var(--foto); height: var(--foto);
  object-fit: cover;
  border-radius: 4px;
  border: 1px solid var(--border-soft);
  background: var(--panel);
}


/* El crédito de las fotos: una nota al pie, con el peso de una nota al pie. No se puede
   quitar —es condición de la licencia CC BY-SA— pero tampoco tiene que competir con las
   tarjetas: 10 px, apagado, y se enciende al posarse encima como todo enlace de la app. */
.mapa-credito-fotos {
  margin: 6px 2px 0;
  font-size: 10px;
  line-height: 1.4;
  opacity: .72;
}
.mapa-credito-fotos a { color: inherit; text-decoration: underline; }
.mapa-credito-fotos a:hover { color: var(--accent); }

/* EL ICONO QUE SUSTITUYE A LA FOTO cuando no la hay (encargo del dueño, 2026-09-10: «si hay
   fotos que no se encuentran, dejar una imagen genérica de un icono dependiendo del tipo de
   vivienda»). No es decoración: la fuente de fotos es Mapillary y su cobertura medida en
   Santiago es del 50%, así que la MITAD de la columna cae aquí. Un rectángulo vacío repetido
   ciento veinte veces se lee como una app rota; un icono dice «esto es un departamento».

   VAN EN EL CSS COMO `data:` Y NO COMO ARCHIVOS, por lo mismo que el marcador del hero: ya
   viajan en la hoja que el navegador necesita igual, así que no cuestan una petición — y son
   ~200 bytes cada uno. Con diez archivos serían diez peticiones para dibujar diez líneas.

   El trazo va en el gris apagado del texto secundario y NUNCA en cian: en esta app el cian
   significa interacción, y un icono cian prometería un clic que no existe.

   `otro` es el respaldo y su dibujo es un PIN, no una casa: cuando no se sabe qué es el
   inmueble, un icono de casa afirmaría algo que nadie midió (regla 3 aplicada al dibujo).
   Lo usan los tipos que la base deja en `pendiente` o vacíos. */
.mapa-card-icono {
  display: block;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 46%;
  background-color: var(--panel);
}
.mapa-card-icono,
.mapa-card-icono[data-tipo="otro"],
.mapa-card-icono[data-tipo="otros"],
.mapa-card-icono[data-tipo="pendiente"],
.mapa-card-icono[data-tipo="sin_clasificar"],
.mapa-card-icono[data-tipo=""] { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23566575' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 21s7-6.4 7-11a7 7 0 1 0-14 0c0 4.6 7 11 7 11z'/%3E%3Ccircle cx='12' cy='10' r='2.4'/%3E%3C/svg%3E"); }
.mapa-card-icono[data-tipo="departamento"] { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23566575' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 21V7h7v14'/%3E%3Cpath d='M11 21V3h9v18'/%3E%3Cpath d='M7 11h1M7 15h1M14 7h1M18 7h1M14 11h1M18 11h1M14 15h1M18 15h1'/%3E%3C/svg%3E"); }
.mapa-card-icono[data-tipo="casa"] { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23566575' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 11 12 4l9 7'/%3E%3Cpath d='M5 10v10h14V10'/%3E%3Cpath d='M10 20v-6h4v6'/%3E%3C/svg%3E"); }
.mapa-card-icono[data-tipo="terreno"] { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23566575' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 17h18'/%3E%3Cpath d='M5 17V9l7-4 7 4v8'/%3E%3Cpath d='M5 13h14'/%3E%3C/svg%3E"); }
.mapa-card-icono[data-tipo="oficina"] { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23566575' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 21V4h14v17'/%3E%3Cpath d='M9 8h2M14 8h2M9 12h2M14 12h2M9 16h2M14 16h2'/%3E%3C/svg%3E"); }
.mapa-card-icono[data-tipo="local"] { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23566575' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 9h16l-1-4H5L4 9z'/%3E%3Cpath d='M5 9v11h14V9'/%3E%3Cpath d='M9 20v-6h6v6'/%3E%3C/svg%3E"); }
.mapa-card-icono[data-tipo="industrial"] { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23566575' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 20V11l5 3V11l5 3V8l8 4v8z'/%3E%3Cpath d='M3 20h18'/%3E%3C/svg%3E"); }
.mapa-card-icono[data-tipo="estacionamiento"] { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23566575' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4' y='4' width='16' height='16' rx='2'/%3E%3Cpath d='M10 16V8h3a2.5 2.5 0 0 1 0 5h-3'/%3E%3C/svg%3E"); }
.mapa-card-icono[data-tipo="bodega"] { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23566575' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 8 12 4l8 4v10l-8 4-8-4z'/%3E%3Cpath d='M4 8l8 4 8-4'/%3E%3Cpath d='M12 12v8'/%3E%3C/svg%3E"); }
.mapa-card-icono[data-tipo="agricola"] { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23566575' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 20h18'/%3E%3Cpath d='M12 20v-6'/%3E%3Cpath d='M12 14c-3 0-5-2-5-5 3 0 5 2 5 5z'/%3E%3Cpath d='M12 14c3 0 5-2 5-5-3 0-5 2-5 5z'/%3E%3C/svg%3E"); }

/* «VER FICHA» ES UN CONTROL, NO UNA NOTA AL PIE (encargo del dueño, 2026-09-09: «dejar más
   visible para el usuario la ficha»). Iba en `muted` a 10 px y se perdía: en una tarjeta que
   ya se puede pulsar entera para otra cosa, la acción secundaria tiene que verse o no
   existe.
   Se pinta como cápsula con el borde y el texto en cian —que en esta app significa
   interacción y nada más— y se llena al pasar por encima. No se le da el peso de un botón
   sólido: la acción principal de la tarjeta sigue siendo llevar el mapa hasta ella, y dos
   controles igual de fuertes serían dos preguntas a la vez. */
.mapa-card-ficha {
  position: absolute; right: 8px; bottom: 7px;
  padding: 3px 8px;
  font-size: var(--fs-xs); line-height: 1; font-weight: 600;
  letter-spacing: .04em;
  color: var(--accent); text-decoration: none;
  border: 1px solid var(--accent-dim-2);
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}
.mapa-card-ficha:hover, .mapa-card-ficha:focus-visible {
  background: var(--accent); color: var(--bg); text-decoration: none;
}
/* SELECCIONADA: es la tarjeta del pin que se acaba de pulsar en el mapa. Tiene que
   distinguirse del hover sin gritar, y son dos cosas distintas: el hover es un aclarado
   que se va solo; esto es una marca que SE QUEDA. Por eso no se dice con el fondo —que ya
   habla del puntero— sino con el borde y una quilla más gruesa, más la elevación de
   `--shadow-1`. Cian y no semáforo: estar seleccionado es interacción, no un dato sobre el
   remate.
   El orden en el archivo es la regla: al ir DESPUÉS del hover, su quilla gana cuando las
   dos aplican, y una tarjeta marcada no pierde la marca porque el mouse pase por encima. */
.mapa-card.active {
  /* SE SUBIÓ EL VOLUMEN el 2026-09-11: el dueño reportó que «el destacado de la tarjeta…
     actualmente no se nota mucho». Antes eran un borde ATENUADO (`--accent-dim-2`) y una
     quilla de 3 px; contra un panel oscuro, con la tarjeta ya elevada por su sombra, la
     diferencia no llegaba a leerse de un vistazo.

     Sube en TRES cosas y ninguna es el fondo: el borde pasa al cian PLENO, la quilla a
     5 px y se añade un aro exterior de 1 px. El fondo se deja quieto a propósito, porque
     el fondo ya es el idioma del HOVER (`#202b39`): si la selección también lo usara, una
     tarjeta marcada y una tarjeta bajo el puntero dirían lo mismo — y son dos cosas
     distintas, una se queda y la otra se va sola.

     Sigue siendo CIAN y no semáforo: estar seleccionado es interacción, no un dato sobre
     el remate. Y sigue yendo DESPUÉS del hover en el archivo, que es lo que hace que su
     quilla gane cuando las dos aplican. */
  border-color: var(--accent);
  box-shadow: inset 5px 0 0 var(--accent), 0 0 0 1px var(--accent-dim-2), var(--shadow-1);
}

/* SIN UBICACIÓN: aquí ya no hay nada que pintar, y conviene dejar dicho por qué en vez de
   que el hueco parezca un olvido. Alrededor de un cuarto de los remates no trae coordenadas
   (en la BASE, 136 de 486), y hasta el 2026-09-07 su tarjeta estaba en esta columna con
   borde punteado y una línea que decía «sin ubicación todavía». Desde que el listado sigue
   al mapa esa tarjeta NO EXISTE: a un remate sin punto no se le puede preguntar si cae
   dentro de la vista, y dejarlo colgando contradiría el encargo —«si se ven dos, que se
   muestren esas dos»—. Ahora viaja como un recuento explicado en `.mapa-avisos`, enlazado
   a la VISTA LISTADO, que es donde sí sale. Sus dos reglas —`.mapa-card.sin-ubicacion` y
   `.mapa-card-motivo`— se retiraron con ella: un estilo sin elemento es deuda, y el día que
   alguien reponga la clase sin reponer el estilo, la señal se pierde en silencio, que es
   peor que no tenerla. */

/* El interior de la tarjeta. La primera línea manda comuna y días a los extremos:
   `space-between` y no una rejilla de columnas porque los días miden dos caracteres y la
   comuna quince, y una columna fija los dejaría a los dos mal.
   Los FORMATOS no se escriben aquí. Los días, el mínimo en UF y el descuento los pintan
   `diasCelda`, `minimoUF` y `descuentoHTML` de common.js, que ya traen sus clases
   (`.dim`, `.muted`, `.warn`, `.neg`, `.dcto`, `.num`) — el mismo número tiene que leerse
   igual en el mapa, en el listado y en la ficha. */
.mapa-card-top {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  font-size: var(--fs-xs);
}
/* La comuna rotula la tarjeta, así que va en el gris de encabezado: es la etiqueta, y la
   dirección de la línea siguiente es el contenido. No se pasa a versales — la comuna se
   escribe igual en el listado y en el tablero, y una tercera forma del mismo campo obliga
   a comprobar que es el mismo campo. */
.mapa-card-top b { color: var(--th-text); font-weight: 600; letter-spacing: .06em; }
/* La dirección es lo que identifica el remate de un vistazo: color de texto pleno. El
   recorte a dos líneas lo pone la regla que comparte con la tarjeta del tablero, arriba;
   la separación la pone el `gap` del flex, así que aquí el margen se anula. */
/* UNA sola línea, con puntos suspensivos. Es lo que hace que todas las tarjetas midan
   igual; el texto completo va en el `title` del elemento y entero en la ficha. */
/* LA DIRECCIÓN DE LA TARJETA DEL MAPA: UNA línea, no dos.
   El selector lleva `.mapa-card` delante A PROPÓSITO. La regla compartida de arriba es
   `:is(.card .addr, .mapa-card-addr)`, y dentro de un `:is()` la especificidad la fija el
   argumento MÁS específico —`.card .addr`, que son dos clases—, así que un `.mapa-card-addr`
   suelto pierde y el `display: -webkit-box` de allá se queda. Costó verlo: la dirección se
   veía cortada a hachazos, sin puntos suspensivos, y `scrollWidth` decía que no desbordaba
   porque quien recortaba era el ancestro, no ella.
   Dos líneas están bien en el tablero, donde la tarjeta crece; aquí no, porque todas las
   tarjetas tienen que medir lo mismo. */
.mapa-card .mapa-card-addr {
  margin: 0; color: var(--text);
  display: flex; align-items: baseline; gap: 4px; min-width: 0;
}
/* SE RECORTA LA DIRECCIÓN, NO LA FILA. El ⚠ del lead sin confirmar va FUERA del trozo que
   se corta: con la elipsis sobre toda la línea, una dirección larga se comía la marca — y
   esa marca se muestra SIEMPRE (regla 2 de CLAUDE.md), no cuando sobra sitio. */
.mapa-card .mapa-card-addr .dir {
  display: block; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mapa-card .mapa-card-addr .tag-lead { flex: 0 0 auto; }
.mapa-card-datos { font-size: var(--fs-xs); color: var(--muted); }
.mapa-card-dcto { font-size: var(--fs-xs); }

/* ── EL MAPA POR DENTRO ──
   El tooltip de Leaflet viene blanco de fábrica y sobre una satelital oscura es un papel
   encendido: se viste con los mismos tokens que el resto: fondo de panel, borde y sombra
   de la app. La flecha se pinta aparte porque Leaflet la dibuja con `border-color`. */
.mapa-lienzo .leaflet-tooltip {
  background: var(--panel); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-ctl);
  box-shadow: var(--shadow-1);
  font-family: var(--sans); font-size: var(--fs-sm); line-height: 1.35;
  padding: 7px 10px;
  /* EL ANCHO, EN SU SEGUNDA VERSIÓN — y la primera está contada porque fue un error de
     lectura que costó un ciclo. El dueño dijo «ese cuadro es un rectángulo alargado que no
     permite desplegar la información; si lo rotas en 90 grados se verá mejor», y eso se
     interpretó como ESTRECHARLO: 190 px y `text-wrap: balance`. El resultado, en su captura
     del 2026-09-07, fue lo contrario de lo pedido — una COLUMNA de cinco líneas con dos
     palabras cada una, más ilegible que la franja original. `balance` fue el culpable
     concreto: reparte las líneas a lo ancho MÍNIMO que las iguala, así que sobre un texto
     de seis palabras encoge la caja muy por debajo del `max-width`. Su segundo encargo lo
     corrige sin ambigüedad: «más cuadrado o más alargado».

     Los 260 px están MEDIDOS sobre las 238 direcciones con pin de la base real, ya sin la
     comuna repetida (ver `pinDe` en `mapa.js`): mediana 28 caracteres, p90 39, máximo 51. A
     este cuerpo eso deja la mitad larga de la cartera en UNA línea y el resto en dos —
     nunca en cinco. No se pone más ancho porque el tooltip flota sobre el lienzo y tapa los
     pines vecinos, que son justo los que se estaban comparando.

     `white-space: normal` es obligatorio y no decorativo: Leaflet trae `nowrap` de fábrica
     y sin esto el `max-width` no envuelve nada, solo recorta. Gana por especificidad
     (0,2,0 contra 0,1,0), no por orden de carga — el cascade de Leaflet ya mordió una vez
     en el número del cúmulo y aquí no puede. */
  white-space: normal; max-width: 260px;
}
.mapa-lienzo .leaflet-tooltip b { display: block; margin-bottom: 2px; }
.mapa-lienzo .leaflet-tooltip-top::before { border-top-color: var(--border); }
.mapa-lienzo .leaflet-tooltip-bottom::before { border-bottom-color: var(--border); }

/* LA BURBUJA DE UN CÚMULO de pines. `mapa.js` la construye con un `divIcon` propio y la
   plantilla deja fuera a propósito la hoja `MarkerCluster.Default.css`, que las pinta
   verde, amarilla y naranja según cuántos remates hay debajo: en esta interfaz el verde y
   el amarillo SON el semáforo, y un cúmulo verde se leería como «doce oportunidades
   favorables» cuando solo significa «doce puntos juntos». El color es dato, no decoración.
   Como esa hoja no se carga, TODO el aspecto de la burbuja se decide aquí — no es una
   corrección sobre el estilo del plugin, es el estilo. Va en cian porque un cúmulo se pulsa
   y se abre: es interacción.

   POR QUÉ EL NÚMERO SE CENTRA EN UN HIJO Y NO AQUÍ (encargo del dueño, 2026-09-07: «que la
   cantidad de remates esté en el centro del círculo negro; hacer un ajuste estético para
   que el borde se vea un poco más»). La versión anterior YA centraba con `flex` sobre esta
   misma caja y aun así se veía descentrada, así que no bastaba con repetir el centrado: el
   descentrado tenía una causa, y es del CASCADE. Esta burbuja es TAMBIÉN un
   `.leaflet-marker-icon` —Leaflet le pone esa clase junto a la nuestra—, `leaflet.css`
   declara `display: block` para ella con la MISMA especificidad que `.mapa-cluster`, y la
   plantilla carga las hojas de Leaflet al final del `<body>`, o sea DESPUÉS de `style.css`.
   Empatan en peso, gana la última, y el `display: flex` de aquí no llegaba a aplicarse
   nunca: el número caía arriba a la izquierda.
   Se cierra por los DOS lados, porque el modo de falla es mudo:
     · el selector lleva el TIPO (`div.mapa-cluster`) y con eso pesa más que cualquier regla
       de una sola clase que Leaflet publique hoy o mañana;
     · y el número vive en `.mapa-cluster-n`, un elemento que NINGUNA hoja ajena nombra. Ahí
       el centrado no depende de quién cargue antes, que es lo que lo hace robusto. Es
       también lo que declara `mapa.js` al construir el `divIcon`.
   Lo que NO era: el anclaje. `mapa.js` fija `iconAnchor` en la mitad exacta de `iconSize`,
   así que la caja cae sobre su coordenada. Ni la hoja base de markercluster, que no mide
   nada: la que pinta y dimensiona las burbujas es `MarkerCluster.Default.css`, y esta
   página no la carga.
   Y una decisión de más, del mismo encargo: la sombra deja de ir desplazada hacia abajo
   (era `0 4px 12px`) y pasa a ser SIMÉTRICA. En una tarjeta ese desplazamiento simula una
   luz cenital, pero aquí el borrón oscuro se lee como parte del disco negro, el círculo
   PERCIBIDO baja y el número vuelve a parecer alto. Ningún cálculo de centrado arregla eso:
   se centra la caja, no lo que el ojo ve. Un marcador sobre un plano no tiene de dónde
   recibir luz. */
div.mapa-cluster {
  /* Tiene que medir lo mismo que el `CLUSTER_PX` de `mapa.js`, y no son dos fuentes de
     verdad: el estilo EN LÍNEA que escribe Leaflet gana siempre sobre una hoja, así que
     esto es el suelo —la burbuja sigue siendo redonda y del tamaño previsto si algún día el
     icono llega sin medida—, nunca un valor que pueda contradecir al otro. */
  --cluster-d: 34px;
  width: var(--cluster-d); height: var(--cluster-d);
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--panel); color: var(--accent);
  font-family: var(--mono); font-size: var(--fs-sm); font-weight: 600; line-height: 1;
  /* EL ARO: 2 px y un cian a media asta. El encargo pedía verlo «un poco más» y el límite
     es que siga siendo dato de fondo: con el cian pleno, una burbuja cualquiera gritaría lo
     mismo que la resaltada y se perdería la señal del hover. Queda a media distancia, y la
     distinción con `.mapa-cluster-resaltado` se sostiene sobre TRES cosas a la vez —el aro
     pleno, el halo que crece de 5 a 8 px y el fondo—, no sobre una sola.
     El aro se dibuja con una sombra `inset` y no con un `border` para no tocar los 34×34:
     si la caja y el recuadro no miden lo mismo, el pin se sale de su coordenada. */
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent) 55%, transparent),
              0 0 0 5px color-mix(in srgb, var(--accent) 12%, transparent),
              0 0 10px rgba(0, 0, 0, .6);
  transition: background .12s ease, box-shadow .12s ease;
}
/* EL NÚMERO. Ocupa la burbuja entera y centra dentro de sí mismo, así que da igual qué
   `display` acabe teniendo el padre: el centro es el centro. Y se centra con `place-items`
   y no ajustando el `line-height` a la medida de la burbuja, que es la forma frágil de
   hacer lo mismo: obliga a mantener dos números en sintonía —el alto y el interlineado— y
   se descuadra sola en cuanto cambia el tamaño del cúmulo o la fuente del sistema. */
.mapa-cluster-n {
  display: grid; place-items: center;
  width: 100%; height: 100%;
}
div.mapa-cluster:hover { background: var(--accent-dim); }
/* RESALTADA: el puntero está sobre una tarjeta cuyo pin vive dentro de este cúmulo, así
   que el pin no se ve y hay que responder donde el remate está de verdad. Se enciende el
   aro y crece el halo —el mismo aumento de presencia que `ESTILO_HOVER` le da a un pin
   suelto—, sin cambiar de color: sigue siendo el mismo cúmulo, más despierto.
   Lleva las DOS clases en el selector para pesar lo mismo que `div.mapa-cluster:hover` y
   ganarle por orden. Si pesara menos no se notaría casi nunca —el hover suele estar en otro
   elemento—, pero la burbuja resaltada perdería su fondo justo al pasarle el puntero por
   encima, que es cuando más quiere verse. */
div.mapa-cluster.mapa-cluster-resaltado {
  background: var(--accent-dim);
  box-shadow: inset 0 0 0 2px var(--accent),
              0 0 0 8px color-mix(in srgb, var(--accent) 22%, transparent),
              0 0 10px rgba(0, 0, 0, .6);
}

/* Pantalla angosta: una sola columna, el mapa arriba y debajo la barra lateral entera
   —filtros, avisos y tarjetas, en ese orden—. Se lee bien así: primero el mapa, que es el
   titular de la página; después los controles, pegados a la lista que recortan.
   Y el mapa DEJA de estar pegado, a propósito. Un lienzo sticky que se queda con media
   pantalla de teléfono convierte la lista en una ranura de cuatro centímetros, y lo que
   justificaba tenerlo siempre a la vista —pasar por una tarjeta y ver encenderse su pin—
   no existe en una pantalla sin puntero. Se lee arriba y se scrollea por encima.
   El alto va con `clamp` y no con un `vh` a secas: en un móvil la barra retráctil del
   navegador entra en el cálculo de `vh`, así que el tope en píxeles es el que evita que el
   mapa se coma la primera tarjeta. El corte en 900px es el mismo de `.detail-grid`. */
@media (max-width: 900px) {
  .mapa-layout { grid-template-columns: 1fr; }
  .mapa-vista { position: static; height: clamp(280px, 52vh, 420px); }
}

/* ═══════════════ LA PORTADA DE `/` — EL LANDING (2026-09-07) ═══════════════
   Encargo del dueño: «que sea algo atractivo para el usuario, menos Bloomberg y mucho más
   amigable para el usuario del día a día, básicamente que genere el gancho para después
   navegar en la web … que no sea solo texto, ideal incorporar alguna imagen».

   TODA la app está afinada para lo contrario, y con razón: es una herramienta de análisis y
   su densidad es una virtud —treinta filas comparables de un vistazo, cinco tamaños de
   letra y ni uno más—. Esta es la única pantalla donde esa densidad estorba: quien llega no
   viene a comparar nada todavía, viene a entender en cinco segundos qué es esto. Así que
   aquí se invierte el reparto —mucho aire, una idea por bloque, la tipografía grande donde
   de verdad manda— SIN estrenar una segunda estética: los colores, los radios, las sombras
   y el idioma de los botones son los de siempre. Es la misma marca en otro registro, no un
   micrositio aparte.

   Cuatro decisiones que conviene leer antes de mover nada:

   · LA ESCALA. La cabecera de esta hoja dice «5 tokens, nada fuera de ellos», y el mayor
     mide 20 px: es la escala de una pantalla de trabajo, y sostiene todo lo demás. Un
     titular de portada a 20 px no es sobrio, es invisible. El landing declara SU escala
     —tres tamaños de display— y la declara sobre `.landing-hero`, que es lo que impide que
     se filtre: fuera de esta sección esos tokens no existen y ninguna regla de la app puede
     alcanzarlos. La regla no se rompe, se acota.

   · EL CIAN NO SE TOCA: sigue significando SOLO interacción. Por eso la cifra grande —que
     es un dato— va en color de texto, y el cian se queda para lo que se pulsa. En una
     pantalla llena de imagen eso es justo lo que deja ver a la primera qué es pulsable, y
     de paso evita el vicio del género, que es pintar del color de las acciones un número
     que no lleva a ninguna parte.

   · EL TEXTO A LA IZQUIERDA, no centrado, y por dos motivos que se refuerzan. Uno es del
     encuadre: la exportación de Esri va de −70,78 a −70,50, así que la cordillera cae a la
     DERECHA — dejarle ese lado libre al cerro es lo que hace que la foto se vea, en vez de
     quedar de textura detrás de un bloque de texto. El otro lo dice la plantilla: «CREAR
     CUENTA» desaparece cuando hay sesión, y en una fila centrada eso movería de sitio al
     botón que queda. Alineados a la izquierda, el primero no se mueve nunca.

   · UNA SOLA BARRA DE SCROLL, y por eso aquí no hay ni un `100vw`. La forma cómoda de sacar
     una sección a sangre es `margin-inline: calc(50% - 50vw)`, y en esta app está prohibida
     de hecho: `html` reserva el gutter de la barra (`scrollbar-gutter: stable`), `100vw` lo
     INCLUYE, y la sección acabaría unos píxeles más ancha que el área visible — o sea,
     barra horizontal en la única página que no la tenía. Se consigue lo mismo por el otro
     lado: se le quita el padding a `<main>` en esta página y el landing se administra sus
     propios márgenes. No hay resta que pueda salir mal. */

/* A SANGRE. `<main>` trae 18 px de costado y un tope de 1500 px pensados para las pantallas
   de datos; la portada los anula y reparte el aire por dentro.
   Si un navegador no entendiera `:has()`, esta regla se cae entera y el landing queda dentro
   del padding de siempre: más angosto y con la foto sin llegar al borde, pero completo y
   legible. Es la misma apuesta que ya hace `.mapa-avisos` más arriba, con el mismo modo de
   falla benigno — y por eso los tokens de escala NO viven aquí sino en la sección: si se
   perdieran con la regla, el titular se quedaría sin tamaño, que ya no es benigno. */
main:has(.landing-hero) { padding: 0; max-width: none; }

/* EL CANAL DE LECTURA, y es UNO para las dos secciones. El hero se pinta a lo ancho de la
   ventana y los bloques van centrados en una columna, así que la salida fácil —un margen
   fijo para el hero, un `max-width` centrado para los bloques— dejaría el titular pegado al
   borde y las tres columnas empezando muy adentro. Dos alineaciones distintas en una página
   de dos secciones se leen como dos páginas pegadas, y esta tiene que leerse de un tirón.
   `max(20px, calc(50% - 540px))` resuelve las dos puntas con una sola expresión: sobre
   1120 px de ventana centra el contenido en una columna de 1080, y por debajo se planta en
   20 px de margen. En un teléfono de 390 px deja 350 px útiles. */
.landing-hero, .landing-bloques { padding-inline: max(20px, calc(50% - 540px)); }

/* ── EL HERO ──
   `isolation: isolate` no es adorno: crea el contexto de apilamiento dentro del cual vive
   `.hero-fondo` con su `z-index: -1`. Sin él, un z-index negativo se va por detrás del fondo
   de la PÁGINA y la foto desaparece en cuanto algún ancestro tenga fondo propio. Con él, el
   fondo queda encerrado aquí: por encima del color de esta sección y por debajo de su
   contenido, que es exactamente el orden que se quiere.

   EL FONDO PROPIO ES PARTE DEL DISEÑO, NO UN HUECO. Si la exportación de Esri no llega
   —servicio caído, sin red, un bloqueador de contenido— `landing.js` retira la imagen y
   marca la sección, y lo que queda es esto: la base oscura de la app, un halo cian arriba a
   la izquierda y otro más profundo abajo a la derecha. Es el punto de señal del logo hecho
   fondo, y se ve terminado. La foto suma; la página no depende de ella para no parecer rota.

   El velo del pie va también aquí, en el fondo de respaldo, y no solo sobre la foto: es lo
   que funde la portada con la sección siguiente, y tiene que seguir haciéndolo el día que la
   foto no esté. Cuando la foto sí está, esta capa queda debajo y no se ve — dos veces el
   mismo remate no cuesta nada y evita que el corte reaparezca justo en el caso raro.

   La altura es la ventana MENOS la barra superior, con un tope de 680 px. El tope es lo que
   deja asomar el borde de la sección siguiente en pantallas altas: una portada que ocupa
   exactamente el alto visible esconde que hay más abajo, y entonces el gancho se queda sin
   su segundo paso. La línea con `svh` repite la de arriba para los navegadores que la
   entienden: en un teléfono, `vh` cuenta la barra retráctil del navegador como si nunca
   estuviera, y la portada se pasa de largo. La de `vh` queda de respaldo. */
.landing-hero {
  /* DOS tamaños en el titular, como cuando llevaba una cifra (encargo del dueño,
     2026-09-08). `--fs-titular` es exacto al que tenía el texto bajo el número y no se
     toca; `--fs-display` es el de la palabra grande.

     EL DISPLAY BAJÓ un 43% el mismo día, en un segundo encargo suyo: «que el texto
     Encuentra sea más pequeño, más grande que el texto de al lado, pero al menos 40% más
     pequeño que actualmente». Venía de `clamp(34px, 11vw, 108px)`, que era la escala de una
     CIFRA de tres dígitos: a 108 px una palabra de nueve letras no es un titular, es un
     cartel, y aplastaba la frase que la sigue.

     Los números salen de mirar la PROPORCIÓN con `--fs-titular`, que es lo que decide si
     se ve bien, no el tamaño absoluto. Con estos tres valores va de 1,58x en un teléfono a
     2,07x en escritorio — la relación clásica entre un display y su bajada. Con el valor
     anterior era de 3,6x, que es lo que se leía como desproporción.

     EL SUELO (30 px) NO baja un 40% más, y es deliberado: en un teléfono de 320 px la
     palabra ya medía 35 px y no 108 —el `clamp` nunca llegaba arriba ahí—, así que recortar
     un 40% la dejaría en 21 px contra los 19 px del texto de al lado. Serían dos tamaños
     indistinguibles, o sea perder la jerarquía que este mismo encargo pide conservar. La
     reducción del 43% se cumple de 500 px para arriba, que es donde el dueño la vio. */
  --fs-display: clamp(30px, 6vw, 62px);
  --fs-titular: clamp(19px, 3.2vw, 30px);
  /* LA MEDIDA DEL HERO: dónde termina, a la derecha, el titular Y el buscador (encargo del
     dueño, 2026-09-08: «encuadrar el texto "oportunidades antes que los demás." para que
     tenga el mismo largo que donde termina el botón buscar»). Una sola variable y no dos
     números iguales: dos números iguales dejan de serlo a la primera vez que alguien mueve
     uno, y aquí la coincidencia ES el efecto — un borde derecho común entre dos elementos
     que no se parecen en nada más.

     ESTÁ MEDIDO, no elegido: «oportunidades antes que los demás.» en Segoe UI Semibold
     —lo que resuelve `font-weight: 600`— ocupa **16,74 veces su cuerpo** contando el
     `letter-spacing: .01em` de sus 34 caracteres. O sea 502 px a 30 px de cuerpo.
     Se redondea ARRIBA a 17 a propósito, y la asimetría importa: pasarse deja el texto unos
     píxeles antes del botón y eso no se ve; quedarse corto lo parte en dos líneas y deja
     «demás.» huérfano, y eso sí. Con 17 la diferencia es de 5 a 8 px en todo ancho sobre
     520 px, y nunca envuelve.
     El buscador venía de 470 px fijos: el que se movió es él, porque la frase no se podía
     encoger sin tocar `--fs-titular`, que el dueño mandó dejar como estaba.

     LA MEDIDA SE DERIVA DEL TAMAÑO DE LETRA, no es un número fijo, y esa es la diferencia
     entre que el borde coincida en la pantalla del dueño o en TODAS. `--fs-titular` es un
     `clamp` que encoge con el viewport, así que con un 508 px fijo el texto se quedaba a
     88 px del botón en una tablet y a 194 px en 600 px de ancho: alineado solo arriba de
     938 px, y desalineado —peor que antes— en todo lo demás. Como el ancho de un texto es
     PROPORCIONAL a su cuerpo, atar la medida al cuerpo alinea las dos cosas a cualquier
     escala.

     EL 17 CODIFICA ESTA FRASE, y hay que decirlo porque es lo que se rompe sin avisar. Si
     el dueño cambia el titular, el número deja de valer y hay que volver a MEDIR la frase
     nueva (su ancho en em, con el tracking incluido) — no hay CSS que lo calcule solo, y el
     síntoma de no hacerlo no es un error sino un borde derecho que dejó de coincidir.

     EN OTRA MÁQUINA la frase mide otra cosa —system-ui es SF Pro en Mac y otra en Linux— y
     puede no caber. No es un fallo: `text-wrap: balance` en el titular la reparte en dos
     líneas parejas en vez de dejar la huérfana. Se degrada bien, que es lo máximo que se
     puede prometer cuando la medida depende de una fuente que no controlamos. */
  --hero-medida: calc(var(--fs-titular) * 17);
  --fs-linea: clamp(13px, 1.5vw, 16px);
  position: relative;
  isolation: isolate;
  overflow: hidden;                  /* recorta la foto; es hidden, no auto: no crea barra */
  display: flex; flex-direction: column;
  justify-content: center; align-items: flex-start; gap: 22px;
  padding-block: 34px 30px;
  /* LA ALTURA DEJA ASOMAR LO DE ABAJO A PROPÓSITO (encargo del dueño, 2026-09-08: «dejar
     estos textos levemente más arriba para que la primera mirada en un notebook normal no
     te obligue a hacer scroll hacia abajo»).
     Hasta hoy el hero medía la ventana entera menos la barra, con tope de 680: en un
     portátil de 768 px de alto el área visible ronda los 640, así que el hero se los comía
     los 594 disponibles y la sección siguiente empezaba justo en el borde inferior. Alguien
     que abría la página y no hacía scroll no llegaba a saber que había contenido.
     Los 118 px que se restan son la banda que se le reserva a esa sección — alcanza para el
     filete superior, el número y el título de los cuatro bloques, que es lo que invita a
     seguir. Y el tope baja de 680 a 560 para que en una pantalla grande el hero no crezca
     hasta volver a esconderlos.
     `100svh` va DESPUÉS de `100vh` y no es una duplicación: en un móvil `vh` cuenta la
     ventana con la barra del navegador retraída, así que mide de más justo donde menos
     sitio hay; el navegador que entienda `svh` se queda con la segunda y el que no, con la
     primera. */
  min-height: min(calc(100vh - var(--topbar-h) - 118px), 560px);
  min-height: min(calc(100svh - var(--topbar-h) - 118px), 560px);
  background-color: var(--bg);
  background-repeat: no-repeat;
  background-image:
    /* El velo del pie va PRIMERO porque la primera capa es la de arriba: si fuera la
       última, el halo de abajo a la derecha le pasaría por encima y el borde inferior
       volvería a marcarse justo en la esquina donde más se nota. */
    linear-gradient(180deg, transparent 72%, var(--bg) 100%),
    radial-gradient(58% 52% at 18% 14%, color-mix(in srgb, var(--accent) 14%, transparent) 0%, transparent 70%),
    radial-gradient(85% 75% at 78% 108%, var(--panel-2) 0%, transparent 62%);
}

/* LA CAPA DE LA FOTO. La plantilla la envuelve en su propio `<div>` y eso es lo que permite
   que el crédito viaje CON la imagen: los dos se van juntos cuando no hay nada que atribuir.
   Al llevar `z-index`, este div abre su propio contexto de apilamiento, y por eso el crédito
   —que va con `z-index: 1` unas reglas más abajo— queda por encima del velo sin salirse del
   fondo ni taparle nada al titular. */
.hero-fondo {
  position: absolute; inset: 0; z-index: -1;
  /* LA IMPRESIÓN INSTANTÁNEA (2026-09-08). Es la MISMA foto a 24 px de ancho, en WebP y
     escrita aquí dentro: 134 bytes, o sea que no cuesta una petición ni espera a nada — ya
     está en la hoja de estilos que el navegador necesita igual para pintar cualquier cosa.
     Estirada a pantalla completa queda un degradado borroso con los colores de la foto de
     verdad, y encima va el mismo velo, así que lo que se ve mientras la buena llega es el
     hero con su ambiente y no un agujero.
     El `background-color` de debajo es el suelo: si el WebP no se pudiera decodificar, la
     capa sigue siendo oscura y el titular sigue legible. Nunca un fondo claro — el texto
     del hero es blanco y un destello blanco lo borra durante ese instante. */
  background-color: var(--bg);
  background-image: url("data:image/webp;base64,UklGRn4AAABXRUJQVlA4IHIAAADQAwCdASoYAA4APulep00pJSOiMAgBIB0JQAALgIv4GqZWgGRxQSAA/oP8LpTGHm3S3kQ4wnw/dtfyI9NRt2TUsfHuR27YFf8+eRYJvkCs/RSKtqtNsfvNaqgluy7CA06Kt5qniE5uRVnQq8epehRcAAA=");
  background-size: cover;
  background-position: center 12%;
}
/* `display: contents` hace que el `<picture>` desaparezca de la caja: sin esto es un
   contenedor en línea entre `.hero-fondo` y la foto, y el `height: 100%` del `<img>` se
   mide contra una caja que no tiene altura propia — la foto colapsa. */
.hero-fondo picture { display: contents; }
.hero-foto {
  display: block; width: 100%; height: 100%; border: 0;
  /* `center 12%` y no `center`, y el número está MEDIDO, no elegido a ojo. La foto es 16:9
     y el hero es mucho más apaisado (1440×560 ≈ 2,6:1), así que `cover` recorta arriba y
     abajo — y cuanto más ancha la pantalla, más recorta. Renderizando el recorte real a
     1440 y 1920 px con la posición al 10, 15, 20, 25 y 50%: al 50% la cordillera nevada
     desaparece entera en las dos, al 25% sobrevive en 1440 y se pierde en 1920, y del 15%
     hacia arriba se conserva en las dos. 12% deja la línea de nieve dentro con un margen.
     Importa porque la cordillera es lo que hace que la foto se lea como Santiago, y cae en
     la mitad derecha, que es justo donde el velo es más transparente.
     Si cambia la foto o la altura del hero, hay que volver a mirarlo: no hay CSS que lo
     calcule y el síntoma no es un error, es una montaña que ya no está. */
  object-fit: cover; object-position: center 12%;
}

/* EL VELO, que es lo que hace legible el texto — y es un velo SOBRE LA IMAGEN, nunca un
   texto a media opacidad: bajarle la opacidad a las letras las rompe contra los tejados
   claros y las deja igual de flojas sobre los oscuros, o sea que estropea las dos mitades de
   la foto a la vez.
   Son dos capas con dos trabajos distintos. La horizontal oscurece la IZQUIERDA, que es
   donde cae el texto, y se abre hacia la derecha para que la cordillera siga viéndose: es
   ahí donde la foto hace su trabajo. La vertical cierra el pie en el color de la página.
   Los tonos salen de `--bg` por `color-mix`, así que el día que cambie el fondo de la app el
   velo cambia con él en vez de quedarse en un gris que ya no es de nadie. */
.hero-fondo::after {
  content: ""; position: absolute; inset: 0;
  background:
    linear-gradient(100deg,
      color-mix(in srgb, var(--bg) 84%, transparent) 0%,
      color-mix(in srgb, var(--bg) 72%, transparent) 45%,
      color-mix(in srgb, var(--bg) 28%, transparent) 76%,
      color-mix(in srgb, var(--bg) 12%, transparent) 100%),
    linear-gradient(180deg,
      color-mix(in srgb, var(--bg) 40%, transparent) 0%,
      transparent 26%,
      color-mix(in srgb, var(--bg) 52%, transparent) 76%,
      var(--bg) 100%);
}

/* SIN FOTO (la marca la pone `landing.js` cuando la imagen no llega). Se apaga la capa
   entera: sin imagen no hay nada que velar —el velo solo oscurecería el fondo de respaldo,
   que ya está calibrado— y no hay nada que atribuir, así que el crédito se va con ella.
   Es la mitad de CSS del acuerdo que el script deja escrito en su comentario; la otra mitad
   es retirar el `<img>`, porque un `<img>` roto pinta un icono gris en unos navegadores y
   nada en otros, y «se ve bien igual» no puede depender de cuál. */
.landing-hero.sin-foto .hero-fondo { display: none; }

/* La caja de texto. 660 px es la medida de lectura de un titular corto: más ancha, la
   segunda línea obliga a barrer la pantalla en vez de leerla de un vistazo. */
.hero-texto { position: relative; width: 100%; max-width: 660px; }

/* EL TITULAR. Lleva la cifra dentro, así que la cifra se declara BLOQUE: a 108 px, una
   cifra en línea con un texto de 30 px comparte línea base con él y el conjunto queda
   descolgado. En dos renglones —el número y lo que dice el número— se lee como lo que es.
   `text-wrap: balance` reparte las líneas del titular en lugar de dejar una huérfana de dos
   palabras, que en una frase corta es el defecto que más se nota. */
.hero-titular {
  margin: 0; max-width: var(--hero-medida);
  font-size: var(--fs-titular); font-weight: 600; line-height: 1.25;
  letter-spacing: .01em; color: var(--text);
  text-wrap: balance;
}
/* LA PRIMERA PALABRA DEL TITULAR, con el formato que tenía la cifra: mono como toda cifra
   de la app, en negrita, en bloque y en color de TEXTO —no en cian, que es solo
   interacción—. El dueño lo pidió así al cambiar el número por su frase, y se hereda
   entero salvo dos cosas.

   NO se llama `.hero-cifra`: aquí ya no hay ninguna cifra, y una clase que nombra lo que no
   es manda a buscar un número al próximo que lea la hoja. Se fue con él `tabular-nums`, que
   solo alinea dígitos y sobre una palabra no hace nada.

   EL ANCHO HAY QUE RECALCULARLO cada vez que se mueva `--fs-display`, y no hay red que lo
   atrape: en mono cada carácter ocupa lo mismo, «Encuentra» son NUEVE avances (~0,6 em cada
   uno) y es UNA palabra, así que ningún wrap la parte — o cabe, o desborda la caja.
   Con `clamp(30px, 6vw, 62px)` el peor caso es el escritorio: 9 × 0,6 × 62 = ~335 px dentro
   de los 660 de `.hero-texto`, la mitad justa. En un teléfono de 320 px son ~162 px sobre
   ~280 disponibles. Sobra por todos lados, que es lo que se ganó al bajar de 108. */
.hero-palabra {
  display: block; margin-bottom: 3px;
  font-family: var(--mono);
  font-size: var(--fs-display); font-weight: 700; line-height: 1;
  letter-spacing: -.02em; color: var(--text);
}

/* QUÉ ES ESTO, en una frase. Va en `--th-text` y no en `--muted`: es el segundo renglón que
   se lee, no una nota al pie, y sobre una foto el gris de las notas se apaga demasiado.
   La medida se acota en `ch` porque quien manda en una línea de lectura es el número de
   caracteres, no los píxeles. */
.hero-linea {
  margin: 14px 0 0; max-width: 58ch;
  font-size: var(--fs-linea); line-height: 1.6; color: var(--th-text);
}

/* EL BUSCADOR. Va antes que los botones porque escribir una comuna es el primer gesto que
   hace la gente al abrir una página así. Se ve más «vestido» que un campo de la app —más
   alto, con fondo casi opaco— porque aquí flota sobre una fotografía: los inputs de las
   pantallas de trabajo se apoyan en un panel que en el hero no existe.
   El fondo lleva un 8% de transparencia y no cero: lo justo para que se note que hay una
   imagen detrás, sin que el texto que se escribe compita con un tejado. */
.hero-buscador {
  display: flex; gap: 8px; flex-wrap: wrap;
  margin: 26px 0 0; width: 100%; max-width: var(--hero-medida);
}
.hero-buscador input {
  flex: 1 1 13em; min-width: 0;
  padding: 11px 14px; font-size: var(--fs-md);
  background: color-mix(in srgb, var(--panel) 92%, transparent);
  border-color: var(--border);
}
/* Mismo alto que las dos acciones de abajo (11 px arriba y abajo): tres controles en
   columna con tres alturas distintas se leen como tres estilos distintos. */
.hero-buscador button { padding: 11px 20px; }

/* ── LAS DOS ACCIONES ──
   Hablan el idioma de los botones del pie de la ficha: el mismo ancho mínimo de
   `.accion-pipe` —210 px, que es lo que iguala dos rótulos de largos distintos—, la misma
   letra y la misma cadencia. Un botón de decisión tiene que reconocerse igual en toda la
   app, y esa medida se fijó hace dos días. Lo único que crece es un pelín el aire interior
   (11/22 en vez de 10/20): en la ficha el botón cierra un informe largo y aquí compite con
   una fotografía a pantalla completa.
   Lo que aquí NO se copia es el empate de color, y el motivo es que la pregunta es otra. En
   la ficha son dos caras de una misma decisión —me lo llevo o no—, y pintar una más fuerte
   sería contestarla por el dueño. Aquí no compiten: una entra a mirar el mapa, gratis y sin
   cuenta, y la otra abre una cuenta. Mirar es el paso siguiente para todo el mundo y crear
   cuenta lo es solo para quien ya vio algo que le interesa, así que la primera se lleva el
   cian y la segunda queda en el gris de los secundarios. Igualarlas no sería neutralidad:
   sería obligar a elegir entre dos cosas que van en orden.
   Ese gris es exactamente el de `.boton-link.secundario`, que es como esta app escribe «la
   otra opción» desde que existe la puerta. No es un botón tímido: tiene su borde, su fondo y
   su tamaño completos. */
.hero-acciones {
  display: flex; justify-content: flex-start; gap: 12px; flex-wrap: wrap;
  margin: 20px 0 0;
}
.accion-hero {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 210px; padding: 11px 22px;
  border: 1px solid transparent; border-radius: var(--r-ctl);
  background: var(--accent-dim); color: var(--accent);
  font-size: var(--fs-sm); font-weight: 600; letter-spacing: .06em;
}
.accion-hero:hover { background: var(--accent-dim-2); text-decoration: none; }
/* Este `display: inline-flex` es justo el que revivía el «CREAR CUENTA» oculto: lo devuelve
   a su sitio la regla global `[hidden]` del principio de la hoja, que es donde está contado
   el mecanismo. No se repite aquí un `.accion-hero[hidden]`: dos reglas para el mismo hecho
   se desincronizan, y la global cubre además los elementos que aún no existen. */
/* LA SECUNDARIA SE RECONOCE POR `data-visitante`, que ya está en la plantilla: es el atributo
   con el que `landing.js` esconde el botón cuando hay sesión, o sea que marca exactamente la
   acción de crear cuenta. Añadirle además una clase `secundaria` sería un segundo marcador
   para el mismo hecho —y dos marcadores del mismo hecho se desincronizan—, y de paso un
   selector para un elemento que no existe, que es la deuda que esta hoja ya limpió una vez
   con `.mapa-card.sin-ubicacion`. El día que la acción secundaria no sea la del visitante,
   este selector se cambia; hoy dice la verdad. */
.hero-acciones .accion-hero[data-visitante] {
  background: color-mix(in srgb, var(--panel-2) 88%, transparent);
  color: var(--text); border-color: var(--border); font-weight: normal;
}
.hero-acciones .accion-hero[data-visitante]:hover {
  background: var(--border-soft); border-color: var(--muted);
}

/* AQUÍ VIVÍA `.hero-cifras`, el estilo de la franja de dos cifras del hero. Se retiró con
   la franja el 2026-09-08 (encargo del dueño). Se borra en vez de dejarse «por si vuelve»:
   una regla sin elemento al que aplicar es la misma deuda que este archivo ya limpió con
   `.add-ref[hidden]` y con `.mapa-card.sin-ubicacion`, y la próxima persona que la lea va a
   creer que hay una franja que mantener. Si vuelve, se escribe otra vez — son doce líneas. */

/* AQUÍ VIVÍA `.hero-credito`, la atribución a Esri sobre la foto. Se retiró el 2026-09-08
   con la foto que la exigía: el dueño puso una imagen PROPIA del proyecto y Esri ya no
   tiene nada que ver con la portada.

   Conviene dejar dicho por qué se va ahora y no antes, porque ese mismo día se había
   contestado que NO se podía. Cuando el dueño pidió «borrar el copyright en la foto, solo
   en caso de ser posible», no lo era: la imagen se servía gratis y sin clave PORQUE se
   atribuía, así que borrar el crédito era usar el servicio fuera de sus términos (regla 5)
   y no una decisión de diseño. Lo que se hizo entonces fue dejarla en el peso de una nota
   al pie, y se dejó escrito el único camino real: «cambiar la imagen por una que no la
   exija». Es exactamente lo que pasó.

   Y no se sustituye por otro crédito: no se sabe qué condiciones trae la imagen nueva.
   Inventar una atribución es tan falso como omitir la que toca. Si hace falta, la dicta el
   dueño y vuelve una regla como la que estaba. */

/* ── BAJO EL PLIEGUE: TRES BLOQUES Y SE ACABÓ ──
   Encontrar · valorar · avisar, una línea cada uno. Este proyecto retiró DOS muros de texto
   esta misma semana por encargo del dueño —los rótulos del tablero y el «datos actualizados
   hace 1 h»—; la portada no puede estrenar el tercero.
   El ancho lo pone el canal de lectura compartido de más arriba, así que la primera columna
   arranca donde arranca el titular. */
/* CUATRO EN UNA FILA, y esa es la decisión (2026-09-08). Con `auto-fit` y los textos
   nuevos —cuatro párrafos de unas cuarenta palabras— el navegador acababa dando tres
   columnas y una huérfana debajo, que se lee como un error de maquetación. Y con dos filas
   de dos, la mitad de los bloques cae bajo el pliegue justo en el encargo que pedía lo
   contrario. Cuatro columnas fijas dejan ~250 px cada una: unas siete líneas por párrafo,
   estrecho pero legible, y los cuatro títulos visibles de una mirada.
   Bajo 900 px pasan a dos y bajo 620 a una (más abajo): ahí ya no hay pliegue que ganar y
   una columna de 250 px con siete líneas se vuelve una tira. */
.landing-bloques {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 18px 24px;
  /* EL AIRE SE RECORTA (encargo del dueno, 2026-09-14: «sube levemente los textos 01, 02,
     03, 04 para que el usuario en un laptop no tenga la opcion de hacer scroll down»).
     Venia de 30/52 px.

     LO QUE DA, MEDIDO en el navegador a tres tamanos de laptop antes y despues: la pagina
     baja de 800 a ~745 px y el scroll sobrante de 175 a ~120 px en un 1366x768. **NO lo
     elimina**, y hay que decirlo: los bloques ya terminaban justo en el borde inferior de
     la pagina, asi que aqui no habia mas de 55 px que recuperar — el resto del sobrante es
     el HERO, y el dueno eligio expresamente no tocarlo (opcion «solo subir los bloques,
     sin tocar el hero») para conservar el asomo que decidio el 2026-09-08. Bajar mas el
     aire no gana nada: pegaria los cuatro textos a la foto.

     Si algun dia se quiere el scroll en cero, el numero esta medido y es el hero: hay que
     quitarle ~120 px de tope, y eso es una decision suya, no un ajuste. */
  padding-block: 8px 20px;
  counter-reset: lp-bloque;
}
@media (max-width: 900px) { .landing-bloques { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .landing-bloques { grid-template-columns: 1fr; } }
/* Cada bloque se apoya en una línea fina y no en una caja. Un panel con borde y sombra es el
   vestido de las pantallas de datos, y cuatro seguidos devuelven la página al registro del
   que el dueño la quería sacar. La línea ordena igual y no encierra nada. */
.landing-bloque { border-top: 1px solid var(--border-soft); padding-top: 14px; }
/* La numeración 01 · 02 · 03 · 04 la pone el CSS, y se lleva sola el bloque que se añada o
   se quite. Es decoración con sentido —marca que las cuatro cosas van EN ESE ORDEN, que es
   el recorrido real: encuentras, guardas lo que te interesa, lo valorizas y recién entonces
   revisas los papeles— y por eso no se le pide a la plantilla que la escriba: un número de
   adorno en el marcado es un número que un lector de pantalla lee en voz alta. */
.landing-bloque::before {
  counter-increment: lp-bloque;
  content: counter(lp-bloque, decimal-leading-zero);
  display: block; margin-bottom: 9px;
  font-family: var(--mono); font-size: var(--fs-xs); letter-spacing: .12em;
  color: var(--dim);
}
/* El `h2` de la app no trae márgenes propios (los pone cada sitio) y el del navegador aquí
   abre un hueco de media línea entre el número y el título. */
.landing-bloque h2 { margin: 0 0 7px; font-size: var(--fs-lg); letter-spacing: .01em; }
.landing-bloque p {
  /* Sin `max-width` en `ch`: la columna ya la acota la rejilla de cuatro, y un tope de
     44 caracteres encima dejaba media columna vacía a la derecha de cada párrafo. El
     interlineado baja de 1,7 a 1,55 porque los textos del dueño (2026-09-08) son cuatro
     veces más largos que los de antes: con 1,7 la sección crecía lo que el hero acababa
     de ceder.

     JUSTIFICADOS por encargo del dueño (2026-09-08). `hyphens: auto` NO es un extra que se
     coló: es lo que hace que justificar aquí sea una mejora y no un estropicio. Estas
     columnas miden ~250 px y a 13 px caben unos 38 caracteres por línea, y justificar una
     medida tan corta sin permitir cortes reparte el sobrante entre cinco o seis espacios —
     los famosos ríos blancos bajando por el párrafo, que se ven MÁS desordenados que el
     borde irregular que se venía a corregir. Con guiones, el sobrante por línea es de unos
     pocos píxeles.
     Funciona porque `base.html` declara `<html lang="es">`: sin ese atributo el navegador no
     sabe qué diccionario de partición usar y `auto` no hace nada. Si algún día alguien lo
     quita, esto se degrada a justificado sin guiones — feo, no roto. */
  margin: 0;
  font-size: var(--fs-md); line-height: 1.55; color: var(--muted);
  text-align: justify; hyphens: auto;
}

/* ── PANTALLA ANGOSTA ──
   El corte en 700 px es donde el velo horizontal deja de servir: con el texto ocupando todo
   el ancho, la parte clara de la derecha le pasa por debajo a las últimas palabras de cada
   línea. Se cambia por un velo VERTICAL parejo, que oscurece la foto entera — se pierde algo
   de cordillera y se gana que el titular se lea, y en un teléfono esa es la única prioridad.
   El corte en 520 px es el del teléfono en vertical, y los 390 px del encargo caen dentro con
   margen. Ahí solo cambia lo que de verdad se rompe: dos botones de 210 px no caben en una
   fila de 350 px útiles, así que se apilan a ancho completo, que además es el tamaño de toque
   cómodo.
   EL BUSCADOR NO SE APILA, y es deliberado: el campo y su botón suman 267 px sobre esos 350,
   así que caben —y separados dejarían de leerse como una barra de búsqueda para leerse como
   dos controles sueltos, además de sumar otro renglón a una primera pantalla que ya va
   justa—. Si alguna vez no cupieran, el `flex-wrap` que ya trae los apila solo.
   La cifra y el titular se encogen solos con su `clamp()`. */
/* En pantalla angosta los botones del banner vuelven a medir lo suyo. Igualarlos al más
   ancho multiplica el ancho total por el número de botones, y esa barra ya va justa aquí:
   la coherencia visual de los recuadros vale en el escritorio, donde sobra sitio, y no
   puede pagarse con una barra de scroll horizontal en un teléfono. */
@media (max-width: 760px) {
  .topbar nav { display: flex; }
}

@media (max-width: 700px) {
  .hero-fondo::after {
    background: linear-gradient(180deg,
      color-mix(in srgb, var(--bg) 66%, transparent) 0%,
      color-mix(in srgb, var(--bg) 80%, transparent) 45%,
      color-mix(in srgb, var(--bg) 93%, transparent) 88%,
      var(--bg) 100%);
  }
}
@media (max-width: 520px) {
  /* El `52px` de abajo no es estético: es la banda que se le reserva al crédito de Esri.
     Aquí la franja de cifras ocupa el ancho completo, así que sin reservarle sitio el
     crédito le quedaría debajo — y ahora que está en el mismo plano, encima. Dos líneas de
     11 px con su relleno miden ~40 px; 52 deja aire y no aprieta el titular. */
  .landing-hero { padding-block: 32px 52px; gap: 24px; }
  .hero-acciones { flex-direction: column; align-items: stretch; }
  .hero-buscador { max-width: none; }
  .accion-hero { min-width: 0; }
  .landing-bloques { padding-block: 26px 40px; gap: 14px; }
}

/* ══════════ CONTROL DE CALIDAD: columnas parejas y celdas que se pulsan ══════════
   Encargo del dueño (2026-09-08): «que las columnas queden de tamaños uniformes» y «si
   presiono una de las celdas, que abra una ventana con las propiedades asociadas».

   COLUMNAS PAREJAS con `table-layout: fixed`, no con un `width` por columna. El motivo es
   el mismo que en los botones del banner: aquí el ancho no lo decide esta hoja, lo deciden
   los RÓTULOS —«M²» y «ESTUDIO MUESTRA» en la misma fila— y encima el número de columnas
   CAMBIA solo, porque la tabla de origen pinta una por fuente que haya en la base. Con
   `fixed` el navegador reparte el ancho disponible en partes iguales y la tabla se ajusta
   sola el día que entre un scraper nuevo. La primera columna se deja más ancha porque
   lleva el nombre del tipo, que es texto y no una cifra.
   El encabezado necesita `word-break`: «ESTUDIO MUESTRA» no cabe en una celda de 1/9 y sin
   esto desborda en vez de partirse. */
.tabla-calidad { table-layout: fixed; width: 100%; }
.tabla-calidad th:first-child, .tabla-calidad td:first-child { width: 15%; text-align: left; }

/* EL ENCABEZADO SE PARTE EN VARIAS LÍNEAS, y el prefijo `table.grid` es la mitad del
   arreglo (reporte del dueño, 2026-09-11: «los textos de los encabezados de las columnas
   están traslapados en las columnas finales»).
   Aquí había ya un `word-break: break-word; hyphens: auto` puesto con ese mismo propósito
   —y no hacía NADA—, porque falta lo único que de verdad decide: `white-space`.
   `table.grid th` declara `nowrap`, y con `nowrap` puesto ni `word-break` ni `hyphens`
   pueden partir una línea: no hay línea que partir. El texto desbordaba la celda y, con
   `table-layout: fixed`, se montaba sobre la de al lado — que es exactamente lo que se ve.
   Y `.tabla-calidad th` a secas (0,1,1) PIERDE contra `table.grid th` (0,1,2), así que el
   selector necesita `table.grid` delante. **Es la CUARTA vez que este proyecto tropieza
   con la misma trampa de especificidad** —ya está escrita en CLAUDE.md para `td`, con
   `tabla-lectura`— y la lección se repite entera: el síntoma no es un error en ninguna
   consola, es una pantalla que se ve mal y que hay que MIRAR. */
table.grid.tabla-calidad th {
  white-space: normal; overflow-wrap: anywhere; hyphens: auto;
  font-size: var(--fs-xs); line-height: 1.25; vertical-align: bottom;
}

/* La celda con dato SE PULSA y tiene que decirlo sin gritar: el cursor y un subrayado
   punteado que solo aparece al pasar por encima. No va en cian —el cian es interacción,
   pero aquí lo interactivo son 70 celdas y pintarlas todas convertiría la tabla en un
   semáforo— y el CERO no se pulsa: detrás no hay nada que enseñar. */
.tabla-calidad td.pulsable { cursor: pointer; }
.tabla-calidad td.pulsable:hover { color: var(--accent); text-decoration: underline dotted; }
.tabla-calidad td.pulsable:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* LA VENTANA. Es el primer `<dialog>` de la app y se usa el NATIVO a propósito, por lo
   mismo que el `<details>` de la ficha: trae el foco atrapado, el Esc y el fondo inerte
   sin inventar un patrón de interacción que esta app no tiene en ninguna otra parte.
   Sobre la única barra de scroll: un modal es su propio contenedor y el fondo queda
   bloqueado, así que sigue habiendo UNA barra a la vez — que es lo que esa regla protege
   («dos barras anidadas obligan a adivinar cuál mover»). */
dialog.dlg-calidad {
  width: min(920px, 92vw); max-height: 82vh; padding: 0;
  background: var(--panel); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-panel);
  box-shadow: var(--shadow-1);
}
dialog.dlg-calidad::backdrop { background: rgba(0, 0, 0, .62); }
/* LA VENTANA DEL CUPO (2026-09-14). Comparte `.dlg-cab` y `.dlg-cuerpo` con la de CONTROL
   DE CALIDAD —es el mismo patron y no se copia— y solo cambia la medida: aqui hay dos
   parrafos y dos botones, no una tabla de propiedades. */
dialog.dlg-cupo {
  width: min(460px, 92vw); padding: 0;
  background: var(--panel); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-panel);
  box-shadow: var(--shadow-1);
}
dialog.dlg-cupo::backdrop { background: rgba(0, 0, 0, .62); }
dialog.dlg-cupo .dlg-cuerpo { padding: 15px 18px 18px; }
.dlg-cupo-motivo { margin: 0 0 10px; font-size: var(--fs-md); line-height: 1.55; }
dialog.dlg-cupo .muted { margin: 0 0 16px; font-size: var(--fs-sm); line-height: 1.55; }
.dlg-cupo-acciones { display: flex; gap: 10px; flex-wrap: wrap; }
/* VER PLANES es lo que la ventana propone, asi que lleva el cian pleno del boton primario;
   SEGUIR MIRANDO es la salida y se queda en secundario. Es un <a> y no un <button> porque
   NAVEGA a /cuenta, donde vive TU PLAN — un boton que cambia de pagina esconde su destino
   al clic derecho y al teclado. Repite los valores del boton base porque no puede heredar
   de un selector de tipo. */
.btn-plan {
  display: inline-flex; align-items: center; padding: 7px 14px;
  background: var(--accent-dim); color: var(--accent); text-decoration: none;
  border: 1px solid transparent; border-radius: var(--r-ctl);
  font-size: var(--fs-sm); letter-spacing: .06em; font-weight: 600;
}
.btn-plan:hover { background: var(--accent-dim-2); }
.dlg-cab {
  display: flex; align-items: baseline; gap: 12px;
  padding: 14px 18px; border-bottom: 1px solid var(--border);
  position: sticky; top: 0; background: var(--panel); z-index: 1;
}
.dlg-cab h3 { margin: 0; font-size: var(--fs-sm); color: var(--th-text); letter-spacing: .06em; }
.dlg-cab .dlg-n { font-family: var(--mono); color: var(--muted); font-size: var(--fs-xs); }
.dlg-cab button { margin-left: auto; }
.dlg-cuerpo { padding: 0 18px 18px; }
.dlg-cuerpo table.grid { margin-top: 0; }

/* LAS TARJETAS DE PLAN (encargo del dueño, 2026-09-10). Se usan en el paso 2 del registro
   y en /cuenta, con el mismo componente: dos versiones de una tabla de precios se
   contradicen el día que uno de los dos números cambie, y aquí el número es dinero. */
/* LAS TRES EN UNA LÍNEA, UNA AL LADO DE OTRA (encargo del dueño, 2026-09-14). Eran
   `auto-fit, minmax(230px, 1fr)`, que necesita 714 px para las tres — y el panel de
   `/cuenta` mide 686 (medido en Chromium a 1440), así que MAX se caía a una segunda fila y
   una tabla de precios de tres columnas se leía en dos. Con `repeat(3, 1fr)` las tres
   comparten fila y ancho, que es lo que hace comparable una tabla de precios.
   El `minmax(0, 1fr)` y no `1fr` a secas: el mínimo por defecto de una pista de grid es
   `auto`, así que una tarjeta con una palabra larga se niega a encoger y desborda la fila.
   Bajo 720 px se apilan: tres columnas de 230 no caben en un teléfono, y ahí el orden
   vertical es la única lectura posible. */
.planes-grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px; margin: 10px 0 4px;
}
@media (max-width: 720px) { .planes-grid { grid-template-columns: 1fr; } }
.plan-card {
  display: flex; flex-direction: column; gap: 10px; position: relative;
  padding: 16px; border: 1px solid var(--border-soft); border-radius: var(--r-ctl);
  background: var(--panel-2); cursor: pointer;
}
.plan-card:hover { border-color: var(--border); }
/* El radio se oculta A LA VISTA pero NO al teclado ni al lector de pantalla: nada de
   `display:none`, que lo sacaría del orden de tabulación y dejaría la tarjeta inalcanzable
   sin ratón. Se recorta a un píxel, que es la técnica que conserva el foco. */
.plan-radio {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}
/* El foco del radio se pinta en la TARJETA, que es lo que la persona ve. Sin esto,
   recorrer los planes con el teclado no muestra dónde estás. */
.plan-card:has(.plan-radio:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.plan-estado {
  margin-top: auto; padding-top: 8px; font-size: var(--fs-xs); letter-spacing: .1em;
  color: var(--muted); text-align: center;
}
.plan-card[data-estado="actual"] .plan-estado { color: var(--accent); }
/* El plan vigente se marca con el CIAN de la marca, que en esta app significa
   interacción — y aquí la casilla ES el estado de una acción que se puede cambiar. */
.plan-card[data-estado="actual"] { border-color: var(--accent); box-shadow: var(--shadow-1); }
.plan-card h4 { margin: 0; letter-spacing: .12em; }
.plan-resumen { margin: 4px 0 0; font-size: var(--fs-xs); line-height: 1.4; }
.plan-precio { display: flex; flex-direction: column; gap: 2px; }
.plan-cifra { font-size: 26px; line-height: 1.1; }
.plan-mes, .plan-neto { color: var(--muted); font-size: var(--fs-xs); }
.plan-pesos { color: var(--text); font-size: var(--fs-sm); }
.plan-ventajas { margin: 0; padding-left: 18px; font-size: var(--fs-sm); line-height: 1.7; }
/* Lo que el plan NO incluye va tachado y no se omite: es la regla de «siempre se muestran
   TODOS los campos», y es lo que hace comparable una tabla de precios sin ir y volver. */
.plan-ventajas s { color: var(--muted); }
.plan-elegir { width: 100%; margin-top: auto; }
/* UN ERROR NO SE PINTA EN GRIS. `#plan-msg` nace `.muted` porque casi siempre dice
   «guardando…», y cuando lo que dice es por qué NO se pudo, en gris y al pie de tres
   tarjetas no lo ve nadie — que es la mitad de por qué el dueño lo leyó como «pegado». */
#plan-msg.neg { color: var(--red); font-weight: 600; }
/* LA TARJETA QUE HOY NO SE PUEDE CONTRATAR: borde punteado y rótulo apagado, la misma
   señal de «aquí falta algo» del canal que todavía no existe en AVISOS y de la tabla
   fantasma. No lleva ⚠ ni rojo: no hay nada que el usuario pueda corregir. */
.plan-card[data-estado="sin-cobro"] { border-style: dashed; cursor: default; }
.plan-card[data-estado="sin-cobro"]:hover { border-color: var(--border-soft); }
.plan-card[data-estado="sin-cobro"] .plan-estado { color: var(--muted); }
.plan-card[data-estado="actual"] .plan-elegir { opacity: .7; cursor: default; }
/* EL AVISO DE RETRACTO NO ES LETRA PEQUEÑA, y esto es una exigencia de forma, no de
   diseño: el Decreto 52/2022 (vigente desde el 28-02-2025) obliga a ponerlo junto al
   precio, con tamaño NO INFERIOR al del precio y usando literalmente «derecho a
   retracto». Achicarlo sería incumplir. */
.plan-retracto {
  margin: 12px 0 0; padding: 10px 12px; font-size: var(--fs-sm); line-height: 1.6;
  border: 1px solid var(--border-soft); border-radius: var(--r-ctl); color: var(--text);
}

/* EL AVISO DE MODO DE PRUEBA (2026-09-11, con Mercado Pago). Va con el ámbar PLENO y no
   con el desaturado de dato: la intensidad plena es la de cápsulas y alertas puntuales, y
   esto es exactamente eso — aparece una vez, en una pantalla, y solo cuando el servidor
   está conectado con credenciales de prueba. En un servidor que cobra de verdad no existe,
   así que no puede volverse ruido.
   Se apoya en la clase `warn`, que ya trae el color; aquí solo se le da la caja y el sitio,
   los mismos de `.plan-retracto` para que las dos notas se lean como un par. */
.plan-prueba {
  margin: 12px 0 0; padding: 10px 12px; font-size: var(--fs-sm); line-height: 1.6;
  border: 1px solid currentColor; border-radius: var(--r-ctl);
}

/* ═══ REDISEÑO DEL LISTADO (encargo del dueño, 2026-09-11) ═══════════════════════════════
   La maqueta que él validó está en el artefacto del mismo día. La densidad no baja: la fila
   lleva la misma información; lo que cambia es la jerarquía dentro de ella. */

/* LA FOTO DE CALLE abre la fila. 44 px es la mitad de la miniatura del mapa: aquí la fila
   mide ~46 px de alto y una foto mayor la estiraría, que es exactamente lo que un listado
   de mil filas no puede permitirse. El ancho es FIJO para que la columna no baile mientras
   las fotos van llegando. */
/* 55 px, un 25 % más que los 44 iniciales (encargo del dueño, 2026-09-11). El alto de la
   fila lo marca la foto, así que sube con ella y no hay un segundo número que ajustar. */
.col-foto { width: 63px; padding-left: 6px !important; padding-right: 0 !important; }
.fila-foto {
  width: 55px; height: 55px; border-radius: 4px; object-fit: cover; display: block;
  /* `background-color`, NUNCA el atajo `background`. El atajo resetea `background-repeat`
     a `repeat`, y como esta regla va DESPUÉS de `.mapa-card-icono` —misma especificidad,
     gana la última— el icono del tipo se dibujaba EN MOSAICO: cuatro edificios en la celda
     de un departamento, que es como lo cazó el dueño. Es la misma familia de trampa que la
     especificidad de `:is()` en las tarjetas del mapa: el síntoma no es un error, es algo
     que solo se ve mirando la pantalla. */
  background-color: var(--panel-2); border: 1px solid var(--border-soft);
}
/* EL ICONO ES EL MISMO DEL MAPA, sin retoques (encargo del dueño: «no los nuevos creados»).
   `.mapa-card-icono` ya trae `no-repeat`, centrado y un tamaño RELATIVO (46 %) que se
   adapta solo a la caja: no hace falta —ni conviene— darle un tamaño en píxeles aquí. Un
   segundo juego de medidas para el mismo dibujo es lo que los desincroniza. */

/* DIRECCIÓN Y COMUNA EN UNA CELDA. Eran dos columnas que respondían la misma pregunta. */
.celda-dir { display: grid; gap: 1px; text-align: left; }
.celda-dir b { font-weight: 500; }
.celda-sub {
  font-family: var(--mono); font-size: var(--fs-xs); letter-spacing: .06em;
  color: var(--muted); text-transform: uppercase;
}
/* La celda de dirección es la única alineada a la izquierda de la tabla, y con motivo: es
   texto de largo variable, y centrarlo deja los inicios de línea en sitios distintos en
   cada fila — imposible de recorrer con la vista. */
#tabla td:has(.celda-dir) { text-align: left; }


/* LA FRANJA DE CARTERA. Responde «¿cómo viene la semana?» antes de la primera fila. Son
   datos que ya estaban, repartidos por la pantalla o solo en la ficha. */
.franja-cartera {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  border: 1px solid var(--border-soft); border-radius: var(--r-ctl); overflow: hidden;
  margin-bottom: 10px;
}
.franja-cartera > div { padding: 9px 13px; border-right: 1px solid var(--border-soft); }
.franja-cartera > div:last-child { border-right: 0; }
.franja-cartera dt {
  margin: 0 0 1px; font-family: var(--mono); font-size: var(--fs-xs);
  letter-spacing: .12em; color: var(--muted);
}
.franja-cartera dd { margin: 0; font-family: var(--mono); font-size: var(--fs-lg); }
.franja-cartera dd small { font-size: var(--fs-sm); color: var(--muted); }

/* El crédito de las fotos. La licencia CC BY-SA lo exige y el `title` de cada miniatura no
   basta: tiene que haber una atribución visible en la pantalla que las sirve. */
.credito-fotos { margin: 8px 0 0; font-size: var(--fs-xs); color: var(--muted); }
.credito-fotos a { color: var(--muted); }
.credito-fotos a:hover { color: var(--accent); }

/* ═══ REDISEÑO DE LA FICHA (aprobado por el dueño, 2026-09-11) ═══════════════════════════
   La ficha abría con la dirección y nada más: para saber cuánto cuesta, cuánto menos que
   el mercado y cuándo se remata había que leer ocho filas repartidas en dos bloques. */
/* LA ESTRUCTURA ES LA MISMA EN TODAS LAS FICHAS, y esto costó un reporte del dueño
   (2026-09-11): «algunas están con el botón descartar en la misma línea que la dirección,
   otras con este botón abajo». La primera versión ponía identidad, cifras y acciones como
   tres hermanos con `flex-wrap`, así que la longitud de la DIRECCIÓN decidía si los
   botones cabían al lado o saltaban de línea — cada ficha se veía distinta según su texto.

   Ahora son DOS columnas y no tres: a la izquierda la identidad con sus acciones debajo,
   a la derecha las cifras. Lo que envuelve es la columna entera, nunca un botón suelto, y
   la maqueta no depende del largo de ningún texto. */
.ficha-head {
  display: flex; flex-wrap: wrap; align-items: flex-start; gap: 14px 26px;
  padding: 14px 16px; margin-bottom: 12px;
  border: 1px solid var(--border-soft); border-radius: var(--r-ctl);
  background: var(--panel);
}
.ficha-id {
  flex: 1 1 340px; min-width: 0; display: grid; gap: 8px; justify-items: start;
}
.ficha-id h2 { margin: 0; }
/* Dónde está, qué es, cuánto mide y su rol. En mono y atenuado: identifica, no compite. */
.ficha-sub {
  margin: 2px 0 0; font-family: var(--mono); font-size: var(--fs-xs);
  letter-spacing: .1em; color: var(--muted); text-transform: uppercase;
}
/* LOS DIAS QUE FALTAN, en la linea de identidad y DESTACADOS (encargo del dueno,
   2026-09-14). Bajaron de la esquina, donde competian con las dos cifras con las que se
   decide; aqui van donde el ojo ya pasa al leer que es el inmueble.

   «Destacado» se resuelve con PESO y CUERPO, no con un fondo de color: el semaforo de
   `diasCelda` ya trae su tono —rojo a tres dias, ambar a siete— y pintarle ademas una
   capsula convertiria en alerta permanente una fila que casi siempre es informativa. Los
   tonos son los DESATURADOS de dato, que es lo que corresponde dentro de un texto.

   `text-transform: none` porque el subtitulo va en versalitas y «47 DIAS» se lee peor que
   «47 dias»; y el `letter-spacing` vuelve a cero por lo mismo. */
.ficha-dias {
  font-weight: 600; font-size: var(--fs-sm); letter-spacing: 0; text-transform: none;
  color: var(--text);
}
/* Las clases las pone `diasCelda` y son las de toda la app: `neg` a tres dias o menos,
   `warn` a siete, `muted` cuando ya paso y `dim` sin fecha publicada. Se nombran las
   cuatro y con DOS clases cada una (0,2,0) a proposito: `.neg` y `.warn` ya existen
   globales con la misma especificidad que `.ficha-dias`, asi que sin esto mandaria el
   ORDEN en la hoja — la misma trampa de cascada que ya costo una tarde con
   `.mapa-card .dir`, y que este proyecto lleva contadas cinco veces. */
.ficha-dias.neg { color: var(--red-data); }
.ficha-dias.warn { color: var(--yellow-data); }
.ficha-dias.muted, .ficha-dias.dim { color: var(--muted); font-weight: 400; }
/* LAS CIFRAS DE LA DECISION. No es el «KPI strip» que se elimino el 2026-08-31: aquello
   duplicaba en tarjetas datos que ya estaban abajo; esto es la identidad del remate, y lo
   de abajo es el detalle que la sostiene.

   DOS FILAS DE DOS desde el 2026-09-14 (encargo del dueno): MINIMO y su descuento arriba,
   MAXIMO y el suyo debajo. Era una fila de TRES en flex y la tercera —«se remata en»— se
   fue al subtitulo. Rejilla y no flex porque ahora hay que ALINEAR columnas entre las dos
   filas: en flex, un «MAXIMO» mas corto que «MINIMO» dejaria los dos descuentos en x
   distintas y la pareja se leeria como cuatro cifras sueltas.

   ALINEADAS CON LA DIRECCION, no esquinadas (encargo del dueno, 2026-09-14, tarde: «que
   el minimo y descuento quede alineado con la direccion, que no quede mas esquinado, para
   que se vea mas estetico»). Nacieron esa misma manana con margenes negativos que se comian
   parte del relleno de la cabecera —el recurso de los chips de RENTABILIDAD— y ahi el
   efecto es el contrario: aquellos son controles en el canto de un panel y estas son DATOS
   que acompanan a la direccion, asi que despegarlas del texto las dejaba flotando. Con
   `margin: 0` el primer rotulo arranca en la misma linea superior que el <h2>. */
/* ── EL SELECTOR DE OFERTA MÁXIMA (encargo del dueño, 2026-09-15) ──────────────────
   Reemplaza a las cuatro cifras de la esquina. Aquellas contestaban «cuánto es»; esta
   contesta «hasta cuánto ofrezco», que es la decisión que se toma en esta pantalla.
   Mide 320 px: lo que cabe junto a la dirección sin robarle el ancho al título. */
.ofertador { flex: 0 0 auto; width: 320px; align-self: start; }
.of-cab { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.of-rot {
  font-family: var(--mono); font-size: var(--fs-xs); letter-spacing: .12em; color: var(--muted);
}
.of-valor {
  font-family: var(--mono); font-size: var(--fs-lg); font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* La barra: el carril en el gris de los bordes y el tirador en CIAN, que en esta app es
   el color de lo que se pulsa. Se estilan los dos motores porque `accent-color` solo
   pinta el tirador y aquí el carril también tiene que verse sobre el panel oscuro. */
.of-range {
  -webkit-appearance: none; appearance: none; width: 100%; height: 22px;
  background: transparent; margin: 6px 0 2px; cursor: grab;
}
.of-range:active { cursor: grabbing; }
.of-range::-webkit-slider-runnable-track {
  height: 6px; border-radius: 3px; background: var(--border);
}
.of-range::-moz-range-track { height: 6px; border-radius: 3px; background: var(--border); }
.of-range::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; width: 16px; height: 16px; margin-top: -5px;
  border-radius: 50%; background: var(--accent); border: 2px solid var(--panel);
}
.of-range::-moz-range-thumb {
  width: 16px; height: 16px; border-radius: 50%; background: var(--accent);
  border: 2px solid var(--panel);
}
.of-range:disabled { cursor: default; opacity: .55; }
.of-range:disabled::-webkit-slider-thumb { background: var(--muted); }
.of-range:disabled::-moz-range-thumb { background: var(--muted); }
/* Las dos marcas son los EXTREMOS de la barra, así que se alinean con sus puntas.
   CADA UNA EN UNA SOLA LÍNEA (encargo del dueño, 2026-09-15: «que la tarjeta no sobrepase
   el alto del botón descartar»): con el rótulo sobre la cifra, la tarjeta medía 111 px
   contra los 96 de la columna de la izquierda y estiraba la cabecera entera. */
.of-marcas {
  display: flex; justify-content: space-between; gap: 8px;
  font-family: var(--mono); font-size: var(--fs-xs); color: var(--muted);
}
.of-marcas i { font-style: normal; letter-spacing: .06em; margin-right: 4px; }
.of-marcas span:last-child { text-align: right; }
/* EL DESCUENTO Y EL AVISO COMPARTEN RENGLÓN, y es lo que hace que la tarjeta quepa:
   con el aviso en su propia línea medía 114 px contra los 96 de la columna de la izquierda
   —encargo del dueño, 2026-09-15— y los dos casos que lo muestran son los más comunes (un
   remate recién agregado y uno que todavía no está en MIS REMATES). */
.of-pie {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  margin-top: 6px; font-size: var(--fs-sm); color: var(--muted);
}
/* El color del descuento lo pone `descuentoHTML` de `common.js`, que es quien escribe
   ese número en toda la app. Aquí hubo una pareja `.pos`/`.neg` propia y se retiró con el
   formateador duplicado que los usaba. */
/* SIN `nowrap` A PROPÓSITO: con él, un aviso más largo se saldría de la tarjeta sin que
   nada creciera — el desbordamiento mudo que este proyecto persigue. Envolviendo, el alto
   sube y la guarda del navegador (`test_cabecera_y_filtros`) lo caza. */
.of-aviso { font-size: var(--fs-xs); }
.ofertador.sin-barra .of-pie { margin-top: 2px; }

/* El bloque de `.ficha-kpis` y `.ficha-max` se retiró el 2026-09-15 con las cuatro cifras
   de la esquina: hoy ahí va el SELECTOR DE OFERTA (`.ofertador`, más arriba). Un estilo sin
   elemento que lo use es CSS que nadie ejercita y que no se entera de que dejó de
   funcionar; `.kpi-editable` sobrevive porque el selector sigue llevando su chip. */
.kpi-editable {
  margin-left: 6px; padding: 1px 5px; border-radius: 3px;
  background: var(--accent-dim); color: var(--accent);
  font-size: 9px; letter-spacing: .08em; text-transform: uppercase;
}
/* Las dos acciones, arriba y a la vista. Conservan lo que importaba de la decisión del
   2026-09-07: MISMO tamaño y MISMO color entre sí — son dos respuestas a la misma
   pregunta, y pintar DESCARTAR como secundaria sería responderla por el usuario. */
/* Las acciones cuelgan de la identidad, así que su borde izquierdo es el mismo que el de
   la dirección — encargo del dueño: «el primero de estos botones debe estar alineado al
   comienzo del texto de la dirección». Sale del `justify-items: start` de la columna, no
   de un margen calculado: un margen se desalinea la primera vez que alguien cambie el
   relleno de la cabecera. */
/* `justify-self: stretch` y no `align-self`: en una grilla el que manda en el eje
   HORIZONTAL es `justify`. La columna lleva `justify-items: start`, que encoge a cada hijo
   a su contenido; esto le devuelve el ancho completo a la fila de acciones para que su
   borde izquierdo sea el mismo de la columna — o sea, el mismo de la «P» de la dirección. */
.ficha-acciones { display: flex; margin-top: 2px; justify-self: stretch; }
.ficha-acciones .acciones-estado {
  margin: 0; padding: 0; border: 0; display: flex; gap: 8px; flex-wrap: wrap;
  /* `justify-content: flex-start` NO es redundante, y esto costó un segundo reporte del
     dueño: `.acciones-estado` nació CENTRADA —vivía sola al pie de la ficha, donde
     centrarla era lo correcto— y al mudarse a la cabecera esa regla siguió aplicando. Yo
     reseté el margen, el relleno y el borde, pero no el centrado, así que los botones se
     quedaban centrados dentro de su caja en vez de arrancar donde arranca la dirección.
     Mudar un componente exige revisar TODAS las reglas que traía puestas, no solo las que
     estorban a simple vista. */
  justify-content: flex-start; width: 100%;
}
/* El mensaje de estado ocupa su propia línea (`flex-basis: 100%`) y venía centrado por lo
   mismo. Aquí se lee bajo los botones, alineado con ellos. */
.ficha-acciones .pipe-msg { text-align: left; }
@media (max-width: 780px) {
  /* En un teléfono el selector ocupa el ancho: 320 px fijos lo dejarían casi tocando los
     bordes y con la marca de la derecha pegada al canto. */
  .ofertador { width: 100%; }
  .ficha-acciones { width: 100%; }
}
