:root {
  --brand: #F26522;
  --brand-300: #FFA270;
  --brand-400: #FF7A38;
  --brand-500: #F26522;
  --brand-600: #D94D0C;
  --brand-700: #B83A05;
  --brand-glow: rgba(242, 101, 34, 0.35);
  --gold: #F59E0B;
  --gold-400: #FBBF24;
}

.hidden {
  display: none !important;
}

/* Visibilidad específica para Sidebar Desktop.
 *
 * El `!important` de la primera regla NO sobra. El aside lleva también la
 * clase `flex` de Tailwind, que se inyecta en una hoja en línea creada por su
 * script; esa hoja va después de este fichero, así que con la misma
 * especificidad `.flex` ganaba por orden y la barra lateral de escritorio se
 * colaba en el móvil tapando media pantalla. Es la misma trampa que con
 * `hidden`: aquí no se puede empatar, hay que ganar. */
.sidebar-desktop {
  display: none !important;
}
@media (min-width: 1024px) {
  .sidebar-desktop {
    display: flex !important;
  }
}

/* ==========================================================================
   TEMA OSCURO (DARK MODE - PREDETERMINADO)
   ========================================================================== */
body.dark, body:not(.light) {
  --bg-main: #08080C;
  --bg-card: rgba(18, 18, 26, 0.82);
  --bg-card-strong: rgba(14, 14, 20, 0.96);
  --bg-input: rgba(255, 255, 255, 0.06);
  --border-color: rgba(255, 255, 255, 0.10);
  --text-main: #FFFFFF;
  --text-muted: #CBD5E1;
  --text-subtle: #94A3B8;
  --table-header-bg: #12121A;
  --table-row-hover: rgba(242, 101, 34, 0.08);
  --table-even-row: rgba(255, 255, 255, 0.018);
  --card-shadow: 0 16px 36px -6px rgba(0, 0, 0, 0.75), 0 0 24px rgba(242, 101, 34, 0.14), inset 0 1px 0 rgba(255, 255, 255, 0.1);
  --orb-opacity-1: 0.32;
  --orb-opacity-2: 0.26;
  --grid-line: rgba(242, 101, 34, 0.06);
}

/* ==========================================================================
   TEMA CLARO (LIGHT MODE - ALTO CONTRASTE Y VIBRANTE NARANJA)
   ========================================================================== */
body.light {
  --bg-main: #F4F6F9;
  --bg-card: rgba(255, 255, 255, 0.94);
  --bg-card-strong: #FFFFFF;
  --bg-input: #FFFFFF;
  --border-color: #E2E8F0;
  --text-main: #0F172A;
  --text-muted: #334155;
  /* #64748B daba 4,40:1 sobre el fondo claro y no llegaba al minimo de
     4,5:1. #4B5563 da 6,98:1. */
  --text-subtle: #4B5563;
  --table-header-bg: #F8FAFC;
  --table-row-hover: rgba(242, 101, 34, 0.08);
  --table-even-row: #FAFCFF;
  --card-shadow: 0 12px 28px -4px rgba(242, 101, 34, 0.10), 0 4px 12px -2px rgba(0, 0, 0, 0.04), 0 0 0 1px #E2E8F0;
  --orb-opacity-1: 0.18;
  --orb-opacity-2: 0.14;
  --grid-line: rgba(242, 101, 34, 0.08);
}

/* Corrección de visibilidad para Modo Claro */
body.light .text-white,
body.light [class*="text-white"] {
  color: #0F172A !important;
}

body.light header .text-white,
body.light header [class*="text-white"],
body.light .btn-primary .text-white,
body.light .btn-primary [class*="text-white"],
body.light .badge-white,
body.light .carnet-card .text-white {
  color: #FFFFFF !important;
}

body.light .text-white\/80,
body.light .text-white\/70,
body.light .text-white\/60,
body.light .text-white\/55,
body.light .text-white\/50 {
  color: #334155 !important;
}

body.light .text-white\/45,
body.light .text-white\/40,
body.light .text-white\/35,
body.light .text-white\/30 {
  color: #64748B !important;
}

body.light .bg-ink-950,
body.light .bg-ink-900,
body.light .bg-ink-800 {
  background-color: #FFFFFF !important;
}

body.light .border-white\/10,
body.light .border-white\/20,
body.light .border-white\/5 {
  border-color: #E2E8F0 !important;
}

body.light .divide-white\/5 > * {
  border-color: #E2E8F0 !important;
}

body.light .bg-white\/\[\.04\],
body.light .bg-white\/\[\.03\],
body.light .bg-white\/5,
body.light .bg-white\/10 {
  background-color: rgba(242, 101, 34, 0.05) !important;
}

body.light tr[data-id]:hover {
  background-color: rgba(242, 101, 34, 0.07) !important;
}

html {
  scroll-behavior: smooth;
}

body {
  background-color: var(--bg-main);
  color: var(--text-main);
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  min-height: 100vh;
  position: relative;
  overflow-x: hidden;
  transition: background-color 0.3s ease, color 0.3s ease;
}

::selection {
  background: var(--brand);
  color: #FFFFFF;
}

.font-display {
  font-family: 'Outfit', sans-serif;
}

/* ==========================================================================
   FONDOS 3D / MALLA ESPACIAL FUTSAL CON EL NARANJA OFICIAL
   ========================================================================== */
.bg-canvas {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  overflow: hidden;
}

.bg-canvas::before {
  content: '';
  position: absolute;
  inset: -100px;
  background-image: 
    linear-gradient(to right, var(--grid-line) 1.5px, transparent 1.5px),
    linear-gradient(to bottom, var(--grid-line) 1.5px, transparent 1.5px);
  background-size: 54px 54px;
  mask-image: radial-gradient(circle at 50% 30%, black 45%, transparent 88%);
  -webkit-mask-image: radial-gradient(circle at 50% 30%, black 45%, transparent 88%);
}

.orb {
  position: absolute;
  border-radius: 9999px;
  filter: blur(88px);
  pointer-events: none;
  animation: floatOrb 14s ease-in-out infinite alternate;
}

.orb-1 {
  width: 580px;
  height: 580px;
  background: radial-gradient(circle, var(--brand) 0%, var(--brand-700) 65%, transparent 80%);
  opacity: var(--orb-opacity-1);
  top: -160px;
  left: -100px;
}

.orb-2 {
  width: 520px;
  height: 520px;
  background: radial-gradient(circle, var(--gold) 0%, var(--brand) 60%, transparent 80%);
  opacity: var(--orb-opacity-2);
  bottom: -150px;
  right: -80px;
  animation-duration: 18s;
  animation-direction: alternate-reverse;
}

.orb-3 {
  width: 380px;
  height: 380px;
  background: radial-gradient(circle, var(--brand-400) 0%, transparent 70%);
  opacity: 0.16;
  top: 35%;
  right: 20%;
  animation-duration: 12s;
}

@keyframes floatOrb {
  0% { transform: translate3d(0, 0, 0) scale(1); }
  50% { transform: translate3d(35px, -25px, 0) scale(1.08); }
  100% { transform: translate3d(-20px, 30px, 0) scale(0.95); }
}

/* ==========================================================================
   TARJETAS GLASS Y NEOMÓRFICAS
   ========================================================================== */
.glass {
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  transition: all 0.25s ease;
}

.glass-strong {
  background: var(--bg-card-strong);
  border: 1px solid var(--border-color);
  backdrop-filter: blur(24px);
  -webkit-backdrop-filter: blur(24px);
  transition: all 0.25s ease;
}

.neo {
  box-shadow: var(--card-shadow);
}

/* ==========================================================================
   CONTROLES Y FORMULARIOS
   ========================================================================== */
.inp {
  width: 100%;
  border-radius: 0.75rem;
  border: 1.5px solid var(--border-color);
  background: var(--bg-input);
  padding: 0.7rem 0.95rem;
  font-size: 0.875rem;
  color: var(--text-main);
  outline: none;
  transition: all 0.2s ease;
}

.inp:hover {
  border-color: var(--brand-400);
}

.inp:focus {
  border-color: var(--brand);
  box-shadow: 0 0 0 3.5px rgba(242, 101, 34, 0.28);
  background: var(--bg-input);
}

select.inp option {
  background: var(--bg-card-strong);
  color: var(--text-main);
}

/* ==========================================================================
   BOTONES
   ========================================================================== */
.btn-primary {
  background: linear-gradient(135deg, var(--brand-400), var(--brand) 55%, var(--brand-700));
  box-shadow: 0 6px 20px rgba(242, 101, 34, 0.45);
  transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
  color: #FFFFFF !important;
  cursor: pointer;
}

.btn-primary:hover {
  transform: translateY(-1.5px);
  box-shadow: 0 10px 28px rgba(242, 101, 34, 0.6);
  filter: brightness(1.08);
}

.btn-ghost {
  border: 1.5px solid var(--border-color);
  background: var(--bg-input);
  transition: all 0.2s ease;
  color: var(--text-main);
  cursor: pointer;
}

.btn-ghost:hover {
  background: rgba(242, 101, 34, 0.12);
  border-color: var(--brand);
  color: var(--brand);
}

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.35rem;
  height: 2.35rem;
  border-radius: 0.7rem;
  border: 1.5px solid var(--border-color);
  background: var(--bg-input);
  transition: all 0.2s ease;
  color: var(--text-main);
  cursor: pointer;
  flex: none;
}

.icon-btn:hover {
  background: rgba(242, 101, 34, 0.18);
  border-color: var(--brand);
  color: var(--brand);
  transform: translateY(-1px);
}

.icon-btn.danger:hover {
  background: rgba(244, 63, 94, 0.18);
  border-color: #F43F5E;
  color: #F43F5E;
}

/* ==========================================================================
   NAVEGACIÓN LATERAL IZQUIERDA (SIDEBAR)
   ========================================================================== */
.nav-btn {
  transition: all 0.2s ease;
  border-left: 3.5px solid transparent;
  color: var(--text-muted);
  cursor: pointer;
}

.nav-btn:hover {
  color: var(--brand);
  background: rgba(242, 101, 34, 0.08);
}

.nav-btn.active {
  background: linear-gradient(90deg, rgba(242, 101, 34, 0.24), rgba(242, 101, 34, 0.05));
  color: var(--brand);
  border-left-color: var(--brand);
  font-weight: 800;
}

/* ==========================================================================
   PAGINADOR
   ========================================================================== */
.page-btn {
  min-width: 2.2rem;
  height: 2.2rem;
  border-radius: 0.6rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.85rem;
  font-weight: 700;
  border: 1.5px solid var(--border-color);
  background: var(--bg-input);
  color: var(--text-main);
  transition: all 0.15s ease;
  cursor: pointer;
}

.page-btn:hover {
  border-color: var(--brand);
  color: var(--brand);
}

.page-btn.active {
  background: linear-gradient(135deg, var(--brand-400), var(--brand));
  color: #FFFFFF !important;
  border-color: var(--brand);
  box-shadow: 0 4px 12px rgba(242, 101, 34, 0.4);
}

/* ==========================================================================
   LOGIN CARD
   ========================================================================== */
.login-card {
  border-radius: 1.85rem;
  padding: 1.5px;
  background: linear-gradient(135deg, rgba(242, 101, 34, 0.95), rgba(255, 255, 255, 0.2), rgba(245, 158, 11, 0.75));
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.85), 0 0 40px rgba(242, 101, 34, 0.35);
}

.login-card > div {
  background: var(--bg-card-strong);
  border-radius: 1.8rem;
  overflow: hidden;
}

.login-brand {
  padding: 2.5rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  border-bottom: 1px solid var(--border-color);
  background: linear-gradient(145deg, rgba(0, 0, 0, 0.7), transparent 60%, rgba(242, 101, 34, 0.15));
}

@media (min-width: 768px) {
  .login-brand {
    border-bottom: none;
    border-right: 1px solid var(--border-color);
  }
}

.logo-card {
  background: #FFFFFF;
  border-radius: 1.25rem;
  box-shadow: 0 0 0 2.5px rgba(242, 101, 34, 0.8), 0 16px 36px rgba(0, 0, 0, 0.3);
}

/* ==========================================================================
   MODALES
   ========================================================================== */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(5, 5, 8, 0.75);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  z-index: 80;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  overflow-y: auto;
}

.modal-body {
  background: var(--bg-card-strong);
  color: var(--text-main);
  border: 1.5px solid var(--border-color);
  border-radius: 1.5rem;
  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
  max-height: 90vh;
  overflow-y: auto;
}

/* ==========================================================================
   ANIMACIONES
   ========================================================================== */
.shine {
  position: relative;
  overflow: hidden;
}

.shine::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 40%;
  background: linear-gradient(105deg, transparent, rgba(255, 255, 255, 0.3), transparent);
  animation: shineSweep 3.4s ease infinite;
}

@keyframes shineSweep {
  0% { transform: translateX(-120%); }
  100% { transform: translateX(220%); }
}

@keyframes spinFast {
  to { transform: rotate(360deg); }
}
.spin-anim {
  animation: spinFast 0.65s linear infinite;
}

.anim-in { animation: fadeUp 0.35s cubic-bezier(0.16, 1, 0.3, 1) both; }
.anim-pop { animation: popIn 0.25s cubic-bezier(0.16, 1, 0.3, 1) both; }

@keyframes fadeUp {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes popIn {
  from { opacity: 0; transform: scale(0.96) translateY(6px); }
  to { opacity: 1; transform: scale(1) translateY(0); }
}

/* ==========================================================================
   SCROLLBARS
   ========================================================================== */
.scroll-thin::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}
.scroll-thin::-webkit-scrollbar-track {
  background: transparent;
}
.scroll-thin::-webkit-scrollbar-thumb {
  background: rgba(242, 101, 34, 0.35);
  border-radius: 99px;
}
.scroll-thin::-webkit-scrollbar-thumb:hover {
  background: var(--brand);
}

/* ==========================================================================
   CARNET DIGITAL
   ========================================================================== */
.carnet-card {
  background: linear-gradient(135deg, #181824 0%, #0D0D14 100%);
  border: 1.5px solid rgba(242, 101, 34, 0.6);
  border-radius: 1.25rem;
  position: relative;
  overflow: hidden;
  color: #FFFFFF !important;
}
.carnet-card::before {
  content: '';
  position: absolute;
  top: -50%;
  left: -50%;
  width: 200%;
  height: 200%;
  background: radial-gradient(circle, rgba(242, 101, 34, 0.25) 0%, transparent 65%);
  pointer-events: none;
}

/* ==========================================================================
   FONDO DEL PANEL (TRAS EL INICIO DE SESIÓN)
   Aurora en el naranja del club + pista de futsal trazada + viñeta.
   Se define aparte del fondo del login para que cada pantalla tenga carácter.
   ========================================================================== */
body.dark, body:not(.light) {
  --ac-line: rgba(255, 255, 255, 0.062);
  --ac-line-strong: rgba(242, 101, 34, 0.14);
  --ac-aurora-opacity: 0.55;
  --ac-beam: rgba(242, 101, 34, 0.20);
  --ac-vignette: rgba(3, 3, 7, 0.88);
  --ac-cool: #2B1B4D;
}

body.light {
  --ac-line: rgba(15, 23, 42, 0.055);
  --ac-line-strong: rgba(242, 101, 34, 0.16);
  --ac-aurora-opacity: 0.26;
  --ac-beam: rgba(242, 101, 34, 0.13);
  --ac-vignette: rgba(100, 116, 139, 0.20);
  --ac-cool: #C7D2FE;
}

/* Al entrar en el panel se retira el fondo de la pantalla de acceso */
body.app-on .bg-canvas {
  opacity: 0;
  transition: opacity 0.6s ease;
}

.app-canvas {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

/* El contenido del panel siempre por encima del fondo.
   Sólo <main> necesita posicionarse: la cabecera es sticky y el sidebar y el
   drawer ya son fixed con su propio z-index. Tocar su `position` los rompería. */
#app > main {
  position: relative;
  z-index: 1;
}

/* --- Capa 1: aurora de color en deriva lenta --- */
.ac-aurora {
  position: absolute;
  inset: -25%;
  opacity: var(--ac-aurora-opacity);
  filter: blur(70px);
  background:
    radial-gradient(38% 42% at 18% 22%, var(--brand) 0%, transparent 62%),
    radial-gradient(34% 38% at 82% 12%, var(--gold) 0%, transparent 60%),
    radial-gradient(46% 50% at 70% 88%, var(--ac-cool) 0%, transparent 65%),
    radial-gradient(30% 34% at 34% 78%, var(--brand-700) 0%, transparent 62%);
  animation: acDrift 30s ease-in-out infinite alternate;
}

/* --- Capa 2: pista de fútbol sala trazada a línea fina --- */
.ac-court {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(1500px, 132vw);
  aspect-ratio: 5 / 3;
  border: 2px solid var(--ac-line);
  border-radius: 6px;
  animation: acBreathe 24s ease-in-out infinite alternate;
}

/* Círculo central */
.ac-court::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 21%;
  aspect-ratio: 1;
  border: 2px solid var(--ac-line-strong);
  border-radius: 50%;
}

/* Línea de medio campo */
.ac-court::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 2px;
  transform: translateX(-50%);
  background: var(--ac-line);
}

/* --- Capa 3: halo cálido en la cabecera --- */
.ac-beam {
  position: absolute;
  top: -30vh;
  left: 50%;
  transform: translateX(-50%);
  width: 120vw;
  height: 60vh;
  background: radial-gradient(ellipse at center, var(--ac-beam) 0%, transparent 68%);
  filter: blur(28px);
}

/* --- Capa 4: viñeta para que las tarjetas destaquen --- */
.ac-vignette {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 92% 78% at 50% 42%, transparent 42%, var(--ac-vignette) 100%);
}

@keyframes acDrift {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  50%  { transform: translate3d(3%, -2.5%, 0) scale(1.06); }
  100% { transform: translate3d(-2.5%, 2%, 0) scale(1.02); }
}

@keyframes acBreathe {
  0%   { opacity: 0.85; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 1;    transform: translate(-50%, -50%) scale(1.03); }
}

/* Sacudida de la tarjeta de acceso al fallar las credenciales */
.anim-shake {
  animation: shakeCard 0.42s cubic-bezier(0.36, 0.07, 0.19, 0.97);
}

@keyframes shakeCard {
  10%, 90% { transform: translateX(-2px); }
  20%, 80% { transform: translateX(4px); }
  30%, 50%, 70% { transform: translateX(-7px); }
  40%, 60% { transform: translateX(7px); }
}

/* Sin animaciones para quien las tenga desactivadas en el sistema */
@media (prefers-reduced-motion: reduce) {
  .ac-aurora,
  .ac-court,
  .orb,
  .anim-shake {
    animation: none !important;
  }
}


/* ==========================================================================
   ESCÁNER DE ABONO
   ========================================================================== */
.esc-visor {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: 1rem;
  overflow: hidden;
  background: #05050A;
  border: 1px solid var(--border-color);
}

.esc-visor video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: #05050A;
}

.esc-placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.85rem;
  padding: 1.5rem;
  text-align: center;
  color: rgba(255, 255, 255, 0.55);
  font-size: 0.8rem;
  line-height: 1.5;
}

.esc-placeholder [data-icon] {
  font-size: 2.4rem;
  opacity: 0.35;
}

/* Con la cámara encendida el marcador de ayuda estorba */
.esc-visor.activo .esc-placeholder {
  display: none;
}

.esc-zona {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  /* Oscurece todo menos el marco, para guiar el encuadre */
  background: rgba(0, 0, 0, 0.28);
}

.esc-marco {
  position: relative;
  width: 78%;
  aspect-ratio: 1.62;
  border: 2px solid rgba(255, 255, 255, 0.45);
  border-radius: 0.9rem;
  box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.34);
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.esc-marco.detectado {
  border-color: var(--brand);
  box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.34), 0 0 26px rgba(242, 101, 34, 0.55);
}

.esc-esquina {
  position: absolute;
  width: 22px;
  height: 22px;
  border: 3px solid var(--brand);
  transition: border-color 0.2s ease;
}

.ee-tl { top: -3px; left: -3px; border-right: 0; border-bottom: 0; border-top-left-radius: 0.9rem; }
.ee-tr { top: -3px; right: -3px; border-left: 0; border-bottom: 0; border-top-right-radius: 0.9rem; }
.ee-bl { bottom: -3px; left: -3px; border-right: 0; border-top: 0; border-bottom-left-radius: 0.9rem; }
.ee-br { bottom: -3px; right: -3px; border-left: 0; border-top: 0; border-bottom-right-radius: 0.9rem; }

/* Veredicto del acceso */
.esc-ok {
  border-left: 5px solid #34D399 !important;
  background: linear-gradient(90deg, rgba(52, 211, 153, 0.12), transparent 45%), var(--bg-card);
}

.esc-aviso {
  border-left: 5px solid #FBBF24 !important;
  background: linear-gradient(90deg, rgba(251, 191, 36, 0.12), transparent 45%), var(--bg-card);
}

.esc-error {
  border-left: 5px solid #FB7185 !important;
  background: linear-gradient(90deg, rgba(251, 113, 133, 0.12), transparent 45%), var(--bg-card);
}

.icon-btn.active {
  border-color: var(--brand) !important;
  color: var(--brand) !important;
  background: rgba(242, 101, 34, 0.15) !important;
}


/* ==========================================================================
   TABLAS CON CABECERA FIJA
   `position: sticky` va en los <th>, no en el <tr>: en el <tr> no es fiable.
   Y hace falta `border-collapse: separate`, porque con `collapse` los bordes
   de una celda fija se quedan atrás al desplazar.
   ========================================================================== */
.tabla {
  width: 100%;
  text-align: left;
  border-collapse: separate;
  border-spacing: 0;
}

.tabla thead th {
  position: sticky;
  top: 0;
  z-index: 20;
  /* Fondo OPACO: si se ve a través, las filas se cuelan por debajo */
  background: var(--table-header-bg);
  color: var(--text-subtle);
  font-weight: 800;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.045em;
  white-space: nowrap;
  padding: 0.85rem 0.75rem;
  /* El borde va por sombra: un `border` en celda fija se pierde al desplazar */
  box-shadow: inset 0 -1px 0 var(--border-color);
}

.tabla thead th[data-sort] {
  cursor: pointer;
  user-select: none;
  transition: color 0.15s ease;
}

.tabla thead th[data-sort]:hover {
  color: var(--brand);
}

/* Indicador de ordenación: sustituye a las flechas escritas a mano */
.tabla thead th[data-sort]::after {
  content: '↕';
  margin-left: 0.35rem;
  opacity: 0.35;
  font-size: 0.9em;
}

.tabla thead th[data-sort].orden-asc::after { content: '↑'; opacity: 1; color: var(--brand); }
.tabla thead th[data-sort].orden-desc::after { content: '↓'; opacity: 1; color: var(--brand); }

.tabla tbody tr {
  transition: background-color 0.15s ease;
}

.tabla tbody tr:nth-child(even) {
  background: var(--table-even-row);
}

.tabla tbody tr:hover {
  background: var(--table-row-hover);
}

.tabla tbody td {
  padding: 0.85rem 0.75rem;
  border-top: 1px solid var(--border-color);
  vertical-align: middle;
}

.tabla tbody tr.seleccionada {
  background: rgba(242, 101, 34, 0.12);
}

/* El contenedor con scroll redondea, así que la tabla no debe desbordarlo */
.tabla-wrap {
  border-radius: 1rem;
  overflow: hidden;
}


/* ==========================================================================
   LISTA DE ACCESOS DEL DÍA
   ========================================================================== */
.acceso-fila {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  padding: 0.6rem 0.5rem;
  border-bottom: 1px solid var(--border-color);
  border-radius: 0.6rem;
  transition: background-color 0.15s ease;
}

.acceso-fila:last-child {
  border-bottom: 0;
}

.acceso-fila.cursor-pointer:hover {
  background: var(--table-row-hover);
}

.chip-filtro {
  padding: 0.3rem 0.7rem;
  border-radius: 0.6rem;
  border: 1px solid var(--border-color);
  background: transparent;
  color: var(--text-subtle);
  font-size: 11px;
  font-weight: 700;
  cursor: pointer;
  transition: all 0.15s ease;
}

.chip-filtro:hover {
  color: var(--brand);
  border-color: var(--brand-400);
}

.chip-filtro.active {
  background: rgba(242, 101, 34, 0.15);
  border-color: var(--brand);
  color: var(--brand);
}

/* ==========================================================================
   AVISO DE GUARDADO
   `localStorage` falla en silencio: se llena, o lo bloquea una ventana
   privada. Sin este cartel la aplicación seguiría igual mientras el trabajo
   del día se pierde al cerrar, así que ocupa el ancho entero y no se cierra.
   ========================================================================== */
.aviso-guardado {
  position: sticky;
  top: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.7rem 1rem;
  font-size: 12px;
  font-weight: 700;
  line-height: 1.35;
  background: #B45309;
  color: #fff;
  border-bottom: 1px solid rgba(0, 0, 0, 0.3);
}

.aviso-guardado.critico {
  background: #B91C1C;
  animation: latido-aviso 2.4s ease-in-out infinite;
}

@keyframes latido-aviso {
  0%, 100% { box-shadow: inset 0 0 0 0 rgba(255, 255, 255, 0); }
  50%      { box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.35); }
}

.aviso-guardado button {
  color: #fff;
  opacity: 0.9;
}
.aviso-guardado button:hover { opacity: 1; }

/* ==========================================================================
   ESTADO DEL COBRO
   ========================================================================== */
.chip-pago {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 800;
  white-space: nowrap;
  border: 1px solid transparent;
}

.chip-pago.pagado {
  background: rgba(16, 185, 129, 0.15);
  border-color: rgba(16, 185, 129, 0.35);
  color: #10B981;
}

.chip-pago.pendiente {
  background: rgba(245, 158, 11, 0.15);
  border-color: rgba(245, 158, 11, 0.4);
  color: #F59E0B;
}

/* Campo con un valor que no supera su comprobación (DNI con letra errónea) */
.inp-error {
  border-color: #F43F5E !important;
  box-shadow: 0 0 0 3px rgba(244, 63, 94, 0.15) !important;
}

/* ==========================================================================
   PARTIDO ACTIVO EN EL ESCÁNER
   ========================================================================== */
.esc-partido {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem 1rem;
  border-radius: 1rem;
  border: 1px solid var(--border-color);
  background: var(--bg-card);
  backdrop-filter: blur(10px);
}

.esc-partido.sin-partido {
  border-color: rgba(245, 158, 11, 0.4);
  background: rgba(245, 158, 11, 0.06);
}

/* ==========================================================================
   CALENDARIO DE PARTIDOS
   ========================================================================== */
.partido-fila {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding: 0.75rem;
  border-radius: 0.9rem;
  border: 1px solid transparent;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.partido-fila + .partido-fila {
  border-top: 1px solid var(--border-color);
  border-radius: 0.9rem;
}

.partido-fila:hover {
  background: var(--table-row-hover);
}

.partido-fila.activo {
  border-color: var(--brand);
  background: rgba(242, 101, 34, 0.08);
}

.partido-fila.jugado .partido-fecha {
  opacity: 0.45;
}

.partido-fecha {
  flex: none;
  width: 3.4rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0.35rem 0;
  border-radius: 0.7rem;
  background: var(--bg-input);
  border: 1px solid var(--border-color);
  line-height: 1.1;
}

.partido-dia {
  font-family: 'Outfit', system-ui, sans-serif;
  font-size: 1.15rem;
  font-weight: 800;
  color: var(--brand);
}

.partido-mes {
  font-size: 9.5px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-subtle);
}

.partido-hora {
  font-size: 9.5px;
  font-weight: 700;
  opacity: 0.55;
  margin-top: 0.1rem;
}

.chip-provisional,
.chip-activo {
  font-size: 9.5px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 0.1rem 0.4rem;
  border-radius: 0.35rem;
  white-space: nowrap;
}

.chip-provisional {
  background: rgba(245, 158, 11, 0.15);
  border: 1px solid rgba(245, 158, 11, 0.4);
  color: #F59E0B;
}

.chip-activo {
  background: rgba(242, 101, 34, 0.18);
  border: 1px solid var(--brand);
  color: var(--brand);
}

/* ==========================================================================
   DISEÑO MÓVIL · «MODO PUERTA»
   ==========================================================================

   Quién usa esto y cómo: gente del club, en buena parte mayor y sin soltura
   con el móvil, DE PIE en la puerta del pabellón, con una mano, con cola
   detrás y a veces de noche. Eso manda sobre cualquier consideración
   estética:

   - Nada por debajo de 17 px de texto útil, y el veredicto a 40 px.
   - Los botones que hay que pulsar miden 60 px de alto y ocupan el ancho.
     Nada de iconos sueltos sin etiqueta: al lado del icono va la palabra.
   - Las dos formas de comprobar un abono —cámara y a mano— pesan LO MISMO.
     Si la cámara falla, la alternativa tiene que estar a la vista, no
     escondida más abajo.
   - El aviso de acceso baja desde arriba y dice QUÉ HACER, no sólo qué ha
     pasado: «Puede pasar», «Mándalo a taquilla».
   - Navegación abajo, al alcance del pulgar, siempre visible y con texto.
   ========================================================================== */

/* ---------- Escala base ---------------------------------------------- */
@media (max-width: 900px) {
  html { font-size: 17px; }

  /* Hueco inferior para que la barra de navegación no tape el contenido */
  #app > main { padding-bottom: 6.5rem; }

  /* Títulos de sección: uno por pantalla, grande y sin adornos */
  #app main h1 {
    font-size: 1.75rem;
    line-height: 1.15;
  }

  /* Los textos diminutos de escritorio no valen aquí.
     OJO CON EL SELECTOR: `#modalContainer` cuelga del <body>, FUERA de `#app`,
     así que con `#app main` a secas las etiquetas de TODOS los diálogos -las
     que dicen qué se está escribiendo- se quedaban en 12 px, en mayúsculas y
     al 70 % de opacidad. Ilegibles para quien usa esto. */
  #app main .text-\[10px\],
  #app main .text-\[10\.5px\],
  #app main .text-\[11px\],
  #modalContainer .text-\[10px\],
  #modalContainer .text-\[10\.5px\],
  #modalContainer .text-\[11px\] { font-size: 0.8rem !important; }
  #app main .text-xs,
  #modalContainer .text-xs { font-size: 0.85rem !important; }
  #app main .text-sm,
  #modalContainer .text-sm { font-size: 0.95rem !important; }
}

/* ---------- Botones y campos ------------------------------------------ */
@media (max-width: 900px) {
  .btn-primary,
  .btn-ghost {
    min-height: 3.1rem;
    font-size: 1rem;
  }

  .icon-btn {
    width: 3rem;
    height: 3rem;
  }

  .inp {
    min-height: 3.1rem;
    font-size: 1.05rem;
    padding: 0.8rem 1rem;
  }

  /* Cualquier grupo de botones de cabecera pasa a columna y ancho completo */
  #app main section > div:first-child .flex.flex-wrap.items-center,
  #app main section > div:first-child > .flex.items-center {
    width: 100%;
  }
  #app main section > div:first-child .flex.flex-wrap > button,
  #app main section > div:first-child .flex.items-center > button {
    flex: 1 1 auto;
    justify-content: center;
  }
}

/* Botón grande de acción principal, para el modo puerta */
.btn-gigante {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.7rem;
  width: 100%;
  min-height: 3.9rem;
  padding: 0 1.2rem;
  border-radius: 1.1rem;
  font-family: 'Outfit', sans-serif;
  font-size: 1.15rem;
  font-weight: 800;
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: transform 0.12s ease, filter 0.15s ease;
}
.btn-gigante:active { transform: scale(0.98); }
.btn-gigante [data-icon] svg { width: 1.6rem; height: 1.6rem; }

.btn-gigante.principal {
  background: linear-gradient(135deg, var(--brand-400), var(--brand) 55%, var(--brand-700));
  color: #fff;
  border: none;
  box-shadow: 0 8px 22px rgba(242, 101, 34, 0.45);
}
.btn-gigante.secundario {
  background: var(--bg-input);
  color: var(--text-main);
  border: 2px solid var(--border-color);
}
.btn-gigante.secundario:active { border-color: var(--brand); }

.btn-gigante.parar {
  background: rgba(244, 63, 94, 0.14);
  color: #F43F5E;
  border: 2px solid rgba(244, 63, 94, 0.45);
}

/* ==========================================================================
   BARRA DE NAVEGACIÓN INFERIOR
   Sustituye al menú de hamburguesa en el móvil: siempre visible, al alcance
   del pulgar y con la palabra debajo del icono. Un menú que hay que abrir es
   un menú que no se encuentra.
   ========================================================================== */
.nav-inferior {
  display: none;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 45;
  background: var(--bg-card-strong);
  border-top: 1px solid var(--border-color);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  padding-bottom: env(safe-area-inset-bottom, 0);
  box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.28);
}

@media (max-width: 1023px) {
  .nav-inferior { display: flex; }
}

.nav-inferior button {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.2rem;
  min-height: 4rem;
  padding: 0.5rem 0.25rem;
  color: var(--text-subtle);
  font-size: 0.72rem;
  font-weight: 700;
  border: none;
  background: none;
  cursor: pointer;
  border-top: 3px solid transparent;
  transition: color 0.15s ease, border-color 0.15s ease;
}

.nav-inferior button [data-icon] svg {
  width: 1.5rem;
  height: 1.5rem;
}

.nav-inferior button.active {
  color: var(--brand);
  border-top-color: var(--brand);
  background: rgba(242, 101, 34, 0.07);
}

/* ==========================================================================
   VEREDICTO DEL ESCÁNER
   En el móvil ocupa la pantalla entera. Quien está en la puerta tiene que
   saber si pasa o no pasa desde medio metro y sin leer: color, símbolo y
   una palabra. Debajo, en cristiano, qué tiene que hacer.
   ========================================================================== */


/* ==========================================================================
   PANTALLA DEL ESCÁNER EN MÓVIL
   ========================================================================== */
@media (max-width: 900px) {
  /* El visor manda: todo lo demás se aparta */
  .esc-visor { max-height: 45vh; }

  /* Los dos caminos, cámara y a mano, con el mismo peso visual */
  .esc-acciones {
    display: grid;
    gap: 0.7rem;
  }

  /* Contadores en una sola línea, sin tres tarjetas */
  .esc-contadores {
    display: flex !important;
    gap: 0;
    padding: 0;
    background: none !important;
    border: none !important;
    box-shadow: none !important;
  }
  .esc-contadores > div {
    flex: 1 1 0;
    padding: 0.6rem 0.3rem !important;
  }

  /* La lista de accesos, plegada por defecto: en la puerta estorba */
  .esc-historial[data-plegado="1"] .esc-historial-cuerpo { display: none; }
}

/* Cabecera plegable del historial */
.esc-historial-boton {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  width: 100%;
  padding: 0.9rem 0;
  background: none;
  border: none;
  color: var(--text-main);
  cursor: pointer;
  text-align: left;
}
.esc-historial-boton .chevron {
  transition: transform 0.2s ease;
  flex: none;
  opacity: 0.5;
}
.esc-historial[data-plegado="0"] .esc-historial-boton .chevron {
  transform: rotate(180deg);
}

/* ==========================================================================
   HOJA DE ENTRADA MANUAL
   Cuando la cámara no lee —carnet gastado, poca luz, funda con reflejos—,
   esto es lo que salva la situación. Pantalla completa, un solo campo y una
   cifra enorme, para que se vea lo que se está tecleando sin gafas.
   ========================================================================== */
.hoja-manual {
  position: fixed;
  inset: 0;
  z-index: 62;
  display: flex;
  flex-direction: column;
  background: var(--bg-main);
  padding: 1.25rem;
  padding-bottom: calc(1.25rem + env(safe-area-inset-bottom, 0));
}

.hoja-manual-cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--border-color);
}

.hoja-manual-titulo {
  font-family: 'Outfit', sans-serif;
  font-size: 1.35rem;
  font-weight: 800;
}

.hoja-manual-cuerpo {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1.1rem;
}

.campo-numero {
  width: 100%;
  text-align: center;
  font-family: 'Outfit', sans-serif;
  font-size: 3.4rem;
  font-weight: 900;
  letter-spacing: 0.04em;
  padding: 1rem 0.75rem;
  border-radius: 1.1rem;
  border: 2.5px solid var(--border-color);
  background: var(--bg-input);
  color: var(--text-main);
  outline: none;
}
.campo-numero:focus {
  border-color: var(--brand);
  box-shadow: 0 0 0 4px rgba(242, 101, 34, 0.25);
}
/* Sin las flechitas del input numérico, que sólo estorban con el dedo */
.campo-numero::-webkit-outer-spin-button,
.campo-numero::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.campo-numero { -moz-appearance: textfield; }

/* ==========================================================================
   FICHAS EN LUGAR DE TABLA
   Una tabla de doce columnas en una pantalla de 390 px no se lee: obliga a
   desplazar en dos direcciones y a hacer zoom. En el móvil, cada abonado es
   una ficha con lo que de verdad se busca en la puerta o en la taquilla.
   ========================================================================== */
.ficha-lista { display: none; }

@media (max-width: 900px) {
  .tabla-wrap { display: none; }
  .ficha-lista { display: grid; gap: 0.7rem; }
}

.ficha {
  display: flex;
  align-items: stretch;
  gap: 0.9rem;
  padding: 0.9rem;
  border-radius: 1rem;
  border: 1.5px solid var(--border-color);
  background: var(--bg-card);
  cursor: pointer;
}
.ficha:active { border-color: var(--brand); }

.ficha-numero {
  flex: none;
  width: 4.4rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border-radius: 0.8rem;
  background: var(--bg-input);
  border: 1px solid var(--border-color);
  line-height: 1.1;
}
.ficha-numero b {
  font-family: 'Outfit', sans-serif;
  font-size: 1.35rem;
  font-weight: 900;
  color: var(--brand);
}
.ficha-numero span {
  font-size: 0.68rem;
  font-weight: 700;
  opacity: 0.55;
}
.ficha.vip .ficha-numero b { color: var(--gold); }

.ficha-datos { flex: 1; min-width: 0; }
.ficha-nombre {
  font-family: 'Outfit', sans-serif;
  font-size: 1.02rem;
  font-weight: 800;
  text-transform: uppercase;
  line-height: 1.2;
  overflow-wrap: anywhere;
}
.ficha-linea {
  font-size: 0.85rem;
  opacity: 0.65;
  margin-top: 0.2rem;
}
.ficha-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-top: 0.5rem;
}

/* ==========================================================================
   PARTIDOS EN MÓVIL
   Los tres botones de icono suelto (escanear, editar, borrar) eran
   indistinguibles. Aquí el que importa lleva su palabra y ocupa el ancho;
   los otros dos quedan aparte y más pequeños.
   ========================================================================== */
@media (max-width: 900px) {
  .partido-fila {
    display: grid;
    grid-template-columns: 3.6rem 1fr auto;
    grid-template-areas:
      "fecha datos entradas"
      "acciones acciones acciones";
    gap: 0.6rem 0.85rem;
    padding: 0.9rem;
    border: 1.5px solid var(--border-color);
    border-radius: 1rem;
  }
  .partido-fila + .partido-fila { border-top: 1.5px solid var(--border-color); }
  .partido-fecha { grid-area: fecha; width: 100%; }
  .partido-fila > .min-w-0 { grid-area: datos; }
  .partido-fila > .flex-none.text-right { grid-area: entradas; }
  .partido-acciones {
    grid-area: acciones;
    display: grid;
    grid-template-columns: 1fr auto auto;
    gap: 0.5rem;
    padding-top: 0.7rem;
    border-top: 1px solid var(--border-color);
  }
}

/* Botón «escanear aquí» de cada partido */
.btn-escanear-partido {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 2.9rem;
  padding: 0 1rem;
  border-radius: 0.8rem;
  border: 2px solid var(--border-color);
  background: var(--bg-input);
  color: var(--text-main);
  font-weight: 800;
  font-size: 0.9rem;
  cursor: pointer;
  white-space: nowrap;
}
.btn-escanear-partido.activo {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
}
.partido-fila:not(.activo) .btn-escanear-partido:active { border-color: var(--brand); }

/* En escritorio los tres botones vuelven a su fila, sin ocupar de más */
@media (min-width: 901px) {
  .partido-acciones {
    display: flex;
    gap: 0.3rem;
    align-items: center;
  }
  .btn-escanear-partido {
    min-height: 2.35rem;
    font-size: 0.8rem;
  }
}

/* ==========================================================================
   AYUDA EN PANTALLA
   Un par de líneas explicando qué hacer, para quien abre la aplicación por
   primera vez y no tiene a quién preguntar.
   ========================================================================== */
.ayuda {
  display: flex;
  gap: 0.75rem;
  padding: 0.9rem 1rem;
  border-radius: 1rem;
  background: rgba(242, 101, 34, 0.07);
  border: 1px solid rgba(242, 101, 34, 0.25);
  font-size: 0.9rem;
  line-height: 1.45;
}
.ayuda [data-icon] { color: var(--brand); flex: none; }
.ayuda b { font-weight: 800; }

/* ==========================================================================
   RETOQUES GENERALES DE MÓVIL
   ========================================================================== */
@media (max-width: 900px) {
  /* Los diálogos, a pantalla casi completa y con aire para el pulgar */
  .modal-body {
    max-height: 94vh;
    overflow-y: auto;
    border-radius: 1.25rem;
  }
  .modal-backdrop { padding: 0.6rem; }

  /* La cabecera del móvil, más alta y legible */
  #app > header { padding: 0.85rem 1rem; }

  /* El aviso de guardado no puede comerse la pantalla */
  .aviso-guardado {
    font-size: 0.85rem;
    align-items: flex-start;
    padding: 0.8rem 0.9rem;
  }

  /* LA CABECERA DEL MÓVIL YA OCUPA EL TOPE.
     La barra de temporada y el aviso de guardado también son `sticky top:0`,
     así que al desplazar se pegaban DEBAJO de la cabecera y desaparecían: la
     pregunta «¿qué temporada estoy viendo?» dejaba de tener respuesta, y el
     aviso naranja llegaba a tapar el menú y el botón de sincronizar. */
  .barra-temporada { top: 4.5rem; }
  .aviso-guardado { top: 4.5rem; }

  /* Casillas de 16 px no se aciertan con el dedo. El suelo de la casa son 44 */
  input[type="checkbox"], input[type="radio"] {
    width: 1.35rem;
    height: 1.35rem;
  }

  /* Los KPI en dos columnas, no en una tira ilegible */
  #statsGrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  #stkKpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Pantallas muy estrechas: una sola columna */
@media (max-width: 380px) {
  #statsGrid,
  #stkKpis { grid-template-columns: 1fr; }
}

/* Apaisado: el visor no puede dejar el resultado fuera de la pantalla */
@media (max-height: 520px) and (orientation: landscape) {
  .esc-visor { max-height: 70vh; }
}

/* Quien haya pedido menos animación, que no las tenga */
@media (prefers-reduced-motion: reduce) {
  .btn-gigante:active { transform: none; }
}

/* ==========================================================================
   JERARQUÍA EN MÓVIL
   El primer borrador dejaba el botón importante por debajo del pliegue:
   título a dos líneas, subtítulo a dos, el aviso del partido y un visor
   grande y vacío antes de llegar a «Abrir la cámara». En la puerta eso
   significa que hay que desplazar antes de poder hacer nada.
   ========================================================================== */
@media (max-width: 900px) {
  /* Cabeceras de página compactas */
  #app main h1 { font-size: 1.5rem; }
  #app main section > div:first-child { padding-bottom: 0.9rem; }
  .subtitulo-pagina { display: none; }

  /* La marca de la cabecera no debe partirse en dos líneas */
  #app > header .font-display { white-space: nowrap; font-size: 0.95rem; }

  /* LOS BOTONES, LO PRIMERO. El visor sólo importa con la cámara encendida. */
  .esc-columna {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
  }
  .esc-columna > * { margin-top: 0 !important; }
  .esc-acciones { order: -2; }
  .ayuda { order: -1; }

  /* Con la cámara apagada el visor se encoge: es un cartel, no una imagen */
  .esc-visor:not(.activo) {
    max-height: 8rem;
  }
  .esc-visor:not(.activo) .esc-placeholder [data-icon] svg {
    width: 2rem;
    height: 2rem;
  }
  .esc-visor:not(.activo) .esc-placeholder p {
    font-size: 0.82rem;
  }

  /* El aviso del partido, a una línea de altura razonable */
  .esc-partido { padding: 0.7rem 0.85rem; }

  /* Los KPI de partidos, después de la lista: primero el calendario */
  #page-partidos > .grid.grid-cols-2 { order: 2; }
  #page-partidos { display: flex; flex-direction: column; gap: 1.15rem; }
  #page-partidos > * { margin-top: 0 !important; }
  #page-partidos > div:first-child { order: -3; }
  #page-partidos > #parAvisoProvisional { order: -2; }
  #page-partidos > .glass.neo.rounded-2xl:last-child { order: -1; }
}

/* La cabecera del móvil, sin solapes.
 *
 * El botón de hamburguesa desaparece: con la barra inferior hay dos caminos
 * al mismo sitio, y dos caminos al mismo sitio confunden a quien no está
 * hecho a esto. «Más», abajo, es el único. */
@media (max-width: 1023px) {
  #btnMobileMenu { display: none !important; }
}

@media (max-width: 900px) {
  #app > header > div:first-child { min-width: 0; }
  #app > header .font-display {
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 1rem;
  }
}

/* Todos los partidos de esta pantalla son en casa, así que «STV Roldán vs»
 * se repite quince veces y se come el nombre del rival, que es lo único que
 * distingue un partido de otro. En el móvil se queda sólo el rival. */
@media (max-width: 900px) {
  .local-vs { display: none; }
  .partido-fila .font-display.text-sm { font-size: 1.05rem !important; }
}

/* En el móvil, el panel enseña primero lo que se mira —cifras, próximo
 * partido, avisos— y deja al final lo que se usa de vez en cuando. */
@media (max-width: 900px) {
  #page-dashboard { display: flex; flex-direction: column; gap: 1.4rem; }
  #page-dashboard > * { margin-top: 0 !important; }
}

/* Las exportaciones y las copias ya no viven en el panel: se movieron a
 * Configuracion, porque en la primera pantalla iban por delante de los
 * numeros de la temporada. En el movil siguen necesitando ser botones
 * grandes y a lo ancho, esten donde esten. */
@media (max-width: 900px) {
  .acciones-secundarias { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }
  .acciones-secundarias button { min-height: 48px; justify-content: center; }
}


/* ==========================================================================
   AFORO
   Avisa, no bloquea. Quien está en la puerta decide, no la aplicación.
   ========================================================================== */
.esc-aforo {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.7rem 1rem;
  border-radius: 1rem;
  font-weight: 800;
  font-size: 0.95rem;
  border: 1px solid var(--border-color);
  background: var(--bg-card);
}
.esc-aforo.holgado { opacity: 0.75; }
.esc-aforo.cerca {
  border-color: rgba(245, 158, 11, 0.5);
  background: rgba(245, 158, 11, 0.12);
  color: #F59E0B;
}
.esc-aforo.lleno {
  border-color: rgba(244, 63, 94, 0.6);
  background: rgba(244, 63, 94, 0.15);
  color: #F43F5E;
}

/* ==========================================================================
   BÚSQUEDA POR NOMBRE EN LA PUERTA
   ========================================================================== */
/* Al escribir letras el campo deja de ser una cifra gigante y se vuelve
   legible como texto: «MARTÍNEZ» a 54 px no cabe en ninguna pantalla. */
.campo-numero.modo-texto {
  font-size: 1.5rem;
  letter-spacing: 0.01em;
  text-transform: uppercase;
}

.hm-resultados {
  display: grid;
  gap: 0.5rem;
  max-height: 46vh;
  overflow-y: auto;
}

.hm-resultado {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  width: 100%;
  padding: 0.75rem 0.9rem;
  border-radius: 0.9rem;
  border: 1.5px solid var(--border-color);
  background: var(--bg-input);
  color: var(--text-main);
  text-align: left;
  cursor: pointer;
}
.hm-resultado:active { border-color: var(--brand); }

.hm-resultado-num {
  flex: none;
  min-width: 3.2rem;
  text-align: center;
  font-family: 'Outfit', sans-serif;
  font-size: 1.2rem;
  font-weight: 900;
  color: var(--brand);
}
.hm-resultado.caducado .hm-resultado-num { color: var(--text-subtle); }
.hm-resultado.caducado { opacity: 0.65; }

.hm-resultado-datos { min-width: 0; }
.hm-resultado-datos b {
  display: block;
  font-size: 0.95rem;
  font-weight: 800;
  text-transform: uppercase;
  line-height: 1.2;
}
.hm-resultado-datos small {
  display: block;
  font-size: 0.78rem;
  opacity: 0.6;
  margin-top: 0.1rem;
}

/* ==========================================================================
   ACCESO ANULADO
   Se queda a la vista, tachado. Un registro del que se pueden borrar cosas
   sin dejar rastro no sirve para cuadrar un aforo.
   ========================================================================== */
.acceso-fila.anulado {
  opacity: 0.55;
}
.acceso-fila.anulado .font-display,
.acceso-fila.anulado .font-bold {
  text-decoration: line-through;
}


/* ==========================================================================
   CONTRASTE EN TEMA CLARO
   El naranja corporativo como COLOR DE TEXTO daba 2,91:1 sobre el fondo
   claro, cuando el mínimo legible es 4,5:1. Se usa en dos docenas de sitios,
   muchos en texto pequeño, así que era el fallo de contraste más extendido
   de la aplicación, y golpea justo al público mayor.
   `--brand-700` (#B83A05) ya estaba en la paleta y da 5,32:1: no hace falta
   inventar ningún color nuevo. En fondo oscuro el naranja se queda como está.
   ========================================================================== */
body.light .text-brand,
body.light .text-brand-400,
body.light [class*="text-brand"] {
  color: var(--brand-700) !important;
}

/* Sobre fondos oscuros dentro del tema claro (el carnet, la cabecera del
   escáner) el naranja vivo sí se lee y se conserva. */
body.light .carnet-card .text-brand,
body.light .veredicto .text-brand {
  color: var(--brand) !important;
}

/* Los iconos naranjas siguen la misma regla que el texto */
body.light .text-brand [data-icon] svg { stroke: currentColor; }

/* ==========================================================================
   FOCO VISIBLE
   No había ninguna regla de `:focus-visible` en todo el proyecto: al recorrer
   la interfaz con el tabulador no se veía dónde estaba el foco en ningún
   botón. `:focus-visible` y no `:focus` para que el aro no salga al pulsar
   con el dedo o con el ratón, sólo cuando se navega con teclado.
   ========================================================================== */
:focus-visible {
  outline: 3px solid var(--brand);
  outline-offset: 2px;
  border-radius: 0.4rem;
}

.btn-primary:focus-visible,
.btn-ghost:focus-visible,
.btn-gigante:focus-visible,
.icon-btn:focus-visible,
.nav-inferior button:focus-visible,
.nav-btn:focus-visible,
.chip-filtro:focus-visible,
.ficha:focus-visible,
.hm-resultado:focus-visible,
.btn-escanear-partido:focus-visible {
  outline: 3px solid var(--brand);
  outline-offset: 3px;
}

/* Sobre el cartel del veredicto, que ya es naranja o rojo, el aro va blanco */
.veredicto :focus-visible {
  outline-color: #fff;
}

/* Quien pide más contraste, lo tiene */
@media (prefers-contrast: more) {
  :root { --border-color: currentColor; }
  .btn-ghost, .icon-btn, .inp { border-width: 2px; }
  body.light { --text-subtle: #334155; }
}

/* La barra del escáner dice DOS cosas: el ancho es cuánto falta para
   confirmar la lectura, el color es cuánto se fía de las cifras. Sin el
   color, «casi lo tengo» y «no veo nada» eran indistinguibles. */
#escBarra.conf-baja  { background: #F43F5E; }
#escBarra.conf-media { background: linear-gradient(to right, var(--gold), var(--brand)); }
#escBarra.conf-alta  { background: #10B981; }

/* ==========================================================================
   CALENDARIO CON ESCUDOS, PLANTILLA Y ACTA  ·  v3.7.0
   ==========================================================================

   Los escudos son PNG de 128x128 recortados al contenido y centrados en un
   cuadrado. Por eso aquí basta con darles un lado y `object-fit: contain`:
   todos ocupan lo mismo sin tener que corregir uno a uno los que venían con
   más margen alrededor.
   ========================================================================== */

/* ---------- Enfrentamiento en la fila del partido --------------------- */
.par-enfrentamiento {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}

.par-escudo {
  flex: none;
  /* 26 px dejaban el escudo hecho un borrón: son imágenes de 128 px con
     bastante detalle (letras, laureles) y a ese tamaño no se distingue un
     equipo de otro. */
  width: 2.25rem;
  height: 2.25rem;
  object-fit: contain;
}

/* Un rival añadido a mano no tiene escudo: se pone su inicial y no un hueco */
.par-escudo-vacio {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--bg-input);
  border: 1px solid var(--border-color);
  /* Acompaña al tamaño del escudo de arriba */
  font-size: 0.92rem;
  font-weight: 800;
  color: var(--text-subtle);
}

.par-equipo {
  font-family: 'Outfit', system-ui, sans-serif;
  font-weight: 700;
  font-size: 0.85rem;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text-muted);
}

/* El propio equipo va destacado: en una lista de treinta filas es lo que
   permite saber de un vistazo de qué lado se lee el marcador. */
.par-equipo.propio {
  color: var(--text-main);
  font-weight: 800;
}

.par-vs {
  flex: none;
  font-size: 0.7rem;
  font-weight: 800;
  text-transform: uppercase;
  opacity: 0.35;
  padding: 0 0.15rem;
}

.par-resultado {
  flex: none;
  font-family: 'Outfit', system-ui, sans-serif;
  font-size: 1rem;
  font-weight: 800;
  padding: 0.1rem 0.5rem;
  border-radius: 0.45rem;
  background: var(--bg-input);
  border: 1px solid var(--border-color);
  white-space: nowrap;
}

.par-resultado .par-guion {
  opacity: 0.4;
  margin: 0 0.2rem;
  font-weight: 600;
}

.par-resultado.gana {
  color: #10B981;
  border-color: rgba(16, 185, 129, 0.45);
  background: rgba(16, 185, 129, 0.10);
}

.par-resultado.empata {
  color: var(--gold);
  border-color: rgba(245, 158, 11, 0.45);
  background: rgba(245, 158, 11, 0.10);
}

.par-resultado.pierde {
  color: #F43F5E;
  border-color: rgba(244, 63, 94, 0.45);
  background: rgba(244, 63, 94, 0.10);
}

.par-chip-jornada,
.par-chip-sede {
  font-size: 9.5px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 0.1rem 0.4rem;
  border-radius: 0.35rem;
  white-space: nowrap;
}

.par-chip-jornada {
  background: var(--bg-input);
  border: 1px solid var(--border-color);
  color: var(--text-subtle);
}

.par-chip-sede.casa {
  background: rgba(242, 101, 34, 0.14);
  border: 1px solid rgba(242, 101, 34, 0.4);
  color: var(--brand);
}

.par-chip-sede.fuera {
  background: rgba(148, 163, 184, 0.12);
  border: 1px solid var(--border-color);
  color: var(--text-subtle);
}

/* Un partido fuera no tiene ni escáner ni aforo: se marca con una franja
   lateral para que la fila se distinga sin tener que leer la etiqueta. */
.partido-fila.fuera {
  border-left: 3px solid var(--border-color);
}

.btn-acta {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.45rem 0.8rem;
  border-radius: 0.6rem;
  font-size: 0.72rem;
  font-weight: 800;
  white-space: nowrap;
  background: var(--bg-input);
  border: 1px solid var(--border-color);
  color: var(--text-muted);
  transition: border-color 0.15s ease, color 0.15s ease;
}

.btn-acta:hover {
  border-color: var(--brand);
  color: var(--brand);
}

/* ---------- Elegir dónde se juega ------------------------------------- */
.opcion-sede {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.6rem 0.75rem;
  border-radius: 0.7rem;
  border: 1px solid var(--border-color);
  background: var(--bg-input);
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.opcion-sede span {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
}

.opcion-sede strong { font-size: 0.85rem; font-weight: 800; }
.opcion-sede small { font-size: 0.7rem; opacity: 0.55; }

.opcion-sede:has(input:checked) {
  border-color: var(--brand);
  background: rgba(242, 101, 34, 0.08);
}

.opcion-sede input { accent-color: var(--brand); width: 1.05rem; height: 1.05rem; }

/* ---------- Pestañas -------------------------------------------------- */
.pestanyas {
  display: flex;
  gap: 0.4rem;
  padding: 0.3rem;
  border-radius: 0.9rem;
  background: var(--bg-input);
  border: 1px solid var(--border-color);
  width: fit-content;
  max-width: 100%;
  overflow-x: auto;
}

.pestanyas button {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.55rem 1.1rem;
  border-radius: 0.65rem;
  font-size: 0.82rem;
  font-weight: 800;
  color: var(--text-subtle);
  white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease;
}

.pestanyas button:hover { color: var(--text-main); }

.pestanyas button.activa {
  background: var(--brand);
  color: #fff;
  box-shadow: 0 4px 14px var(--brand-glow);
}

/* ---------- Rejilla de fichas ----------------------------------------- */
.rejilla-plantilla {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 1rem;
}

.ficha-jugadora {
  border-radius: 1rem;
  overflow: hidden;
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  box-shadow: var(--card-shadow);
  cursor: pointer;
  transition: transform 0.15s ease, border-color 0.15s ease;
}

.ficha-jugadora:hover,
.ficha-jugadora:focus-visible {
  transform: translateY(-2px);
  border-color: var(--brand);
}

/* El marco cuadrado es lo que hace que la rejilla no baile: las fotos ya
   salen a 600x600 desde el recortador, así que aquí sólo hay que mantener
   la proporción. */
.ficha-foto {
  position: relative;
  aspect-ratio: 1 / 1;
  background: linear-gradient(160deg, rgba(242, 101, 34, 0.18), var(--bg-input));
  display: flex;
  align-items: center;
  justify-content: center;
}

.ficha-foto img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.2s ease;
}

/* La foto sólo tapa la inicial cuando ha cargado de verdad: si IndexedDB
   falla o la foto no está, se queda la inicial y nunca un cuadro en blanco. */
.ficha-foto img.cargada { opacity: 1; }

.ficha-iniciales {
  font-family: 'Outfit', system-ui, sans-serif;
  font-size: 2.2rem;
  font-weight: 800;
  color: var(--brand);
  opacity: 0.55;
  user-select: none;
}

.ficha-dorsal {
  position: absolute;
  bottom: 0.4rem;
  left: 0.4rem;
  z-index: 1;
  min-width: 1.7rem;
  text-align: center;
  font-family: 'Outfit', system-ui, sans-serif;
  font-size: 0.85rem;
  font-weight: 800;
  color: #fff;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(4px);
  border-radius: 0.4rem;
  padding: 0.1rem 0.35rem;
}

.ficha-cinta {
  position: absolute;
  top: 0.4rem;
  right: 0.4rem;
  z-index: 1;
  font-size: 9.5px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.15rem 0.45rem;
  border-radius: 0.35rem;
  background: rgba(244, 63, 94, 0.9);
  color: #fff;
}

.ficha-jugadora.de-baja .ficha-foto img { filter: grayscale(1); opacity: 0.55; }
.ficha-jugadora.de-baja .ficha-datos { opacity: 0.6; }

.ficha-datos { padding: 0.65rem 0.7rem 0.75rem; }

.ficha-nombre {
  font-family: 'Outfit', system-ui, sans-serif;
  font-size: 0.88rem;
  font-weight: 800;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ficha-rol {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--text-subtle);
  margin-top: 0.1rem;
}

.ficha-pie {
  font-size: 0.72rem;
  opacity: 0.7;
  margin-top: 0.25rem;
}

/* ---------- Foto dentro de la ficha ----------------------------------- */
.foto-editor {
  position: relative;
  width: 150px;
  height: 150px;
  border-radius: 1rem;
  overflow: hidden;
  background: linear-gradient(160deg, rgba(242, 101, 34, 0.18), var(--bg-input));
  border: 1px solid var(--border-color);
  display: flex;
  align-items: center;
  justify-content: center;
}

.foto-editor img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.foto-editor img.cargada { opacity: 1; }

/* ---------- Recortador a 600x600 -------------------------------------- */
.recorte-marco {
  position: relative;
  width: 100%;
  max-width: 300px;
  margin: 0 auto;
  aspect-ratio: 1 / 1;
  border-radius: 0.9rem;
  overflow: hidden;
  background: #000;
  border: 1px solid var(--border-color);
}

.recorte-marco canvas {
  width: 100%;
  height: 100%;
  display: block;
  cursor: grab;
}

.recorte-marco canvas:active { cursor: grabbing; }

/* Guía de tercios: ayuda a centrar la cara sin taparla */
.recorte-guia {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to right, transparent 33.2%, rgba(255,255,255,0.28) 33.2%, rgba(255,255,255,0.28) 33.5%, transparent 33.5%, transparent 66.4%, rgba(255,255,255,0.28) 66.4%, rgba(255,255,255,0.28) 66.7%, transparent 66.7%),
    linear-gradient(to bottom, transparent 33.2%, rgba(255,255,255,0.28) 33.2%, rgba(255,255,255,0.28) 33.5%, transparent 33.5%, transparent 66.4%, rgba(255,255,255,0.28) 66.4%, rgba(255,255,255,0.28) 66.7%, transparent 66.7%);
}

/* ---------- Acta del partido ------------------------------------------ */
.acta-marcador {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 0.75rem;
  padding: 1rem 0.75rem;
  border-radius: 1rem;
  background: var(--bg-input);
  border: 1px solid var(--border-color);
}

.acta-equipo {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  min-width: 0;
}

.acta-escudo {
  width: 4rem;
  height: 4rem;
  object-fit: contain;
}

.acta-nombre {
  font-family: 'Outfit', system-ui, sans-serif;
  font-size: 0.78rem;
  font-weight: 800;
  text-align: center;
  line-height: 1.15;
  color: var(--text-muted);
}

.acta-goles {
  display: flex;
  align-items: center;
  gap: 0.3rem;
}

/* Campos grandes: esto se rellena en la grada, con una mano y con prisa */
.acta-input {
  width: 3.2rem;
  height: 3.4rem;
  text-align: center;
  font-family: 'Outfit', system-ui, sans-serif;
  font-size: 1.8rem;
  font-weight: 800;
  border-radius: 0.7rem;
  background: var(--bg-card-strong);
  border: 1px solid var(--border-color);
  color: var(--text-main);
  -moz-appearance: textfield;
  appearance: textfield;
}

.acta-input:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-glow);
}

/* Las flechitas del campo numérico estorban más que ayudan a este tamaño */
.acta-input::-webkit-outer-spin-button,
.acta-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

.acta-guion {
  font-family: 'Outfit', system-ui, sans-serif;
  font-size: 1.4rem;
  font-weight: 800;
  opacity: 0.35;
}

.acta-fila {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem 0.6rem;
  border-radius: 0.6rem;
  background: var(--bg-input);
  border: 1px solid var(--border-color);
}

.acta-min {
  flex: none;
  width: 2.4rem;
  font-family: 'Outfit', system-ui, sans-serif;
  font-size: 0.8rem;
  font-weight: 800;
  color: var(--brand);
}

.tar-punto {
  flex: none;
  width: 0.75rem;
  height: 1.05rem;
  border-radius: 0.15rem;
}

.tar-amarilla { background: #FBBF24; }
.tar-roja { background: #F43F5E; }

/* ---------- En el móvil ----------------------------------------------- */
@media (max-width: 900px) {
  /* Fichas más grandes: se elige con el dedo, no con el ratón */
  .rejilla-plantilla { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }

  /* La fila del partido se reordena: los escudos y el resultado mandan, y
     los botones pasan a una línea propia a lo ancho. */
  .partido-fila { flex-wrap: wrap; }
  .partido-fila .partido-acciones { width: 100%; }
  .btn-acta { flex: 1; justify-content: center; min-height: 3rem; font-size: 0.85rem; }

  .par-equipo { font-size: 0.95rem; }
  .par-escudo { width: 1.9rem; height: 1.9rem; }
  .par-resultado { font-size: 1.15rem; }

  .acta-input { width: 3.8rem; height: 4rem; font-size: 2.1rem; }
  .acta-escudo { width: 3.4rem; height: 3.4rem; }
  .foto-editor { width: 170px; height: 170px; }
}

/* ==========================================================================
   FICHAJES Y BAJAS  ·  v3.7.1
   ========================================================================== */

/* Un botón que no toca en esta pestaña. Va con `!important` porque compite
   con la utilidad `flex` de Tailwind, cuya hoja se inyecta DESPUÉS de ésta y
   gana los empates por orden de documento. Ya pasó dos veces antes. */
.oculto-por-pestanya { display: none !important; }

.mov-fila {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.45rem 0.6rem;
  border-radius: 0.7rem;
  border: 1px solid var(--border-color);
  background: var(--bg-input);
}

/* La flecha dice de un vistazo si alguien entra o sale, sin leer nada */
.mov-flecha {
  flex: none;
  width: 1.4rem;
  height: 1.4rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-weight: 800;
  font-size: 0.85rem;
  line-height: 1;
}

.mov-fila.entra { border-left: 3px solid #10B981; }
.mov-fila.sale { border-left: 3px solid #F43F5E; }

.mov-fila.entra .mov-flecha { background: rgba(16, 185, 129, 0.15); color: #10B981; }
.mov-fila.sale .mov-flecha { background: rgba(244, 63, 94, 0.15); color: #F43F5E; }

.mov-foto {
  position: relative;
  flex: none;
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 0.55rem;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(160deg, rgba(242, 101, 34, 0.18), var(--bg-card));
}

.mov-foto img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.mov-foto img.cargada { opacity: 1; }

/* La inicial aquí es pequeña: el tamaño de la ficha grande se sale del marco */
.mov-foto .ficha-iniciales { font-size: 0.8rem; }

.mov-nombre {
  font-family: 'Outfit', system-ui, sans-serif;
  font-size: 0.85rem;
  font-weight: 800;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.mov-detalle {
  font-size: 0.72rem;
  color: var(--text-subtle);
  margin-top: 0.05rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.mov-fecha {
  flex: none;
  font-size: 0.72rem;
  font-weight: 700;
  opacity: 0.55;
  white-space: nowrap;
}

@media (max-width: 900px) {
  .mov-fila { padding: 0.6rem; }
  .mov-nombre { font-size: 0.95rem; }
  .mov-foto { width: 2.6rem; height: 2.6rem; }
  /* En pantalla estrecha la fecha estorba más de lo que aporta: el orden ya
     la cuenta, y el nombre necesita todo el ancho que pueda. */
  .mov-fecha { display: none; }
}

/* Cada pestaña tiene nombre largo y nombre corto. En el móvil «Cuerpo
   técnico» y «Fichajes y bajas» no caben en 375 px, y una pestaña a la que
   hay que llegar deslizando es una pestaña que nadie encuentra. */
.pes-corto { display: none; }

@media (max-width: 900px) {
  /* Las tres a lo ancho, repartidas y con altura de dedo: 40 px se quedaba
     por debajo del mínimo que se fijó para esta aplicación, y aquí buena
     parte de quien la usa tiene la vista y el pulso de sesenta y tantos. */
  .pestanyas {
    width: 100%;
    overflow-x: visible;
  }

  .pestanyas button {
    flex: 1 1 0;
    min-width: 0;
    min-height: 3rem;
    padding: 0.55rem 0.4rem;
    justify-content: center;
    font-size: 0.8rem;
  }

  /* El icono se va: en tres botones estrechos, la palabra vale más */
  .pestanyas button svg { display: none; }

  .pes-largo { display: none; }
  .pes-corto { display: inline; }
}

/* ==================== BARRA DE TEMPORADA ====================
   Un solo selector para toda la aplicación. Va pegado arriba porque la
   pregunta «¿de qué año es esto que estoy viendo?» tiene que poder
   responderse sin desplazarse, sobre todo al consultar el histórico.

   Nota de orden: Tailwind se inyecta DESPUÉS de esta hoja, así que las
   reglas que compiten con una utilidad suya llevan !important. */
.barra-temporada {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6rem 0.9rem;
  margin: -0.25rem 0 0;
  border: 1px solid var(--border-color);
  border-radius: 1rem;
  background: var(--bg-card-strong);
  backdrop-filter: blur(14px);
  box-shadow: var(--card-shadow);
}

.bt-etiqueta {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.7rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-subtle);
  white-space: nowrap;
}

.bt-etiqueta svg { width: 1rem; height: 1rem; }

.bt-select {
  width: auto !important;
  min-width: 8.5rem;
  min-height: 2.75rem;
  font-size: 0.95rem;
}

/* El aviso de que se está mirando historia. No es decorativo: es lo que
   impide leer las cifras de 2021/22 creyendo que son las de este año. */
.bt-aviso {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: 1 1 16rem;
  min-width: 0;
  padding: 0.4rem 0.7rem;
  border-radius: 0.75rem;
  border: 1px solid rgba(245, 158, 11, 0.35);
  background: rgba(245, 158, 11, 0.12);
  font-size: 0.78rem;
  font-weight: 700;
  line-height: 1.35;
}

/* El tema oscuro es el de partida en esta hoja, igual que arriba: si el color
   claro fuera la base, se vería un destello ámbar al arrancar. */
body.dark .bt-aviso, body:not(.light) .bt-aviso { color: #FCD34D; }

/* 5,9:1 sobre el fondo del aviso: el ámbar de Tailwind se queda en 2,4:1 */
body.light .bt-aviso { color: #92400E; }

.bt-aviso svg { width: 1rem; height: 1rem; flex: none; }

.bt-volver {
  margin-left: auto;
  flex: none;
  min-height: 2.25rem;
  padding: 0 0.75rem;
  border-radius: 0.6rem;
  border: 1px solid currentColor;
  font-size: 0.72rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

.bt-volver:hover { background: rgba(245, 158, 11, 0.2); }

@media (max-width: 640px) {
  /* En el móvil la barra se parte en dos filas: el selector a lo ancho y el
     aviso debajo. Apretarlo en una sola deja el desplegable en 4 caracteres. */
  .barra-temporada { gap: 0.5rem; padding: 0.55rem 0.7rem; }
  .bt-select { flex: 1 1 auto; min-width: 0; }
  .bt-aviso { flex: 1 1 100%; flex-wrap: wrap; }
  /* 44 px es el suelo táctil de esta aplicación. En el escritorio 36 valen
     porque hay ratón; con el dedo, no. */
  .bt-volver { margin-left: 0; min-height: 2.75rem; width: 100%; }
}

/* ==================== MANDO DE TEMPORADA (panel de resumen) ====================
   La barra de arriba está bien para moverse entre años mientras se trabaja,
   pero como primer contacto no funciona: se lee como un filtro y se pasa por
   alto. Aquí ocupa sitio y dice para qué sirve. */
.mando-temporada {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem 1.25rem;
  border-radius: 1.25rem;
  border: 1px solid var(--border-color);
  border-left: 5px solid var(--brand);
  background: var(--bg-card-strong);
  box-shadow: var(--card-shadow);
}

.mt-icono {
  width: 3rem;
  height: 3rem;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 1rem;
  background: var(--brand);
  color: #fff;
}

.mt-icono svg { width: 1.5rem; height: 1.5rem; }

.mt-texto { flex: 1 1 14rem; min-width: 0; }

.mt-titulo {
  font-size: 0.72rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--brand);
}

.mt-ayuda {
  font-size: 0.82rem;
  line-height: 1.4;
  color: var(--text-subtle);
  margin-top: 0.15rem;
}

.mt-control {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex: none;
}

/* Grande a propósito: es el mando que cambia toda la pantalla, no un filtro */
.mt-select {
  width: auto !important;
  min-width: 11rem;
  min-height: 3.25rem;
  font-size: 1.15rem !important;
  font-weight: 800;
}

.mt-volver {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-height: 3.25rem;
  padding: 0 0.9rem;
  border-radius: 0.9rem;
  border: 1px solid var(--border-color);
  background: var(--bg-input);
  font-size: 0.78rem;
  font-weight: 800;
  white-space: nowrap;
}

.mt-volver:hover { border-color: var(--brand); color: var(--brand); }
.mt-volver svg { width: 1rem; height: 1rem; }

/* Consultando un año que no es el que se juega: se marca en ámbar para que no
   se confunda con la temporada en curso ni de reojo. */
.mando-temporada.es-historico {
  border-left-color: #F59E0B;
  background: rgba(245, 158, 11, 0.08);
}

.mando-temporada.es-historico .mt-icono { background: #F59E0B; }
body.dark .mando-temporada.es-historico .mt-titulo,
body:not(.light) .mando-temporada.es-historico .mt-titulo { color: #FCD34D; }
body.light .mando-temporada.es-historico .mt-titulo { color: #92400E; }

@media (max-width: 720px) {
  .mando-temporada { flex-wrap: wrap; gap: 0.75rem; padding: 0.9rem 1rem; }
  .mt-texto { flex: 1 1 100%; order: 2; }
  /* El control se parte en dos filas. En una sola, «Volver a la vigente» no
     puede encoger (va en nowrap) y empujaba el documento 63 px a la derecha:
     con el desplegable al lado no cabían los dos en 390 px. */
  .mt-control { flex: 1 1 100%; order: 3; flex-wrap: wrap; }
  .mt-select { flex: 1 1 100%; min-width: 0; }
  .mt-volver { flex: 1 1 100%; justify-content: center; }
}


/* ==================== ÁREA DE IMPRESIÓN ====================
   `#printArea` sólo existe para el papel. `print.css` va con media="print",
   así que su `display:block !important` no llega nunca a la pantalla — pero
   sin esta regla tampoco había nada que lo escondiera, y en cuanto se
   imprimía una vez el listado entero y el escudo a tamaño natural se
   quedaban incrustados en la página. */
#printArea {
  display: none;
}


/* ==================== ANCHO DEL CONTENIDO ====================
   Antes era `max-w-[1440px] mx-auto`: un tope fijo. En un monitor de 2560 px
   el menú lateral ocupaba su sitio y el contenido se quedaba en una columna
   de 1440 centrada, con dos franjas vacías enormes a los lados y las tablas
   apretadas en medio. Se veía justo lo contrario de lo que se espera al tener
   más pantalla.

   Ahora el ancho acompaña a la ventana y el tope sólo entra en juego cuando
   la línea se haría incómoda de leer. El relleno también crece: en una
   pantalla ancha, pegar el contenido al borde queda tan mal como comprimirlo. */
.lienzo {
  width: 100%;
  max-width: 100%;
  margin-inline: auto;
  padding: 1rem;
}

@media (min-width: 768px) {
  .lienzo { padding: 2rem; }
}

@media (min-width: 1600px) {
  .lienzo { padding: 2rem 3rem; }
}

/* Por encima de esto la línea de texto se hace incómoda de seguir, así que
   aquí sí se pone freno y se centra. Son 2160 px de contenido: casi el doble
   que antes, y sólo afecta a monitores muy grandes. */
@media (min-width: 2280px) {
  .lienzo {
    max-width: 2160px;
    padding: 2rem 3.5rem;
  }
}


/* ==================== TAQUILLA ====================
   Va en el escáner y con el mismo peso visual que comprobar un carnet: en la
   puerta las dos cosas pasan a la vez y con cola delante. */
.taquilla {
  border: 1px solid var(--border-color);
  border-left: 5px solid var(--brand);
  border-radius: 1.25rem;
  background: var(--bg-card-strong);
  box-shadow: var(--card-shadow);
  padding: 1rem 1.1rem;
}

.taq-cabecera {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  margin-bottom: 0.9rem;
}

.taq-cabecera svg { width: 1.3rem; height: 1.3rem; flex: none; color: var(--brand); margin-top: 0.1rem; }

.taq-titulo {
  font-family: 'Outfit', system-ui, sans-serif;
  font-weight: 800;
  font-size: 1rem;
}

.taq-ayuda {
  font-size: 0.78rem;
  line-height: 1.35;
  color: var(--text-subtle);
  margin-top: 0.1rem;
}

.taq-fila {
  display: flex;
  align-items: flex-end;
  gap: 0.7rem;
  flex-wrap: wrap;
}

.taq-precio {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  flex: 0 0 9rem;
}

.taq-precio > span {
  font-size: 0.68rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-subtle);
}

.taq-campo { position: relative; }

.taq-campo .inp {
  min-height: 3.5rem;
  font-size: 1.35rem;
  font-weight: 800;
  padding-right: 2rem;
  text-align: right;
}

.taq-eur {
  position: absolute;
  right: 0.7rem;
  top: 50%;
  transform: translateY(-50%);
  font-weight: 800;
  color: var(--text-subtle);
  pointer-events: none;
}

.taq-boton { flex: 1 1 12rem; min-height: 3.5rem !important; }

.taq-resumen {
  margin-top: 0.9rem;
  font-size: 0.82rem;
  font-weight: 700;
}

.taq-lista {
  margin-top: 0.5rem;
  max-height: 11rem;
  overflow-y: auto;
}

.taq-lista li {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem 0;
  border-top: 1px solid var(--border-color);
  font-size: 0.8rem;
}

.taq-lista .taq-hora { font-variant-numeric: tabular-nums; color: var(--text-subtle); }
.taq-lista .taq-imp  { margin-left: auto; font-weight: 800; }

@media (max-width: 520px) {
  /* El botón a lo ancho: con el dedo y con prisa, cuanto más grande mejor */
  .taq-precio { flex: 1 1 100%; }
  .taq-boton  { flex: 1 1 100%; }
}

/* ==================== CARNET CON EL ARTE REAL ====================
   El SVG lleva su propio viewBox, así que basta con darle el ancho: el número
   cae solo en el recuadro a cualquier escala. */
.carnet-real {
  border-radius: 0.9rem;
  overflow: hidden;
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.35);
  line-height: 0;
}

.carnet-svg {
  display: block;
  width: 100%;
  height: auto;
}


/* ==========================================================================
   ESCÁNER · ENTRADA Y SALIDA
   ==========================================================================

   Un carnet sólo entra si consta fuera y sólo sale si consta dentro. Eso
   convierte al selector de modo en la pieza más delicada de la pantalla: si
   está mal puesto, TODAS las lecturas siguientes salen rechazadas y nadie
   entiende por qué.

   Por eso no es un interruptor pequeño en una esquina: son dos botones que
   ocupan el ancho, de 64 px de alto, y el modo elegido además tiñe el resto
   de la pantalla. En la puerta se mira de reojo, y el color se ve antes que
   cualquier palabra.
   ========================================================================== */

.sentido-selector {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.6rem;
}

.sentido-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  min-height: 64px;
  border-radius: 1.1rem;
  border: 2.5px solid var(--border-color);
  background: var(--bg-card);
  color: var(--text-main);
  font-family: 'Outfit', sans-serif;
  font-size: 1.1rem;
  font-weight: 800;
  letter-spacing: 0.03em;
  /* Sin transición de color: el cambio tiene que leerse como instantáneo */
  transition: transform 0.12s ease, border-color 0.12s ease;
  opacity: 0.45;
}

.sentido-btn [data-icon] svg { width: 1.4rem; height: 1.4rem; }
.sentido-btn:active { transform: scale(0.985); }

.sentido-btn.activa { opacity: 1; }

.sentido-btn.entrada.activa {
  border-color: #10B981;
  background: rgba(16, 185, 129, 0.16);
  color: #10B981;
}

.sentido-btn.salida.activa {
  border-color: #38BDF8;
  background: rgba(56, 189, 248, 0.16);
  color: #38BDF8;
}

/* El modo salida se nota en toda la pantalla, no sólo en el botón: el marco
   del visor cambia de color para que nadie escanee media cola creyendo que
   está registrando entradas. */
body.modo-salida .esc-marco { border-color: #38BDF8; }
body.modo-salida .esc-esquina { border-color: #38BDF8; }


/* --------------------------------------------------------------------------
   TECLADO NUMÉRICO

   Propio, no el del sistema. El del teléfono sale pequeño, tarda en
   aparecer, tapa media pantalla y en muchos móviles trae letras pegadas a
   las cifras. Aquí las teclas miden 68 px, están siempre y sólo hay diez.
   -------------------------------------------------------------------------- */

.teclado-pantalla {
  text-align: center;
  font-family: 'Outfit', sans-serif;
  font-size: 3.6rem;
  font-weight: 900;
  line-height: 1.1;
  letter-spacing: 0.05em;
  padding: 0.6rem 0.75rem;
  border-radius: 1.1rem;
  border: 2.5px solid var(--border-color);
  background: var(--bg-input);
  color: var(--text-main);
  /* Alto fijo: sin esto la pantalla salta al escribir la primera cifra y
     el teclado entero se mueve bajo el dedo. */
  min-height: 5.2rem;
  display: flex;
  align-items: center;
  justify-content: center;
}

.teclado-vacio { opacity: 0.22; }

.hoja-manual.es-salida .teclado-pantalla { border-color: #38BDF8; }

.teclado {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.55rem;
}

.tecla {
  min-height: 68px;
  border-radius: 1rem;
  border: 1.5px solid var(--border-color);
  background: var(--bg-card);
  color: var(--text-main);
  font-family: 'Outfit', sans-serif;
  font-size: 1.9rem;
  font-weight: 800;
  transition: transform 0.08s ease, background 0.12s ease;
}

.tecla:active {
  transform: scale(0.94);
  background: var(--bg-hover, rgba(255, 255, 255, 0.08));
}

.tecla [data-icon] svg { width: 1.5rem; height: 1.5rem; }

.tecla.borrar { color: var(--text-subtle); }

.tecla.aceptar {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
}

.hoja-manual.es-salida .tecla.aceptar {
  background: #38BDF8;
  border-color: #38BDF8;
}

/* La búsqueda por nombre existe pero no compite: escribir el número es lo
   que se hace noventa y nueve veces de cada cien. */
.hoja-manual-alterna {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.85rem;
  border-radius: 0.9rem;
  border: 1px solid var(--border-color);
  background: transparent;
  color: var(--text-subtle);
  font-size: 0.8rem;
  font-weight: 700;
}

.hm-busqueda { display: flex; flex-direction: column; gap: 0.6rem; }

/* Las entradas sin abono, plegadas: son una parte del trabajo de la puerta,
   no la principal. */
.taquilla-plegable > summary {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  cursor: pointer;
  padding: 1rem 1.1rem;
  border-radius: 1rem;
  border: 1px solid var(--border-color);
  background: var(--bg-card);
  font-family: 'Outfit', sans-serif;
  font-weight: 800;
  font-size: 0.95rem;
}

.taquilla-plegable > summary::-webkit-details-marker { display: none; }
.taquilla-plegable[open] > summary { margin-bottom: 0.75rem; }

/* En pantallas bajas el teclado no cabe con todo lo demás: se recorta la
   cifra grande antes que las teclas, que son lo que hay que acertar. */
@media (max-height: 700px) {
  .teclado-pantalla { font-size: 2.6rem; min-height: 4rem; }
  .tecla { min-height: 58px; font-size: 1.6rem; }
}


/* ==========================================================================
   BARRAS DE ACCIONES EN MÓVIL Y TABLET
   ==========================================================================

   La de Abonados tenía OCHO botones en una fila sin `flex-wrap`: 853 px en
   una pantalla de 390. La vista entera se desplazaba en horizontal, y con
   ella se estiraba el documento, así que hasta el fondo decorativo crecía.

   Poner `flex-wrap` a secas arregla el desbordamiento pero deja una pared de
   botones de tamaños distintos. En un móvil que se usa de pie, con una mano,
   lo que funciona es una rejilla: la acción principal a lo ancho y el resto
   en dos columnas, todos del mismo tamaño y todos por encima del mínimo que
   se acierta con el dedo.
   ========================================================================== */

@media (max-width: 900px) {
  .barra-acciones {
    display: grid !important;      /* compite con la utilidad `flex` de Tailwind */
    grid-template-columns: 1fr 1fr;
    gap: 0.5rem;
    width: 100%;
  }

  /* La acción principal ocupa las dos columnas: es la que se busca. */
  .barra-acciones .accion-principal { grid-column: 1 / -1; }

  /* Todos iguales y todos alcanzables: 48 px es el mínimo con el que no se
     falla el objetivo yendo de pie. */
  .barra-acciones > button {
    min-height: 48px;
    justify-content: center;
    white-space: nowrap;
  }

  /* Los botones de sólo icono ocupaban una celda entera para nada. Se
     quedan cuadrados y se agrupan al final. */
  .barra-acciones > .icon-btn {
    width: auto;
    min-height: 48px;
  }
}

/* Entre 640 y 900 px -tablet en vertical- caben cuatro por fila sin que
   ninguno se quede estrecho. */
@media (min-width: 641px) and (max-width: 900px) {
  .barra-acciones { grid-template-columns: repeat(4, 1fr); }
}

/* Red de seguridad. Ninguna vista debe desplazarse en horizontal: si algo se
   sale, es un fallo que hay que arreglar donde esté, pero mientras tanto el
   documento no se estira -que era lo que hacía crecer hasta el fondo
   decorativo- y la página sigue usándose.

   Va en `#app` y NO en `body`: en `body` taparía el problema en todas
   partes, incluida la pantalla de acceso, y dejaríamos de enterarnos. */
#app { overflow-x: clip; }


/* ==========================================================================
   ESCÁNER EN EL MÓVIL · CUATRO BOTONES Y NADA MÁS
   ==========================================================================

   Quien está en la puerta es, en buena parte, gente mayor del club, de pie,
   con una mano ocupada y con cola detrás. La pantalla tenía título,
   subtítulo, selector de cámara, linterna, un visor negro vacío, una barra de
   progreso, tres contadores, la taquilla, las herramientas de prueba y el
   historial. Todo eso compitiendo antes de poder pulsar nada.

   Aquí se queda lo imprescindible y en este orden:

       ENTRADA / SALIDA     ·  decide qué significa cada lectura
       ABRIR LA CÁMARA      ·  lo que se hace el 95 % de las veces
       ESCRIBIR EL NÚMERO   ·  cuando la cámara no lee o no hay carnet

   El resto sigue estando, pero plegado y por debajo.
   ========================================================================== */

@media (max-width: 900px) {
  /* El visor apagado es un rectángulo negro que no hace nada y ocupa media
     pantalla. Al encender, la cámara pasa a pantalla completa, así que aquí
     no hace falta ninguna ventanita. */
  #page-escaner .esc-visor:not(.activo) { display: none; }
  #page-escaner .esc-visor:not(.activo) + .esc-estado-caja { display: none; }

  /* Las herramientas de prueba no las usa nadie en la puerta */
  #page-escaner details.glass { display: none; }

  /* El título, en pequeño: ya se sabe dónde se está */
  #page-escaner > div:first-child h1 { font-size: 1.5rem; }
  #page-escaner > div:first-child .subtitulo-pagina { display: none; }

  /* Los dos caminos, cámara y a mano, del mismo tamaño y a lo ancho */
  .esc-acciones { display: grid; gap: 0.7rem; }
  .esc-acciones .btn-gigante { min-height: 68px; font-size: 1.05rem; }

  /* El selector de sentido, más alto todavía: es el que no se puede
     equivocar, y se pulsa con el pulgar sin mirar. */
  .sentido-btn { min-height: 72px; font-size: 1.2rem; }
}


/* ==========================================================================
   LA CÁMARA A PANTALLA COMPLETA
   ==========================================================================

   Sostener el teléfono con una mano y enfocar un carnet que te acercan es más
   fácil cuanto más grande sea la imagen. En la puerta eso son segundos por
   persona, y con cola detrás los segundos se notan.

   Lo que se ve encima del vídeo son LOS MISMOS elementos de la pantalla
   normal, recolocados. No hay una copia para pantalla completa: duplicarlos
   habría significado duplicar sus manejadores, y que uno de los dos se
   quedara atrás al primer cambio.
   ========================================================================== */

@media (max-width: 900px) {
  /* Fuera todo lo que no sea la cámara */
  body.camara-plena > #app > header,
  body.camara-plena .nav-inferior,
  body.camara-plena .barra-temporada,
  body.camara-plena #escPartido,
  body.camara-plena #escAforo,
  body.camara-plena #escAviso,
  body.camara-plena .esc-contadores,
  body.camara-plena .esc-historial,
  body.camara-plena .taquilla-plegable,
  body.camara-plena #page-escaner > div:first-child { display: none !important; }

  body.camara-plena { overflow: hidden; }

  /* TRAMPA DE CSS, y cuesta media hora encontrarla: un antepasado con
     `backdrop-filter` deja de ser transparente para el posicionamiento y pasa
     a ser EL CONTENEDOR DE REFERENCIA de sus hijos `position: fixed`. El
     visor, que pide `inset: 0`, se estiraba dentro de su tarjeta -354x34 px-
     en vez de dentro de la pantalla, y desde fuera parecia que `position:
     fixed` no estuviera puesto.
     La tarjeta `.glass` que lo envuelve lleva `blur(18px)`. A pantalla
     completa no pinta nada, asi que se apaga. */
  body.camara-plena .esc-visor-caja,
  body.camara-plena .esc-columna {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
    filter: none !important;
    transform: none !important;
    background: none !important;
    border: 0 !important;
    padding: 0 !important;
    box-shadow: none !important;
  }

  body.camara-plena .esc-visor {
    position: fixed;
    top: 0; right: 0; bottom: 0; left: 0;
    width: auto;
    max-height: none;
    /* `aspect-ratio` lo escribe el JavaScript en el atributo `style` para que
       el marco coincida con el recorte que se analiza. A pantalla completa
       manda la pantalla, así que hay que ganarle a esa línea. */
    aspect-ratio: auto !important;
    border-radius: 0;
    border: 0;
    z-index: 55;
  }

  /* --- La franja de arriba: qué está pasando ------------------------------ */
  body.camara-plena .esc-estado-caja {
    display: block;
    position: fixed;
    top: 0; left: 0; right: 0;
    z-index: 58;
    margin: 0 !important;
    padding: calc(0.7rem + env(safe-area-inset-top, 0)) 1rem 0.7rem;
    background: linear-gradient(to bottom, rgba(0, 0, 0, 0.82), rgba(0, 0, 0, 0));
  }

  body.camara-plena #escEstado {
    /* El `!important` NO es pereza: compite con
           #app main .text-xs { font-size: 0.85rem !important; }
       que existe para que en el móvil ningún texto útil baje de 13,6 px, y
       sin repetirlo aquí gana esa y el mensaje se queda pequeño. Sobre el
       vídeo, y a medio metro, 0,85rem no se lee. */
    font-size: 1.05rem !important;
    line-height: 1.35;
    opacity: 1;
    color: #fff;
    font-weight: 700;
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
    margin-top: 0.5rem;
  }

  /* --- El pie: sentido y los dos botones ---------------------------------- */
  body.camara-plena .sentido-selector {
    position: fixed;
    left: 0.75rem; right: 0.75rem;
    bottom: calc(9.2rem + env(safe-area-inset-bottom, 0));
    z-index: 58;
  }

  /* Sobre el vídeo hace falta fondo sólido: un botón translúcido encima de
     una imagen en movimiento no se lee. */
  body.camara-plena .sentido-btn {
    background: rgba(10, 10, 14, 0.82);
    min-height: 62px;
  }
  body.camara-plena .sentido-btn.entrada.activa { background: #0B7A45; color: #fff; border-color: #10B981; }
  body.camara-plena .sentido-btn.salida.activa  { background: #0369A1; color: #fff; border-color: #38BDF8; }

  body.camara-plena .esc-acciones {
    display: grid;
    position: fixed;
    left: 0.75rem; right: 0.75rem;
    bottom: calc(0.9rem + env(safe-area-inset-bottom, 0));
    z-index: 58;
    grid-template-columns: 1fr 1fr;
    gap: 0.6rem;
  }

  /* «Abrir la cámara» no pinta nada con la cámara abierta */
  body.camara-plena #escBtnIniciarGrande { display: none !important; }

  body.camara-plena .esc-acciones .btn-gigante {
    min-height: 64px;
    font-size: 0.95rem;
    background: rgba(10, 10, 14, 0.82);
    border: 2px solid rgba(255, 255, 255, 0.35);
    color: #fff;
  }
  body.camara-plena #escBtnDetenerGrande {
    background: rgba(185, 28, 28, 0.9);
    border-color: rgba(255, 255, 255, 0.4);
  }

  /* El aviso de acceso ya NO es un cartel: es una notificacion y se coloca
     ella sola (ver `.aviso-acceso`). Aqui solo hay que asegurarse de que
     queda por encima del video y de los botones. */
  body.camara-plena .aviso-acceso { z-index: 62; }


  /* Con la franja arriba, el estado de debajo sobra: dice lo mismo y peor */
  body.camara-plena.veredicto-abierto .esc-estado-caja { display: none; }

  /* Los botones siguen ahí debajo del veredicto: es lo que permite cambiar de
     ENTRADA a SALIDA o salir sin cerrar nada primero. */
  body.camara-plena.veredicto-abierto .sentido-selector,
  body.camara-plena.veredicto-abierto .esc-acciones { z-index: 61; }
}

/* Apaisado con la cámara a pantalla completa: los botones se van a un lado
   para no comerse el alto, que es lo que escasea. */
@media (max-width: 900px) and (max-height: 520px) and (orientation: landscape) {
  body.camara-plena .sentido-selector {
    left: auto; right: 0.6rem; bottom: auto; top: 0.6rem;
    width: 15rem;
  }
  body.camara-plena .sentido-btn { min-height: 48px; font-size: 0.95rem; }
  body.camara-plena .esc-acciones {
    left: auto; right: 0.6rem; bottom: 0.6rem; width: 15rem;
  }
  body.camara-plena .esc-acciones .btn-gigante { min-height: 48px; font-size: 0.85rem; }
}


/* ==========================================================================
   AVISO DE ACCESO
   ==========================================================================

   Lo que sale al pasar un carnet. Antes era un cartel a pantalla completa, y
   con la camara tambien a pantalla completa eso significaba apagarla y volver
   a encuadrar en cada persona. Ahora baja desde arriba como una notificacion
   y deja la camara trabajando debajo.

   El titulo sigue siendo enorme -se mira de medio metro, de pie y sin gafas-
   pero ya no se come la pantalla. Y la barra de abajo enseña cuanto le queda:
   sin ella, un aviso que se va solo parece que se ha ido por un fallo.
   ========================================================================== */

.aviso-acceso {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 60;
  margin: 0 !important;
  padding: calc(0.85rem + env(safe-area-inset-top, 0)) 0.9rem 0;
  border-radius: 0 0 1.1rem 1.1rem;
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.45);
  color: #fff;
  animation: avisoBaja 0.18s ease-out;
}

@keyframes avisoBaja {
  from { transform: translateY(-100%); }
  to { transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .aviso-acceso { animation: none; }
}

/* Los tres colores, los mismos de siempre: verde pasa, ámbar mira, rojo para.
   Son los que ya se reconocen sin leer. */
.aviso-acceso.v-ok    { background: #0B7A45; }
.aviso-acceso.v-aviso { background: #B45309; }
.aviso-acceso.v-error { background: #B91C1C; }

.aviso-fila {
  display: flex;
  align-items: center;
  gap: 0.85rem;
}

.aviso-icono {
  flex: none;
  width: 3.1rem;
  height: 3.1rem;
  border-radius: 0.9rem;
  background: rgba(255, 255, 255, 0.22);
  display: flex;
  align-items: center;
  justify-content: center;
}
.aviso-icono svg { width: 1.8rem; height: 1.8rem; }

.aviso-texto { flex: 1; min-width: 0; }

.aviso-titulo {
  font-family: 'Outfit', sans-serif;
  font-size: 1.6rem;
  font-weight: 900;
  line-height: 1.05;
  letter-spacing: 0.01em;
}

.aviso-quien {
  font-size: 1rem;
  font-weight: 800;
  line-height: 1.25;
  margin-top: 0.15rem;
  /* Un nombre largo no puede empujar el aviso a dos alturas distintas segun
     quien pase: se corta. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.aviso-nota {
  font-size: 0.82rem;
  line-height: 1.3;
  opacity: 0.85;
  margin-top: 0.1rem;
}

.aviso-cerrar {
  flex: none;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 0.8rem;
  border: 0;
  background: rgba(255, 255, 255, 0.18);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
}
.aviso-cerrar:active { background: rgba(255, 255, 255, 0.3); }
.aviso-cerrar svg { width: 1.2rem; height: 1.2rem; }

/* La cuota sin pagar no impide entrar, pero hay que decidir algo. Mientras
   esto esté delante el aviso NO se cierra solo. */
.aviso-pago {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-top: 0.7rem;
  padding: 0.6rem 0.75rem;
  border-radius: 0.8rem;
  background: rgba(0, 0, 0, 0.28);
  font-size: 0.88rem;
  font-weight: 800;
}

.aviso-cobrar {
  flex: none;
  min-height: 44px;
  padding: 0 1.1rem;
  border-radius: 0.7rem;
  border: 2px solid rgba(255, 255, 255, 0.6);
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
  font-family: 'Outfit', sans-serif;
  font-weight: 900;
  font-size: 0.85rem;
}
.aviso-cobrar:active { background: rgba(255, 255, 255, 0.3); }

/* Cuánto le queda en pantalla */
.aviso-barra {
  height: 4px;
  margin: 0.75rem -0.9rem 0;
  background: rgba(0, 0, 0, 0.25);
  overflow: hidden;
}
.aviso-barra i {
  display: block;
  height: 100%;
  width: 100%;
  background: rgba(255, 255, 255, 0.75);
}

/* En pantallas anchas no tiene sentido que cruce los 2000 px de lado a lado:
   se queda arriba a la derecha, como cualquier notificacion. */
@media (min-width: 901px) {
  .aviso-acceso {
    left: auto;
    right: 1.25rem;
    top: 1rem;
    width: min(30rem, calc(100vw - 2.5rem));
    border-radius: 1.1rem;
    padding: 1rem 1rem 0;
  }
  .aviso-barra { margin: 0.8rem -1rem 0; border-radius: 0 0 1.1rem 1.1rem; }
}

/* Pantallas muy estrechas */
@media (max-width: 380px) {
  .aviso-titulo { font-size: 1.35rem; }
  .aviso-icono { width: 2.6rem; height: 2.6rem; }
  .aviso-icono svg { width: 1.45rem; height: 1.45rem; }
}

/* ==========================================================================
   SINCRONIZACIÓN CON EL SERVIDOR
   Un punto en el botón de sincronizar: nada si está al día, ámbar si hay
   cambios subiendo, rojo sin red. Y en la puerta, además, la frase.
   ========================================================================== */
[data-act="sync-data"] { position: relative; }
[data-act="sync-data"][data-sincro="subiendo"]::after,
[data-act="sync-data"][data-sincro="sin-red"]::after,
[data-act="sync-data"][data-sincro="error"]::after {
  content: '';
  position: absolute;
  top: 4px;
  right: 4px;
  width: 9px;
  height: 9px;
  border-radius: 999px;
  background: #f59e0b;
  box-shadow: 0 0 0 2px var(--bg, #0b0f17);
}
[data-act="sync-data"][data-sincro="sin-red"]::after,
[data-act="sync-data"][data-sincro="error"]::after { background: #ef4444; }

.sincro-texto {
  font-size: 0.85rem !important;
  font-weight: 700;
  line-height: 1.35;
  padding: 0.55rem 0.8rem;
  border-radius: 0.75rem;
  background: rgba(245, 158, 11, 0.14);
  color: #fbbf24;
}
.sincro-texto.sin-red {
  background: rgba(239, 68, 68, 0.16);
  color: #fca5a5;
}
body.light .sincro-texto { color: #92400e; }
body.light .sincro-texto.sin-red { color: #991b1b; }

/* ==========================================================================
   «¿DÓNDE ESTÁN LOS DEMÁS?» · aviso de abonados en otras temporadas
   ========================================================================== */
.aviso-temporadas {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 0.85rem 1rem;
  border-radius: 1rem;
  background: rgba(56, 189, 248, 0.12);
  border: 1px solid rgba(56, 189, 248, 0.3);
}
.aviso-temporadas-txt {
  font-size: 0.9rem !important;
  line-height: 1.4;
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  color: #7dd3fc;
}
body.light .aviso-temporadas-txt { color: #075985; }
.aviso-temporadas-txt b { font-weight: 800; }
.aviso-temporadas-botones {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.aviso-temporadas-botones .btn-ghost {
  min-height: 44px;
  padding: 0 1rem;
  border-radius: 0.8rem;
  font-weight: 700;
  font-size: 0.85rem;
}
@media (max-width: 640px) {
  .aviso-temporadas-botones .btn-ghost { flex: 1 1 auto; }
}

/* ---------- Fichas del censo: seleccionar y actuar con el dedo ----------
   En el móvil la tabla está oculta, así que las casillas y los botones de la
   tabla no existen. Sin esto, «Cobrar» y la papelera de arriba se veían pero
   no se podía seleccionar a nadie, y el carnet no había forma de sacarlo. */
/* La casilla va ENCIMA de la ficha, no en la fila: metida en el flujo,
   empujaba el contenido y la ficha se salía de la pantalla. */
.ficha-marca {
  display: none;
  position: absolute;
  top: 0;
  right: 0;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  cursor: pointer;
}
.ficha-marca input { width: 1.4rem; height: 1.4rem; }
.ficha { position: relative; }
@media (max-width: 900px) {
  /* Hueco para que el nombre no pase por debajo de la casilla */
  .ficha-datos { padding-right: 2.6rem; }
}
.ficha.seleccionada {
  border-color: var(--brand);
  background: rgba(242, 101, 34, 0.08);
}

.ficha-botones {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.6rem;
}
.ficha-btn {
  flex: 1 1 auto;
  min-height: 46px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: 0 0.9rem;
  border-radius: 0.8rem;
  font-weight: 800;
  font-size: 0.85rem;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: rgba(255, 255, 255, 0.06);
  color: inherit;
}
body.light .ficha-btn { border-color: rgba(0, 0, 0, 0.12); background: rgba(0, 0, 0, 0.04); }
.ficha-btn.cobrar {
  background: rgba(16, 185, 129, 0.18);
  border-color: rgba(16, 185, 129, 0.45);
  color: #34d399;
}
.ficha-btn.borrar {
  background: rgba(244, 63, 94, 0.14);
  border-color: rgba(244, 63, 94, 0.35);
  color: #fb7185;
}
.ficha-btn:active { transform: scale(0.98); }

@media (max-width: 900px) {
  .ficha-marca { display: inline-flex; }
}

/* ---------- Pies que no se pierden al final del formulario --------------
   La ficha del abonado son 18 campos y la configuración del club 13: en un
   móvil eso es más de mil píxeles hasta el botón de guardar, y es fácil
   salirse creyendo que se ha guardado. En móvil el pie se queda pegado. */
@media (max-width: 900px) {
  .pie-pegado {
    position: sticky;
    bottom: 0;
    z-index: 5;
    margin: 0 -1.25rem -1.25rem;
    padding: 0.8rem 1.25rem calc(0.8rem + env(safe-area-inset-bottom));
    background: var(--panel, rgba(15, 20, 30, 0.97));
    backdrop-filter: blur(8px);
    border-top: 1px solid rgba(255, 255, 255, 0.12);
  }
  body.light .pie-pegado { background: rgba(255, 255, 255, 0.97); }
  .pie-pegado .btn-primary,
  .pie-pegado .btn-ghost { min-height: 50px; }
  /* En Configuración, que va dentro de la página y no de un diálogo, el pie
     tiene que quedar por encima de la barra de navegación inferior. */
  #page-ajustes .pie-pegado { bottom: 4.6rem; margin-inline: 0; border-radius: 0.9rem; }
}

/* ---------- Renovar temporada en el móvil -------------------------------
   Es el trámite anual de Presidencia y se hacía imposible: cinco columnas de
   ancho fijo dentro de un diálogo estrecho. Aquí se queda lo que hace falta
   para decidir: casilla, número, socio y cuota. */
.tabla-renovar .col-marca { width: 3rem; }
.tabla-renovar .col-num { width: 5rem; }
.tabla-renovar .col-modalidad { width: 7rem; }
.tabla-renovar .col-cuota { width: 6rem; }

@media (max-width: 640px) {
  .tabla-renovar .col-modalidad,
  .tabla-renovar .col-dni { display: none; }
  .tabla-renovar .col-marca { width: 2.6rem; }
  .tabla-renovar .col-num { width: 4rem; }
  .tabla-renovar .col-cuota { width: 5rem; }
  .tabla-renovar th, .tabla-renovar td { padding-inline: 0.4rem; }
  .tabla-renovar td { padding-block: 0.7rem; }
}

/* ---------- «Más acciones» en el censo ---------------------------------- */
.solo-movil { display: none !important; }

@media (max-width: 900px) {
  .solo-movil { display: inline-flex !important; }
  /* Lo de menos uso, detrás del botón «Más acciones» */
  .barra-acciones .accion-secundaria { display: none !important; }
  /* `:not(.hidden)`: los botones que el rol no puede usar llevan esa clase
     y no pueden reaparecer por abrir «Más acciones». */
  .barra-acciones.mostrar-mas .accion-secundaria:not(.hidden) { display: inline-flex !important; }
}

/* ==========================================================================
   ÚLTIMOS RETOQUES DE MÓVIL
   Van al FINAL a propósito: compiten con reglas de más arriba que ganarían
   por orden de aparición (`.barra-temporada{top:0}` es una de ellas).
   ========================================================================== */
@media (max-width: 900px) {
  /* La cabecera del móvil ocupa el tope: estas dos se pegan por debajo, no
     detrás. Antes, al desplazar, la temporada desaparecía y el aviso naranja
     de guardado llegaba a tapar el menú. */
  .barra-temporada { top: 4.6rem; }
  .aviso-guardado { top: 4.6rem; }

  /* Las fichas no pueden ser más anchas que la pantalla: la rejilla las deja
     crecer con su contenido si no se le dice que la columna es de 0 a 1fr. */
  .ficha-lista { grid-template-columns: minmax(0, 1fr); }
  .ficha { max-width: 100%; }
  .ficha-datos { min-width: 0; }
  .ficha-nombre, .ficha-linea { overflow-wrap: anywhere; }

  /* Los botones de cada partido eran tres columnas fijas para hasta cinco
     botones: se salían por la derecha. Ahora se reparten y bajan de línea. */
  .partido-acciones {
    display: flex !important;
    flex-wrap: wrap;
    gap: 0.5rem;
  }
  .partido-acciones .btn-acta,
  .partido-acciones .btn-escanear-partido { flex: 1 1 9rem; min-height: 46px; }
  .partido-acciones .icon-btn { flex: 0 0 auto; min-height: 46px; min-width: 46px; }
}

/* ==========================================================================
   MODO AUTOMÁTICO DE LA PUERTA Y CUENTAS DE CADA ABONADO
   ========================================================================== */
.sentido-selector { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.sentido-btn.auto.activa {
  border-color: var(--brand);
  background: rgba(242, 101, 34, 0.16);
  color: var(--brand);
}
.sentido-ayuda {
  margin-top: 0.4rem;
  font-size: 0.85rem !important;
  line-height: 1.35;
  opacity: 0.7;
  text-align: center;
}
body.modo-auto .esc-marco,
body.modo-auto .esc-esquina { border-color: var(--brand); }

/* Lo que lleva hecho en la puerta, dentro de su ficha */
.historial-acceso {
  padding: 0.85rem 1rem;
  border-radius: 1rem;
  background: rgba(56, 189, 248, 0.09);
  border: 1px solid rgba(56, 189, 248, 0.25);
}
.historial-acceso.vacio { opacity: 0.7; }
.ha-titulo {
  font-size: 0.8rem !important;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  opacity: 0.75;
}
.ha-resumen { font-size: 0.95rem !important; margin-top: 0.15rem; }
.ha-resumen b { font-family: 'Outfit', sans-serif; font-weight: 800; color: #38BDF8; }
body.light .ha-resumen b { color: #0369a1; }
.ha-lista { margin-top: 0.6rem; display: grid; gap: 0.35rem; }
.ha-lista li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.2rem 0.6rem;
  font-size: 0.85rem;
  padding: 0.35rem 0.5rem;
  border-radius: 0.6rem;
  background: rgba(255, 255, 255, 0.05);
}
body.light .ha-lista li { background: rgba(0, 0, 0, 0.04); }
.ha-partido { font-weight: 700; flex: 1 1 auto; }
.ha-fecha { opacity: 0.6; font-variant-numeric: tabular-nums; }
.ha-cuenta { font-weight: 800; white-space: nowrap; }
.ha-cuenta small { font-weight: 600; opacity: 0.6; }
.ha-mas { font-size: 0.8rem !important; opacity: 0.6; margin-top: 0.4rem; }

@media (max-width: 900px) {
  /* Tres botones de 72 px no caben en una fila de 390: AUTOMÁTICO arriba, a
     lo ancho, y debajo las dos puertas fijas. */
  .sentido-selector { grid-template-columns: 1fr 1fr; }
  .sentido-btn.auto { grid-column: 1 / -1; }

  /* Con la cámara a pantalla completa mandan los píxeles: los tres en una
     fila, más bajos, y sin la frase de ayuda. */
  body.camara-plena .sentido-selector { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  body.camara-plena .sentido-btn { min-height: 56px; font-size: 0.92rem; }
  body.camara-plena .sentido-btn.auto { grid-column: auto; }
  body.camara-plena .sentido-ayuda { display: none; }
}
