/* Tokens compartidos de la plataforma de Concesiones de Agua (REPDA).
   Complementa (no reemplaza) el tema de Observable Framework configurado en
   observablehq.config.js. Dirección visual: institucional/sobria — seria,
   data-forward, color solo al servicio del dato (uso, familia, antigüedad),
   nunca decorativo. Ver planeacion/FRONTEND/FUNDAMENTOS-TECNICOS.md §4.
   --font-sans/--transition-fast son tokens propios de este archivo; la marca
   (Inter/JetBrains Mono, rojo de acento, radios) vive en brand.css, cargado
   antes que esta hoja — aquí solo se consume lo que ya define. */

:root {
  --transition-fast: 0.15s ease;
}

body {
  font-family: var(--font-sans);
}

/* Foco visible en todo elemento interactivo (requisito de accesibilidad,
   FUNDAMENTOS-TECNICOS.md §4) — Framework ya trae un tema base, pero los
   controles propios (botones/pestañas/filas de lista) necesitan un anillo de
   foco explícito y consistente. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--theme-foreground-focus, #c4101b);
  outline-offset: 2px;
  border-radius: 2px;
}

/* Transiciones suaves en controles interactivos, para una sensación fluida
   sin depender de movimiento para transmitir información. Respeta
   prefers-reduced-motion. */
@media (prefers-reduced-motion: no-preference) {
  .tab,
  .pill,
  .btn-secondary,
  .badge-list-item,
  .life-cell {
    transition: background-color var(--transition-fast), border-color var(--transition-fast), opacity var(--transition-fast);
  }
}

/* Contenedor estándar de mapa: alto fijo para que MapLibre/deck.gl calculen su
   canvas correctamente; ResizeObserver interno de MapLibre gestiona el resize. */
.map-container {
  position: relative;
  width: 100%;
  height: 60vh;
  min-height: 320px;
  border-radius: var(--radius-md, 0.5rem);
  overflow: hidden;
}

/* Banda de aviso persistente (p.ej. corte fuera de la muestra de dev). Nunca
   depende solo del color: siempre lleva texto explícito.
   NOTA: a propósito NO es `display:flex`. El texto suele traer <em>/<code>
   inline (ver src/altas-bajas.md); en un contenedor flex, cada elemento inline
   se vuelve un flex item independiente y se envuelve por separado, partiendo el
   párrafo en columnas rotas. Bloque simple + el emoji como parte del texto. */
.notice-band {
  display: block;
  padding: 0.5rem 0.75rem;
  border-radius: var(--radius-sm, 0.375rem);
  border: 1px solid color-mix(in srgb, orange 50%, transparent);
  background: color-mix(in srgb, orange 12%, transparent);
  font-size: 0.875rem;
}

/* Glifo de anomalía/alerta (ver components/anomalyBadge.js). */
.anomaly-badge {
  font-weight: 700;
  cursor: help;
}

/* Estado de carga accesible: anunciado vía aria-live en el contenedor padre. */
.loading-state,
.empty-state,
.error-state {
  padding: 1rem;
  color: var(--theme-foreground-muted, #666);
  font-size: 0.9rem;
}

.error-state {
  color: color-mix(in srgb, red 70%, var(--theme-foreground, black));
}

.kpi-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.75rem;
}

@media (max-width: 640px) {
  .kpi-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

.kpi-card {
  padding: 0.75rem 1rem;
  border-radius: var(--radius-md, 0.5rem);
  border: 1px solid var(--theme-foreground-faintest, #e5e5e5);
  border-left: 3px solid var(--data-accent, #c4101b);
}

.kpi-card .kpi-value {
  font-size: 1.5rem;
  font-weight: 600;
  display: block;
}

.kpi-card .kpi-label {
  font-size: 0.8rem;
  color: var(--theme-foreground-muted, #666);
}

.breadcrumbs {
  font-size: 0.85rem;
  color: var(--theme-foreground-muted, #666);
  margin-bottom: 0.5rem;
}

.breadcrumbs a {
  color: inherit;
}

/* --- Componentes de las Fases 3-6 (components/kpiCard.js, usoColor.js,
   lifeStrip.js, csvExport.js) --- */

.kpi-hint {
  display: block;
  font-size: 0.75rem;
  color: var(--theme-foreground-muted, #666);
  margin-top: 0.15rem;
}

/* Figura de gráfica (components/chartFigure.js): agrupa título opcional +
   gráfica + pie interpretativo, ligados semánticamente por <figcaption>/
   aria-describedby — más accesible que un .kpi-hint suelto al lado. */
.chart-figure {
  margin: 0 0 1.25rem;
}

.chart-figure > h4 {
  margin: 0 0 0.35rem;
}

.chart-figure figcaption {
  margin-top: 0.4rem;
  font-size: 0.8rem;
  line-height: 1.4;
  color: var(--theme-foreground-muted, #666);
}

/* Textura diagonal reutilizable para valores que no deben leerse como cifra
   sólida (p.ej. una alerta): la distinción de estado nunca depende solo del
   color (ver FUNDAMENTOS-TECNICOS.md §4). Un mismo patrón en todo el sitio. */
.kpi-card-texture {
  background-image: repeating-linear-gradient(
    45deg,
    color-mix(in srgb, orange 10%, transparent),
    color-mix(in srgb, orange 10%, transparent) 6px,
    transparent 6px,
    transparent 12px
  );
  border-color: color-mix(in srgb, orange 45%, transparent);
}

.sparkline {
  display: block;
  margin-top: 0.35rem;
}

/* Leyendas de color (uso, familia, etc.): swatch + etiqueta, nunca solo color. */
.legend-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: center;
}

.legend-row-compact {
  gap: 0.5rem;
  font-size: 0.8rem;
}

.legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}

.legend-swatch {
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 50%;
  display: inline-block;
  flex: none;
}

/* Tira de vida (presence_bitmap, 23 cortes; components/lifeStrip.js).
   `.life-strip-wrap` agrupa la tira con el eje de años y la clave de
   simbología (ambos opt-in) — variables de geometría compartidas para que el
   eje quede pixel-alineado a las celdas sin duplicar los valores. */
.life-strip-wrap {
  display: inline-block;
  --life-cell-w: 0.9rem;
  --life-gap: 2px;
}

.life-strip {
  display: flex;
  gap: var(--life-gap);
  padding: 0.25rem 0;
}

.life-cell {
  width: var(--life-cell-w);
  height: 1.4rem;
  border-radius: 2px;
  flex: none;
}

.life-present {
  background: color-mix(in srgb, var(--data-accent, #c4101b) 65%, transparent);
}

.life-absent {
  background: var(--theme-foreground-faintest, #e5e5e5);
}

.life-alta,
.life-ultima {
  outline: 2px solid var(--theme-foreground, #1a1a1a);
  outline-offset: -1px;
}

/* Eje de años (opt-in `{axis:true}`): mismo gap/ancho de celda que
   .life-strip vía las variables de .life-strip-wrap, así cada tick cae justo
   debajo de su celda. Decorativo (aria-hidden) — el dato semántico ya vive en
   el `title` de cada celda. */
.life-axis {
  display: flex;
  gap: var(--life-gap);
  margin-top: 2px;
  height: 1.1rem;
}

.life-axis-slot {
  width: var(--life-cell-w);
  flex: none;
  position: relative;
}

.life-tick {
  display: block;
  width: 1px;
  height: 0.3rem;
  margin: 0 auto;
  background: var(--theme-foreground-muted, #666);
}

.life-tick-label {
  position: absolute;
  top: 0.35rem;
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.62rem;
  line-height: 1;
  color: var(--theme-foreground-muted, #666);
  white-space: nowrap;
}

/* Clave de simbología (opt-in `{legend:true}`): reutiliza .life-cell como
   swatch, así nunca se desincroniza de lo que la tira realmente dibuja. */
.life-legend {
  margin-top: 0.4rem;
  font-size: 0.72rem;
}

.life-legend .life-cell {
  width: 0.8rem;
  height: 0.8rem;
  border-radius: 2px;
}

.btn-secondary {
  font: inherit;
  padding: 0.4rem 0.85rem;
  border-radius: var(--radius-sm, 0.375rem);
  border: 1px solid var(--theme-foreground-faintest, #e5e5e5);
  background: transparent;
  color: inherit;
  cursor: pointer;
}

.btn-secondary:hover {
  border-color: var(--theme-foreground-muted, #666);
}

.btn-secondary:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Panel de filtros colapsable (Directorio/Altas y bajas/Anomalías). Fondo
   gris tenue tomado de .panel-filtros en la referencia (custom-style.css). */
.filters-panel {
  border: 1px solid var(--border-subtle, #e3e3e3);
  border-radius: var(--radius-md, 0.5rem);
  padding: 0.5rem 0.85rem 0.85rem;
  margin-bottom: 1rem;
  background: var(--surface-2, #fafafa);
}

.filters-panel summary {
  cursor: pointer;
  font-weight: 600;
  padding: 0.35rem 0;
}

.filters-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1rem 1.25rem;
  align-items: start;
}

/* Observable Inputs pone la etiqueta a ancho fijo (120px) y el campo al lado
   en una sola fila a partir de ~480px de viewport — pensado para formularios
   sueltos de ~360px, no para columnas de ~220px como las de este grid: a ese
   ancho la etiqueta se come casi todo el espacio y el campo queda apachurrado
   (la causa de que los filtros se vean "aplanados", con topes desiguales por
   el `align-items:end` de arriba). Se apila la etiqueta sobre el campo y se
   estira el campo al ancho de la celda — misma técnica de especificidad que
   `.filters-panel .checkbox-filter form` más abajo (dos clases + elemento le
   ganan a `form.inputs-xxx` del framework, sin `!important`, sin depender del
   orden de carga de las hojas de estilo). El buscador (components/
   searchBox.js) envuelve su Inputs.text en un `.searchbox`, así que el mismo
   ajuste se repite un nivel más adentro. */
.filters-panel .filters-grid > form,
.filters-panel .filters-grid .searchbox > form {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  width: 100%;
  max-width: none;
  margin: 0;
}

.filters-panel .filters-grid > form > label,
.filters-panel .filters-grid .searchbox > form > label {
  width: auto;
  padding-bottom: 0.15rem;
}

.filters-panel .filters-grid > form > :not(label),
.filters-panel .filters-grid .searchbox > form > :not(label) {
  width: 100%;
}

/* Par de rangos "desde/hasta" (p.ej. Altas y bajas: primera aparición): una
   sola etiqueta visible para las dos mitades en vez de dos controles con
   etiquetas largas apretadas en su propia celda de ~220px. */
.filter-range-group {
  grid-column: span 2;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.filter-group-label {
  font-size: 0.8rem;
  color: var(--theme-foreground-muted, #666);
}

.filter-range-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}

@media (max-width: 640px) {
  .filters-grid {
    grid-template-columns: 1fr;
  }

  .filter-range-group {
    grid-column: 1;
  }

  .filter-range-row {
    grid-template-columns: 1fr;
  }
}

/* Checkbox de opciones múltiples (uso del agua, tipo de aprovechamiento):
   todas las opciones arrancan marcadas ("desmarca lo que no quieres ver"), así
   que la lista puede ser larga (14 valores de uso) y se acomoda en columnas
   para no alargar demasiado el panel de filtros. Ocupa el ancho completo del
   grid de filtros. */
.checkbox-filter {
  grid-column: 1 / -1;
}

/* Inputs.checkbox trae `width:auto; max-width:640px` (encoge al contenido),
   así que sin un ancho explícito el `columns` de abajo no tiene caja donde
   repartir columnas y colapsa a una sola. `.filters-panel` en el selector le
   gana en especificidad a esa regla del framework (misma cascada, sin
   !important) para poder estirar el formulario al ancho completo del grid. */
.filters-panel .checkbox-filter form {
  width: 100%;
  max-width: none;
}

.checkbox-filter form > div {
  width: 100%;
  columns: 13rem;
  column-gap: 1.25rem;
}

.checkbox-filter form > div > label {
  display: flex;
  break-inside: avoid;
}

/* Fila de glifos de badge dentro de una celda de tabla (components/kpiCard.js
   no aplica aquí; ver directorio.md renderBadgeList). */
.badge-row {
  display: inline-flex;
  gap: 0.25rem;
}

.badge-row .anomaly-badge {
  font-size: 0.9rem;
}

/* Lista de advertencias de la ficha (titulo.md): cada anomalía activa es un
   enlace a su familia/subtipo en Anomalías. */
.badge-list {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.badge-list-item {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  color: inherit;
  text-decoration: none;
  padding: 0.3rem 0.5rem;
  border-radius: var(--radius-sm, 0.375rem);
}

.badge-list-item:hover {
  background: var(--theme-background-alt, color-mix(in srgb, currentColor 6%, transparent));
  text-decoration: underline;
}

/* Mini-mapa de la ficha: más bajo que el mapa principal de una sección. */
.map-container-mini {
  height: 40vh;
  min-height: 240px;
}

/* Leyenda continua azul->rojo (Altas y bajas, antigüedad). */
.temporal-legend {
  max-width: 420px;
  margin: 0.5rem 0 1.5rem;
}

.temporal-legend-bar {
  height: 0.6rem;
  border-radius: 0.3rem;
}

.temporal-legend-labels {
  display: flex;
  justify-content: space-between;
  font-size: 0.75rem;
  color: var(--theme-foreground-muted, #666);
  margin-top: 0.25rem;
}

/* Panel de detalle rápido (peek) de Altas y bajas: hover/clic en mapa o tabla. */
.peek-panel {
  border: 1px solid var(--theme-foreground-faintest, #e5e5e5);
  border-radius: var(--radius-md, 0.5rem);
  padding: 0.75rem 1rem;
  margin-bottom: 1rem;
}

/* Pestañas de familia (Anomalías) — control segmentado, role="tablist". */
.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.75rem 0 1.25rem;
}

.tab {
  font: inherit;
  padding: 0.4rem 0.85rem;
  border-radius: var(--radius-pill, 999px);
  border: 1px solid var(--border-subtle, var(--theme-foreground-faintest, #e5e5e5));
  background: var(--surface-1, #fff);
  color: inherit;
  cursor: pointer;
}

/* Pastilla rellena en el estado activo (mismo lenguaje que .pastilla en la
   referencia): mismo tamaño de control en activo/inactivo, sin salto de
   layout al cambiar de pestaña. */
.tab-active {
  font-weight: 600;
  background: var(--data-accent, #c4101b);
  border-color: var(--data-accent, #c4101b);
  color: #fff;
}

.tab:hover:not(.tab-active) {
  border-color: var(--data-accent, #c4101b);
  color: var(--data-accent, #c4101b);
}

/* --- Explorador de evidencia (Anomalías, components/anomalyExplorer.js) --- */

/* Kicker numerado de sección interna ("01 · El panorama" / "02 · La
   evidencia"): mismo lenguaje visual que .eyebrow de brand.css, pero como
   separador de bloques dentro de una pestaña, no sobre el H1. */
.kicker {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--eyebrow-color, var(--theme-foreground-muted, #666));
  margin: 1.75rem 0 0.6rem;
}

/* Pastillas (subtipo a explorar y dimensión de filtro): mismo lenguaje visual
   que .tab/.tab-active — subrayado rojo contenido para el estado activo,
   además de aria-pressed y del glifo ◆/▲ (nunca solo color). */
.pill-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.6rem 0 1rem;
}

.pill {
  font: inherit;
  font-size: 0.85rem;
  display: inline-flex;
  align-items: baseline;
  gap: 0.35rem;
  padding: 0.3rem 0.75rem;
  border-radius: var(--radius-pill, 999px);
  border: 1px solid var(--border-subtle, var(--theme-foreground-faintest, #e5e5e5));
  background: var(--surface-1, #fff);
  color: inherit;
  cursor: pointer;
}

.pill:hover:not(:disabled):not(.pill-active) {
  border-color: var(--data-accent, #c4101b);
  color: var(--data-accent, #c4101b);
}

/* Pastilla rellena en el estado activo (mismo lenguaje que .pastilla en la
   referencia) — además del relleno, el glifo ◆/▲ (color propio vía estilo
   inline) sigue siendo la señal primaria, nunca solo el color de fondo. */
.pill-active {
  font-weight: 600;
  background: var(--data-accent, #c4101b);
  border-color: var(--data-accent, #c4101b);
  color: #fff;
}

.pill:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Verificación en verde: la regla corrió y no encontró casos. La señal
   primaria es el glifo ✓ y el texto, no el color (mismo criterio que
   .notice-band). Tokens claro/oscuro en la propia clase — el modo oscuro de
   Framework se resuelve solo por prefers-color-scheme (ver brand.css). */
.callout-ok {
  --ok-border: color-mix(in srgb, #1a7f37 50%, transparent);
  --ok-bg: color-mix(in srgb, #1a7f37 10%, transparent);
  display: block;
  padding: 0.5rem 0.75rem;
  border-radius: var(--radius-sm, 0.375rem);
  border: 1px solid var(--ok-border);
  background: var(--ok-bg);
  font-size: 0.875rem;
}

/* Chips de contexto del explorador (única edición con casos, edición
   preliminar, recordatorio alerta≠prueba): informativos, no interactivos. */
.context-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.75rem;
  padding: 0.2rem 0.6rem;
  border-radius: var(--radius-pill, 999px);
  border: 1px solid var(--border-subtle, var(--theme-foreground-faintest, #e5e5e5));
  color: var(--theme-foreground-muted, #666);
}

/* Enlaces dentro de tablas de resultados (título -> ficha): color de tema, sin
   depender del azul de enlace por defecto del navegador — consistente con la
   dirección institucional/sobria del resto del sitio. */
table a {
  color: var(--theme-foreground, inherit);
  text-decoration: underline;
  text-decoration-color: var(--theme-foreground-faintest, #ccc);
  text-underline-offset: 0.15em;
}

table a:hover {
  text-decoration-color: currentColor;
}

/* Grid de KPIs con muchos tiles (p.ej. 11 en Altas y bajas): 3 columnas en
   pantallas medianas antes de colapsar a 2 en móvil, para que no se vea
   demasiado angosto en tablet. */
@media (min-width: 641px) and (max-width: 900px) {
  .kpi-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* Tooltip de mapa (components/mapTooltip.js): sigue al cursor sobre un punto
   de deck.gl mostrando su información más relevante. Posicionado en píxeles
   absolutos dentro de .map-container (position:relative); pointer-events:none
   para no interferir con el picking del propio mapa. */
.map-tooltip {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 5;
  pointer-events: none;
  max-width: 260px;
  padding: 0.5rem 0.65rem;
  border-radius: var(--radius-sm, 0.375rem);
  border: 1px solid var(--theme-foreground-faintest, #e5e5e5);
  background: var(--theme-background, #fff);
  color: var(--theme-foreground, #111);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
  font-size: 0.8rem;
  line-height: 1.35;
}

.map-tooltip[hidden] {
  display: none;
}

.map-tooltip strong {
  display: block;
  margin-bottom: 0.15rem;
}

.map-tooltip .map-tooltip-muted {
  color: var(--theme-foreground-muted, #666);
}

/* Leyenda de mapa (components/mapLegend.js): overlay anclado dentro de
   .map-container, mismo patrón de posicionamiento que .map-tooltip (absoluto,
   position:relative del contenedor) pero anclado a una esquina en vez de
   seguir al cursor. z-index por encima del tooltip (5) para que un swatch no
   quede tapado por él. Fondo translúcido con color-mix sobre el token de
   tema — legible en claro y oscuro sin fijar un color propio. Colapsable
   (ver mapLegend.js) para no tapar el mapa en pantallas angostas. */
.map-legend {
  position: absolute;
  z-index: 6;
  max-width: min(60%, 320px);
  border-radius: var(--radius-sm, 0.375rem);
  border: 1px solid var(--theme-foreground-faintest, #e5e5e5);
  background: color-mix(in srgb, var(--theme-background, #fff) 88%, transparent);
  color: var(--theme-foreground, #111);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
  font-size: 0.78rem;
  overflow: hidden;
}

.map-legend-bottom-left {
  left: 0.5rem;
  bottom: 0.5rem;
}

.map-legend-bottom-right {
  right: 0.5rem;
  bottom: 0.5rem;
}

.map-legend-toggle {
  font: inherit;
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.55rem;
  background: transparent;
  color: inherit;
  border: 0;
  cursor: pointer;
}

.map-legend-title {
  font-weight: 600;
}

.map-legend-body {
  padding: 0 0.55rem 0.5rem;
}

.map-legend-collapsed .map-legend-body {
  display: none;
}

.map-legend-collapsed .map-legend-caret {
  transform: rotate(-90deg);
}

@media (prefers-reduced-motion: no-preference) {
  .map-legend-caret {
    transition: transform var(--transition-fast);
  }
}

/* Las leyendas existentes (usoLegend/temporalLegend) respiran menos aquí
   dentro que sueltas bajo el mapa. */
.map-legend .legend-row {
  gap: 0.4rem 0.6rem;
}

.map-legend .temporal-legend {
  max-width: 260px;
  margin: 0.15rem 0 0;
}

/* Buscador con sugerencias (components/searchBox.js): título/titular en
   Directorio y Altas y bajas. El input reutiliza la clase de inputs de
   Observable para heredar su estilo; la lista de sugerencias flota debajo. */
.searchbox {
  position: relative;
}

.searchbox-suggestions {
  position: absolute;
  z-index: 10;
  top: 100%;
  left: 0;
  right: 0;
  margin: 0.15rem 0 0;
  padding: 0.25rem 0;
  list-style: none;
  max-height: 16rem;
  overflow-y: auto;
  border: 1px solid var(--theme-foreground-faintest, #e5e5e5);
  border-radius: var(--radius-sm, 0.375rem);
  background: var(--theme-background, #fff);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.15);
}

.searchbox-suggestions[hidden] {
  display: none;
}

.searchbox-suggestion {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  padding: 0.35rem 0.65rem;
  cursor: pointer;
  font-size: 0.85rem;
}

.searchbox-suggestion-glyph {
  flex: none;
}

.searchbox-suggestion-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.searchbox-suggestion:hover,
.searchbox-suggestion-active {
  background: var(--theme-background-alt, color-mix(in srgb, currentColor 6%, transparent));
}
