/* ═══════════════════════════════════════════════════════
   EVENTOS.CSS — Sección Eventos
   Header/bottom sheets reusan las clases ya existentes de
   nav.css/reservas.css/global.css (.app-nav*, .bsheet*, .badge*) -- este
   archivo cubre lo que no existe todavía: nav fija + panel de mes, cards de
   evento/cumpleaños y el timeline único (ver "Cambios recientes" -- este
   rediseño reemplaza por completo el sistema de tabs/vistas anterior:
   ícono ojo + burbuja Calendario/Lista, pill Hoy/Semanal/Mensual y pill
   Próximos/Pasados/Todos).
   ═══════════════════════════════════════════════════════ */

/* ── Cabecera sticky (position:sticky, no fixed+spacer, para convivir con el
   scroll normal de #s-eventos en vez de superponerse). Fondo sólido --
   mismo criterio de breakpoint que `.card` (css/ui.css): en mobile real
   (≤600px) `.card` pierde su fondo propio, así que acá hay que usar el
   fondo real de la página (`--bg`) en vez de `--card-bg` para que no se
   transparente el contenido de abajo al scrollear detrás. Padding-top con
   `env(safe-area-inset-top)` (+ 12px fijos) para que no quede pegado al
   notch/borde superior del dispositivo al pinnearse; padding-bottom para
   separarlo del timeline que scrollea debajo. */
.ev-sticky-header {
  position: sticky; top: 0; z-index: 20; background: var(--card-bg);
  padding-top: calc(env(safe-area-inset-top, 0px) + 12px);
  padding-bottom: 14px;
}
@media (max-width: 600px) {
  .ev-sticky-header { background: var(--bg); }
}

/* Bug real corregido (ver "Cambios recientes" -- gap sobre el header sticky
   al entrar/volver a #s-eventos): `.pantalla.activa` (css/global.css) anima
   `transform:translateY(20px)->none` en CADA entrada (ir() saca y vuelve a
   poner la clase `.activa` en todo cambio de pantalla) -- ese transform del
   ancestro también se pinta sobre `.ev-sticky-header`, un hijo directo,
   aunque sea `position:sticky` y ya esté "pegado" al top real del viewport;
   durante los ~600ms de la animación el header queda pintado hasta 20px por
   debajo de su posición real, dejando ver una porción del timeline
   scrolleable que debería estar tapada detrás. Fix: contra-animación con el
   translateY exactamente invertido (misma curva/duración que
   `smoothSlideUp`, css/estilos.css) SOLO en el header -- ambos transforms se
   cancelan en cada instante (mismo % de la curva a la vez, mismo ancestro
   que dispara los dos), así que el header queda pintado en su posición real
   desde el primer frame, sin tocar el slide-up del resto de la pantalla. */
@keyframes evStickyHeaderCounter {
  from { transform: translateY(-20px); }
  to { transform: none; }
}
.pantalla.activa .ev-sticky-header {
  animation: evStickyHeaderCounter 0.6s var(--ease-sheet);
}

/* ── Cabecera de 4 elementos (ver "Cambios recientes" -- rediseño: label de
   mes tocable + 3 íconos fijos lupa/filtros/hoy, reemplaza el ícono ojo +
   pill contextual + ícono filtros del sistema de tabs anterior). Íconos:
   `.app-nav-icon-btn` (css/nav.css, mismo estándar 44×44px/radius:12px/
   ícono 18px que el resto de la app) -- `.ev-header-icon-btn` queda como
   modificador MÍNIMO (solo lo que `.app-nav-icon-btn` no aporta). */
.ev-header-row { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
/* `position:relative` ancla `.ev-filtro-badge` (el número rojo del ícono de
   filtros) -- único aporte propio, todo lo demás (tamaño/fondo/color/hover)
   viene de `.app-nav-icon-btn`. */
.ev-header-icon-btn { position: relative; }
/* Reusa el mismo modificador "activo" para los 2 íconos con burbuja propia
   (filtros/búsqueda) -- togglea con la burbuja abierta, ver _evAbrirPanel()/
   js/eventos.js. */
.ev-header-icon-btn.ev-filtro-toggle-activo { background: var(--brand); color: var(--white); }
/* Label de mes -- tocable, abre/cierra el panel de calendario. Ocupa el
   espacio restante de la fila, alineado a la izquierda. Es una fila flex
   propia (texto + chevron, ver "Cambios recientes" -- rediseño de
   navegación de Calendario, punto 1: el chevron indica "tocable para
   expandir/contraer", reemplaza toda navegación por botones). */
.ev-nav-mes-label {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 2px;
  padding: 10px 4px; margin: 0; border: none; background: transparent;
  font-size: 1rem; font-weight: 800; color: var(--text); cursor: pointer;
  font-family: inherit; transition: color 0.15s;
}
.ev-nav-mes-label span:first-child { text-transform: capitalize; }
.ev-nav-mes-chevron { font-size: 20px; color: var(--muted); transition: color 0.15s; }
.ev-nav-mes-label.ev-nav-mes-label-activo { color: var(--brand); }
.ev-nav-mes-label.ev-nav-mes-label-activo .ev-nav-mes-chevron { color: var(--brand); }

/* ── FAB de #s-eventos (solo admin) ─────────────────────
   Ver comentario HTML (index.html, hijo directo de <body>) -- reemplaza el
   "+" que vivía en .ev-header-row. `.ev-fab-menu` es el contenedor fixed
   completo (centrado igual que .app-bottom-nav/.cta-footer-fixed --
   left:50%+translateX(-50%)+max-width:523px -- para que el botón quede
   pegado a la esquina inferior derecha de la COLUMNA de la app, no del
   viewport entero en desktop ancho); `pointer-events:none` en el
   contenedor + `auto` solo en los botones reales para no robarle taps al
   contenido de abajo por el hueco vacío que deja `align-items:flex-end`. */
.ev-fab-menu {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 523px;
  bottom: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom, 0px) + 16px);
  z-index: 500;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 12px;
  padding-right: var(--space-screen);
  pointer-events: none;
}
.ev-fab-btn {
  pointer-events: auto;
  width: 56px; height: 56px; flex-shrink: 0;
  /* Bug real corregido (ver "Cambios recientes"): usaba `--fab-admin-bg`
     (ámbar, la variable `--fab-admin-*` -- eliminada de `css/colors.css`,
     sin otro consumidor tras este fix) en vez del color de énfasis de la
     app -- pasa a `var(--brand)` (mismo criterio que `.app-nav-icon-btn-brand`,
     el ícono de "Asistencia anticipada" del mismo header). Las 2 opciones
     del menú (`.ev-fab-opcion`, más abajo) TAMBIÉN pasan a `--brand` --
     quedaron un tiempo en ámbar como decisión explícita intermedia, revertida
     después a pedido de unificar el conjunto entero. Forma cuadrada con
     esquinas redondeadas (antes `50%`, circular) -- mismo `border-radius`
     que ya usan `.app-nav-icon-btn`/`.ev-header-icon-btn` (css/nav.css),
     los otros 2 botones de esa fila. */
  border: none; border-radius: 12px;
  background: var(--brand);
  color: var(--white);
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow-lg);
  cursor: pointer;
  transition: background 0.2s ease, transform 0.2s ease;
}
@media (hover: hover) { .ev-fab-btn:hover { background: var(--brand-dk); } }
.ev-fab-btn:active { transform: scale(0.94); }
.ev-fab-btn .material-symbols-outlined { font-size: 1.6rem; display: block; transition: transform 0.25s ease; }
/* Ícono "+" rota a "×" visual mientras el menú está abierto -- togglea
   junto con .ev-fab-abierto (_evFabToggle()/_evFabCerrar(), js/eventos.js). */
.ev-fab-menu.ev-fab-abierto .ev-fab-btn .material-symbols-outlined { transform: rotate(45deg); }
/* Opciones del speed dial -- SIEMPRE en el DOM (no display:none), ocultas
   con opacity+transform+visibility para poder animar la entrada/salida
   (fade+slide) apenas se suma/saca .ev-fab-abierto del contenedor padre. */
.ev-fab-opcion {
  pointer-events: none;
  visibility: hidden;
  display: flex; align-items: center; gap: 8px;
  padding: 10px 16px;
  border: none; border-radius: var(--radius-full);
  /* Antes --fab-admin-bg (ámbar, ver "Cambios recientes" -- decisión
     revertida: pedido explícito de unificar TODO el conjunto del FAB al
     mismo color de énfasis, ya usado en el botón principal). */
  background: var(--brand);
  color: var(--white);
  font-size: 0.9rem; font-weight: 700; font-family: inherit; white-space: nowrap;
  box-shadow: var(--shadow);
  cursor: pointer;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
}
@media (hover: hover) { .ev-fab-opcion:hover { background: var(--brand-dk); } }
.ev-fab-opcion .material-symbols-outlined { font-size: 1.2rem; }
.ev-fab-menu.ev-fab-abierto .ev-fab-opcion {
  pointer-events: auto;
  visibility: visible;
  opacity: 1;
  transform: translateY(0);
}
/* Pequeño stagger -- la opción más lejana del botón principal (primera en
   el DOM, ver index.html) entra/sale con un toque más de delay, cascada
   sutil en vez de las 2 apareciendo en el mismo instante exacto. */
.ev-fab-menu.ev-fab-abierto .ev-fab-opcion:first-child { transition-delay: 0.04s; }

.ev-dot {
  width: 6px; height: 6px; border-radius: 50%; background: var(--brand); flex-shrink: 0;
  cursor: pointer;
}
.ev-dot-cumple {
  width: 6px; height: 6px; border-radius: 50%; background: var(--cumple-dot); flex-shrink: 0;
  cursor: pointer;
}
/* Temporada de descanso (Tanda B, ver MANIFEST.md "Cambios recientes") --
   mismo tamaño/forma que `.ev-dot`/`.ev-dot-cumple`, coexiste con cualquiera
   de los 2 en la misma celda (`.ev-cal-dots` ya es `display:flex`, sin
   límite de hijos) -- un día puede tener evento Y caer dentro de un período
   de descanso a la vez. `--offseason-border` (colors.css) en vez de
   `--offseason-bg` -- acá hace falta un tono con más cuerpo que el fondo
   tenue de la card, mismo criterio que `--cumple-dot` (deriva de `--brand`
   sólido, no de `--cumple-bg`, la mezcla tenue). */
.ev-dot-offseason {
  width: 6px; height: 6px; border-radius: 50%; background: var(--offseason-border); flex-shrink: 0;
  cursor: pointer;
}

/* ── Panel de calendario -- swipe-first, sin chevrones/borde/título repetido
   (ver "Cambios recientes" -- rediseño completo de la navegación de
   Calendario). `#ev-cal-contenido` SIEMPRE muestra la grilla mensual
   completa (ver "Cambios recientes" -- se elimina el estado intermedio de
   franja semanal compacta que existía antes; solo expandido/cerrado, ver
   `_evCalVisible` en js/eventos.js) vía fade de contenido al cambiar de mes;
   el techo del `.ev-header-burbuja` exterior se re-mide en cada cambio
   (ver `_evCalActualizarMaxHeightExterior()`) CON la misma transición de
   `max-height` que abre/cierra el panel entero (ver "Cambios recientes" --
   pedido explícito: un mes de 5 semanas vs. uno de 6 debe animarse, no
   saltar de golpe).
   `padding-top` separa el contenido del renglón de íconos de arriba, sin el
   marco/borde que tenía antes (`.ev-header-burbuja-panel` ya NO se usa acá,
   ver index.html). ────────────────────────────────────────────────────── */
.ev-cal-contenido { padding-top: 8px; }
.ev-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.ev-cal-dow { text-align: center; font-size: 0.62rem; font-weight: 700; color: var(--muted); text-transform: uppercase; padding-bottom: 6px; }
.ev-cal-celda {
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
  gap: 4px; padding: 6px 0 8px; border-radius: 10px; min-height: 46px; cursor: pointer;
  /* Fade del resaltado de selección (.ev-ant-cal-sel/.ev-ant-cal-en-rango,
     más abajo en este archivo) -- en la base, no en esas clases, para que
     aplique sin importar cuál se agregue/quite. Mismos 0.2s que ya usa el
     fade coordinado de pills+botón "Restablecer" (_evAntCalRestablecer(),
     js/eventos.js) -- ver ese comentario para el porqué: _evAntCalRender()
     reconstruye la grilla entera (innerHTML) en cada toque, así que un
     transition acá NO alcanza por sí solo con ese flujo normal (nodos
     nuevos, sin estado "antes" real de qué animar) -- alcanza porque
     _evAntCalRestablecer() ahora quita las clases de las celdas YA
     renderizadas primero, y recién re-renderiza 200ms después. */
  transition: background-color 0.2s ease;
}
.ev-cal-celda.ev-ajeno { opacity: 0.32; }
.ev-cal-num {
  width: 26px; height: 26px; border-radius: 50%; display: flex; align-items: center;
  justify-content: center; font-size: 0.78rem; font-weight: 700; color: var(--text);
  transition: background 0.15s, color 0.15s, outline-color 0.15s;
}
/* "Hoy" (relleno, fijo -- siempre el mismo día real) vs. "fecha
   seleccionada" (anillo, dinámico -- el último día al que se saltó
   explícitamente: tocando una celda de la grilla o el ícono "hoy", nunca por
   navegar de mes con swipe/pill ni por scrollear el timeline a mano -- ver
   "Cambios recientes", `_evCalFechaSeleccionada` en js/eventos.js). Si
   coinciden (fecha seleccionada = hoy) se aplica SOLO el relleno -- `esHoy`
   pisa a `esSeleccionado` en el JS antes de pintar la celda, así que ambas
   clases nunca conviven en el mismo elemento. */
.ev-cal-celda.ev-dia-hoy .ev-cal-num { background: var(--brand); color: var(--white); font-weight: 800; }
.ev-cal-celda.ev-dia-seleccionado .ev-cal-num { outline: 1.5px solid var(--brand); outline-offset: 1.5px; color: var(--brand); font-weight: 800; }
.ev-cal-dots { display: flex; gap: 3px; height: 6px; align-items: center; }
/* Selector horizontal de mes/año en pills (ver "Cambios recientes" -- ahora
   TODOS los 12 meses de una ventana de 2 años alrededor de hoy, no solo los
   que tienen eventos, reusa literal `.historial-pill` de css/home.css,
   mismo patrón de scroll horizontal sin barra visible que
   `#historial-pills-anio`). `.ev-mes-pill-corte` -- corte visual de año
   (línea vertical + año chico), en vez de repetir el año en cada pill. */
/* Ocultamiento de scrollbar ya no hace falta acá -- universal (`*`,
   css/global.css, ver "Cambios recientes"); `-ms-overflow-style` queda (no
   tiene equivalente en la regla universal, target distinto -- IE/Edge viejo).
   Máscara de degradado en ambos bordes (ver "Cambios recientes" -- pedido
   explícito) -- la pill que queda parcialmente cortada por el borde del
   contenedor se desvanece en vez de cortarse en seco; 20px de fade en cada
   punta, resto 100% opaco. `-webkit-mask-image` primero (Safari todavía
   necesita el prefijo para esta propiedad). */
/* `transition: opacity` (ver "Cambios recientes" -- fade LOCAL de esta fila
   al cambiar de mes con el calendario ya abierto, `_evCalCambiarMes()` en
   js/eventos.js): las pills viven al FINAL del contenido del panel,
   después de la grilla, así que mientras `max-height` del panel exterior
   todavía no llegó a su valor final, el borde de recorte del acordeón pasa
   justo por el medio de esta fila -- fadearla localmente (en vez de todo el
   panel, que se probó y se revirtió, "se sentía raro, sin continuidad" con
   el resto del cambio de mes) tapa ese corte sin tocar el "empuje" animado
   de la grilla, que sigue igual que siempre. SOLO corre cuando la cantidad
   de semanas de la grilla realmente cambia (`_evCalContarSemanas()`) -- si
   el mes viejo y el nuevo tienen la misma, el panel no cambia de alto y no
   hace falta ningún fade (ver `_evCalMoverFillPill()`, cirugía de clases +
   la transición de `.historial-pill` de abajo en vez de esto). `0.2s` (ver
   "Cambios recientes" -- subido desde `0.13s`, pedido explícito: a esa
   velocidad se sentía como un corte/glitch en vez de un fade intencional --
   mismo valor para afuera y adentro, no hace falta asimetría). `_evCalCambiarMes()`
   ESPERA a que este fade-out termine de verdad (`transitionend` de
   `opacity`) antes de repintar contenido -- si se repintara en el mismo
   tick que se dispara el fade, lo que en realidad se vería desvanecerse
   sería el contenido NUEVO (ya en el DOM) en vez del viejo. */
.ev-mes-pills-row {
  display: flex; align-items: center; gap: 8px; overflow-x: auto; padding: 14px 2px 2px; -ms-overflow-style: none;
  -webkit-mask-image: linear-gradient(to right, transparent, black 20px, black calc(100% - 20px), transparent);
  mask-image: linear-gradient(to right, transparent, black 20px, black calc(100% - 20px), transparent);
  transition: opacity 0.2s ease;
}
.ev-mes-pill-corte {
  flex-shrink: 0; display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 0 4px; align-self: stretch; justify-content: center;
}
.ev-mes-pill-corte span { font-size: 0.62rem; font-weight: 700; color: var(--muted); }
/* `.historial-pill` (css/home.css) ya trae `transition: all 0.2s` -- cubre
   `background-color`/`color` (los únicos 2 que cambian al togglear
   `.activa`) de por sí, pero acá se fija explícito y un poco más rápido
   (0.15s) para el Camino B de `_evCalCambiarMes()` (mismo cantidad de
   semanas, sin fade de la fila): al mover `.activa` de una pill a la otra
   con cirugía puntual (`_evCalMoverFillPill()`), el relleno se ve
   deslizarse/transicionar de una a otra en vez de un cambio seco --
   pedido explícito. Ningún otro uso de `.historial-pill` (año de Mis
   Reservas, js/home.js) togglea una 3ra propiedad que dependiera del
   `all` genérico, así que acotar la lista acá no rompe nada más. */
.historial-pill { transition: background-color 0.15s ease, color 0.15s ease; }

/* ── Timeline único (ver "Cambios recientes" -- reemplaza `.ev-lista` +
   `.ev-fecha-label` por texto: cada fecha lleva un badge lateral tipo Google
   Calendar en vez de un rótulo de texto arriba). ─────────────────────── */
.ev-timeline { margin-top: 20px; text-align: left; }
/* Encabezado de mes ("AGOSTO 2026") -- separador de sección dentro del
   scroll, más peso visual que el badge de fecha (marca un cambio de mes,
   no de día) -- leído por `_evActualizarNavMesPorScroll()` (js/eventos.js,
   `data-anio`/`data-mes`) para mantener el label de la nav fija sincronizado
   con la sección que se está scrolleando. */
.ev-mes-header {
  font-size: 0.8rem; font-weight: 800; color: var(--brand); text-transform: uppercase;
  letter-spacing: 1px; margin: 22px 0 14px; padding-left: 2px;
}
.ev-mes-header:first-child { margin-top: 0; }
/* Fila fecha: badge fijo a la izquierda (día+número, ver
   `.ev-fecha-badge*` más abajo) + columna de cards a la derecha. */
/* `scroll-margin-top` via CSS var (ver "Cambios recientes" -- antes 90px
   fijo, roto con el calendario expandido: `_evScrollAFecha()`, js/eventos.js,
   la actualiza al alto REAL de la cabecera sticky justo antes de cada
   scroll; 90px es solo el default antes de la primera actualización). */
.ev-fecha-grupo { display: flex; gap: 12px; margin-bottom: 18px; scroll-margin-top: var(--ev-sticky-h, 90px); }
.ev-fecha-badge { flex-shrink: 0; width: 38px; display: flex; flex-direction: column; align-items: center; gap: 4px; padding-top: 2px; }
.ev-fecha-badge-dia { font-size: 0.62rem; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: 0.4px; }
.ev-fecha-badge-num {
  width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  font-size: 0.85rem; font-weight: 700; color: var(--text); border: 1.5px solid transparent;
}
.ev-fecha-badge-num.ev-fecha-badge-hoy { background: var(--brand); color: var(--white); }
.ev-fecha-items { flex: 1; min-width: 0; }
.ev-fecha-items > *:last-child { margin-bottom: 0; }
.ev-lista-vacia {
  text-align: center; padding: 40px 16px; color: var(--muted); font-size: 0.85rem;
  background: var(--surface-2); border: 1.5px solid var(--border-light); border-radius: 14px;
}
.ev-lista-vacia .material-symbols-outlined { font-size: 2.2rem; display: block; margin-bottom: 8px; color: var(--brand); }
/* Separador HOY/MAÑANA/PRÓXIMA SEMANA (ver "Cambios recientes" -- restaurado,
   mismo componente que existía antes de unificar las vistas del timeline). */
/* `scroll-margin-top` (ver "Cambios recientes" -- bug real: el separador
   -HOY- quedaba parcialmente tapado por la cabecera sticky al tocar el
   ícono "hoy", porque solo `.ev-fecha-grupo` tenía este margen, nunca el
   separador que vive justo arriba en el flujo) -- mismo criterio dinámico
   que ya usa `.ev-fecha-grupo` (`_evScrollAFecha()`, js/eventos.js). */
.ev-hoy-separador { display: flex; align-items: center; gap: 10px; margin: 4px 0 14px; scroll-margin-top: var(--ev-sticky-h, 90px); }
.ev-hoy-separador::before, .ev-hoy-separador::after { content: ''; flex: 1; height: 1.5px; background: var(--border-light); }
.ev-hoy-separador span { font-size: 0.72rem; font-weight: 800; color: var(--brand); text-transform: uppercase; letter-spacing: 1px; }
/* ── Card de evento ───────────────────────────────────── */
.ev-card {
  padding: 14px; border-radius: 14px; margin-bottom: 10px;
  background: var(--surface-2); border: 1.5px solid var(--border-light);
  animation: smoothSlideUp 0.35s var(--ease-sheet) both;
}
/* Fila superior (ícono+título+hora a la izquierda, ver "Cambios recientes" --
   revertido a este layout: 2 tandas atrás el ícono había pasado al badge de
   fecha compartido del timeline y la acción a una columna lateral, ambos
   cambios revertidos por pedido explícito). Antes esto era `.ev-card` mismo
   (`display:flex`); se separa en un hijo propio para que otro contenido a
   ancho completo (ej. el aviso de estado) pueda vivir como OTRO hijo de
   `.ev-card`, debajo de esta fila, en vez de forzar a `.ev-card` entero a
   acomodar 2 layouts distintos con una sola regla. Reusada tal cual por
   `_evCardCumpleHtml()` (avatar+body) -- mismo rol, ningún cambio visual
   para esa card. */
.ev-card-top-row { display: flex; gap: 12px; align-items: center; }
.ev-card-icon {
  width: 42px; height: 42px; min-width: 42px; border-radius: 12px; background: var(--brand-light);
  display: flex; align-items: center; justify-content: center; color: var(--brand);
}
.ev-card-icon .material-symbols-outlined { font-size: 1.3rem; }
.ev-card-body { flex: 1; min-width: 0; }
.ev-card-titulo { font-size: 0.9rem; font-weight: 800; color: var(--text); margin-bottom: 2px; }
/* Ícono de tipo INLINE junto al título (ver "Cambios recientes" -- restaurado:
   reemplaza `.ev-card-icon` cuadrado en columna propia -- sin esa columna,
   `.ev-card-body` ocupa todo el ancho disponible de `.ev-card-top-row`.
   `.ev-card-icon` sigue existiendo tal cual solo para la fila compacta de
   "Pasados"; `_evCardCumpleHtml()` usa este mismo patrón inline (ícono
   `cake`). `margin-bottom` se muda de `.ev-card-titulo` a esta fila --
   adentro, el título no lleva margen propio para no descentrarse del
   ícono. Sin chevron -- la card entera ya es tocable. */
.ev-card-titulo-row { display: flex; align-items: center; gap: 5px; margin-bottom: 2px; }
.ev-card-titulo-row .ev-card-titulo { margin-bottom: 0; }
.ev-card-icono-inline { font-size: 16px; color: var(--brand); flex-shrink: 0; }
.ev-card-sub { font-size: 0.78rem; color: var(--muted); display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.ev-card-sub .material-symbols-outlined { font-size: 14px; }
/* Card de temporada de descanso (Tanda B, ver MANIFEST.md "Cambios
   recientes") -- mismo `.ev-card` base (ancho completo, radio, animación de
   entrada), solo cambia fondo/borde (`--offseason-bg`/`--offseason-border`,
   colors.css) para distinguirse de una card de evento normal sin competir
   con ella. `.ev-card-icono-inline` hereda `color:var(--brand)` por default
   (regla de arriba) -- acá se pisa a `--muted` para que se lea "apagada"
   (pedido explícito, junto con la opacidad levemente reducida de la card
   entera). No interactiva para usuarios normales -- sin `onclick` propio, a
   diferencia de cualquier otra card de este timeline. Con `_adminToken`
   presente sí suma 2 botones de acción (editar/borrar, ver "Cambios
   recientes") reusando literal `.ev-ant-card-edit`/`.ev-ant-card-del` de
   Asistencia anticipada -- ningún CSS nuevo para esto. */
.ev-card-offseason { background: var(--offseason-bg); border-color: var(--offseason-border); opacity: 0.92; }
.ev-card-offseason .ev-card-icono-inline { color: var(--muted); }
/* Pill grande de estado -- geometría COMPARTIDA (ver "Cambios recientes":
   antes solo Cancelado/No se entrena, `_evEstadoNotaPillHtml()`; ahora
   también "Llegué a horario"/"Llegué tarde", `_evAsistenciaRealHtml()`,
   mismas 2 funciones en js/eventos.js -- pedido explícito: mismo tamaño/
   forma entre los 4 estados posibles, solo cambia color+ícono+texto). UN
   SOLO componente de geometría reusado tal cual en la card
   (`_evCardEventoHtml()`, dentro de `.ev-card-body`, ancho completo, debajo
   de título/hora -- mismo tratamiento que admin/pasado) y en el detalle
   (`#ev-detalle-rsvp`, mismo ancho completo de esa sección) -- el color va
   en un modificador aparte (`.ev-estado-pill-danger`/`-success`/`-warning`,
   justo abajo), nunca hardcodeado acá. */
.ev-estado-pill {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  width: 100%; margin-top: 10px; padding: 9px 14px; border-radius: 12px;
  border: 1px solid transparent; font-size: 0.82rem; font-weight: 700;
}
.ev-estado-pill .material-symbols-outlined { font-size: 18px; }
#ev-detalle-rsvp .ev-estado-pill { margin-top: 0; }
/* Cancelado/No se entrena -- mismo tono rojo de advertencia de siempre. */
.ev-estado-pill-danger { background: var(--danger-bg); color: var(--danger); border-color: var(--danger-bdr); }
/* Llegué a horario -- mismo verde que `--success` usa en el resto de la app. */
.ev-estado-pill-success { background: var(--success-bg); color: var(--success); border-color: var(--success-bdr); }
/* Llegué tarde -- mismo ámbar/naranja que `--warning` usa en el resto de la app. */
.ev-estado-pill-warning { background: var(--warning-bg); color: var(--warning); border-color: var(--warning-bdr); }

/* ── "¿Asistiré?" (usuario) — barra segmentada única ──────────────────
   Reemplaza los 3 botones-pill independientes (con borde propio cada uno,
   ver "Cambios recientes") por un solo control: un contenedor (.ev-rsvp-seg)
   con un indicador que se desliza (.ev-rsvp-slider, transform:translateX,
   mismo mecanismo que .tp-slider/.tp-seg de css/reservas.css) en vez de 3
   botones separados. Un único componente (_evRsvpBarraHtml()/
   _evPosicionarRsvpSlider(), js/eventos.js), parte de la card completa
   (_evCardEventoHtml()) reusada tal cual en Semana/Calendario/Lista (ver
   "Cambios recientes" — la variante `.ev-rsvp-seg-compacta` que existió acá
   se sacó al rediseñar la card: ya no hay una fila resumida aparte que la
   necesitara). Tamaño compacto (ajuste final, ver "Cambios recientes"):
   ícono chico arriba + texto chico abajo, mismas medidas exactas en los 3
   sitios (un solo bloque CSS, sin overrides por vista). */
.ev-asistire-wrap { margin-top: 12px; }
.ev-rsvp-seg {
  position: relative; display: flex; background: var(--surface-light);
  border: 1.5px solid var(--border-light); border-radius: 16px; overflow: hidden;
}
/* El fondo del indicador (verde/rojo/violeta ATENUADO según la opción
   activa, ver "Cambios recientes" -- antes un fill sólido de color puro con
   texto blanco encima, corregido para usar el mismo lenguaje que el resto
   de badges/chips de la app: fondo tenue + texto/ícono saturado, nunca
   texto blanco sobre un color plano) se fija por JS (_EV_RSVP_BG,
   js/eventos.js) -- mismos 3 tokens `-bg` fijos de siempre, independientes
   del color de énfasis (mismo criterio documentado en "Cambios recientes":
   "Asistiré" sigue siendo verde pase lo que pase con --brand). `opacity:0`
   inicial evita un indicador en (0,0) visible antes de la primera medición
   real. El texto/ícono saturado de la opción activa NO se fija acá por JS
   -- va en la regla `.ev-rsvp-opt.activa[data-estado=...]` más abajo, un
   color fijo por estado (mismo criterio que el resto de esta barra). */
.ev-rsvp-slider {
  position: absolute; top: 3px; bottom: 3px; left: 0; width: 0; border-radius: 13px;
  z-index: 0; opacity: 0;
}
.ev-rsvp-slider.animado {
  transition: transform 0.32s cubic-bezier(.16,1,.3,1), width 0.32s cubic-bezier(.16,1,.3,1),
    background-color 0.18s ease, opacity 0.18s ease;
}
/* Ícono arriba, texto abajo (ver "Cambios recientes") -- flex-direction:column
   sobre el mismo HTML (ícono antes que texto en el marcado, sin cambios),
   compacto a propósito, mismo componente reusado tal cual en la card de
   vista previa y en la pantalla de detalle. La opción activa/inactiva
   transiciona color (y background-color, por si algún día un botón llega a
   tener fondo propio en vez de compartir el del slider) en 0.18s -- rango
   pedido de 150-200ms, en vez de un cambio instantáneo. */
.ev-rsvp-opt {
  position: relative; z-index: 1; flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 2px; padding: 12px 4px; font-size: 0.625rem; font-weight: 700;
  cursor: pointer; font-family: inherit; white-space: nowrap;
  transition: color 0.18s ease, background-color 0.18s ease;
}
/* Gris apagado fijo (ver "Cambios recientes" -- bug real: `var(--text-2)`
   es suficientemente claro en dark mode (`#C9A090`) que se leía casi blanco
   contra el fondo oscuro de `.ev-rsvp-seg`, no lo esperado. Mismo criterio
   que `_EV_RSVP_BG` (verde/rojo/violeta, js/eventos.js): color FIJO,
   independiente del tema/color de énfasis -- solo la opción que coincide
   con `e.miEstado` lleva color (`.activa`, ver las 3 reglas de abajo --
   ícono+texto en el tono SATURADO de ese color, ver "Cambios recientes" --
   antes blanco sobre el indicador sólido), las otras 2 siempre en este
   gris. */
.ev-rsvp-opt { color: #c98a8a; }
.ev-rsvp-opt .material-symbols-outlined { font-size: 18px; flex-shrink: 0; color: #8a6a6a; }
.ev-rsvp-opt.activa[data-estado="Asistiré"], .ev-rsvp-opt.activa[data-estado="Asistiré"] .material-symbols-outlined { color: var(--success); }
.ev-rsvp-opt.activa[data-estado="No asistiré"], .ev-rsvp-opt.activa[data-estado="No asistiré"] .material-symbols-outlined { color: var(--danger); }
.ev-rsvp-opt.activa[data-estado="No jugador"], .ev-rsvp-opt.activa[data-estado="No jugador"] .material-symbols-outlined { color: var(--purple); }
/* Slider de 2 estados del roster admin (`.ev-rsvp-seg-roster`, ver esa
   clase más abajo) -- mismos 2 colores que ya usa el resto del archivo para
   este mismo par de estados (`.ev-stat-asisten`/`.ev-stat-no-jugador` en las
   tarjetas de estadística, `.ev-estado-pill-success`/`-warning` en la pill
   de la propia persona). */
.ev-rsvp-opt.activa[data-estado="A tiempo"], .ev-rsvp-opt.activa[data-estado="A tiempo"] .material-symbols-outlined { color: var(--success); }
.ev-rsvp-opt.activa[data-estado="Tarde"], .ev-rsvp-opt.activa[data-estado="Tarde"] .material-symbols-outlined { color: var(--amber-dark); }


/* ── Reemplazo de solo lectura de la barra de RSVP para eventos pasados
   (fecha < hoy o Estado = Finalizado, ver _evRsvpBarraHtml()) -- mismo
   `.badge`/variantes de color ya usados en la fila compacta de "Pasados",
   solo agrandado a ancho completo para ocupar el lugar de la barra
   editable que reemplaza. Sin interacción (no es un botón). */
/* `text-transform:none` (ver "Cambios recientes") -- `.badge` base es
   uppercase (css/ui.css), acá se lee en sentence case ("Llegué a horario",
   pedido explícito) -- scoped a esta clase, no toca `.badge` en sí (otras
   pantallas sí quieren mayúsculas). */
.ev-rsvp-readonly { display: flex; width: 100%; justify-content: center; padding: 10px; border-radius: 16px; font-size: 0.78rem; text-transform: none; }

/* ── Card de evento (ver "Cambios recientes": la card entera es tocable,
   navega a la pantalla de detalle -- la fila de avatares y "Más información"
   que vivían acá se mudaron ahí). Sin chevron propio (ver "Cambios
   recientes" -- eliminado de las 3 vistas, `.ev-card-chevron` sin más
   consumidores): la afordancia de "esto navega" es solo `cursor:pointer` +
   el estilo de card clickeable ya establecido en el resto de la app. Excluye
   .ev-card-cumple a propósito: la card de cumpleaños no lleva onclick,
   cursor:pointer ahí sería una afordancia falsa. */
.ev-card:not(.ev-card-cumple) { cursor: pointer; }

/* ── Variante admin: lista de asistentes de la CARD (chips, acordeón
   colapsable) + roster de gestión del DETALLE (buscador + sliders, más
   abajo) + "Agregar persona" (bottom sheet remoto, usado por la card).
   ──────────────────────────────────────────────────────────────────── */
.ev-asistentes-list { margin-top: 10px; }
/* Acordeón de la lista de nombres de la CARD (ver "Cambios recientes" --
   colapsada por default): mismo TIPO de mecanismo que `.admin-dash-banner-*`
   (css/admin.css, `adminToggleBanner()`) y `.ev-ant-acc-*` (más abajo en
   este archivo) -- techo fijo generoso en vez de medir `scrollHeight` en
   cada toggle, ver `_evAsistAdminToggle()`/js/eventos.js. **Sin cambios**
   en esta sesión -- una tanda anterior había reemplazado el CONTENIDO de
   este acordeón por el roster+buscador (ver más abajo), revertido acá: el
   contenido vuelve a ser la lista de chips (`.ev-asistente-row`, más abajo)
   + "Agregar persona" (`.ev-btn-agregar-persona`, más abajo). */
.ev-asist-admin-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 4px 2px; cursor: pointer; }
.ev-asist-admin-header-titulo { font-size: 0.78rem; font-weight: 700; color: var(--text-2); }
.ev-asist-admin-chevron { color: var(--muted); font-size: 1.1rem; transition: transform 0.3s; flex-shrink: 0; }
.ev-asist-admin-header.abierto .ev-asist-admin-chevron { transform: rotate(180deg); }
.ev-asist-admin-body { max-height: 0; overflow: hidden; transition: max-height 0.35s cubic-bezier(0.16,1,0.3,1), opacity 0.3s ease; opacity: 0; }
.ev-asist-admin-body.abierto { max-height: 2000px; opacity: 1; }
.ev-asist-admin-body-inner { display: flex; flex-direction: column; gap: 6px; padding-top: 6px; }
.ev-asistente-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 6px 0; border-bottom: 1px solid var(--border-light); }
.ev-asistente-row:last-child { border-bottom: none; }
.ev-asistente-nombre { font-size: 0.8rem; font-weight: 600; color: var(--text); }
/* Roster completo de la subpantalla dedicada "Marcar asistencia" (ver
   "Cambios recientes" -- diseño final, #s-eventos-marcar-asistencia,
   index.html): buscador (`.app-nav-search`, reusado tal cual del resto de
   la app -- ver css/nav.css, sin clase propia acá) + `.ev-marcar-lista` con
   cada fila (`.ev-roster-fila`/`.ev-roster-nombre`, reusadas del roster
   original) de todo el equipo. Sin techo/scroll propio -- a diferencia del
   diseño intermedio que vivió en el acordeón del detalle, esta es una
   pantalla de página completa, scrollea con el resto del contenido. */
.ev-marcar-lista { display: flex; flex-direction: column; }
.ev-roster-fila { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 7px 0; border-bottom: 1px solid var(--border-light); }
.ev-roster-fila:last-child { border-bottom: none; }
.ev-roster-nombre { font-size: 0.8rem; font-weight: 600; color: var(--text); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ev-roster-vacio { padding: 14px 4px; color: var(--muted); font-size: 0.78rem; text-align: center; }
/* Slider de 2 estados por persona -- reusa LITERAL `.ev-rsvp-seg`/
   `.ev-rsvp-opt`/`.ev-rsvp-slider` (mismo componente que la barra "¿Asistiré?"
   de 3 opciones, más arriba), esta clase es solo el modificador de tamaño
   para que quepa angosto al lado del nombre en vez de ocupar el ancho de la
   card entera. */
.ev-rsvp-seg-roster { width: 148px; flex-shrink: 0; }
.ev-rsvp-seg-roster .ev-rsvp-opt { padding: 7px 4px; font-size: 0.6rem; }
.ev-rsvp-seg-roster .ev-rsvp-opt .material-symbols-outlined { font-size: 15px; }
.ev-btn-agregar-persona {
  /* margin-bottom, no margin-top (ver "Cambios recientes" -- reposicionado
     arriba de "Asistencia (N)"): ahora es el primer hijo de
     .ev-asistentes-list, el espacio de arriba ya lo da el margin-top del
     contenedor; acá hace falta el espacio de ABAJO, antes del header. */
  margin-bottom: 10px; width: 100%; padding: 9px; border-radius: 10px; border: 1.5px dashed var(--border-mid);
  background: transparent; color: var(--muted); font-size: 0.76rem; font-weight: 700; cursor: pointer;
  font-family: inherit; display: flex; align-items: center; justify-content: center; gap: 5px; transition: all 0.15s;
}
.ev-btn-agregar-persona:hover { border-color: var(--brand); color: var(--brand); background: var(--brand-lightest); }
.ev-btn-agregar-persona .material-symbols-outlined { font-size: 16px; }
.ev-persona-row {
  display: flex; align-items: center; gap: 10px; padding: 11px 16px; cursor: pointer;
  font-size: 0.85rem; font-weight: 600; color: var(--text); transition: background 0.12s;
}
.ev-persona-row:hover { background: var(--surface-2); }
.ev-persona-row .material-symbols-outlined { font-size: 18px; color: var(--brand); }

/* ── Card de cumpleaños ───────────────────────────────── */
/* Ver "Cambios recientes" -- pedido explícito de Victor: la card en sí ya no
   lleva fondo/borde propios (`--cumple-bg`), hereda el mismo `background`/
   `border` que cualquier `.ev-card` normal. `position:relative` +
   `overflow:hidden` SÍ se conservan tal cual -- el confetti
   (`#ev-confetti-<id>`, `_evLanzarCumpleConfetti()`) depende de que este sea
   el containing block de su canvas `position:absolute;inset:0` para quedar
   contenido dentro de la card en vez de superponerse a toda la pantalla. */
.ev-card-cumple { position: relative; overflow: hidden; }
/* Avatar circular + ícono `cake` inline junto al título (ver "Cambios
   recientes" -- reemplaza el `.ev-card-icon` cuadrado propio + el título en
   color fijo `--cumple-border`/`--cumple-text` que tenía antes): mismo
   tratamiento exacto que cualquier `.ev-card` de evento normal
   (`.ev-card-titulo-row`/`.ev-card-icono-inline`/`.ev-card-titulo` en
   `var(--text)`, sin clase de color propia para cumpleaños) -- `46×46`,
   mismo tamaño que ya usa `.aj-avatar-row` en Ajustes (`css/perfil.css`)
   para el mismo componente (`.avatar-pill`, `css/global.css`). Hidratado por
   `_evHidratarAvatares()` (`js/eventos.js`), no por CSS. */
.ev-card-cumple-avatar { width: 46px; height: 46px; }

/* ── Pantalla de detalle de evento (#s-eventos-detalle, ver "Cambios
   recientes" — reemplaza el bottom sheet + la modal de asistentes de la
   tanda anterior por una pantalla de pantalla completa). ──────────────── */
.ev-detalle-section { margin-bottom: 22px; }
.ev-detalle-section:last-child { margin-bottom: 0; }
/* Mitad del gap genérico de `.ev-detalle-section` -- pedido puntual para
   achicar SOLO la separación entre la descripción y la barra de RSVP que
   sigue, sin tocar el resto de las secciones (que comparten esa misma clase
   y deben seguir en 22px). Scoped por id, mismo criterio que
   `#ev-detalle-info .fi-pills` arriba. */
#ev-detalle-info { margin-bottom: 11px; }
/* Nav compacta sticky (ver "Cambios recientes" -- reemplaza el #top-bar
   genérico solo para esta pantalla, ver TOP_BAR_CONFIG/js/ui.js): vive
   afuera de `.card` (hermano directo dentro de `.contenedor`, mismo criterio
   que #top-bar -- fondo constante de página, no el de una tarjeta), pegada
   arriba con `position:sticky`. Sin `box-shadow` (ver "Cambios recientes" --
   sacado a pedido explícito, la sombra hacia abajo que separaba del
   contenido que pasa por detrás al scrollear no se quería más). */
.ev-detalle-sticky { position: sticky; top: 0; z-index: 20; background: var(--bg); padding: 16px 0 14px; }
/* Niveles 2 y 3 del sticky apilado (ver "Cambios recientes" -- barra de RSVP
   y grid de estadísticas, cada uno fijo justo debajo del anterior). A
   diferencia de `.ev-detalle-sticky` (afuera de `.card`, fondo `--bg`
   constante), estos 2 SÍ viven dentro de `.card` -- mismo criterio de
   breakpoint que `.ev-sticky-header` (sección Eventos, arriba en este mismo
   archivo): `--card-bg` por default, `--bg` en mobile real (≤600px, donde
   `.card` pierde su propio fondo). `top` se fija por JS
   (`_evDetalleActualizarSticky()`, js/eventos.js) a partir de la altura
   real renderizada del nivel anterior -- nunca un valor fijo acá. `padding-
   bottom` (no `margin-bottom`) para que el espaciado quede DENTRO de la caja
   pintada (con fondo), evitando un hueco transparente al quedar pegado. */
.ev-detalle-rsvp-sticky, .ev-detalle-stats-sticky {
  position: sticky; background: var(--card-bg); margin-bottom: 0; padding-bottom: 12px; padding-top: 1px;
}
.ev-detalle-rsvp-sticky { z-index: 15; }
.ev-detalle-stats-sticky { z-index: 10; }
@media (max-width: 600px) {
  .ev-detalle-rsvp-sticky, .ev-detalle-stats-sticky { background: var(--bg); }
}
.ev-detalle-nav-row { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
/* Ícono de tipo SUELTO (pedido explícito, ver "Cambios recientes"): sin el
   cuadrado de fondo que tenía antes (`.ev-detalle-icon-grande`, eliminada) --
   compite menos con el botón circular de la flecha atrás al lado. */
.ev-detalle-nav-icono { color: var(--brand); font-size: 22px; flex-shrink: 0; }
/* `flex:1` (ver MANIFEST.md "Cambios recientes" -- ícono "Editar evento" del
   sticky nav): antes solo `min-width:0`, sin crecer -- suficiente cuando
   `.ev-detalle-nav-row` no tenía nada después. Con el `.app-nav-actions` del
   ícono `edit` (admin, `_evDetalleStickyHtml()`) como hermano siguiente,
   este bloque necesita ocupar el resto del ancho disponible para empujarlo
   al borde derecho de la fila -- mismo criterio que `.app-nav` genérica
   (`justify-content:space-between`), acá resuelto con `flex:1` porque
   `.ev-detalle-nav-row` no usa ese layout. */
.ev-detalle-nav-texto { min-width: 0; flex: 1; }
/* `font-size`/`letter-spacing` igualados a `.app-nav-title` (css/nav.css,
   ver "Cambios recientes" -- auditoría de consistencia de nav: antes
   0.74rem/0.6px, más chico que el estándar 0.86rem/1.2px que ya comparten
   el resto de los títulos de nav de la app -- `font-weight`/`color`/
   `text-transform` ya coincidían). No se reusa la clase `.app-nav-title`
   tal cual (posicionamiento `absolute` + padding pensados para la fila
   `.app-nav` genérica, layout distinto al de `#ev-detalle-sticky`) -- se
   iguala el tamaño real, no la mecánica de posicionamiento. */
.ev-detalle-tipo {
  font-size: 0.86rem; font-weight: 800; color: var(--brand); text-transform: uppercase; letter-spacing: 1.2px;
}
.ev-detalle-fechahora { font-size: 0.85rem; color: var(--text-2); font-weight: 600; margin-top: 2px; }
/* Pills -- ver "Cambios recientes" -- rediseño, corrección sobre un intento
   anterior: reuso LITERAL de `.fi-pill`/`.fi-pills`/`.fi-pill-hora`/
   `.fi-pill-lugar`/`.fi-pill-maps`/`.fi-pill-fin`/`.fi-pill-dur`
   (css/reservas.css, panel "Más información" de Reservas) -- cero clases
   `.ev-detalle-pill*` propias, para que las 2 pantallas queden
   pixel-idénticas (tamaño/padding/color/radio de borde) en vez de
   "parecidas" con valores aproximados a mano. Solo el espaciado contra la
   descripción que sigue (`.fi-pills` en Reservas no lo necesita -- ahí
   siempre es el último elemento de `.fi-content`) se resuelve acá, scoped
   al contenedor real de esta pantalla, sin tocar `.fi-pills` en sí. */
#ev-detalle-info .fi-pills { margin-bottom: 14px; }
.ev-detalle-desc { font-size: 0.85rem; color: var(--text-2); line-height: 1.65; margin: 0; }
.ev-detalle-titulo-seccion { font-size: 1rem; font-weight: 800; color: var(--text); margin-bottom: 12px; }
/* Grid de 4 tarjetas de estadística (ver "Cambios recientes" -- reemplaza
   la línea de texto "Asisten X · No asisten X..."), cada una tocable para
   filtrar la lista de abajo (_evFiltrarAsistenciaPorGrupo(), js/eventos.js).
   `.activa` marca cuál está filtrando -- borde + fondo del mismo color que
   ya tiene el número, mayor contraste que el resto. */
.ev-stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-bottom: 20px; padding-top: 3px; }
.ev-stat-card {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  padding: 12px 4px; border-radius: 12px; cursor: pointer; border: 1.5px solid transparent;
  background: var(--surface-light); transition: all 0.15s;
}
.ev-stat-num { font-size: 1.3rem; font-weight: 800; line-height: 1; }
.ev-stat-label { font-size: 0.6rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.3px; color: var(--muted); text-align: center; }
.ev-stat-asisten .ev-stat-num { color: var(--success-dark); }
.ev-stat-no-asisten .ev-stat-num { color: var(--danger); }
.ev-stat-no-jugador .ev-stat-num { color: var(--amber-dark); }
.ev-stat-sin-respuesta .ev-stat-num { color: var(--muted); }
.ev-stat-asisten.activa { border-color: var(--success-dark); background: var(--success-bg); }
.ev-stat-no-asisten.activa { border-color: var(--danger); background: var(--danger-bg); }
.ev-stat-no-jugador.activa { border-color: var(--amber-dark); background: var(--warning-bg); }
.ev-stat-sin-respuesta.activa { border-color: var(--muted); background: var(--surface-2); }
/* Botón "Tomar asistencia" (ver "Cambios recientes", diseño final -- ocupa
   el lugar donde antes vivía la card "Ausentes" en la fila de estadísticas
   de un evento pasado, admin-only, ver _evStatCardMarcarAsistenciaHtml()/
   js/eventos.js). `<button>` real en vez de `<div>` -- reusa `.ev-stat-card`
   tal cual (mismo tamaño/padding/radio que el resto de la fila) más este
   modificador, que solo pisa lo que hace falta para que se vea como acción
   (ícono/color de marca) en vez de como una estadística clickeable-para-filtrar.
   `grid-column: span 2` (ver "Cambios recientes" -- pedido explícito:
   ensanchar aprovechando el espacio libre) -- `_EV_GRUPOS_ASISTENCIA_REAL`
   solo tiene 2 entradas, así que este botón es el 3er ítem de `.ev-stats-grid`
   (4 columnas) -- sin el span, la 4ta columna quedaba vacía a la derecha.
   Borde dashed (antes sólido transparente, heredado de `.ev-stat-card`) --
   mismo estilo que `.ev-btn-agregar-persona` (la card de home), para
   diferenciarlo visualmente de las tarjetas de borde continuo. */
.ev-stat-marcar { font: inherit; color: var(--brand); grid-column: span 2; border-style: dashed; border-color: var(--border-mid); }
.ev-stat-marcar .material-symbols-outlined { font-size: 20px; }
.ev-stat-marcar .ev-stat-label { color: var(--brand); }
.ev-stat-marcar:hover { border-color: var(--brand); background: var(--brand-lightest); }
/* Desglose agrupado (Asisten/No asisten/No jugador/Sin respuesta) --
   reusado tal cual de la ex-modal de asistentes, ahora inline acá. */
.ev-asist-grupo { margin-bottom: 18px; }
.ev-asist-grupo:last-child { margin-bottom: 0; }
.ev-asist-grupo-titulo { font-size: 0.74rem; font-weight: 800; color: var(--brand); text-transform: uppercase; letter-spacing: 0.6px; margin-bottom: 10px; }
/* Fila de persona (ver "Cambios recientes" -- rediseño): avatar más grande
   (avatar-pill--sm, 44px, css/global.css), nombre más grande/pesado, tinte
   sólido sutil de fondo por estado -- `color-mix` con el mismo color fijo
   ya usado en las tarjetas de estadística de arriba (14%, sin degradé). */
.ev-asist-persona { display: flex; align-items: center; gap: 10px; padding: 10px; border-radius: 10px; font-size: 15px; font-weight: 600; color: var(--text); margin-bottom: 6px; }
.ev-asist-persona:last-child { margin-bottom: 0; }
.ev-asist-persona-asisten { background: color-mix(in srgb, var(--success-dark) 14%, transparent); }
.ev-asist-persona-no-asisten { background: color-mix(in srgb, var(--danger) 14%, transparent); }
.ev-asist-persona-no-jugador { background: color-mix(in srgb, var(--amber-dark) 14%, transparent); }
.ev-asist-persona-sin-respuesta { background: color-mix(in srgb, var(--muted) 14%, transparent); }
/* Nombre de la fila -- `flex:1` con truncado (ver "Cambios recientes",
   punto 3 del pedido de Victor) para que el toggle inline admin-only
   (`.ev-rsvp-seg-roster`, agregado como hermano de este span solo cuando
   `_evGrupoAsistenciaHtml()` recibe `conToggle`) tenga un ancho fijo propio
   a la derecha sin que un nombre largo lo empuje fuera de la card. Sin
   toggle (RSVP/no-admin), el único efecto visible es que el nombre puede
   crecer un poco más -- inofensivo. */
.ev-asist-persona-nombre { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Badge numérico -- cuenta de los 2 filtros (lugar/tipo) CON al menos una
   opción seleccionada (no el total de opciones marcadas, ver
   `_evActualizarBadgeFiltros()`/js/eventos.js) -- oculto (`display:none`,
   togleado por JS) si ninguno tiene selección. Ancla contra
   `.ev-header-icon-btn` (`position:relative`, ver más arriba). */
.ev-filtro-badge {
  position: absolute; top: -4px; right: -4px; min-width: 16px; height: 16px; padding: 0 3px;
  border-radius: 999px; background: var(--danger); color: var(--white);
  font-size: 0.62rem; font-weight: 800; display: flex; align-items: center; justify-content: center;
  line-height: 1; box-shadow: 0 0 0 2px var(--card-bg);
}

/* ── Burbujas de cabecera (mes/filtros/búsqueda, ver "Cambios recientes" --
   rediseño): `.ev-header-burbuja` es el wrapper MECÁNICO (max-height/
   overflow -- SIN un `max-height` fijo en `.abierta`, bug real ya corregido
   antes acá: un techo mucho más alto que el contenido real hace que la
   transición se vea instantánea en vez de animada. Se fija por JS con
   `scrollHeight` real, ver `_evAbrirPanel()`, js/eventos.js).
   `.ev-header-burbuja-panel` adentro es la capa VISUAL -- panel simple
   (marco redondeado + fondo), sin ningún conector hacia el ícono que lo
   abrió. Las 2 burbujas (mes/búsqueda+filtros fusionados, ver "Cambios
   recientes") comparten este mismo mecanismo -- solo una abierta a la vez,
   ver `_evTogglePanel()`. */
.ev-header-burbuja { max-height: 0; overflow: hidden; transition: max-height 0.28s var(--ease-sheet); }
/* Espacio contra el contenido que sigue inmediatamente debajo (el timeline)
   -- sin esto, la burbuja abierta quedaba pegada contra ese contenido. El
   margen vive AFUERA de la caja (`overflow:hidden` de la burbuja colapsada
   no lo toca) -- por eso tiene que quedar condicionado a `.abierta`, si no
   dejaría un hueco vacío permanente incluso colapsada. */
.ev-header-burbuja.abierta { margin-bottom: 18px; }
/* Panel simple, sin colita -- rectángulo redondeado sin ningún conector
   hacia el ícono que lo abrió. */
.ev-header-burbuja-panel {
  margin-top: 16px; padding: 14px;
  border: 1.5px solid var(--border-light); border-radius: 14px; background: var(--surface-2);
}

/* Filtros Lugar/Tipo (ver "Cambios recientes" -- se saca el filtro "Mes" que
   tenía antes, ahora el mes se maneja desde el panel de mes) -- botones
   "trigger" (mismo look/idea que .admin-destino-trigger de css/admin.css:
   parecen un <select>) que despliegan una burbuja inline debajo de la fila
   en vez de abrir un bottom sheet -- _evToggleFiltroBurbuja()/
   #ev-filtro-burbuja-<campo> en js/eventos.js/index.html. */
/* Subtítulo "Filtros" arriba de Lugar/Tipo (ver "Cambios recientes" --
   búsqueda+filtros fusionados en un solo panel) -- reusa `.seccion-label`
   literal (css/ui.css, mismo componente que ya usan Home/Reservas/Ajustes
   para subtítulos de sección), solo suma el margen contra el buscador de
   arriba (`.seccion-label` ya trae su propio `margin-bottom`). */
.ev-filtros-label { margin-top: 18px; }
.ev-filtros-row { display: flex; gap: 8px; margin-bottom: 4px; }
.ev-filtro-trigger {
  flex: 1; min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 4px;
  padding: 10px 6px 10px 12px; border-radius: 18px; border: 1.5px solid var(--border-light);
  background: var(--surface-light); color: var(--text-2); font-size: 0.78rem; font-weight: 700;
  cursor: pointer; font-family: inherit; transition: all 0.15s;
}
.ev-filtro-trigger-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ev-filtro-trigger .material-symbols-outlined { font-size: 18px; color: var(--muted); flex-shrink: 0; }
.ev-filtro-trigger.ev-filtro-activo { border-color: var(--brand); color: var(--brand); background: var(--brand-lightest); }
.ev-filtro-trigger.ev-filtro-activo .material-symbols-outlined { color: var(--brand); }

/* Burbuja inline de un filtro (Lugar/Tipo) -- colapsada por default
   (max-height:0 + overflow:hidden), un solo `.abierta` a la vez entre las 2
   (_evToggleFiltroBurbuja(), js/eventos.js) empuja naturalmente el timeline
   de abajo al expandirse en vez de superponerse. Mismo mecanismo/curva que
   .admin-mes-acordeon-body (css/admin.css) y .fi-body (css/reservas.css). */
.ev-filtro-burbuja { max-height: 0; overflow: hidden; transition: max-height 0.28s var(--ease-sheet); }
/* Mismo criterio/valor (18px) que `.ev-header-burbuja.abierta` de arriba --
   para que una burbuja de campo (Lugar/Tipo) abierta tampoco quede pegada
   contra el cierre del panel exterior. */
.ev-filtro-burbuja.abierta { max-height: 320px; margin-bottom: 18px; }
.ev-filtro-burbuja-pills { padding: 10px 2px 16px; }

/* Card compacta de una fila del timeline (eventos pasados) --
   `.ev-card-compacta-wrap` es el contenedor real (fondo/borde/margen);
   `.ev-card-compacta` adentro es solo la fila clickeable (ícono+info+chip de
   asistencia real, ver _evTimelineFilaHtml()). */
.ev-card-compacta-wrap {
  margin-bottom: 8px; background: var(--surface-2); border: 1.5px solid var(--border-light);
  border-radius: 12px; transition: border-color 0.15s; overflow: hidden;
}
.ev-card-compacta-wrap:hover { border-color: var(--brand); }
.ev-card-compacta { display: flex; align-items: center; gap: 12px; padding: 12px 14px; cursor: pointer; }
.ev-card-compacta .ev-card-icon { width: 36px; height: 36px; min-width: 36px; border-radius: 10px; }
.ev-card-compacta .ev-card-icon .material-symbols-outlined { font-size: 1.1rem; }
.ev-card-compacta-info { flex: 1; min-width: 0; }
.ev-card-compacta-titulo { font-size: 0.83rem; font-weight: 700; color: var(--text); }
.ev-card-compacta-sub { font-size: 0.73rem; color: var(--muted); margin-top: 2px; }
/* Sin chevron propio para Cancelado/No se entrena (ver "Cambios recientes"
   -- eliminado, `.ev-chevron-ver` sin más consumidores): `.ev-card-compacta`
   entera ya es tocable, no hace falta la afordancia. */
/* `text-transform:none` (ver "Cambios recientes") -- mismo motivo que
   `.ev-rsvp-readonly` arriba: único `.badge` que vive dentro de
   `.ev-card-compacta` es el chip de asistencia real, sentence case acá. */
.ev-card-compacta .badge { flex-shrink: 0; text-transform: none; }
.ev-card-compacta-wrap .ev-asistire-wrap { margin: 0 14px 12px; }
/* Mismo respiro lateral/inferior que `.ev-asistire-wrap` de arriba, para
   `.ev-estado-pill` cuando la fila compacta lo usa como nota de asistencia
   real (ver "Cambios recientes" -- reusa `_evAsistenciaRealHtml()`, ya no
   arma su propio `.badge` al costado) -- sin esto quedaría pegado a los
   bordes del wrapper (el `margin-top` propio de `.ev-estado-pill` no cubre
   los laterales/abajo que sí necesita acá, fuera de `.ev-card-compacta`).
   `width: auto` (bug real, ver "Cambios recientes" -- corrección del mismo
   fix): `.ev-estado-pill` base fija `width: 100%`, pensado para sus otros 2
   contextos (card completa/detalle), donde no lleva margen lateral propio.
   Acá SÍ lleva margen (14px a cada lado) -- `width: 100%` se calcula sobre
   el ancho del CONTENEDOR, así que sumarle margen encima desborda la caja
   14px de cada lado (100% + 28px totales), recortada por el
   `overflow: hidden` de `.ev-card-compacta-wrap`. `.ev-asistire-wrap` nunca
   tuvo este problema porque no fuerza ningún `width` propio -- un bloque
   normal ya resta los márgenes solo. */
.ev-card-compacta-wrap .ev-estado-pill { margin: 0 14px 12px; width: auto; }
/* Mismo respiro lateral/inferior que `.ev-asistire-wrap`/`.ev-estado-pill` de
   arriba -- gestión de asistencia admin restaurada en la fila compacta
   (`_evAccionAdminHtml()`, ver "Cambios recientes", bug real corregido:
   antes solo vivía en la card completa de hoy/futuro, nunca en esta fila de
   eventos pasados). Sin `width` propio (mismo caso que `.ev-asistire-wrap`,
   no el de `.ev-estado-pill`), un bloque normal ya resta los márgenes solo. */
.ev-card-compacta-wrap .ev-asistentes-list { margin: 0 14px 12px; }
/* Jerarquía visual pasados/futuros (ver "Cambios recientes",
   `_evTimelineFilaHtml()`/js/eventos.js). Antes: opacidad reducida (0.55) en
   TODA la card compacta -- comprometía la legibilidad del contenido
   (nombre/hora/lista de asistentes), pedido explícito de Victor de sacarla.
   Reemplazada por 2 indicadores sutiles que no tocan el texto: ícono
   forzado a gris apagado (ya existía, sin cambios) + borde algo más
   marcado (`--border-mid` en vez de `--border-light`, el tono por default
   de `.ev-card-compacta-wrap`) -- alcanza para diferenciar "esto ya pasó"
   sin afectar el contraste de ningún texto. Futuros (`.ev-card`, la card
   completa) sin cambios. */
.ev-card-compacta-wrap.ev-pasado-atenuado { border-color: var(--border-mid); }
.ev-card-compacta-wrap.ev-pasado-atenuado .ev-card-icon { background: var(--surface-2); color: var(--muted); }

/* Buscador -- reusa `.app-nav-search` literal (css/nav.css, mismo pill que
   Ajustes). `caret-color:transparent` ya lo cubre la regla global por
   atributo `input[placeholder*="uscar" i]` (css/ui.css) -- ver
   MANIFEST.md "Cambios recientes". */

@media (prefers-color-scheme: dark) {
  .ev-lista-vacia { background: var(--surface-2); border-color: var(--border-light); }
}

/* ── Asistencia anticipada (ev-ant-*, #s-eventos-anticipada) ──────────────
   Estado "resumen": lista de cards -- reusa .ev-card-top-row/.ev-card-icon/
   .ev-card-body de la card de evento (arriba) para el ícono+texto, solo
   agrega el wrapper con borde propio (mismo criterio que
   .ev-card-compacta-wrap más arriba) y el botón de eliminar (mismo patrón
   que .adm-talla-quitar, css/admin.css, con el ícono real `delete` en vez
   del glifo "✕"). */
.ev-ant-card {
  display: flex; align-items: center; gap: 10px; padding: 12px 14px; margin-bottom: 8px;
  background: var(--surface-2); border: 1.5px solid var(--border-light); border-radius: 12px;
}
.ev-ant-card .ev-card-top-row { flex: 1; min-width: 0; }
.ev-ant-card-sub { font-size: 0.76rem; color: var(--muted); margin-top: 2px; }
.ev-ant-card-del, .ev-ant-card-edit {
  width: 40px; height: 40px; min-width: 40px; border-radius: 10px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center; cursor: pointer; font-family: inherit;
}
.ev-ant-card-del { border: 1.5px solid var(--error-light-border); background: var(--error-lightest); color: var(--error); }
/* Ícono editar (ver "Cambios recientes") -- mismo patrón/tamaño que el de
   eliminar, colores neutros de marca en vez de danger (no es una acción
   destructiva) -- mismo par brand-light/brand que ya usa .ev-ant-cal-nav-btn
   más abajo en este archivo. Va ANTES del botón de eliminar en el DOM
   (orden editar -> eliminar). */
.ev-ant-card-edit { border: 1.5px solid transparent; background: var(--brand-light); color: var(--brand); }
.ev-ant-card-del .material-symbols-outlined, .ev-ant-card-edit .material-symbols-outlined { font-size: 1.15rem; }
/* Advertencia destacada del bsheet "¿Eliminar lugar?" (#ev-lugar-sheet-eliminar,
   ver MANIFEST.md "Cambios recientes") -- cuenta de eventos futuros/pasados
   vinculados al venue, tokens de `--warning*` (css/colors.css), mismos que
   ya usa `.ev-estado-pill-warning` en este archivo -- ningún color
   hardcodeado. */
.ev-lugar-eliminar-warn { display: flex; align-items: flex-start; gap: 10px; background: var(--warning-bg); border: 1px solid var(--warning-bdr); border-radius: 12px; padding: 12px 14px; }
.ev-lugar-eliminar-warn .material-symbols-outlined { color: var(--warning); font-size: 1.2rem; flex-shrink: 0; }
.ev-lugar-eliminar-warn span:last-child { font-size: 0.82rem; color: var(--text); line-height: 1.4; }

/* Párrafo intro de "Estado a aplicar" (ver "Cambios recientes" -- antes vivía
   AFUERA del acordeón, con su propio colapso al scrollear; ahora es
   contenido normal dentro de `.ev-ant-acc-body-inner` de esa sección --
   colapsa/aparece junto con el resto del acordeón, sin transición propia). */
.ev-ant-intro { font-size: 0.85rem; color: var(--muted); text-align: center; line-height: 1.5; margin: 0 0 14px; }

/* Estado "wizard" -- acordeón de 2 secciones, "Estado a aplicar" y
   "Frecuencia" (ver "Cambios recientes" -- reemplaza la secuencia de pasos
   anterior por completo, incluyendo el paso 0 introductorio y los dots de
   progreso `.salud-prog`, que ya no se usan en esta pantalla).
   `.ev-ant-acc-header`/`.ev-ant-acc-body` son hermanos sueltos (ver
   "Cambios recientes" -- antes anidados en un `.ev-ant-acc` común, separados
   para que el header pueda ser `position:sticky` en TODO el alto del
   wizard, no solo el de su propia sección colapsada) -- `_evAntSetAcordeon()`
   togglea `.abierto` en los 2 por separado, mismo resultado visual de
   siempre (chevron rota, resumen colapsado visible solo cerrado, body con
   max-height+opacity). Mismo TIPO de transición que ya usa el acordeón de
   banners de Mi Liga (.admin-dash-banner-*, css/admin.css/adminToggleBanner()) --
   se revisó también .datos-seccion* (css/perfil.css, la otra referencia
   nombrada en el pedido, "Ajustes") pero resultó ser CSS sin ningún
   consumidor real en el HTML actual (rediseño anterior de Ajustes que dejó
   de usarlo), así que la referencia vigente que se siguió es Mi Liga. A
   diferencia de Mi Liga (max-height medido con `scrollHeight` en cada
   toggle), el BODY acá usa un techo fijo generoso (900px, mismo criterio
   que `.datos-seccion-body.abierta{max-height:2500px}` de perfil.css) -- el
   contenido de "Frecuencia" cambia de alto según la sub-elección (grilla de
   meses vs. calendario, con 5 o 6 semanas), un techo fijo ya cubre
   cualquier combinación sin necesitar re-medir en cada cambio.
   Sticky apilado (ver "Cambios recientes"): mismo mecanismo que
   `.ev-detalle-sticky`/`-rsvp-sticky`/`-stats-sticky` (niveles de
   #s-eventos-detalle, más arriba en este archivo) -- `top` de cada header
   lo fija `_evAntActualizarStickyAcordeon()` (js/eventos.js) a partir del
   alto real renderizado de los niveles de arriba (nunca un valor fijo acá).
   z-index escalonado (20/19, mismo rango que `.ev-sticky-header`/
   `.ev-detalle-sticky`, z-index:20) para que "Estado a aplicar" (el de
   arriba) pinte encima de "Frecuencia" si sus bordes llegaran a solaparse. */
.ev-ant-acc-header {
  display: flex; align-items: center; gap: 8px; padding: 14px 16px; cursor: pointer;
  background: var(--surface-2); border: 1.5px solid var(--border-light); border-radius: 12px;
  margin-bottom: 10px; position: sticky;
}
#ev-ant-acc-estado-header { z-index: 20; }
#ev-ant-acc-frecuencia-header { z-index: 19; }
/* Fondo sólido en mobile real -- bug real encontrado con Playwright: `--surface-2`
   tiene alpha (`rgba(255,245,235,0.95)`, colors.css), suficientemente
   transparente para que el contenido de más abajo (pills/calendario)
   se transparentara detrás de los headers pineados al scrollear. Mismo
   criterio/breakpoint que `.ev-sticky-header` (arriba en este archivo,
   comentario completo ahí): en mobile real (≤600px) usar `--bg` (opaco,
   hex sin alpha) en vez de `--card-bg`/`--surface-2`. Solo el HEADER lo
   necesita -- el body nunca queda pineado sobre nada, su propio
   `--surface-2` translúcido no genera este problema. */
@media (max-width: 600px) {
  .ev-ant-acc-header { background: var(--bg); }
}
.ev-ant-acc-header.abierto { border-radius: 12px 12px 0 0; border-bottom-color: transparent; margin-bottom: 0; }
.ev-ant-acc-titulo { font-weight: 800; font-size: 0.88rem; color: var(--text); flex-shrink: 0; }
/* Resumen compacto de lo elegido (ej. "Asistiré", "5/7/2026 - 20/7/2026")
   -- visible SOLO con la sección colapsada, mismo lugar donde antes estaba
   el chevron solo (pedido explícito: "header colapsado con la pill
   seleccionada visible"). Vacío (nada elegido todavía) no ocupa espacio. */
.ev-ant-acc-resumen { flex: 1; min-width: 0; font-size: 0.8rem; font-weight: 700; color: var(--brand); text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; }
.ev-ant-acc-header.abierto .ev-ant-acc-resumen { display: none; }
.ev-ant-acc-chevron { color: var(--muted); font-size: 1.2rem; transition: transform 0.3s; flex-shrink: 0; }
.ev-ant-acc-header.abierto .ev-ant-acc-chevron { transform: rotate(180deg); color: var(--brand); }
.ev-ant-acc-body {
  max-height: 0; overflow: hidden; opacity: 0; background: var(--surface-2);
  border: 0 solid var(--border-light); border-radius: 0 0 12px 12px;
  transition: max-height 0.45s cubic-bezier(0.16,1,0.3,1), opacity 0.3s ease, margin 0.3s ease;
}
/* `border-width` (no `border`) solo en los 3 lados que corresponde -- ver
   comentario de arriba, arrancar en `0` evita una línea de 1.5px visible
   con la sección cerrada (max-height:0 no evita que un borde >0 siga
   pintando su propio grosor). */
.ev-ant-acc-body.abierto { max-height: 900px; opacity: 1; margin-bottom: 10px; border-width: 0 1.5px 1.5px; }
.ev-ant-acc-body-inner { padding: 2px 16px 16px; }

/* Paso "Tipo de rango" -> "Por meses": grilla de 3 columnas, año actual,
   meses pasados deshabilitados (ver _evAntRenderMesesGrid(), js/eventos.js
   -- no existía ningún componente reusable de grilla de meses en el
   proyecto, ver MANIFEST.md). */
.ev-ant-mes-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 4px; }
.ev-ant-mes-cell {
  padding: 12px 6px; border-radius: 12px; border: 1.5px solid transparent; background: var(--brand-20);
  color: var(--muted); font-size: 0.82rem; font-weight: 700; text-align: center; cursor: pointer;
  font-family: inherit; transition: all 0.15s; user-select: none;
}
.ev-ant-mes-cell.activo { background: var(--brand); color: var(--white); border-color: var(--brand); }
.ev-ant-mes-cell.deshabilitado { opacity: 0.4; cursor: not-allowed; pointer-events: none; }

/* Paso "Frecuencia" -> "Por período"/"Indefinido": calendario inline
   (ev-ant-cal-*, ver _evAntCalRender()/js/eventos.js) -- reemplaza el modal
   de calendario genérico (bug real: arranca en 1990). Reusa el layout de
   grilla real del panel de mes de Home Eventos (.ev-cal-grid/.ev-cal-dow/
   .ev-cal-celda/.ev-cal-num/.ev-ajeno, arriba en este archivo) sin tocarlo
   -- es un componente nuevo, con su propio mes navegable y sus propios
   estados de selección, sin abrir ningún panel/modal. */
/* Línea "Desde el [pill] hasta el [pill]" -- reemplaza los 2 .datos-campo
   verticales de antes (ver "Cambios recientes"), mismo componente reusado
   tal cual para el "Desde" del paso Indefinido (consistencia visual entre
   ambos). Pill vacía = mismo min-width/min-height que llena, para que
   seleccionar una fecha no cambie el layout de la línea. Centrada (ver
   "Cambios recientes" -- antes alineada a la izquierda por herencia de
   `text-align:left` general). */
.ev-ant-rango-linea { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 6px; font-size: 0.92rem; color: var(--text); text-align: center; }
/* Fila de "Por período"/"Indefinido" que agrupa la línea de fechas + el
   botón ícono "Restablecer". `flex-shrink:0` en la línea de fechas (ver
   `.ev-ant-rango-fila .ev-ant-rango-linea` abajo) para que nunca sea la que
   ceda espacio y aplaste las pills por debajo de su `min-width:92px`. */
/* `max-height` animado (ver "Cambios recientes" -- antes el alto de esta
   fila cambiaba de golpe al aparecer/ocultarse el botón "Restablecer" --
   30px vs. el alto de una sola línea de texto -- empujando el layout sin
   transición). Alto real medido por JS en cada cambio, no un valor fijo
   (mismo criterio que `_evAbrirPanel()`/`_evCerrarPanel()`, más arriba en
   este archivo) -- `overflow:hidden` recorta durante la animación, sin
   afectar el layout final una vez asentado (el `max-height` inline queda
   igualado al `scrollHeight` real). */
.ev-ant-rango-fila { display: flex; align-items: center; justify-content: center; gap: 10px; flex-wrap: wrap; overflow: hidden; transition: max-height 0.2s ease; }
.ev-ant-rango-fila .ev-ant-rango-linea { flex-shrink: 0; flex-wrap: nowrap; }
/* Estado sin fecha elegida de "Por período" (ver "Cambios recientes" -- ya
   no muestra la línea "Desde el ___ hasta el ___" vacía, un texto
   instructivo simple en su lugar). */
.ev-ant-rango-vacio { color: var(--muted); }
.ev-ant-fecha-pill {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 92px; min-height: 30px; padding: 4px 10px;
  background: var(--brand-20); border-radius: 8px;
  font-size: 0.85rem; font-weight: 700; color: var(--text);
  cursor: pointer; user-select: none;
}
.ev-ant-cal-nav { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.ev-ant-cal-nav-btn {
  width: 36px; height: 36px; border-radius: 10px; border: none; background: var(--brand-light);
  color: var(--brand); display: flex; align-items: center; justify-content: center; cursor: pointer;
  font-family: inherit;
}
.ev-ant-cal-nav-btn .material-symbols-outlined { font-size: 20px; }
.ev-ant-cal-nav-label { font-size: 0.88rem; font-weight: 800; color: var(--text); text-transform: capitalize; }
/* Botón "Restablecer" (ícono `undo`, sin texto) -- compartido por "Por
   período" e "Indefinido" (antes "Por período" tenía un botón de texto
   propio, unificado a este mismo componente por consistencia). Alto/ancho
   30px para calzar EXACTO con la altura real de `.ev-ant-fecha-pill` (su
   `min-height:30px` en box-sizing:border-box, css/global.css, es la altura
   final ya que el contenido nunca la supera) -- antes era 36x36 (tamaño de
   `.ev-ant-cal-nav-btn`) y se veía visiblemente más alto que la pill de
   fecha al lado. Oculto por default (`display:none` en `index.html`) --
   `_evAntSetBotonRestablecerVisible()` (js/eventos.js) lo revela con fade
   recién cuando el usuario tocó al menos una fecha. */
.ev-ant-btn-restablecer-icono {
  width: 30px; height: 30px; flex-shrink: 0; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border: 1.5px solid var(--brand); border-radius: 10px; background: none;
}
.ev-ant-btn-restablecer-icono .material-symbols-outlined { font-size: 18px; }
/* "Sin fecha de fin." centrada junto con la línea de fecha de arriba, como
   un solo bloque (ver "Cambios recientes" -- antes heredaba `text-align:left`
   general, mientras que `.ev-ant-rango-linea`/`.ev-ant-rango-fila` de arriba
   ya se centraban solas). */
.ev-ant-indefinido-nota { font-size: 0.78rem; color: var(--muted); margin-top: 6px; text-align: center; }
/* "Hoy" -- SIEMPRE con stroke rojo distintivo, sin importar el resto de la
   selección (pedido explícito). `box-shadow` (no `outline`/`background`)
   para poder convivir con `.ev-ant-cal-sel` (fondo `--brand` sólido) o
   `.ev-ant-cal-en-rango` (fondo `--brand` tenue) sin que ninguno lo tape. */
.ev-ant-cal-hoy .ev-cal-num { box-shadow: 0 0 0 2px var(--danger); }
/* Endpoint seleccionado -- Desde/Hasta de "Por período", o la única fecha
   de "Indefinido". Días intermedios del rango (solo "Por período") quedan
   con el mismo fondo tenue de celda completa, sin el relleno sólido del
   círculo -- mismo lenguaje que un date-range picker tipo "ida y vuelta"
   de aerolínea. */
.ev-ant-cal-sel, .ev-ant-cal-en-rango { background: var(--brand-light); border-radius: 10px; }
.ev-ant-cal-sel .ev-cal-num { background: var(--brand); color: var(--white); font-weight: 800; }
/* Fechas pasadas (ver "Cambios recientes" -- una asistencia anticipada nunca
   debería poder aplicarse retroactivamente): gris, sin interacción -- el
   `onclick` ya viene omitido desde _evAntCalRender() (js/eventos.js), acá
   solo el tratamiento visual. `pointer-events:none` además del `onclick`
   ausente, doble seguro contra cualquier listener delegado futuro. */
.ev-ant-cal-pasado { opacity: 0.32; pointer-events: none; cursor: not-allowed; }
.ev-ant-cal-pasado .ev-cal-num { color: var(--muted); }

/* Skeleton del estado "resumen" mientras carga _evAntFetchReglas()
   (ver "Cambios recientes" -- reemplaza el loader de pantalla completa que
   tenía antes). Reusa `.fi-skel-block` (el shimmer genérico, css/reservas.css
   -- mismo criterio que `_skeletonFechasHtml()`/js/reservas.js, no se
   inventa un shimmer propio) con el tamaño/forma de `.ev-ant-card` real:
   ícono 42px (mismo que `.ev-card-icon`) + 2 barras (título/sub). */
.ev-ant-skel-icon { width: 42px; height: 42px; border-radius: 12px; flex-shrink: 0; }
.ev-ant-skel-title { width: 65%; height: 14px; margin-bottom: 8px; }
.ev-ant-skel-sub { width: 45%; height: 11px; }

/* Toast scopeado a #s-eventos-anticipada mientras el footer del wizard está
   visible -- `#app-toast` (css/estilos.css) es un `<div>` creado por JS y
   agregado directo a `<body>` la primera vez que se llama mostrarToast()
   (mismo criterio de "position:fixed va directo en <body>, no dentro de
   .pantalla/.card" que ya rige #cta-footer-fixed, ver MANIFEST.md), así que
   el override también apunta ahí sin anidarlo dentro de ninguna pantalla.
   Por defecto el toast queda a `bottom:28px` (fijo, global) -- sobre esta
   pantalla, con el footer fijo del wizard ocupando esa franja, el toast le
   tapaba las pills en vez de mostrarse por encima del botón "Continuar"/
   "Aplicar". `body.ev-ant-footer-visible` la togglea _evAntActualizarFooter()/
   _evAntOcultarFooter() (js/eventos.js) junto con el propio footer, nunca
   suelta. El offset suma el alto real del footer (12px padding-top + ~50px
   del botón `.btn` + 12px padding-bottom, css/estilos.css `.cta-footer-fixed`/
   `.btn`) más 12px de aire, sobre el mismo `bottom` base que ya usa
   `#cta-footer-eventos-anticipada` (`var(--bottom-nav-h)` + safe area, para
   convivir con `.app-bottom-nav`). */
body.ev-ant-footer-visible #app-toast {
  bottom: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom, 0px) + 86px);
}
/* "Por período"/"Indefinido" (ver "Cambios recientes" -- rediseño a pills)
   revelan el calendario inline debajo, que en pantallas bajas (confirmado
   con Playwright a 667px de alto, ej. iPhone SE) llega a ocupar la franja
   del toast subido de arriba -- la última fila de días quedaba tapada por
   un toast de error tras "Aplicar". `body.ev-ant-cal-abierto` la togglea
   `_evAntMostrarSubFrecuencia()` (js/eventos.js) solo para esos 2
   sub-pasos -- "Por meses" no lo necesita, su grilla de 4 filas no llega a
   esa franja en ningún alto probado.
   IMPORTANTE -- por qué `max(..., 100vh - 300px)` y no un extra fijo en px:
   el contenido de este paso (pills de frecuencia + grilla) cuelga del
   HEADER (ancla arriba, posición fija sea cual sea el alto de pantalla),
   mientras que el toast cuelga del `bottom` (ancla abajo, se aleja más del
   contenido cuanto más alta es la pantalla). Un extra fijo en px calcado de
   un solo alto de prueba (667px) rompía en pantallas más altas -- a 844px
   ya sobra tanto que el toast quedaba METIDO adentro de la grilla en vez de
   arriba (se verificó con Playwright, no fue teórico). `100vh - 300px`
   escala con el alto real de la pantalla porque `grid.getBoundingClientRect().top`
   se midió con Playwright en ~316px, estable en los 4 altos probados
   (667/700/740/844px, mismo ancho 390px) -- 300px = ese valor con ~16px de
   aire. El `max()` con la regla general de arriba (línea de más arriba) es
   el piso de seguridad: en pantallas altas, donde `100vh - 300px` da menos
   que el offset general, se queda con el offset general (nunca empuja el
   toast más abajo de donde ya estaba). */
body.ev-ant-footer-visible.ev-ant-cal-abierto #app-toast {
  bottom: max(
    calc(var(--bottom-nav-h) + env(safe-area-inset-bottom, 0px) + 86px),
    calc(100vh - 300px)
  );
}

/* ═══════════════════════════════════════════════════════
   VENUES -- "Editar lugares" (#s-eventos-lugares) y formulario compartido
   con "Crear evento" (#s-eventos-lugar-form, ver js/eventos.js). La lista
   de lugares reusa .ev-ant-card/.ev-card-top-row/.ev-card-icon/.ev-card-body/
   .ev-ant-card-sub tal cual (mismo look que el resumen de Asistencia
   anticipada, arriba en este archivo) -- sin clases nuevas para eso. Pills
   de ícono/reserva/recurrencia/unidad reusan .aj-pills-row/.aj-pill
   (css/perfil.css). El calendario de fecha única (_evLugarCalRender(),
   js/eventos.js) reusa .ev-cal-grid/.ev-cal-dow/.ev-cal-celda/.ev-cal-num/
   .ev-ajeno/.ev-ant-cal-hoy/.ev-ant-cal-sel/.ev-ant-cal-pasado y el nav de
   mes .ev-ant-cal-nav* (todos ya definidos arriba). Lo único genuinamente
   nuevo son el selector de días de la semana (sin precedente en la app) y
   los wrappers de layout del formulario (campo/label/inputs/mapa).
   ═══════════════════════════════════════════════════════ */

.ev-lugar-campo { margin-top: 20px; }
.ev-lugar-campo:first-child { margin-top: 0; }
.ev-lugar-campo-label { font-size: 0.8rem; font-weight: 700; color: var(--text); margin: 0 0 8px; }

/* Input genérico de texto/número -- mismo idioma visual que
   #aj-sheet-texto-input (css/perfil.css, borde de marca + fondo -2). */
.ev-lugar-input {
  width: 100%; padding: 13px 16px; background: var(--surface-2); border: 1.5px solid var(--brand);
  border-radius: 12px; font-size: 0.88rem; font-family: inherit; color: var(--text); box-sizing: border-box;
}
.ev-lugar-input::placeholder { color: var(--muted); }

.ev-lugar-cada-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.ev-lugar-input-num { max-width: 96px; flex-shrink: 0; text-align: center; }
.ev-lugar-cada-row .aj-pills-row { flex: 1; min-width: 180px; }

/* Buscador de lugares (Places Autocomplete) -- reusa .app-nav-search literal
   (css/nav.css, mismo pill que Ajustes/#ev-search-input).
   `caret-color:transparent` ya lo cubre la regla global por atributo
   `input[placeholder*="uscar" i]` (css/ui.css). */

.ev-lugar-mapa-wrap {
  position: relative; height: 220px; border-radius: 12px; overflow: hidden;
  background: var(--surface-2); margin-bottom: 8px;
}
.ev-lugar-mapa-nota { font-size: 0.72rem; color: var(--muted); text-align: center; margin: 0; }

/* Selector de días de la semana (L-M-X-J-V-S-D), multi-select -- sin
   precedente en el resto de la app (Asistencia anticipada elige MESES, no
   días de la semana), primer componente nuevo real de esta sección. Mismo
   par de colores brand-light/brand que .aj-pill (reposo/activa), en un
   círculo en vez de una pill -- 7 letras sueltas se leen mejor como círculos
   chicos en fila que como pills anchas con texto de 1 char. */
.ev-dias-row { display: flex; gap: 8px; justify-content: space-between; }
.ev-dia-circulo {
  width: 38px; height: 38px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  font-size: 0.82rem; font-weight: 700; color: var(--muted); background: var(--brand-light);
  border: 1.5px solid transparent; cursor: pointer; user-select: none; transition: all 0.15s;
  flex-shrink: 0;
}
.ev-dia-circulo.activa { background: var(--brand); color: var(--white); border-color: var(--brand); }

.ev-lugar-cal-resumen { text-align: center; font-size: 0.85rem; font-weight: 700; color: var(--text); margin-top: 10px; }
.ev-lugar-cal-resumen:empty::before { content: 'Toca una fecha en el calendario'; color: var(--muted); font-weight: 500; font-size: 0.8rem; }

@media (prefers-color-scheme: dark) {
  .ev-lugar-input { background: var(--surface-2); }
}

/* ═══════════════════════════════════════════════════════
   "Crear evento" (#s-eventos-crear, js/eventos.js _evCrear*) -- wizard de
   3 pasos (Tipo → Lugar → Detalles, ver MANIFEST.md "Cambios recientes" --
   antes 2 pasos, Lugar/Recurrencia, sin selector de tipo), separado del
   formulario de "Editar lugares" (arriba en este archivo, sin cambios). El
   motor de pasos+progreso reusa .salud-paso/.salud-prog/.salud-prog-dot
   TAL CUAL (css/perfil.css, cargado siempre) -- ver MANIFEST.md para la
   aclaración de por qué el precedente real es el wizard de "Ficha de
   salud" y no "Asistencia anticipada" (esa pantalla migró de ese mismo
   motor a un acordeón de una sola pantalla en una tanda anterior). El resto
   del paso "Detalles" (pills de recurrencia, días de la semana, calendario)
   reusa los mismos selectores ya definidos arriba para el formulario de
   Venues (.aj-pills-row/.aj-pill/.ev-dia-circulo/.ev-cal-grid/
   .ev-ant-cal-nav*) -- 3 componentes son genuinamente nuevos acá: la card
   vertical de tipo (paso "Tipo"), la card seleccionable de lugar existente
   (paso "Lugar"), y el stepper de hora (paso "Detalles"). ═══════════════ */

/* Paso "Tipo" -- 3 cards verticales (ícono grande + título + descripción),
   modificador de .ev-ant-card (horizontal por default en el resto de la
   app) a layout en columna centrada, solo para esta variante. */
.ev-crear-tipo-card {
  flex-direction: column; align-items: center; text-align: center; gap: 6px;
  padding: 22px 16px; cursor: pointer; transition: border-color 0.15s, background 0.15s;
}
.ev-crear-tipo-card-icono { font-size: 2.2rem; color: var(--brand); }
.ev-crear-tipo-card-titulo { font-size: 0.95rem; font-weight: 800; color: var(--text); }
.ev-crear-tipo-card-desc { font-size: 0.78rem; color: var(--muted); line-height: 1.4; }
@media (hover: hover) { .ev-crear-tipo-card:hover { border-color: var(--brand); } }

/* Paso "Lugar" -- card de venue existente, seleccionable (a diferencia de
   .ev-ant-card en "Editar lugares", que abre el formulario al tocar, acá
   togglea selección con un borde de marca -- misma base visual, estado
   distinto). La card "Agregar lugar" (_evCrearAgregarLugarHtml(),
   js/eventos.js) reusa esta misma clase, sin `.activa` -- nunca queda
   seleccionada, solo navega. */
.ev-crear-venue-card { cursor: pointer; border-color: var(--border-light); transition: border-color 0.15s, background 0.15s; }
.ev-crear-venue-card.activa { border-color: var(--brand); background: var(--brand-light); }
/* "Agregar lugar" -- borde punteado, sin fondo sólido (la línea ya la
   distingue de las cards de venues reales, no hace falta relleno). Pedido
   con `var(--border-color)`, variable que no existe en css/colors.css --
   `--border-mid` es el tono real más parecido (más marcado que
   `--border-light`, apropiado para una afordancia de "agregar"). */
.ev-crear-venue-add { border: 2px dashed var(--border-mid); background: transparent; }

/* Stepper de hora -- 2 columnas (Horas/Minutos) con flechas arriba/abajo
   (mismo botón circular que .ev-ant-cal-nav-btn, reusado tal cual, solo
   rotado a vertical) + pills AM/PM debajo. Sin rueda de scroll infinito
   (pedido explícito) -- valor grande y quieto, solo cambia por tap en las
   flechas, primer selector de hora de la app (los horarios de Venues se
   cargaban siempre a mano en Sheets hasta esta tanda). Componente genérico,
   reusable con cualquier prefijo de id (ver _evHoraStepper* en
   js/eventos.js), no atado a "Crear evento" pese a vivir en esta sección. */
.ev-hora-stepper { display: flex; flex-direction: column; align-items: center; gap: 14px; }
.ev-hora-stepper-cols { display: flex; align-items: center; gap: 10px; }
.ev-hora-stepper-col { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.ev-hora-stepper-col .ev-ant-cal-nav-btn { width: 40px; height: 40px; }
.ev-hora-stepper-valor {
  min-width: 56px; text-align: center; font-size: 1.6rem; font-weight: 800; color: var(--text);
  font-variant-numeric: tabular-nums;
}
.ev-hora-stepper-sep { font-size: 1.6rem; font-weight: 800; color: var(--text); padding-bottom: 8px; }
.ev-hora-stepper-meridiano { display: flex; gap: 8px; }

/* #s-eventos-editar ("Editar evento", admin, ver MANIFEST.md "Cambios
   recientes") -- pantalla de página completa, reemplaza el bottom sheet de
   la tanda anterior por completo. Las 3 filas del paso 0 (Lugar/Horario/
   Descripción) reusan `.ev-ant-acc-header`/`-body`/`-titulo`/`-resumen`/
   `-chevron` TAL CUAL (más abajo en este archivo, acordeón de Asistencia
   anticipada) -- mismo chrome/mecánica de expandir (max-height+opacity,
   chevron que rota), la única diferencia real es que acá NUNCA se cierran
   las otras filas al abrir una (`_evEditarToggleCampo()`, js/eventos.js, no
   llama a ningún "cerrar las demás" -- pedido explícito: "se pueden
   expandir y editar varios campos, en cualquier orden"). El calendario de
   "Por período" del paso 1 reusa igual de literal `.ev-ant-cal-nav*`/
   `.ev-cal-grid`/`.ev-ant-fecha-pill`/`.ev-ant-rango-linea`/
   `.ev-ant-rango-fila`/`.ev-ant-btn-restablecer-icono` (todas más abajo en
   este archivo) -- cero CSS de calendario propio para esta pantalla. */
.ev-editar-campo-icono { color: var(--brand); font-size: 20px; flex-shrink: 0; }
/* Color neutro del valor colapsado de una fila SIN cambio pendiente (ver
   comentario de arriba) -- `.ev-ant-acc-resumen` sola ya es brand por
   default (pensada para "esto es lo que ya elegiste", siempre un color de
   marca en Asistencia anticipada); acá el default debe ser neutro (todavía
   no hay ningún cambio) y pasar a brand recién cuando el campo SÍ cambió --
   el "indicador sutil de modificado" pedido. `_evEditarActualizarResumenCampo()`
   agrega/saca esta clase según `_evEditarCambios` tenga o no esa clave. */
.ev-ant-acc-resumen.ev-editar-valor-original { color: var(--text-2); font-weight: 600; }
/* Fix real (ver MANIFEST.md "Cambios recientes"): `.ev-ant-acc-body-inner`
   (reusada tal cual de Asistencia anticipada, `padding:2px 16px 16px` más
   abajo en este archivo) trae solo 2px de aire arriba -- alcanza ahí porque
   su primer hijo real siempre es `.ev-ant-intro`, un párrafo con su propio
   `margin-bottom`. Acá el primer hijo de cada fila es contenido pegado al
   borde sin margen propio (pills de lugar, el stepper de hora, el
   `<textarea>`) -- con solo 2px, el contenido expandido quedaba pegado a la
   barra de título del acordeón. Scoped a `#ev-editar-paso-campos` (no se
   toca `.ev-ant-acc-body-inner` en general -- Asistencia anticipada sigue
   igual). */
#ev-editar-paso-campos .ev-ant-acc-body-inner { padding-top: 14px; }
/* Textarea de descripción -- auto-crece con el contenido (JS,
   _evEditarDescripcionInput(), js/eventos.js), así que no necesita su propio
   scroll ni la manija de resize nativa (ambos redundantes con el
   auto-ajuste). `resize:none` + `overflow-y:hidden` son las 2 propiedades
   que le faltaban al textarea genérico de `css/ui.css` para este patrón --
   ese sí trae `resize:vertical` por default en el resto de la app, donde no
   hay auto-crecimiento. */
#ev-editar-descripcion-input { resize: none; overflow-y: hidden; }
/* Contador "[n]/150" -- límite real puesto por `maxlength="150"` en el HTML,
   este texto solo refleja el estado. `--warning` (colors.css, mismo token
   que el resto de la app usa para "atención, sin ser un error todavía") en
   vez de un color hardcodeado, activado por `_evEditarDescActualizarContador()`
   cuando quedan 20 caracteres o menos. */
.ev-editar-desc-contador { text-align: right; font-size: 0.72rem; font-weight: 600; color: var(--muted); margin-top: 6px; }
.ev-editar-desc-contador.ev-editar-desc-contador-limite { color: var(--warning); }
/* 2° consumidor del mismo patrón (Tanda C2, "Nuevo evento único", ver
   MANIFEST.md "Cambios recientes") -- misma regla de auto-crecimiento, con
   su propio id porque `#ev-editar-descripcion-input` está scoped por ID (no
   por clase). El contador SÍ reusa las 2 clases genéricas de arriba tal
   cual (`.ev-editar-desc-contador`/`.ev-editar-desc-contador-limite`), cero
   CSS nuevo para eso. */
#ev-crear-unico-descripcion-input { resize: none; overflow-y: hidden; }
/* 3° consumidor del mismo patrón -- paso "Detalles" del wizard activo
   (#ev-crear-paso-config, ver MANIFEST.md "Cambios recientes"), mismo
   criterio de ID-scoping que los 2 anteriores. */
#ev-crear-descripcion-input { resize: none; overflow-y: hidden; }

@media (prefers-color-scheme: dark) {
  .ev-crear-venue-card.activa { background: var(--brand-20); }
}

@keyframes evCalCeldaSel {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.18); }
  100% { transform: scale(1); }
}
.ev-crear-rec-resultado {
  font-size: 0.95rem;
  font-weight: 600;
  margin: 0 0 20px;
}
