/*
 * tarjeta-azul.css — la "tarjeta azul": carrusel de 4 pestañas (Vuelo /
 * Datos / Mapa / Contadores) con el número de vuelo, el cronómetro de
 * operación y la pista de progreso.
 *
 * Vivía dentro de atender.css porque era la cabecera de UNA pantalla. Ahora
 * es un componente reutilizable (public/js/tarjeta-azul.js) que montan DOS
 * páginas —atender.html en su cabecera y index.html una vez por tarjeta de
 * vuelo del Home—, así que su hoja de estilos sube a un archivo propio
 * compartido por las dos, en vez de duplicarse o de obligar al Home a cargar
 * los ~2000 px de CSS de la pantalla operativa.
 *
 * Se carga SIEMPRE después de /css/styles.css y reutiliza sus variables de
 * diseño (los tokens de DESIGN.md ya declarados allí en :root): no redefine
 * ningún color ni radio. En atender.html se carga ANTES de atender.css, para
 * que esa hoja pueda seguir pisando los tamaños de la tarjeta en sus propios
 * breakpoints (ver los bloques @media de atender.css, que a propósito NO se
 * movieron acá: escalan la cabecera al ancho de la pantalla operativa, algo
 * que no aplica a las tarjetas del Home, que son angostas siempre).
 *
 * Todo es POR CLASE, sin un solo selector de id: en el Home hay N tarjetas en
 * la misma página y el JS las distingue con atributos data-* y
 * raiz.querySelector(), no con ids.
 *
 * Incluye además dos grupos de primitivos genéricos que la tarjeta necesita y
 * que también vivían en atender.css: la píldora .op-pill-btn y el modal
 * (.modal-overlay/.modal-sheet/…), que usa el mapa a pantalla completa.
 */

/* ── El "hospedaje" de la tarjeta ─────────────────────────────────────
   Lo común a las dos páginas: el fondo navy, el color de texto y el alto de
   tarjeta (--op-slide-h). El alto es una variable y no "lo que ocupe el
   contenido" a propósito: con 4 tarjetas de contenido muy distinto, dejarlo
   libre haría que el bloque —y con él todo lo de abajo— saltara de alto al
   deslizar. */
.op-header,
.op-header-mount {
  --op-slide-h: 130px;
  background: var(--primary);
  color: var(--on-primary);
}

/* atender.html: barra pegajosa de borde a borde.
   En Mobile/Tablet la cabecera va pegada a los bordes LATERALES de la
   pantalla, así que el radio solo tiene sentido abajo: un corte recto de lado
   a lado se sentía estático al hacer scroll. Se combina con el solape de
   .op-main (margin-top negativo, en atender.css) — la tarjeta de info
   "muerde" ligeramente hacia arriba bajo esta curva, en vez de simplemente
   cortar en una línea. En escritorio (>=1080px) esto se pisa con el radio de
   las 4 esquinas, ya en atender.css.
   El padding inferior no reserva franja para .op-conn: ese badge vive en la
   fila de puntos indicadores (.op-carrusel-pie), dentro del flujo, así que no
   puede taparle nada a ninguna tarjeta. */
.op-header {
  position: sticky;
  top: 0;
  z-index: 20;
  padding: 8px 12px 7px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}

/* index.html: la tarjeta azul dentro de una .flight-card del Home. Acá NO es
   pegajosa ni de borde a borde — es un bloque más de la tarjeta blanca, con
   las 4 esquinas redondeadas. Se queda siempre en la escala compacta (la de
   móvil de atender.html) porque el ancho de una .flight-card es angosto sin
   importar el tamaño de la pantalla. */
.op-header-mount {
  padding: 10px 12px 7px;
  border-radius: var(--radius-md);
  overflow: hidden;
}

/* ── Mecánica del carrusel ────────────────────────────────────────────
   scroll-snap nativo sobre un contenedor con overflow-x:auto, y NO un
   manejador de gestos táctiles propio: el navegador ya resuelve gratis el
   arrastre con inercia, la rueda/trackpad horizontal, el teclado (el
   viewport es focusable y las flechas lo desplazan) y el respeto por
   prefers-reduced-motion; además el scroll ocurre en el compositor, así que
   no compite con el repintado del cronómetro cada segundo. El JS
   (configurarCarruselCabecera) solo escucha 'scroll' para sincronizar los
   puntos y mueve el scroll con scrollTo() al pulsar ‹ › o un punto. */
.op-carrusel {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.op-carrusel-viewport {
  display: flex;
  gap: 12px;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  /* El gesto horizontal no debe encadenar con el scroll vertical de la
     página ni con el "volver atrás" del navegador al llegar al borde. */
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  outline: none;
  /* El viewport se estira de borde a borde de la cabecera restando su
     padding, así que cada tarjeta ocupa exactamente el 100% del ancho útil
     (ver .op-slide) y el snap cae siempre en un múltiplo exacto. */
  width: 100%;
}

.op-carrusel-viewport::-webkit-scrollbar { display: none; }

/* Anillo de foco visible solo con teclado: el carrusel es navegable con
   flechas, así que tiene que verse cuándo lo tiene. */
.op-carrusel-viewport:focus-visible {
  outline: 2px solid rgba(255, 255, 255, 0.65);
  outline-offset: 2px;
  border-radius: var(--radius-md);
}

.op-slide {
  flex: 0 0 100%;
  width: 100%;
  height: var(--op-slide-h);
  min-width: 0;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  display: flex;
  flex-direction: column;
  gap: 3px;
  overflow: hidden;
}

/* Fila superior de la tarjeta 1: logo a la izquierda, estado del vuelo a la
   derecha. Antes el logo iba centrado en una fila propia y el estado en otra
   junto al número de vuelo: fundirlas ahorra una fila entera de alto sin que
   ninguno de los dos pierda protagonismo. */
.op-slide-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 24px;
}

.op-brand {
  display: flex;
  align-items: center;
  min-width: 0;
}

/* Wordmark real de GIRAG (blanco). Bajó de 26px a 15px: en el presupuesto de
   alto de la tarjeta 1 (130px para logo + vuelo + cronómetro + pista) 26px
   costaban una fila entera, y a 15px el wordmark sigue siendo perfectamente
   legible porque ahora está solo en su fila, sin nada que le compita al
   lado. */
.op-brand-logo {
  height: 15px;
  width: auto;
  flex-shrink: 0;
}

/* El número de vuelo y la matrícula ya no comparten fila con el badge de
   estado (subió a .op-slide-top), así que pueden ocupar el ancho completo. */
.op-title-row {
  display: flex;
  align-items: baseline;
  justify-content: flex-start;
  gap: 9px;
  min-width: 0;
}

/* Etiqueta y badge EN LÍNEA (no apilados como en el resto de la app): en una
   tarjeta de 130px de alto, apilarlos costaba ~13px que la pista y el
   cronómetro necesitan. En horizontal la caption sigue cumpliendo su función
   —distinguir este badge del badge del cronómetro— sin gastar una fila. */
.op-slide-top .op-status-group {
  flex-direction: row;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

/* Versión compacta de .status-badge (styles.css) para la cabecera: mismos
   colores de estado, un punto menos de padding y de tipografía. */
.op-slide-top .status-badge {
  font-size: 10px;
  padding: 3px 9px;
}

.op-slide-top .status-badge::before { width: 6px; height: 6px; }

/* ── Tarjeta 2: datos del vuelo ───────────────────────────────────────
   Los datos que antes eran .op-chip (una fila de píldoras apretadas, con la
   matrícula y el cliente recortados por ellipsis casi siempre). Con una
   tarjeta entera para ellos caben con etiqueta arriba y valor debajo, a
   tamaño legible.

   La grilla pasó de 2 a 3 columnas al agregarse "Base" (ver UX 2 en
   PLAN_EQUIPOS_Y_BUGS.md: ese dato se mostraba en el bloque de metadatos del
   Home que esta tarjeta reemplaza, y no se puede perder). Con 2 columnas los
   6 datos —Cliente a fila completa + Ruta/Base/Posición/STD/ETD— pedían 4
   filas y no entraban en el alto fijo de la tarjeta (--op-slide-h); con 3
   caben en 3 filas exactas, el mismo alto de antes, y Cliente conserva su
   fila propia (suele ser la razón social completa). La 6.ª celda queda libre
   en atender.html y la ocupa "Tipo" de avión en el Home, donde ese dato
   tampoco tiene otro sitio.

   overflow-y como válvula de seguridad: .op-slide recorta (overflow:hidden),
   así que en una tarjeta muy angosta es mejor poder desplazar la grilla que
   perder un dato sin aviso. */
.op-datos-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px 10px;
  align-content: start;
  min-width: 0;
  flex: 1 1 auto;
  overflow-y: auto;
  scrollbar-width: none;
}

.op-datos-grid::-webkit-scrollbar { display: none; }

/* Cliente suele ser el más largo (razón social completa): fila propia. */
.op-dato.op-dato--ancho { grid-column: 1 / -1; }

.op-dato { min-width: 0; }

.op-dato-label {
  margin: 0;
  font-size: 9px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #a9abe8;
  display: flex;
  align-items: center;
  gap: 4px;
}

.op-dato-label .material-symbols-outlined { font-size: 12px; }

.op-dato-valor {
  margin: 0;
  font-size: 14px;
  font-weight: 700;
  line-height: 1.25;
  color: #fff;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Tarjeta 4: contadores de la operación ───────────────────────────── */
.op-contadores {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  flex: 1 1 auto;
  min-height: 0;
}

.op-contador {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1px;
  min-width: 0;
  background: rgba(255, 255, 255, 0.09);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--radius-md);
  padding: 6px 10px;
}

.op-contador-label {
  margin: 0;
  font-size: 9px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #a9abe8;
}

.op-contador-valor {
  margin: 0;
  font-size: 21px;
  font-weight: 800;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  color: #fff;
}

/* "Pendiente"/"Inconsistente" no son cifras: se pintan más chicos y en el
   tono tenue del resto de la cabecera para que no se lean como un dato. */
.op-contador-valor.op-contador-valor--pend {
  font-size: 14px;
  font-weight: 700;
  color: #c7c9ff;
}

.op-contador-nota {
  margin: 0;
  font-size: 9px;
  font-weight: 600;
  color: #a9abe8;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Encabezado interno de las tarjetas 2, 3 y 4 ─────────────────────── */
.op-slide-kicker {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 9px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #c7c9ff;
  min-height: 18px;
}

.op-slide-kicker .material-symbols-outlined { font-size: 13px; }
.op-slide-kicker-sp { flex: 1 1 auto; }

/* ── Pie del carrusel: flechas + puntos + estado de conexión ─────────── */
.op-carrusel-pie {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 22px;
}

.op-carrusel-nav {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: opacity 0.15s ease, background 0.15s ease;
}

.op-carrusel-nav .material-symbols-outlined { font-size: 17px; }
.op-carrusel-nav:not(:disabled):active { background: rgba(255, 255, 255, 0.24); }
.op-carrusel-nav:disabled { opacity: 0.3; cursor: default; }

.op-carrusel-dots {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-width: 0;
}

/* Objetivo táctil real de 22px (el punto pintado es de 7px, pero el botón
   que lo contiene es del alto de la fila): en plataforma se opera con
   guantes, un blanco de 7px sería inalcanzable. */
.op-carrusel-dot {
  flex-shrink: 0;
  width: 18px;
  height: 22px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  -webkit-tap-highlight-color: transparent;
}

.op-carrusel-dot::before {
  content: '';
  display: block;
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.32);
  transition: width 0.22s ease, background 0.22s ease;
}

/* La tarjeta activa se marca alargando su punto (y en naranja de marca), no
   solo con opacidad: se distingue de un vistazo a plena luz. */
.op-carrusel-dot[aria-selected='true']::before {
  width: 18px;
  background: var(--accent);
}

/* Etiqueta "Estado del vuelo" sobre el badge de estado (punto 7: había dos
   píldoras de aspecto muy similar en la cabecera —esta y
   .op-timer-badge, más abajo en la pista— sin nada que dijera cuál era
   cuál). El cronómetro ya se explica solo con .op-timer-label encima de su
   propia píldora; esta caption le da el mismo tratamiento al estado del
   vuelo, para que ambas queden visualmente en pie de igualdad y no se
   confundan entre sí. */
.op-status-group {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
}

.op-status-caption {
  font-size: 9px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #c7c9ff;
}

.op-flight {
  /* 26px → 22px: la tarjeta 1 ahora reparte 130px entre logo, título,
     cronómetro y pista. Sigue siendo, de lejos, el texto más grande de la
     cabecera, que es lo que la jerarquía necesita. */
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.05;
}

/* Antes 13px: al lado del número de vuelo (26px) se leía como un error
   tipográfico, no como una jerarquía intencional. Ahora comparte casi el
   mismo tamaño — la distinción queda solo en el color (más tenue) y un
   peso ligeramente menor, suficiente para que "cuál es el dato principal"
   siga siendo obvio sin que la matrícula se vea rota. */
.op-matricula {
  /* Mantiene la proporción con .op-flight de antes (~0.85×): la distinción
     con el número de vuelo sigue estando en el color y el peso, no en un
     salto de tamaño que la haría ver rota. */
  font-size: 17px;
  font-weight: 700;
  color: #c7c9ff;
  letter-spacing: -0.01em;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
}

/* ── Tarjeta 3: mapa de posición en vivo ─────────────────────────────
   El mapa es Leaflet + tiles de OpenStreetMap (ver atender.html). La caja
   se pinta oscura por debajo para que, mientras los tiles cargan o cuando
   no hay nada que mostrar, el hueco siga leyéndose como parte de la
   cabecera navy y no como un rectángulo blanco roto. */
.op-mapa-caja {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: #0b0b3a;
  border: 1px solid rgba(255, 255, 255, 0.16);
}

.op-mapa-lienzo {
  position: absolute;
  inset: 0;
  background: #0b0b3a;
}

/* Capa de estado sobre el mapa: "cargando", "sin datos de posición" o el
   motivo del fallo. Cuando está visible tapa el lienzo por completo — nunca
   se deja ver un mapa con una posición vieja detrás de un mensaje de error,
   porque se leería como si el avión estuviera ahí ahora. */
.op-mapa-estado {
  position: absolute;
  inset: 0;
  z-index: 500; /* por encima de los panes de Leaflet (400..700) */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 8px 12px;
  text-align: center;
  background: #0b0b3a;
  color: #c7c9ff;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.35;
}

.op-mapa-estado[hidden] { display: none !important; }
.op-mapa-estado .material-symbols-outlined { font-size: 20px; opacity: 0.7; }

/* Botones de ícono del encabezado de la tarjeta de mapa (actualizar y
   pantalla completa). Misma forma redonda y tenue que .op-carrusel-nav para
   que la cabecera tenga un solo vocabulario de botón sobre navy. */
.op-mapa-icon-btn {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.op-mapa-icon-btn .material-symbols-outlined { font-size: 14px; }
.op-mapa-icon-btn:active { background: rgba(255, 255, 255, 0.24); }

/* Versión a pantalla completa, dentro del .modal-sheet ya existente. */
.op-mapa-sheet { width: 100%; }
.op-mapa-caja--full { height: min(62vh, 520px); }

.op-mapa-nota {
  margin: 10px 0 0;
  font-size: 11px;
  line-height: 1.45;
  color: var(--on-surface-variant);
}

/* Marcador del avión: un glifo rotado a su rumbo real (`track` de OpenSky),
   no un pin genérico — de un vistazo se ve hacia dónde va. */
.op-mapa-avion {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--accent);
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.6));
}

.op-mapa-avion .material-symbols-outlined {
  font-size: 26px;
  font-variation-settings: 'FILL' 1, 'wght' 700, 'GRAD' 0, 'opsz' 24;
}

/* ── Tarjeta de vuelo: pista + cronómetro de operación ──────────────────
   Una sola pieza visual: el cronómetro flota centrado sobre una "pista"
   (una LÍNEA RECTA horizontal; antes era una curva suave) por la que se
   desplaza un avión hasta el punto donde va porcentualmente la atención. Antes de iniciar, el
   avión aparece fuera de la pista aterrizando; al cerrar la atención,
   sale volando por el lado derecho. Arranca/se detiene con los mismos
   hitos que antes (ID_HITO_CRONO_INICIO/FIN en atender.js): esto solo
   cambia la presentación, no la lógica de avance/tiempo. */
.op-runway {
  /* Ocupa lo que sobra de la tarjeta 1 debajo del logo y del título, en vez
     de un margen fijo: así el cronómetro y la pista quedan centrados en ese
     espacio sea cual sea el alto de --op-slide-h. */
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
}

.op-runway-timer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  position: relative;
  z-index: 2;
}

.op-timer-label {
  font-size: 9px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #c7c9ff;
  text-align: center;
  max-width: 100%;
}

.op-runway-timer-pill {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  padding: 3px 14px 3px 7px;
}

.op-timer-badge {
  flex-shrink: 0;
  font-size: 10px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 3px 8px;
  border-radius: 999px;
  color: #fff;
}

.op-timer-badge--normal { background: var(--success); }
.op-timer-badge--alerta { background: var(--warning); }
.op-timer-badge--excedido { background: var(--error); animation: op-timer-pulse 1.1s ease-in-out infinite; }

@keyframes op-timer-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.6; }
}

.op-timer-display {
  /* 27px → 21px. Es el segundo dato más grande de la tarjeta después del
     número de vuelo, y a 21px con dígitos tabulares sigue leyéndose a
     distancia de brazo con guantes puestos. */
  font-size: 21px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  flex-shrink: 0;
}

.op-timer-display.timer-normal { color: #7fe6ac; }
.op-timer-display.timer-warning { color: #fbbf77; }
.op-timer-display.timer-critical { color: #ff8a80; }

/* Pista: el cronómetro queda encima (z-index) y esta franja dibuja el
   recorrido RECTO con ambientación de aeropuerto detrás del avión.
   Deliberadamente minimalista (una franja suave + una guía fina + el
   trazo de avance): nada de patrones punteados ni figuras pequeñas
   sueltas, que a este tamaño solo generan ruido visual. La línea vive
   en un SVG que se estira al ancho/alto de la franja (viewBox 100×40 →
   x = % de ancho, y/40*100 = % de alto: así JS convierte un punto de la
   línea a left/top en % sin más matemática). */
.op-runway-track {
  position: relative;
  /* 40px → 30px: la pista se achica pero conserva la misma línea, el mismo
     avión y el mismo % — sigue siendo reconocible. El SVG usa
     preserveAspectRatio="none" sobre un viewBox 100×40, así que la
     matemática de posicionarAvion() (x = % de ancho, y/40 = % de alto) no
     cambia en absoluto. Ojo con los grosores: ese estiramiento NO es
     uniforme (el eje Y se comprime de 40 a 30), así que el grosor real de
     una línea horizontal es stroke-width × alto/40 — ver el comentario de
     .op-runway-path-fill. */
  height: 30px;
  margin-top: 2px;
}

.op-runway-scene {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

/* Franja ancha y muy tenue: sugiere el asfalto de la pista sin dibujar
   un borde marcado. */
.op-runway-lane {
  fill: none;
  stroke: rgba(255, 255, 255, 0.08);
  stroke-width: 11;
  stroke-linecap: round;
}

/* Guía central, fina y sólida (sin dash): es el "recorrido" en sí. */
.op-runway-path-bg {
  fill: none;
  stroke: rgba(255, 255, 255, 0.32);
  stroke-width: 1.4;
  stroke-linecap: round;
}

/* Trazo de avance. Sube de 2 a 6 unidades de viewBox: como el SVG se estira
   con preserveAspectRatio="none", el grosor real en pantalla es
   stroke-width × (alto de la franja / 40) ⇒ ≈4,5px con la franja de 30px de
   Mobile y ≈5,4px con la de 36px. Se lee de un vistazo cuánto va avanzada la
   atención, pero queda claramente por debajo de los 18px del ícono del avión
   —que sigue siendo el protagonista— y por dentro de la franja de asfalto de
   11 unidades, así que no la desborda.
   NO se usa `vector-effect: non-scaling-stroke` (que daría el grosor en px
   exactos) porque con él el navegador calcula también el dash en px y rompe
   el stroke-dasharray:100 / pathLength="100" del que depende la animación de
   avance. */
.op-runway-path-fill {
  fill: none;
  stroke: var(--accent);
  stroke-width: 6;
  stroke-linecap: round;
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  transition: stroke-dashoffset 0.6s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.op-runway-cloud {
  position: absolute;
  top: 10%;
  color: rgba(255, 255, 255, 0.4);
  pointer-events: none;
  transform: translateY(-50%);
}

.op-runway-cloud .material-symbols-outlined {
  font-size: 12px;
  display: block;
}

.op-runway-cloud--a { left: 20%; }
.op-runway-cloud--b { right: 18%; }

/* Vuelo EN TIERRA (etapa 'en-curso'): el avión va SOBRE la línea recta, o
   sea al 50% del alto de la franja, y con sus 18px ocupa casi todo el
   centro. Las nubes, que en las otras etapas conviven con un avión que
   vuela arriba (top 10%), tienen que quedar CLARAMENTE por encima de él
   para que se lea "cielo arriba / aeronave en pista abajo": suben fuera
   de la franja (top negativo, el SVG y la franja no recortan) y se achican
   un punto. La clase la alterna posicionarAvion() en atender.js. */
.op-runway-track.en-tierra .op-runway-cloud { top: -16%; }
.op-runway-track.en-tierra .op-runway-cloud .material-symbols-outlined { font-size: 11px; }

.op-runway-plane {
  position: absolute;
  left: 0;
  top: 0;
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  transform: translate(-50%, -50%);
  transition: left 0.6s cubic-bezier(0.2, 0.7, 0.3, 1),
              top 0.6s cubic-bezier(0.2, 0.7, 0.3, 1),
              transform 0.6s cubic-bezier(0.2, 0.7, 0.3, 1);
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.4));
  z-index: 2;
}

.op-runway-plane .material-symbols-outlined {
  font-size: 18px;
  color: var(--accent);
  font-variation-settings: 'FILL' 1, 'wght' 600, 'GRAD' 0, 'opsz' 24;
}

.op-runway-pct {
  position: absolute;
  left: 0;
  top: 0;
  transform: translate(-50%, -16px);
  font-size: 9px;
  font-weight: 800;
  color: #fff;
  background: rgba(0, 0, 0, 0.32);
  padding: 1px 6px;
  border-radius: 999px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  pointer-events: none;
  z-index: 3;
  transition: left 0.6s cubic-bezier(0.2, 0.7, 0.3, 1),
              top 0.6s cubic-bezier(0.2, 0.7, 0.3, 1),
              opacity 0.3s ease;
}

.op-runway[data-etapa='programada'] .op-runway-pct,
.op-runway[data-etapa='finalizada'] .op-runway-pct {
  opacity: 0;
}

/* Pie de la tarjeta 1: la etiqueta larga del cronómetro (#timer-label, que
   antes tenía fila propia arriba de la píldora) y el conteo de actividades
   comparten esta única línea. Fusionarlas ahorró ~15px de alto sin perder
   ninguno de los dos textos. */
.op-runway-sub {
  margin: 0;
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 5px;
  font-size: 9px;
  font-weight: 600;
  color: #c7c9ff;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
}

.op-runway-sub > span {
  overflow: hidden;
  text-overflow: ellipsis;
}

.op-runway-sep { opacity: 0.5; flex-shrink: 0; }

/* Antes vivía fuera del flujo (position:absolute, esquina inferior derecha
   de la cabecera) y había que reservarle una franja con padding-right para
   que no tapara los chips. Con el carrusel eso ya no hace falta: se sienta
   en la fila de puntos indicadores (.op-carrusel-pie), a la derecha, dentro
   del flujo — no puede taparle nada a ninguna tarjeta y no cuesta una fila
   propia porque comparte la de los puntos. */
.op-conn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 9px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: rgba(255, 255, 255, 0.1);
  padding: 3px 8px;
  border-radius: 999px;
  flex-shrink: 0;
  max-width: 45%;
  overflow: hidden;
  white-space: nowrap;
}

.op-conn .conn-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #767685;
}

.op-conn.live .conn-dot {
  background: #34d399;
  box-shadow: 0 0 0 3px rgba(52, 211, 153, 0.25);
  animation: pulse-dot 1.8s ease-in-out infinite;
}

.op-conn.down .conn-dot { background: #fca5a5; }
.op-conn.down { color: #ffd9d9; }

/* Botón "Contraer todo" / "Expandir todo": alterna el acordeón de todas las
   categorías de #fases-container a la vez, reutilizando el toggle propio de
   cada .fase-card (ver alternarTodasLasFases() en atender.js). Vive en la
   fila de título del panel de Actividades, visible en cualquier tamaño de
   pantalla (no es exclusivo de escritorio).

   .op-pill-btn es la MISMA forma (píldora: radio 999px, borde fino, texto
   uppercase peso 800, ~11px, min-height táctil) generalizada a TODOS los
   botones de la pantalla —los de la columna "Información adicional"
   (#btn-completar-info, #push-activar/#push-desactivar/#push-reintentar en
   public/js/push.js), #btn-nueva-novedad y también los de dentro de los 4
   modales— que antes usaban el sistema .op-btn (rectangular), ya
   eliminado. Se comparte el mismo bloque de reglas para que todos
   permanezcan idénticos sin duplicar valores. */
.op-toggle-all,
.op-pill-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  flex-shrink: 0;
  border: 1px solid var(--outline-variant);
  background: var(--surface-container-lowest);
  color: var(--on-surface-variant);
  border-radius: 999px;
  padding: 6px 12px;
  font-family: inherit;
  font-size: 11px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  cursor: pointer;
  min-height: 36px;
  -webkit-tap-highlight-color: transparent;
}

.op-toggle-all .material-symbols-outlined,
.op-pill-btn .material-symbols-outlined { font-size: 16px; }
.op-toggle-all:active,
.op-pill-btn:not(:disabled):active { filter: brightness(0.95); }
.op-pill-btn:disabled { cursor: not-allowed; opacity: 0.55; }

/* Variantes de énfasis de .op-pill-btn: misma forma/tipografía siempre,
   solo cambia el color según la intención de la acción.
   - .op-pill-btn-outline: acción primaria de menor urgencia ("Completar
     información" solo edita datos ya existentes) → borde/texto en
     var(--primary), sin relleno.
   - .op-pill-btn-accent: acción primaria de mayor urgencia/CTA ("Activar
     notificaciones" inicia una suscripción push) → relleno var(--accent),
     el naranja de marca.
   - .op-pill-btn-primary: acción principal que ESCRIBE en la operación
     ("Confirmar", "Guardar", "Subir evidencia") → relleno var(--primary),
     el azul dominante de la app.
   - .op-pill-btn-danger: acciones de novedad/incidencia, que ya se leen en
     rojo en el resto de la pantalla ("Registrar novedad", "Guardar" del
     modal de novedad) → relleno var(--error).
   Sin modificador (solo .op-pill-btn) queda la variante neutra/secundaria,
   idéntica a .op-toggle-all: usada para "Desactivar", "Cerrar" y para
   "Cancelar" en los modales (es la acción de descarte: debe verse presente
   pero claramente por debajo de la principal). */
.op-pill-btn-outline {
  background: transparent;
  border-color: var(--primary);
  color: var(--primary);
}

.op-pill-btn-accent {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.op-pill-btn-primary {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--on-primary);
}

.op-pill-btn-danger {
  background: var(--error);
  border-color: var(--error);
  color: #fff;
}

/* Único control segmentado de la app (ver Equipos, "MÓDULO NUEVO — Equipos"
   en PLAN_EQUIPOS_Y_BUGS.md): dos .op-pill-btn adyacentes y mutuamente
   excluyentes (Propio/Alquilado). .op-pill-btn-selected marca cuál de las
   dos está activa — mismo relleno que .op-pill-btn-primary, para que "la
   opción elegida" se lea con el mismo peso visual que cualquier otra
   acción principal de la pantalla, sin inventar un color nuevo. */
.op-pill-btn-selected {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--on-primary);
}

/* Verde WhatsApp (#25d366). Es el ÚNICO color de esta hoja que no sale de
   los tokens de :root, y a propósito: no es un color del sistema de diseño
   de GIRAG sino el color de marca de un servicio de terceros. El botón
   "Confirmar y compartir" abre WhatsApp de verdad (ver compartirActividad()
   en atender.js), así que pintarlo con el verde + el logo de WhatsApp es lo
   que le dice al operador a dónde va a ir a parar el mensaje. Se deja como
   clase reutilizable —y no como estilo en línea, que es como estaba— para
   que ese valor viva en un solo sitio si mañana aparece otro botón de
   compartir. */
.op-pill-btn-whatsapp {
  background: #25d366;
  border-color: #25d366;
  color: #fff;
}

.op-pill-btn-block { width: 100%; }

/* Tamaño grande de la píldora: MISMA forma y misma tipografía, solo más
   área táctil. Se usa en las acciones principales de los modales, donde la
   píldora de 36px se quedaba corta para el objetivo táctil recomendado de
   44px (el operador las toca con guantes y en plataforma). */
.op-pill-btn-lg {
  min-height: 44px;
  padding: 10px 18px;
  font-size: 13px;
}

.op-pill-btn-lg .material-symbols-outlined { font-size: 18px; }

/* Hueco donde el JS inyecta un icono de la biblioteca SVG dentro de una
   píldora (hoy, el logo de WhatsApp de "Confirmar y compartir"). */
.op-pill-btn-ico { display: inline-flex; align-items: center; flex-shrink: 0; }
.op-pill-btn .girag-svg-icon { width: 16px; height: 16px; }
.op-pill-btn-lg .girag-svg-icon { width: 18px; height: 18px; }

/* ── Modales (bottom sheet en móvil, centrado en pantallas grandes) ─── */
.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: rgba(16, 18, 32, 0.55);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0;
  overscroll-behavior: contain;
}

.modal-overlay[hidden] { display: none; }

.modal-sheet {
  background: var(--surface-container-lowest);
  width: 100%;
  max-height: 92vh;
  overflow-y: auto;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  padding: 16px 16px calc(18px + env(safe-area-inset-bottom, 0px));
  box-shadow: var(--shadow-elevated);
  animation: op-sheet-in 0.18s ease-out;
}

@keyframes op-sheet-in {
  from { transform: translateY(18px); opacity: 0.6; }
  to { transform: translateY(0); opacity: 1; }
}

.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 14px;
}

.modal-title {
  font-size: 17px;
  font-weight: 800;
  color: var(--primary);
  margin: 0;
  overflow-wrap: anywhere; /* los nombres de hito pueden ser largos */
}

/* Antetítulo del modal de confirmar hito: la línea pequeña que dice QUÉ se
   está por hacer ("Confirmar actividad") encima del nombre real del hito,
   que es el título grande. Antes el nombre del hito iba como un dato más
   dentro del cuerpo, con la etiqueta "Nombre de la acción" encima y el
   título genérico "Confirmar acción" arriba: dos textos compitiendo y una
   etiqueta que no aportaba nada (el modal se abre justo después de tocar
   ese hito, el contexto ya está dado). */
.modal-kicker {
  font-size: 10px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--outline);
  margin: 0 0 2px;
}

.modal-head-texto { min-width: 0; }

/* Descripción del hito, debajo del título. :empty la esconde: muchos hitos
   no traen descripción y no debe quedar un hueco. */
.modal-lead {
  margin: -8px 0 14px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--on-surface-variant);
}

.modal-lead:empty { display: none; }

.modal-close {
  border: 0;
  background: var(--surface-container);
  color: var(--on-surface-variant);
  border-radius: 50%;
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
}

.modal-actions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-top: 18px;
}

/* Acción alternativa que cuelga de la fila de acciones (hoy solo
   "Confirmar y compartir"): mismo grupo visual, sin separadores. */
.modal-action-extra { margin-top: 10px; }

/* Mismo tratamiento de pantalla grande que ya tenía atender.css (donde esta
   regla sigue estando, idéntica): en pantallas anchas el bottom sheet pasa a
   modal centrado. Se repite acá porque index.html no carga atender.css. */
@media (min-width: 768px) {
  .modal-overlay { align-items: center; padding: 20px; }
  .modal-sheet { max-width: 540px; border-radius: var(--radius-lg); }
}
