/*
 * Radar de Remates — sistema visual.
 *
 * Escrito de cero para este producto, no copiado de Obra360: Obra360 es un
 * ERP de obra (tablas densas, formularios largos, Gantt); esto es un mapa de
 * 500 licitaciones donde lo único que de verdad importa, a primera vista, es
 * "¿vale la pena ir a este remate?" — una pregunta que se contesta con UN
 * número y UN color. Todo lo demás en esta hoja está para no estorbarle a
 * ese número.
 *
 * Tipografía del sistema (system-ui): sin fuente externa ni embebida, cero
 * peso extra y cero parpadeo de fuente sin cargar (FOUT). Un mapa que tarda
 * en pintar tiles no necesita además esperar una tipografía.
 */

/* ─────────────────────────────────────────────────────────────────────────
   1. Tokens
   ───────────────────────────────────────────────────────────────────────── */
:root {
  /* Tipografía: la pila del sistema operativo, en el orden que cubre las
     cuatro plataformas mayoritarias sin traer ni un byte por red. */
  --rm-fuente: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  /* Superficie y texto — grises NEUTROS (sin matiz), a propósito: el gris de
     "sin puntaje" tiene que leerse como AUSENCIA de color, nunca como un
     rojo o un ámbar deslavados. Un gris con tinte cálido se confundiría con
     el rojo apagado de una pantalla mal calibrada. */
  --rm-fondo:        #F6F7F5;
  --rm-superficie:   #FFFFFF;
  --rm-borde:        #D9DCD8;
  --rm-texto:        #1C231F;
  --rm-texto-suave:  #4B564E;
  --rm-texto-tenue:  #6B776E;

  /* Marca: verde radar. Es el mismo verde que usan los estados "bien" del
     sistema de puntaje — a propósito: la marca y "esto vale la pena" hablan
     el mismo idioma visual. */
  --rm-marca:        #1B6E4A;
  --rm-marca-fuerte: #14563A;
  --rm-marca-suave:  #E4F2EA;

  --rm-foco:         #0B5FFF;   /* anillo de :focus-visible, nunca el verde de marca:
                                   necesita distinguirse de "seleccionado/activo". */
  --rm-peligro:      #9B1C1C;
  --rm-peligro-suave:#FBE9E9;

  /* ── Los colores del puntaje: lo más mirado de toda la aplicación ──
     Hoy (2026-09-08), con OpenStreetMap caído, 452 de las 500 licitaciones
     no tienen puntaje: ese gris va a ser, de lejos, el color más visto de
     Radar de Remates. Por eso NO es "rojo apagado" ni "ámbar apagado": es
     gris neutro, sin matiz, para que "no se pudo medir" nunca se confunda de
     un vistazo con "puntaje malo". Son mensajes opuestos:
       - rojo  → SE MIDIÓ y el resultado es malo. Andá con cuidado.
       - gris  → NO SE MIDIÓ. No hay opinión que dar todavía.
     Fondo claro + texto oscuro del mismo matiz en los tres casos: separa el
     color por PAREJAS de contraste suficiente (AA) y no por un solo tono
     saturado que falla en fondo blanco. */
  --rm-score-alto-bg:    #E1F3E8;   /* verde  · score >= 70 */
  --rm-score-alto-fg:    #146238;
  --rm-score-alto-borde: #9CD3B3;

  --rm-score-medio-bg:    #FCEFD7;  /* ámbar  · 40 <= score < 70 */
  --rm-score-medio-fg:    #8A5A00;
  --rm-score-medio-borde: #F0CC8C;

  --rm-score-bajo-bg:    #FBE7E7;   /* rojo   · score < 40 */
  --rm-score-bajo-fg:    #9B1C1C;
  --rm-score-bajo-borde: #EFB4B4;

  --rm-score-sin-bg:     #E7E9E6;   /* gris NEUTRO · score = NULL, "no medido" */
  --rm-score-sin-fg:     #4B564E;
  --rm-score-sin-borde:  #C7CCC4;

  /* Ubicación aproximada por comuna (55 de 500): un morado que no se parece a
     NINGUNO de los cuatro colores de puntaje de arriba ni a la marca ni al
     foco — a propósito, es la regla más importante de la pantalla del mapa
     (Task 3): quien mira tiene que distinguir de un vistazo "el terreno está
     acá" (círculo sólido, color de puntaje) de "el terreno está en algún
     lugar de esta comuna" (rombo punteado, siempre morado, nunca del color
     del puntaje — ver mapa.js). */
  --rm-aprox:        #6E4A9E;
  --rm-aprox-suave:  #F1EBF9;
  --rm-aprox-borde:  #C9B4E0;

  --rm-radio:    10px;
  --rm-radio-lg: 16px;
  --rm-sombra:   0 1px 2px rgba(20, 30, 24, .06), 0 4px 14px rgba(20, 30, 24, .06);

  --rm-espacio-1: 4px;
  --rm-espacio-2: 8px;
  --rm-espacio-3: 12px;
  --rm-espacio-4: 16px;
  --rm-espacio-5: 24px;
  --rm-espacio-6: 32px;
  --rm-espacio-7: 48px;

  --rm-ancho-maximo: 1180px;

  color-scheme: light;
}

/* ─────────────────────────────────────────────────────────────────────────
   2. Reset mínimo
   ───────────────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: var(--rm-fuente);
  /* 16px es el PISO en toda la app, no sólo en mobile: la media query de
     abajo nunca lo hace más chico, sólo ajusta el ritmo vertical. Un texto
     base menor a 16px en un formulario dispara el zoom automático de Safari
     en iOS al enfocar un <input> — el criterio de aceptación existe por eso. */
  font-size: 16px;
  line-height: 1.5;
  color: var(--rm-texto);
  background: var(--rm-fondo);
  -webkit-font-smoothing: antialiased;
}
img, svg { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
a { color: var(--rm-marca-fuerte); }
h1, h2, h3, p { margin: 0 0 var(--rm-espacio-3); }

/* Foco visible en TODO lo que recibe foco. :focus-visible y no :focus: así
   un clic de mouse no deja un anillo azul dando vueltas, pero tabular sí lo
   muestra siempre — que es exactamente lo que el criterio de aceptación pide
   comprobar en Playwright. */
:focus-visible {
  outline: 3px solid var(--rm-foco);
  outline-offset: 2px;
  border-radius: 4px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ─────────────────────────────────────────────────────────────────────────
   3. Estructura de página
   ───────────────────────────────────────────────────────────────────────── */
.rm-shell { min-height: 100dvh; display: flex; flex-direction: column; }
.rm-ancho { width: 100%; max-width: var(--rm-ancho-maximo); margin: 0 auto; padding: 0 var(--rm-espacio-4); }
.rm-ancho-full { max-width: none; }

.rm-main { flex: 1; padding-block: var(--rm-espacio-6); }

/* El mapa (Task 3) es la única pantalla que necesita ocupar el alto
   disponible ENTERO, sin el margen vertical que las demás sí quieren: un
   panel fijo con scroll propio y un mapa a pantalla completa, no una tarjeta
   flotando en medio de espacio en blanco. El encadenado flex (shell → main →
   .rm-ancho → .rm-mapa-shell) es lo que le da a cada nivel una altura
   DEFINIDA que el siguiente puede heredar con flex:1 — sin `min-height:0` en
   cada eslabón, un flex item nunca se encoge por debajo del alto de su
   contenido y el mapa terminaría más alto que la pantalla, con scroll de
   PÁGINA en vez de quedarse fijo. */
.rm-main--sin-relleno { padding-block: 0; display: flex; flex-direction: column; min-height: 0; }
.rm-main--sin-relleno > .rm-ancho { flex: 1; display: flex; min-height: 0; padding-inline: 0; }

/* `min-height: 100dvh` (la regla de arriba, .rm-shell) es un PISO: si el
   contenido pide más alto, el shell crece y la página entera scrollea — lo
   que quieren acceso.php/cuenta.php, tarjetas cortas que a veces sobran de
   alto. El mapa es al revés: el panel de filtros tiene de sobra contenido
   para superar el viewport (11 campos + la leyenda), y sin un TECHO real acá
   ese contenido empuja a TODA la cadena de flex a crecer con él —
   `min-height:0` en cada eslabón no alcanza si ningún ancestro tiene una
   altura de verdad, sólo un mínimo. `height` (no `min-height`) es lo que
   convierte a .rm-shell en un techo real: de ahí para abajo, cada flex:1
   tiene que REPARTIRSE esa altura fija, y el que no entra scrollea
   ADENTRO suyo (.rm-mapa-panel, overflow-y:auto) en vez de estirar la
   página entera. */
.rm-shell--fijo { height: 100dvh; }

.rm-pie {
  padding-block: var(--rm-espacio-5);
  color: var(--rm-texto-tenue);
  font-size: .9em;
  border-top: 1px solid var(--rm-borde);
  display: flex; flex-wrap: wrap; gap: var(--rm-espacio-2) var(--rm-espacio-5);
  justify-content: space-between;
}

/* ── Barra superior ── */
.rm-top {
  background: var(--rm-superficie);
  border-bottom: 1px solid var(--rm-borde);
  position: sticky; top: 0; z-index: 20;
}
.rm-top-in {
  display: flex; align-items: center; gap: var(--rm-espacio-5);
  min-height: 64px; flex-wrap: wrap; padding-block: var(--rm-espacio-2);
}
.rm-marca {
  display: flex; align-items: center; gap: var(--rm-espacio-2);
  font-weight: 700; text-decoration: none; color: var(--rm-texto);
  white-space: nowrap;
}
.rm-logo { width: 26px; height: 26px; flex: none; }

.rm-nav {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--rm-espacio-1);
  margin-left: auto;
}
.rm-nav a, .rm-nav button {
  display: inline-flex; align-items: center;
  min-height: 44px; padding: 0 var(--rm-espacio-3);
  border-radius: var(--rm-radio);
  text-decoration: none; color: var(--rm-texto-suave);
  font-weight: 600; font-size: .95em;
  border: 1px solid transparent; background: none;
  cursor: pointer;
}
.rm-nav a:hover { background: var(--rm-marca-suave); color: var(--rm-marca-fuerte); }
.rm-nav a[aria-current="page"] {
  background: var(--rm-marca-suave); color: var(--rm-marca-fuerte);
  border-color: var(--rm-score-alto-borde);
}
.rm-salir { margin: 0; }
.rm-salir button { color: var(--rm-texto-suave); }
.rm-salir button:hover { background: var(--rm-peligro-suave); color: var(--rm-peligro); }

/* ─────────────────────────────────────────────────────────────────────────
   4. Tarjetas, formularios, botones
   ───────────────────────────────────────────────────────────────────────── */
.rm-tarjeta {
  background: var(--rm-superficie);
  border: 1px solid var(--rm-borde);
  border-radius: var(--rm-radio-lg);
  box-shadow: var(--rm-sombra);
  padding: var(--rm-espacio-6);
}
.rm-tarjeta--angosta { max-width: 480px; }
.rm-tarjeta h2 { margin-top: var(--rm-espacio-6); font-size: 1.1em; }

/* Utilidad de color de texto — evita el style="" inline, que la CSP de esta
   app bloquea (style-src sólo admite 'self' y el nonce de <style>, nunca un
   atributo style="" suelto). */
.rm-texto-suave { color: var(--rm-texto-suave); }

.rm-campo { margin-bottom: var(--rm-espacio-4); }
.rm-campo label {
  display: block; margin-bottom: var(--rm-espacio-2);
  font-weight: 600; font-size: .95em;
}
.rm-campo input {
  width: 100%;
  min-height: 44px;
  padding: var(--rm-espacio-2) var(--rm-espacio-3);
  border: 1px solid var(--rm-borde);
  border-radius: var(--rm-radio);
  background: var(--rm-superficie);
  font-size: 16px; /* nunca menor: dispara zoom en iOS si es < 16px */
}
.rm-campo input:focus-visible { border-color: var(--rm-foco); }
.rm-campo small { display: block; margin-top: var(--rm-espacio-1); color: var(--rm-texto-tenue); font-size: .875em; }

.rm-boton {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; min-width: 44px;
  padding: 0 var(--rm-espacio-5);
  border-radius: var(--rm-radio);
  border: 1px solid var(--rm-marca);
  background: var(--rm-marca);
  color: #fff;
  font-weight: 700; font-size: 1em;
  cursor: pointer;
  width: 100%;
}
.rm-boton:hover { background: var(--rm-marca-fuerte); border-color: var(--rm-marca-fuerte); }
.rm-boton:disabled { opacity: .6; cursor: not-allowed; }
.rm-boton--secundario {
  background: var(--rm-superficie); color: var(--rm-marca-fuerte);
}
.rm-boton--secundario:hover { background: var(--rm-marca-suave); }

/* ── Aviso inline (login fallido, errores de formulario) ──
   role="alert" lo pone el HTML/JS; acá sólo el color, deliberadamente
   distinto del rojo de "puntaje bajo" en TONO (más frío, con borde), para
   que un aviso de formulario no se lea por accidente como un puntaje. */
.rm-aviso {
  display: flex; align-items: flex-start; gap: var(--rm-espacio-2);
  padding: var(--rm-espacio-3) var(--rm-espacio-4);
  border-radius: var(--rm-radio);
  background: var(--rm-peligro-suave);
  color: var(--rm-peligro);
  border: 1px solid var(--rm-score-bajo-borde);
  font-size: .95em;
  margin-bottom: var(--rm-espacio-4);
}
.rm-aviso[hidden] { display: none; }

/* Variante neutra/positiva: "cerraste sesión", no un error de formulario.
   Mismos tokens que .rm-score--alto, para no inventar un color nuevo. */
.rm-aviso--ok {
  background: var(--rm-score-alto-bg);
  color: var(--rm-score-alto-fg);
  border-color: var(--rm-score-alto-borde);
}

.rm-toast {
  position: fixed; bottom: var(--rm-espacio-5); left: 50%;
  transform: translateX(-50%);
  background: var(--rm-texto); color: #fff;
  padding: var(--rm-espacio-2) var(--rm-espacio-4);
  border-radius: var(--rm-radio); box-shadow: var(--rm-sombra);
  font-size: .95em; z-index: 50;
}
.rm-toast[hidden] { display: none; }

/* ─────────────────────────────────────────────────────────────────────────
   5. Los colores del puntaje
   ───────────────────────────────────────────────────────────────────────── */
.rm-score {
  display: inline-flex; align-items: center; gap: var(--rm-espacio-1);
  min-height: 32px; padding: 0 var(--rm-espacio-3);
  border-radius: 999px;
  font-weight: 700; font-size: .95em;
  border: 1px solid transparent;
  white-space: nowrap;
}
.rm-score--alto  { background: var(--rm-score-alto-bg);  color: var(--rm-score-alto-fg);  border-color: var(--rm-score-alto-borde); }
.rm-score--medio { background: var(--rm-score-medio-bg); color: var(--rm-score-medio-fg); border-color: var(--rm-score-medio-borde); }
.rm-score--bajo  { background: var(--rm-score-bajo-bg);  color: var(--rm-score-bajo-fg);  border-color: var(--rm-score-bajo-borde); }
/* --sin lleva ADEMÁS un guion en vez de un número (lo pone el HTML/JS que
   use esta clase): el color ya alcanza para diferenciarlo del rojo, pero un
   segundo canal —la forma del contenido— es gratis y ayuda a quien no
   distingue bien los colores. */
.rm-score--sin   { background: var(--rm-score-sin-bg);   color: var(--rm-score-sin-fg);   border-color: var(--rm-score-sin-borde); }

/* ─────────────────────────────────────────────────────────────────────────
   6. Contenido ancho (tablas, etc.) — nunca scroll horizontal de la PÁGINA
   ───────────────────────────────────────────────────────────────────────── */
.rm-tabla-envoltorio { width: 100%; overflow-x: auto; }
.rm-tabla { width: 100%; border-collapse: collapse; min-width: 560px; }
.rm-tabla th, .rm-tabla td { text-align: left; padding: var(--rm-espacio-2) var(--rm-espacio-3); border-bottom: 1px solid var(--rm-borde); }

/* ─────────────────────────────────────────────────────────────────────────
   7. Pantalla de acceso — una tarjeta centrada, sin distracción
   ───────────────────────────────────────────────────────────────────────── */
/* Modificador de .rm-ancho (el envoltorio que abre pagina_cabecera()), no
   una página aparte: así .rm-main sigue siendo el único que define la altura
   —flex:1 dentro de .rm-shell— y este modificador sólo centra ADENTRO de esa
   altura ya resuelta. min-height:100% funciona porque .rm-main SÍ tiene una
   altura definida (la que le da flex-grow), y esto es su hijo directo. */
.rm-ancho--centrado {
  min-height: 100%;
  display: flex; align-items: center; justify-content: center;
}
.rm-ancho--centrado .rm-tarjeta { width: 100%; max-width: 400px; }
.rm-ancho--centrado .rm-marca { justify-content: center; margin-bottom: var(--rm-espacio-5); font-size: 1.15em; text-decoration: none; }
.rm-ancho--centrado .rm-marca .rm-logo { width: 32px; height: 32px; }

/* Oculto visualmente pero presente para lector de pantalla: cada página de
   este par (acceso/cuenta) necesita un <h1> real, aunque la marca de arriba
   ya diga el nombre del producto en pantalla. */
.rm-visualmente-oculto {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ─────────────────────────────────────────────────────────────────────────
   8. Responsive — el criterio de aceptación es 390px sin scroll horizontal
   ───────────────────────────────────────────────────────────────────────── */
@media (max-width: 640px) {
  .rm-ancho { padding-inline: var(--rm-espacio-3); }
  .rm-top-in { min-height: 56px; }
  .rm-nav { gap: 0; width: 100%; justify-content: space-between; }
  .rm-tarjeta { padding: var(--rm-espacio-5); }
  .rm-pie { flex-direction: column; gap: var(--rm-espacio-2); }
}

/* ─────────────────────────────────────────────────────────────────────────
   9. El mapa (Task 3) — la pantalla principal
   ───────────────────────────────────────────────────────────────────────── */

/* Panel de filtros a la izquierda en escritorio, mapa ocupando el resto.
   860px de quiebre, más ancho que los 640px del resto del sistema: un panel
   de 320px con formulario adentro necesita más aire que una barra de nav. */
.rm-mapa-shell {
  flex: 1;
  display: flex;
  min-height: 0;
  position: relative;
}

.rm-mapa-panel {
  flex: 0 0 320px;
  width: 320px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  background: var(--rm-superficie);
  border-right: 1px solid var(--rm-borde);
  padding: var(--rm-espacio-4);
}
.rm-mapa-panel-cabecera { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--rm-espacio-3); }
.rm-mapa-panel-titulo { margin: 0; font-size: 1.1em; }
/* El botón de cerrar sólo tiene sentido cuando el panel es un overlay
   (móvil): en escritorio el panel es parte fija del layout, cerrarlo dejaría
   el filtro inaccesible sin scroll ni forma de reabrirlo. */
.rm-mapa-panel-cerrar { display: none; }

.rm-mapa-resultado {
  font-weight: 700; font-size: 1.05em;
  padding: var(--rm-espacio-2) 0;
  color: var(--rm-texto);
}
.rm-mapa-descartados {
  background: var(--rm-score-medio-bg); color: var(--rm-score-medio-fg);
  border: 1px solid var(--rm-score-medio-borde);
  border-radius: var(--rm-radio);
  padding: var(--rm-espacio-2) var(--rm-espacio-3);
  font-size: .875em;
  margin-bottom: var(--rm-espacio-3);
}
.rm-mapa-descartados[hidden] { display: none; }

.rm-mapa-rango { border: 0; padding: 0; margin: 0 0 var(--rm-espacio-4); }
.rm-mapa-rango legend { font-weight: 600; font-size: .95em; padding: 0; margin-bottom: var(--rm-espacio-2); }
.rm-mapa-rango-par { display: grid; grid-template-columns: 1fr 1fr; gap: var(--rm-espacio-3); }
.rm-mapa-rango-par .rm-campo { margin-bottom: 0; }

.rm-campo select {
  width: 100%; min-height: 44px;
  padding: var(--rm-espacio-2) var(--rm-espacio-3);
  border: 1px solid var(--rm-borde); border-radius: var(--rm-radio);
  background: var(--rm-superficie); font-size: 16px;
}
.rm-campo--check label {
  display: flex; align-items: center; gap: var(--rm-espacio-2);
  font-weight: 400; min-height: 44px; cursor: pointer;
}
.rm-campo--check input { width: 20px; height: 20px; flex: none; }

.rm-mapa-leyenda { margin-bottom: var(--rm-espacio-3); padding-bottom: var(--rm-espacio-4); border-bottom: 1px solid var(--rm-borde); }
.rm-mapa-leyenda h2 { font-size: .95em; margin-bottom: var(--rm-espacio-3); }
.rm-mapa-panel-subtitulo { font-size: .95em; margin-bottom: var(--rm-espacio-3); }
.rm-mapa-leyenda ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--rm-espacio-3); }
.rm-mapa-leyenda li { display: flex; align-items: flex-start; gap: var(--rm-espacio-2); font-size: .85em; color: var(--rm-texto-suave); line-height: 1.4; }
.rm-mapa-leyenda-punto { width: 14px; height: 14px; border-radius: 50%; flex: none; margin-top: 3px; border: 2px solid; box-sizing: border-box; }
.rm-mapa-leyenda-punto--alto  { background: var(--rm-score-alto-bg);  border-color: var(--rm-score-alto-fg); }
.rm-mapa-leyenda-punto--medio { background: var(--rm-score-medio-bg); border-color: var(--rm-score-medio-fg); }
.rm-mapa-leyenda-punto--bajo  { background: var(--rm-score-bajo-bg);  border-color: var(--rm-score-bajo-fg); }
.rm-mapa-leyenda-punto--sin   { background: var(--rm-score-sin-bg);   border-color: var(--rm-score-sin-fg); }
.rm-mapa-leyenda-simbolo--aprox {
  width: 14px; height: 14px; flex: none; margin-top: 3px;
  background: var(--rm-aprox-suave); border: 2px dashed var(--rm-aprox);
  box-sizing: border-box; transform: rotate(45deg);
}

/* Botón flotante para abrir el panel — sólo existe visualmente en móvil
   (ver el @media de abajo): en escritorio el panel ya está siempre visible.
   Dos cosas que no son obvias acá:
     1. A la DERECHA, no a la izquierda: los controles de zoom de Leaflet
        viven arriba a la izquierda — compartir esquina significa competir
        por el MISMO pixel, no sólo por capas.
     2. z-index bien por encima de 1000: leaflet.css (vendorizado, no se
        toca) ya usa hasta 700 en sus panes y 1000 en sus controles — CUALQUIER
        z-index nuestro por debajo de eso queda tapado por el mapa mismo,
        aunque esté en un rincón donde a simple vista no debería haber nada
        del mapa encima. Se vio literalmente así en la verificación de esta
        tarea: el botón existía, medía bien, y aun así no se veía. */
.rm-mapa-toggle {
  display: none;
  position: absolute; top: var(--rm-espacio-3); right: var(--rm-espacio-3);
  z-index: 1100;
  min-height: 44px; padding: 0 var(--rm-espacio-4);
  border-radius: var(--rm-radio); border: 1px solid var(--rm-borde);
  background: var(--rm-superficie); color: var(--rm-texto);
  font-weight: 700; box-shadow: var(--rm-sombra); cursor: pointer;
}
.rm-mapa-panel-fondo { display: none; }

.rm-mapa-contenedor { flex: 1; position: relative; min-width: 0; }
.rm-mapa { position: absolute; inset: 0; }
/* Fondo gris si los tiles de OpenStreetMap no cargan (o todavía no
   terminaron): la aplicación sigue funcionando, los puntos y polígonos son
   nuestros y no dependen de la red de OSM. */
.rm-mapa .leaflet-container { background: var(--rm-borde); font-family: var(--rm-fuente); }

.rm-mapa-carga, .rm-mapa-error {
  position: absolute; top: var(--rm-espacio-3); right: var(--rm-espacio-3);
  z-index: 1100;
  background: var(--rm-superficie); border-radius: var(--rm-radio);
  padding: var(--rm-espacio-2) var(--rm-espacio-3);
  box-shadow: var(--rm-sombra); font-size: .9em;
}
.rm-mapa-carga[hidden], .rm-mapa-error[hidden] { display: none; }
.rm-mapa-error { background: var(--rm-peligro-suave); color: var(--rm-peligro); border: 1px solid var(--rm-score-bajo-borde); }

/* ── El símbolo de "ubicación aproximada por comuna" en el mapa mismo:
   rombo punteado morado, nunca un círculo sólido del color del puntaje —
   ver el comentario largo de --rm-aprox más arriba. ── */
.rm-marcador-aprox {
  width: 18px; height: 18px; box-sizing: border-box;
  background: var(--rm-aprox-suave);
  border: 2.5px dashed var(--rm-aprox);
  transform: rotate(45deg);
}

/* ── Clusters: agrupan marcadores cercanos en pantalla (ver mapa.js). El
   anillo exterior es un conic-gradient con la proporción de verde/ámbar/
   rojo/gris del grupo — la MISMA lectura de un vistazo que un punto suelto,
   a escala. El borde PUNTEADO es la señal de "este grupo mezcla o es
   enteramente aproximaciones por comuna" — nunca se pierde al agrupar. ── */
.rm-cluster-icono { background: none; border: none; }
.rm-cluster {
  width: 100%; height: 100%; box-sizing: border-box;
  border-radius: 50%; border-width: 3px; border-style: solid;
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--rm-sombra); cursor: pointer; position: relative;
}
.rm-cluster-num {
  width: 68%; height: 68%; border-radius: 50%;
  background: var(--rm-superficie); color: var(--rm-texto);
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: .85em;
}
.rm-cluster-badge {
  position: absolute; top: -6px; right: -6px;
  min-width: 20px; height: 20px; padding: 0 5px;
  border-radius: 999px; background: var(--rm-aprox); color: #fff;
  font-size: .7em; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 0 0 2px var(--rm-superficie);
}

/* ── El popup ── */
.rm-popup { min-width: 220px; max-width: 260px; font-size: .92em; }
.rm-popup h3 { margin: 0 0 var(--rm-espacio-1); font-size: 1.05em; }
.rm-popup p { margin: 0 0 var(--rm-espacio-1); color: var(--rm-texto-suave); }
.rm-popup-aprox {
  background: var(--rm-aprox-suave); color: var(--rm-aprox);
  border: 1px solid var(--rm-aprox-borde); border-radius: var(--rm-radio);
  padding: var(--rm-espacio-2); font-size: .875em; font-weight: 600;
  margin-bottom: var(--rm-espacio-2);
}
/* El KMZ del portal contradice su propia ficha (44 de 500, 2026-09-09):
   mismos tokens que .rm-aviso (--rm-peligro), a propósito distinto del morado
   de .rm-popup-aprox — esto no es sólo "aproximado", es "el propio portal
   publica un archivo que no concuerda con sus datos". */
.rm-popup-conflicto {
  background: var(--rm-peligro-suave); color: var(--rm-peligro);
  border: 1px solid var(--rm-score-bajo-borde); border-radius: var(--rm-radio);
  padding: var(--rm-espacio-2); font-size: .875em; font-weight: 600;
  margin-bottom: var(--rm-espacio-2);
}
.rm-popup-score { margin: var(--rm-espacio-2) 0; }
.rm-popup-score-motivo { font-size: .85em; color: var(--rm-texto-tenue); margin-top: var(--rm-espacio-1); }
.rm-popup .rm-boton { margin-top: var(--rm-espacio-2); min-height: 40px; }

@media (max-width: 860px) {
  .rm-mapa-shell { flex-direction: column; }

  .rm-mapa-toggle { display: inline-flex; }
  /* El aviso de carga/error comparte esquina con el botón de Filtros en
     móvil: se apila debajo, no encima. */
  .rm-mapa-carga, .rm-mapa-error { top: 64px; }

  /* Panel fuera de pantalla (off-canvas): un panel FIJO se come el mapa
     entero en 390px, así que en móvil vive escondido detrás de un botón. */
  .rm-mapa-panel {
    position: fixed; z-index: 1140; inset: 0 12vw 0 0;
    width: auto; max-width: 360px;
    box-shadow: var(--rm-sombra);
    transform: translateX(-100%);
    transition: transform .22s ease-out;
  }
  .rm-mapa-panel--abierta { transform: translateX(0); }
  .rm-mapa-panel-cerrar {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 44px; min-height: 44px;
    border: 1px solid var(--rm-borde); border-radius: var(--rm-radio);
    background: none; font-size: 1.1em; cursor: pointer;
  }
  .rm-mapa-panel-fondo {
    display: block; position: fixed; inset: 0; z-index: 1130;
    background: rgba(28, 35, 31, .45);
  }
  .rm-mapa-panel-fondo[hidden] { display: none; }

  .rm-mapa-contenedor { flex: 1; width: 100%; }
}

/* ─────────────────────────────────────────────────────────────────────────
   10. La ficha (Task 4) — la pantalla del producto
   ───────────────────────────────────────────────────────────────────────── */

.rm-ficha-volver {
  display: inline-flex; align-items: center; min-height: 44px;
  font-weight: 600; text-decoration: none; margin-bottom: var(--rm-espacio-4);
}
.rm-ficha-cargando { color: var(--rm-texto-tenue); }

.rm-ficha-cabecera { margin-bottom: var(--rm-espacio-5); }
.rm-ficha-cabecera h1 { font-size: 1.5em; margin-bottom: var(--rm-espacio-2); }
.rm-ficha-meta {
  display: flex; flex-wrap: wrap; gap: var(--rm-espacio-2);
  align-items: center; margin-bottom: var(--rm-espacio-2);
}
/* Insignia neutra (código, estado, modalidad, tipo) — misma forma que
   .rm-score pero sin comprometerse a un color de puntaje: acá no se está
   midiendo nada, sólo etiquetando. */
.rm-ficha-insignia {
  display: inline-flex; align-items: center;
  min-height: 30px; padding: 0 var(--rm-espacio-3);
  border-radius: 999px; border: 1px solid var(--rm-borde);
  background: var(--rm-fondo); color: var(--rm-texto-suave);
  font-size: .875em; font-weight: 600; white-space: nowrap;
}
.rm-ficha-ubicacion { color: var(--rm-texto-suave); font-size: 1.05em; }

.rm-ficha-tarjeta { margin-bottom: var(--rm-espacio-5); }
.rm-ficha-tarjeta h2 { margin-top: 0; font-size: 1.15em; }
.rm-ficha-tarjeta h3 { font-size: 1em; margin: var(--rm-espacio-4) 0 var(--rm-espacio-2); }

/* Valor ausente: SIEMPRE una frase ("Sin precio publicado"), nunca un guion
   ni una celda vacía — itálica y en el tono más tenue para que se lea de un
   vistazo como "esto no es un dato", sin gritar como un error. */
.rm-ficha-ausente { color: var(--rm-texto-tenue); font-style: italic; }

/* Datos generales: pares etiqueta/valor. Grid, no tabla — con una sola
   columna de valores no hace falta scroll horizontal en 390px (regla del
   proyecto), sólo apilar debajo de la etiqueta. */
.rm-ficha-datos { display: grid; gap: var(--rm-espacio-3) var(--rm-espacio-5); grid-template-columns: 1fr; margin: 0; }
@media (min-width: 640px) {
  .rm-ficha-datos { grid-template-columns: 1fr 1fr; }
  .rm-ficha-datos--ancho dd { grid-column: 1 / -1; } /* usos permitidos/prohibidos: texto largo, ocupa las dos columnas */
}
.rm-ficha-datos dt { font-weight: 600; font-size: .875em; color: var(--rm-texto-tenue); margin: 0 0 2px; }
.rm-ficha-datos dd { margin: 0 0 var(--rm-espacio-3); }

/* ── El puntaje ── */
.rm-ficha-score-cabecera { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rm-espacio-3); margin-bottom: var(--rm-espacio-2); }
.rm-ficha-score-numero {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 72px; min-height: 56px; padding: 0 var(--rm-espacio-3);
  border-radius: var(--rm-radio-lg); border: 1px solid transparent;
  font-size: 1.6em; font-weight: 700;
}
.rm-ficha-score-numero--alto  { background: var(--rm-score-alto-bg);  color: var(--rm-score-alto-fg);  border-color: var(--rm-score-alto-borde); }
.rm-ficha-score-numero--medio { background: var(--rm-score-medio-bg); color: var(--rm-score-medio-fg); border-color: var(--rm-score-medio-borde); }
.rm-ficha-score-numero--bajo  { background: var(--rm-score-bajo-bg);  color: var(--rm-score-bajo-fg);  border-color: var(--rm-score-bajo-borde); }
.rm-ficha-score-numero--sin   { background: var(--rm-score-sin-bg);   color: var(--rm-score-sin-fg);   border-color: var(--rm-score-sin-borde); font-size: 1em; padding: 0 var(--rm-espacio-4); }
.rm-ficha-score-cobertura { font-size: 1.05em; color: var(--rm-texto-suave); margin: 0; }

/* Explicación en PALABRAS cuando no hay puntaje: ni un número, ni una
   fracción, ni un porcentaje — ver el docblock de licitaciones.php y
   ficha.js (construirScore()). */
.rm-ficha-score-motivo { color: var(--rm-texto-suave); margin: 0 0 var(--rm-espacio-2); }
.rm-ficha-score-motivo:last-child { margin-bottom: 0; }

.rm-ficha-desglose { list-style: none; margin: var(--rm-espacio-3) 0 0; padding: 0; }
.rm-ficha-desglose-fila {
  display: grid; grid-template-columns: 1fr auto; column-gap: var(--rm-espacio-3);
  align-items: baseline; padding: var(--rm-espacio-2) 0; border-top: 1px solid var(--rm-borde);
}
.rm-ficha-desglose-fila:first-child { border-top: none; }
.rm-ficha-desglose-etiqueta { font-weight: 600; }
.rm-ficha-desglose-fraccion { font-variant-numeric: tabular-nums; font-weight: 700; white-space: nowrap; }
.rm-ficha-desglose-fraccion--sin { font-weight: 400; color: var(--rm-texto-tenue); font-style: italic; }
.rm-ficha-desglose-detalle { grid-column: 1 / -1; color: var(--rm-texto-suave); font-size: .9em; margin: 2px 0 0; }

.rm-ficha-comparables { list-style: none; margin: var(--rm-espacio-2) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--rm-espacio-2); }
.rm-ficha-comparables-fila {
  padding: var(--rm-espacio-2) var(--rm-espacio-3); border-radius: var(--rm-radio);
  border: 1px solid var(--rm-borde); background: var(--rm-fondo); font-size: .9em;
}
/* Grupo ganador: el que de verdad se usó para el puntaje de precio. */
.rm-ficha-comparables-fila--ganador { border-color: var(--rm-marca); background: var(--rm-marca-suave); }
/* Grupo NO evaluado: apagado a propósito — la trampa que este producto no
   se puede permitir (ver el docblock de licitaciones.php). Nunca se
   confunde con "evaluado, cero comparables", que va con opacidad normal. */
.rm-ficha-comparables-fila--apagado { opacity: .55; border-style: dashed; }
.rm-ficha-comparables-fuente { font-weight: 600; }

/* ── El mini-mapa ── */
.rm-ficha-mapa-aviso {
  display: flex; align-items: flex-start; gap: var(--rm-espacio-2);
  padding: var(--rm-espacio-3); border-radius: var(--rm-radio);
  font-size: .9em; font-weight: 600; margin-bottom: var(--rm-espacio-2);
}
.rm-ficha-mapa-aviso--aprox { background: var(--rm-aprox-suave); color: var(--rm-aprox); border: 1px solid var(--rm-aprox-borde); }
.rm-ficha-mapa-aviso--conflicto { background: var(--rm-peligro-suave); color: var(--rm-peligro); border: 1px solid var(--rm-score-bajo-borde); }
.rm-ficha-mapa-vacio {
  padding: var(--rm-espacio-5); text-align: center; color: var(--rm-texto-tenue);
  background: var(--rm-fondo); border: 1px dashed var(--rm-borde); border-radius: var(--rm-radio);
}
.rm-ficha-mapa { height: 320px; border-radius: var(--rm-radio); overflow: hidden; border: 1px solid var(--rm-borde); }
.rm-ficha-mapa .leaflet-container { background: var(--rm-borde); font-family: var(--rm-fuente); height: 100%; }
@media (max-width: 640px) { .rm-ficha-mapa { height: 240px; } }

/* ── Documentos ── */
.rm-ficha-documentos { list-style: none; margin: 0; padding: 0; }
.rm-ficha-documento-fila {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--rm-espacio-2) var(--rm-espacio-3);
  padding: var(--rm-espacio-3) 0; border-top: 1px solid var(--rm-borde);
}
.rm-ficha-documento-fila:first-child { border-top: none; }
.rm-ficha-documento-nombre { flex: 1 1 220px; min-width: 0; overflow-wrap: break-word; }
.rm-ficha-documento-fila a { font-weight: 600; min-height: 44px; display: inline-flex; align-items: center; }
.rm-ficha-documentos-nota { color: var(--rm-texto-tenue); font-size: .875em; margin-top: var(--rm-espacio-3); }

/* ── Historial ── */
.rm-ficha-historial { list-style: none; margin: 0; padding: 0; }
.rm-ficha-historial-fila { padding: var(--rm-espacio-2) 0; border-top: 1px solid var(--rm-borde); font-size: .95em; }
.rm-ficha-historial-fila:first-child { border-top: none; }
.rm-ficha-historial-fecha { color: var(--rm-texto-tenue); font-size: .875em; }

/* ── Pie de la ficha: el aviso que va SIEMPRE, mida o no mida el puntaje ── */
.rm-ficha-disclaimer {
  color: var(--rm-texto-tenue); font-size: .9em; font-style: italic;
  padding-top: var(--rm-espacio-4); border-top: 1px solid var(--rm-borde);
}
/* display/width en la propia clase, no style="" inline (la CSP de esta app
   no admite atributos style sueltos): .rm-boton por defecto es width:100%,
   pero este enlace vive solo dentro de su propia tarjeta y no tiene que
   estirarse a todo el ancho como un botón de formulario. */
.rm-ficha-enlace-original { display: inline-flex; width: auto; }

/* ─────────────────────────────────────────────────────────────────────────
   11. La lista y la exportación (Task 5)
   ───────────────────────────────────────────────────────────────────────── */

.rm-lista-cabecera { margin-bottom: var(--rm-espacio-4); }
.rm-lista-cabecera h1 { margin-bottom: var(--rm-espacio-1); }

.rm-lista-filtros { margin-bottom: var(--rm-espacio-5); padding: var(--rm-espacio-5); }
.rm-lista-filtros-grid {
  display: grid; grid-template-columns: 1fr; gap: 0 var(--rm-espacio-4);
}
@media (min-width: 640px) {
  .rm-lista-filtros-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 960px) {
  .rm-lista-filtros-grid { grid-template-columns: repeat(4, 1fr); }
}
.rm-lista-filtros-acciones {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--rm-espacio-3);
  padding-top: var(--rm-espacio-2); border-top: 1px solid var(--rm-borde);
  margin-top: var(--rm-espacio-2);
}
/* Los dos botones de la barra de acciones se ajustan a su contenido, no al
   ancho completo: son acciones secundarias de la tarjeta de filtros, no el
   único botón de un formulario angosto (que es el caso que .rm-boton
   width:100% sí quiere cubrir, ver acceso.php/cuenta.php). */
.rm-lista-filtros-acciones .rm-boton { width: auto; }

/* ── La tabla: puntaje y licitación son las columnas ESENCIALES —van
   primero, y son las únicas dos que hacen falta para decidir "¿vale la
   pena mirar ésta?"—; el resto se puede desplazar. .rm-tabla-envoltorio
   (sección 6) es quien scrollea, nunca la página entera. ── */
.rm-lista-tabla th:first-child, .rm-lista-tabla td:first-child { min-width: 108px; }
.rm-lista-tabla th:nth-child(2), .rm-lista-tabla td:nth-child(2) { min-width: 220px; }
.rm-lista-tabla th, .rm-lista-tabla td { vertical-align: top; white-space: nowrap; }
.rm-lista-tabla td:nth-child(2) { white-space: normal; }

/* Encabezado ordenable: un <button> a todo lo ancho de la celda —el
   objetivo táctil real, ≥44px— sin aspecto de botón (fondo/borde
   transparentes): se distingue por el cursor y por la flecha que aparece
   cuando esa columna es la que ordena. */
.rm-lista-orden {
  display: inline-flex; align-items: center; gap: var(--rm-espacio-1);
  min-height: 44px; padding: var(--rm-espacio-2) 0;
  background: none; border: none; margin: 0;
  font: inherit; font-weight: 700; color: inherit;
  cursor: pointer; white-space: nowrap;
}
.rm-lista-orden:hover { color: var(--rm-marca-fuerte); }
.rm-lista-flecha { font-size: .8em; color: var(--rm-marca-fuerte); }
/* Activo: además del color de la flecha, el propio encabezado se resalta —
   dos señales (color + peso), nunca sólo un color que alguien con daltonismo
   podría no distinguir del resto de la fila. */
.rm-lista-orden--activo { color: var(--rm-marca-fuerte); text-decoration: underline; text-underline-offset: 3px; }

.rm-lista-titulo {
  display: inline-flex; align-items: center; min-height: 44px;
  font-weight: 700; color: var(--rm-texto);
}
.rm-lista-titulo:hover { color: var(--rm-marca-fuerte); }
.rm-lista-codigo { display: block; color: var(--rm-texto-tenue); font-size: .85em; font-weight: 400; }

/* La cobertura del puntaje, junto al número — la misma idea que el popup
   del mapa y el desglose de la ficha: un puntaje sin decir sobre cuánto se
   midió es un número que parece más honesto de lo que es. */
.rm-lista-cobertura { display: block; color: var(--rm-texto-tenue); font-size: .8em; font-weight: 400; margin-top: 2px; }

.rm-lista-vacio { padding: var(--rm-espacio-6); text-align: center; color: var(--rm-texto-tenue); }

.rm-lista-paginacion {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--rm-espacio-3); margin-top: var(--rm-espacio-4);
}
.rm-lista-paginacion .rm-boton { width: auto; }
