@import url('https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:wght@400;500;600;700;800&display=swap');

/* `touch-action:manipulation` (ver "Cambios recientes") -- deshabilita el
   pinch-to-zoom y el double-tap-to-zoom nativos del navegador (algunos
   navegadores móviles ignoran parcialmente `user-scalable=no` del
   `<meta viewport>` solo, hace falta el equivalente en CSS también) sin
   bloquear el panning/scroll normal, que sigue funcionando igual.
   `overflow-anchor:none` UNIVERSAL (ver "Cambios recientes" -- bug real de
   Eventos: al expandir el calendario, las cards de "hoy"/días cercanos
   parecían desaparecer del timeline; no era un filtro de contenido -- el
   DOM/timeline es idéntico antes y después de expandir, confirmado con
   Playwright -- sino "scroll anchoring" nativo del navegador: al crecer el
   alto del acordeón de arriba, Chrome reajusta `scrollY` solo, tratando de
   mantener algún elemento fijo en su posición de pantalla, pero el elemento
   que termina "anclado" queda por detrás del header ahora más alto
   (`position:sticky`, encima de todo) -- las cards no faltan, están tapadas.
   Con esta propiedad en 0, el navegador deja de tocar `scrollY` por su
   cuenta y el layout normal hace su trabajo solo: el contenido de abajo se
   desplaza hacia abajo al crecer el acordeón, como se espera, sin que
   ninguna card quede escondida detrás de un header que creció. Universal
   (no solo el panel de Calendario) por el mismo criterio que
   `overscroll-behavior` de abajo: cualquier acordeón/burbuja de la app que
   cambie de alto cerca del tope de la pantalla puede disparar el mismo
   comportamiento, presente o futuro -- esta app ya maneja el scroll de
   forma explícita en varios lugares (`_evScrollAFecha()` y afines,
   js/eventos.js), así que dejar que el navegador reajuste `scrollY` por su
   cuenta nunca es deseable acá.
   `overscroll-behavior:contain` -- REVERTIDO de universal a puntual (ver
   "Cambios recientes" -- experimento pendiente de confirmar en dispositivo
   real): la versión universal (`*, *::before, *::after`) lo aplicaba a
   TODO elemento de la app, no solo a los que scrollean -- incluidos
   elementos chicos sin scroll propio ninguno, donde la propiedad no debería
   tener ningún efecto pero sí puede interferir con cómo el compositor
   táctil de un navegador móvil real decide a quién le da el gesto (3
   rondas previas de diagnóstico de "scroll trabado" no encontraron
   evidencia en Chromium headless, pero headless no reproduce fielmente
   bugs de compositor táctil real -- "sin repro" ahí no es lo mismo que
   "sin bug"). Ahora puntual, un grep de todo `overflow-y`/`overflow-x`
   `auto`/`scroll` del proyecto (repetido esta sesión, ya no ~10 sino 6
   contenedores reales) da la lista completa de abajo (`html,body` +
   selectores puntuales) -- ningún otro elemento de la app scrollea hoy.
   `scrollbar-width:none` / `*::-webkit-scrollbar{display:none}` UNIVERSAL
   (ver "Cambios recientes" -- bug real recurrente: la scrollbar nativa
   seguía apareciendo en contenedores nuevos pese a haberse "resuelto" antes;
   auditado con Playwright -- computed style, no adivinado -- el problema
   real es que el ocultamiento de scrollbar NUNCA fue universal como el
   `overscroll-behavior` de arriba: se agregó a mano en 3 lugares puntuales
   (`.time-wheel`, `.ev-mes-pills-row`, `#historial-pills-anio`) y quedó ahí,
   así que cualquier OTRO contenedor con scroll propio -- confirmado en
   `.ddp-year-grid`/`.dp-year-grid`, `scrollbar-width:auto` de fábrica -- se
   quedaba sin la regla, sin que nadie tuviera que "olvidarse" de nada: el
   mecanismo en sí nunca cubría contenedores nuevos. Mismo criterio que
   `overscroll-behavior`: una regla universal en vez de una lista para
   mantener a mano; las 3 declaraciones puntuales de arriba quedan
   redundantes y se sacan. */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; touch-action: manipulation; overflow-anchor: none; scrollbar-width: none; }
*::-webkit-scrollbar { display: none; }
html, body, .time-wheel, .ddp-year-grid, .aj-sub-inner, .modal-info-card, .ev-mes-pills-row, .axis-enter { overscroll-behavior: contain; }
body {
  font-family: 'Be Vietnam Pro', 'Segoe UI', Arial, sans-serif; background: var(--bg); color: var(--text);
  -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;
  -webkit-touch-callout: none;
}
button, input, select, textarea { font-family: inherit; }

/* Selección de texto deshabilitada globalmente (arriba, en body) para que la
   app se sienta nativa y no una página web seleccionable — excepciones
   puntuales donde el usuario SÍ necesita seleccionar/copiar texto: los
   campos de formulario reales (edición normal) y los datos de transferencia
   bancaria (.pago-valor, ver "Cambios recientes" — no hay botón "copiar" ni
   Clipboard API en la app, la única forma de copiarlos hoy es seleccionando
   el texto a mano). */
input, textarea,
.pago-valor {
  -webkit-user-select: text; -moz-user-select: text; -ms-user-select: text; user-select: text;
  -webkit-touch-callout: default;
}

.error-msg {
  background: var(--danger-bg); color: var(--danger); border: 1px solid var(--danger-bdr);
  border-radius: 10px; padding: 12px 16px; font-size: 0.9rem;
  margin-bottom: 16px; display: none; font-weight: 500; animation: fadeIn 0.4s ease;
}

::placeholder { color: var(--hint); font-style: italic; font-size: 0.9rem; opacity: 1; }

.toggle-switch { position: relative; display: inline-block; width: 48px; height: 26px; flex-shrink: 0; }
.toggle-switch input { opacity: 0; width: 0; height: 0; }
.toggle-slider { position: absolute; cursor: pointer; inset: 0; background: var(--brand-20); border-radius: 26px; transition: 0.3s; }
.toggle-slider:before { content: ''; position: absolute; height: 20px; width: 20px; left: 3px; bottom: 3px; background: var(--white); border-radius: 50%; transition: 0.3s; }
.toggle-switch input:checked + .toggle-slider { background: var(--brand); }
.toggle-switch input:checked + .toggle-slider:before { transform: translateX(22px); }

.toggle-wrap { display: inline-block; flex-shrink: 0; }
.toggle-btn { position: relative; width: 48px; height: 26px; padding: 0; border: none; border-radius: 26px; background: var(--brand-20); cursor: pointer; transition: background 0.3s; -webkit-tap-highlight-color: transparent; }
.toggle-thumb { position: absolute; height: 20px; width: 20px; left: 3px; bottom: 3px; background: var(--white); border-radius: 50%; transition: transform 0.3s; display: flex; align-items: center; justify-content: center; }
.toggle-thumb::after {
  font-family: 'Material Symbols Outlined';
  font-size: 13px;
  font-style: normal;
  font-variation-settings: 'FILL' 1, 'wght' 600;
  content: 'close';
  color: var(--muted);
  line-height: 1;
}
.toggle-on .toggle-thumb::after {
  content: 'check';
  color: var(--brand);
}
.toggle-btn.toggle-on { background: var(--brand); }
.toggle-btn.toggle-on .toggle-thumb { transform: translateX(22px); }

.fnac-trigger-btn {
  width: 100%; display: flex; align-items: center; justify-content: space-between;
  padding: 13px 16px; border: 0px solid var(--border-light); border-radius: 12px;
  background: var(--surface-2); color: var(--text); font-size: 0.88rem; font-weight: 500;
  cursor: pointer; transition: border-color 0.25s, box-shadow 0.25s; font-family: inherit;
}
.fnac-trigger-btn:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 4px var(--brand-mid); }
.fnac-placeholder { color: var(--hint); font-style: italic; font-size: 0.88rem; }

/* background:var(--bg) propio (ver "Cambios recientes" — bleed-through de
   Home detrás de Ajustes del perfil): ni .pantalla ni su padre .contenedor/
   .page-wrap tenían fondo opaco antes de esto — dependían enteramente de
   heredar el de body pintado detrás. En el uso normal (una sola .pantalla
   activa a la vez, el resto display:none) eso alcanzaba, pero dejaba CERO
   protección ante cualquier escenario donde dos terminaran superpuestas al
   mismo tiempo (glitch de compositing específico de un dispositivo/motor,
   una regresión futura del mecanismo de superposición, etc.) — la de encima
   quedaría transparentando lo que hay debajo en vez de taparlo por completo.
   Con fondo propio, cualquier .pantalla activa opaca por sí sola, sin
   depender de que nada de abajo esté correctamente oculto. */
.pantalla { display: none; background: var(--bg); }
/* Fade simple de entrada (animation:smoothSlideUp, @keyframes en
   css/estilos.css) — ver MANIFEST, "Cambios recientes": se probó acá la
   transición "shared axis X" (piloteada en aj-sub-*, js/perfil.js, luego
   propagada a .pantalla vía ir(), js/ui.js) y se revirtió por acumular
   demasiados bugs de fondo (superposición, footer/título desincronizados,
   salto de scroll) para el tiempo invertido — se mantiene ÚNICAMENTE en
   aj-sub-* (irAjSub()/cerrarAjSub(), implementación bespoke propia en
   css/perfil.css, nunca usó estas clases). Las clases .axis-enter/.axis-leave
   de abajo y shared/axis-transicion.js quedan sin ningún caller activo hoy
   (no se borraron por si algún flujo futuro los quiere reusar) — no
   asumir que "siguen en uso por aj-sub-*", no es así. */
.pantalla.activa { display: block; animation: smoothSlideUp 0.6s var(--ease-sheet); }

/* ═══════════════════════════════════════════════════════
   Transición "shared axis X" (Material Design 3) — mecánica
   compartida, hoy sin caller activo (ver nota de arriba y
   MANIFEST, "Cambios recientes" — revert de ir()/inscMostrarPaso()).
   Se mantiene por si algún flujo futuro la necesita: la mecánica
   JS vive en shared/axis-transicion.js (función axisTransicion()).

   .axis-enter: la pantalla/paso que entra queda superpuesta
   (position:absolute, relativo al contenedor con position:relative
   más cercano — .contenedor en la app principal, .page-wrap en
   inscripción, ambos ya lo tienen) sobre la que sale, que se queda
   en flujo normal (solo necesita la transition, .axis-leave) — así
   ambas quedan montadas/visibles a la vez durante toda la
   transición, sin apilarse una debajo de la otra en el documento.
   z-index:2 para pintar por encima de la que sale (z-index:auto). */
.axis-enter { position: absolute; inset: 0; z-index: 2; overflow-y: auto; }
.axis-enter, .axis-leave {
  transition: transform 0.32s var(--ease-axis), opacity 0.32s var(--ease-axis);
}

select, option, optgroup { font-family: 'Be Vietnam Pro', sans-serif !important; }

@media (prefers-color-scheme: dark) {
  #gsignin-skeleton, #gsignin-skeleton-main {
    background: linear-gradient(90deg,var(--dk-skeleton-base) 25%,var(--dk-skeleton-shine) 50%,var(--dk-skeleton-shine-2) 75%) !important;
    background-size: 200% 100% !important;
  }
}

/* WhatsApp grupo */
.btn-wp-grupo {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; padding: 16px; border-radius: 12px; border: none;
  font-size: 0.88rem; font-weight: 800; cursor: pointer;
  text-decoration: none; transition: all 0.3s ease; letter-spacing: 0.3px;
  margin-top: 16px; background: var(--wa-bg); color: var(--success);
}
.btn-wp-grupo:hover { background: var(--wa-bg-hover); border-color: var(--wa-brand); transform: translateY(-2px); }

/* ─── MODALES INFO (primera vez) ──────────────────────────── */
.modal-info {
  position: fixed; inset: 0; z-index: 8000;
  display: flex; align-items: center; justify-content: center;
  background: var(--overlay-modal-info); padding: 16px;
  animation: fadeIn 0.25s ease;
}
.modal-info-card {
  background: var(--modal-info-card-bg); border-radius: var(--radius);
  padding: 24px 20px 20px; width: 100%; max-width: 360px;
  border: 2px solid var(--brand); max-height: 90vh; overflow-y: auto;
  animation: smoothSlideUp 0.3s var(--ease-sheet);
}
.modal-info-titulo { font-size: 1.15rem; font-weight: 800; color: var(--text); margin-bottom: 4px; }
.modal-info-sub    { font-size: 0.82rem; color: var(--muted); margin-bottom: 20px; }
.modal-info-item   { display: flex; gap: 14px; align-items: flex-start; margin-bottom: 16px; }
.modal-info-icon   { width: 36px; height: 36px; border-radius: 10px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.modal-info-icon .material-symbols-outlined { font-size: 1.2rem; }
.mi-orange { background: var(--brand-light);  color: var(--brand); }
.mi-green  { background: var(--success-bg);   color: var(--success); }
.mi-blue   { background: var(--info-bg);      color: var(--info); }
.mi-amber  { background: var(--amber-light);  color: var(--amber-dark); }
.mi-purple { background: var(--purple-light); color: var(--purple); }
.modal-info-label  { font-size: 0.87rem; font-weight: 700; color: var(--text); margin-bottom: 3px; }
.modal-info-desc   { font-size: 0.8rem; color: var(--muted); line-height: 1.5; }
.modal-info-hr     { border: none; border-top: 1px solid var(--border-2); margin: 14px 0; width: 100%; }
.modal-info-footer { display: flex; gap: 10px; margin-top: 20px; }
.modal-info-footer .btn-text-simple { flex: 1; font-size: 0.78rem; white-space: nowrap; padding: 10px 6px; line-height: 1.3; }
.modal-info-footer .btn-outline      { flex: 1; font-size: 0.78rem; padding: 20px; }

.otro-texto { width: 100%; }

/* Contador "X/max" genérico, reusado por varios inputs de texto corto de la
   app (nombre de venue x3 en Eventos, "Nombre Derby" en Ajustes -- ver
   "Cambios recientes") -- mismo tamaño/peso que `.ev-editar-desc-contador`
   (css/eventos.css, el contador de la descripción de eventos), pero
   `var(--danger)` en vez de `var(--warning)` al acercarse al límite: acá el
   límite es duro (`maxlength` real en el input, no un soft-cap de
   textarea), así que el color de alerta es el mismo que ya usa la app para
   "esto va a fallar/bloquear" en vez del de "atención". */
.form-char-counter { text-align: right; font-size: 0.72rem; font-weight: 600; color: var(--muted); margin-top: 6px; }
.form-char-counter-limite { color: var(--danger); }

/* ─── BOTTOM SHEETS (chrome compartido: reservas + inscripcion) ──── */
.bsheet-overlay { display: none; position: fixed; inset: 0; background: var(--overlay-dark); }
.bsheet {
  display: none; position: fixed; bottom: 0; left: 0; right: 0; max-width: 480px; margin: 0 auto;
  background: var(--bg); border: 1px solid var(--border-light); border-radius: 20px 20px 0 0;
  flex-direction: column; transform: translateY(100%);
  transition: transform 0.35s cubic-bezier(0.16,1,0.3,1);
}
/* position:relative + ::before invisible: agranda el área táctil a toda la
   franja superior del sheet (el alto reservado arriba de .bsheet-title, no
   solo la barrita de 36×4px) sin tocar el tamaño visual de la barra ni el
   layout de lo que sigue — position:absolute no empuja nada, así que
   .bsheet-title queda exactamente donde estaba. Un click dentro de ::before
   sigue disparando el evento sobre .bsheet-handle (el elemento real; los
   pseudo-elementos no son nodos targeteables aparte), así que el listener
   delegado de shared/bsheet.js no necesitó ningún cambio. */
.bsheet-handle { position: relative; width: 36px; height: 4px; background: var(--border-light); border-radius: 2px; margin: 12px auto 4px; flex-shrink: 0; cursor: pointer; }
.bsheet-handle::before {
  content: '';
  position: absolute;
  top: -12px; bottom: -4px; /* -12px llega hasta el borde superior real de .bsheet; -4px llega hasta donde arranca .bsheet-title */
  left: -50vw; right: -50vw; /* ancho de sobra para cubrir cualquier .bsheet (max-width:480px) sin depender de su ancho real */
}
.bsheet-title {
  font-size: 0.72rem; font-weight: 700; color: var(--brand); text-transform: uppercase; letter-spacing: 1.5px;
  text-align: center; padding: 8px 16px 12px; border-bottom: 1px solid var(--border-light); flex-shrink: 0;
}
.bsheet-body { padding: 16px; }
.bsheet-body--flush { padding: 0; }
.bsheet.bsheet-scroll { max-height: 80vh; }
/* Arrastre real de la manija (shared/bsheet.js, touchstart/touchmove/touchend
   en .bsheet-handle) — mismo criterio que .ptr-indicator.ptr-sin-transicion
   (js/home.js): quita la transition mientras el dedo mueve el sheet en
   tiempo real (sin lag), para no competir con el `transform` que el JS setea
   en cada touchmove; se reactiva al soltar (cierre real o snap-back), ambos
   ya animados por la transition normal de .bsheet declarada arriba. */
.bsheet.bsheet-sin-transicion { transition: none; }

/* ─── AVATAR PILL (nombre de clase histórico — es un círculo simple, ver
   MANIFEST "Cambios recientes": se probaron 2 variantes de cápsula (cuadrado
   rotado 45° estilo Pivot, y luego un rectángulo horizontal simple) y ambas
   se revirtieron por decisión de diseño — círculo es el estándar definitivo
   salvo indicación futura. Sin transforms de ningún tipo. object-fit:cover
   resuelve el recorte de la imagen sin necesitar escalarla a mano. */
.avatar-pill {
  position: relative;
  overflow: hidden;
  border-radius: 50%;
  background: var(--surface-2);
  flex-shrink: 0;
}
.avatar-pill img,
.avatar-pill .avatar-pill-letter {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.avatar-pill .avatar-pill-letter {
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  color: var(--brand);
}
/* Placeholder mientras la foto real carga desde su URL (_avatarSetFotoOInicial(),
   js/ui.js) -- reusa el shimmer de .fi-skel-block (css/reservas.css) tal
   cual, solo reposicionado para llenar el círculo (el propio .avatar-pill ya
   recorta a círculo via overflow:hidden; border-radius acá es explícito por
   las dudas). */
.avatar-pill .avatar-pill-skel { position: absolute; inset: 0; width: 100%; height: 100%; border-radius: 50%; }
/* Tamaños — cuadrados (ancho = alto), tomando el lado más chico de la
   variante pill horizontal que estuvo activa antes de este revert, para no
   agrandar el avatar de golpe. */
.avatar-pill--lg { width: 84px; height: 84px; }
.avatar-pill--md { width: 76px; height: 76px; }
.avatar-pill--sm { width: 44px; height: 44px; }
/* --xs (Eventos, ver MANIFEST.md "Cambios recientes"): fila de avatares
   superpuestos de asistentes + filas de la modal de asistencia -- --sm
   (44px) es de más para un avatar chico repetido en fila, necesitaba un
   escalón menor dentro de la misma escala. */
.avatar-pill--xs { width: 26px; height: 26px; }
.avatar-pill--lg .avatar-pill-letter { font-size: 30px; }
.avatar-pill--md .avatar-pill-letter { font-size: 26px; }
.avatar-pill--sm .avatar-pill-letter { font-size: 15px; }
.avatar-pill--xs .avatar-pill-letter { font-size: 11px; }

/* Botón de editar/cámara — badge circular superpuesto en la esquina
   inferior derecha del avatar. Vive como hermano de `.avatar-pill` dentro de
   un wrapper `position:relative` (nunca hijo directo: `.avatar-pill` tiene
   `overflow:hidden` y recortaría la mitad del badge que sobresale). */
.avatar-pill-edit-btn {
  position: absolute;
  right: -4px;
  bottom: -4px;
  width: 30px; height: 30px; border-radius: 50%;
  background: var(--brand);
  border: 2px solid var(--bg);
  z-index: 2;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.avatar-pill-edit-btn .material-symbols-outlined { font-size: 15px; color: var(--white); }

/* Overlay decorativo del cropper (#modal-crop, js/foto.js) — máscara visual
   circular sobre la imagen a recortar (no recorta nada por sí sola, eso lo
   hace el crop box REAL, invisible, de Cropper.js con `aspectRatio: 1`,
   cuadrado); el `box-shadow` gigante oscurece todo lo que queda afuera del
   círculo. Revertida a círculo por decisión de diseño (ver MANIFEST,
   "Cambios recientes") tras probar 2 variantes de cápsula.

   `top/left/width/height` de acá son solo el ESTADO INICIAL/fallback —
   `_fotoSincronizarMascara()` (js/foto.js) los pisa con valores en px
   calculados a partir de `getCropBoxData()` (el crop box real de Cropper)
   apenas la librería está lista, y los limpia de nuevo a estos valores al
   cerrar el modal. Antes esta máscara era 100% CSS, posicionada con
   porcentajes de `#crop-viewport` sin ninguna relación con el crop box
   real — bug real encontrado y confirmado con Playwright (ver detalle
   completo en el comentario de `_fotoSincronizarMascara()`, js/foto.js):
   el círculo que el usuario veía y el rectángulo que realmente recortaba
   `getCroppedCanvas()` eran dos cálculos independientes que solo
   coincidían por casualidad con una imagen cuadrada — con cualquier foto
   de aspecto distinto (el caso normal) la selección visual no
   correspondía a la foto final. **Desvío deliberado del pedido literal,
   con evidencia (histórico, previo al bug de arriba):** se pidió
   `width:60%;height:60%` fijos — probado tal cual con Playwright, da una
   máscara de ~234×412px (verificado en viewport móvil 390×844): una
   ELIPSE, no un círculo, porque `#crop-viewport` no es cuadrado. Fix de
   ese momento: `aspect-ratio:1` en vez de `height:60%` fijo; `max-height:
   60%` de resguardo para viewports muy anchos/bajos. */
.crop-area {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 60%;
  aspect-ratio: 1;
  max-height: 60%;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  box-shadow: 0 0 0 9999px rgba(0,0,0,.55);
  pointer-events: none;
}
/* Chrome default de Cropper.js oculto — .crop-area de arriba ya cumple de
   guía visual. La regla completa (7 selectores, incluye .cropper-dashed —
   el faltante real en las 2 vueltas anteriores: es la grilla de "regla de
   tercios" que Cropper.js muestra SOLO mientras se arrastra/redimensiona
   (togglea su propia clase cropper-hidden), invisible en cualquier captura
   en reposo — por eso el bug sobrevivió 2 rondas de verificación estática)
   vive en index.html/inscripcion/index.html, en un <style> después del
   <link> de cropper.min.css — a propósito ahí y no acá: gana por
   especificidad (#modal-crop .clase) Y por orden de cascada sobre el CSS
   default de la librería, sin depender de que Cropper.js no reescriba
   estilos inline con !important en alguna versión futura. No duplicar
   estas reglas acá. */