/* Sistema de Folios — Salón Cisne
   Estilo "panel administrativo premium" (referencia: paneles tipo Secure
   Cloud/NetBIT): un lienzo oscuro con degradado y brillo dorado de marca
   sirve de fondo continuo para TODO el layout (sidebar/header incluidos,
   sin cajas ni bordes que los separen del fondo), y encima flota un único
   panel claro por pantalla con esquinas muy redondeadas y sombra — en vez
   de varias tarjetas sueltas con huecos entre ellas. Herramienta de uso
   administrativo interno (no de cara al cliente del salón): una sola
   familia tipográfica sans-serif en todos lados, con jerarquía marcada por
   tamaño/peso, no por cambiar de fuente. */

* { box-sizing: border-box; }

:root {
  /* --- Chrome (header / sidebar / nav): vive directamente sobre el fondo
     oscuro del <body>, sin caja ni borde propios — por eso son overlays
     translúcidos en vez de colores sólidos. --- */
  --chrome-texto: #e9e4d9;
  --chrome-texto-fuerte: #ffffff;
  --chrome-texto-suave: #a89e88;
  --chrome-borde: rgba(255, 255, 255, 0.22);
  --chrome-hover: rgba(255, 255, 255, 0.07);
  --chrome-activo-bg: rgba(212, 175, 55, 0.18);

  /* --- Contenido: claro (blancos/grises fríos, casi sin tinte cálido) para
     que contraste con fuerza contra el chrome oscuro — el dorado queda como
     acento puntual (botones, activos, logo), no como tono de fondo. --- */
  --bg-app: #eef0f2;
  --bg-panel: #ffffff;
  --bg-panel-alt: #f3f4f6;
  --bg-input: #fbfbfc;
  --borde: #e2e4e8;
  --borde-fuerte: #cbcfd6;

  --texto: #262a30;
  --texto-fuerte: #12141a;
  --texto-suave: #6b7280;

  --color-primario: #d4af37;
  --color-primario-oscuro: #a3841f;
  --color-primario-texto: #171106;
  /* Color de los <a> "normales" (fuera de nav/botones): dorado oscuro sobre
     los paneles claros, para que un enlace nunca se vea con el azul default
     del navegador, que desentona con toda la paleta de marca. En el login
     (fondo oscuro, ver body.pagina-login más abajo) se sobreescribe por el
     dorado brillante, que sí contrasta bien ahí. */
  --enlace: var(--color-primario-oscuro);

  --color-peligro: #dc2626;
  --color-peligro-hover: #b91c1c;

  --color-secundario-bg: #eceef1;
  --color-secundario-bg-hover: #e2e5ea;

  --exito-bg: #dcf3e1;
  --exito-texto: #1e7a34;
  --peligro-bg: #fbdcdc;
  --peligro-texto: #a3231b;
  --alerta-bg: #fbe7cf;
  --alerta-texto: #8a4b0f;
  --info-bg: #dbe9fb;
  --info-texto: #1d4f8a;

  --radio: 12px;
  --radio-chico: 10px;
  --radio-panel: 22px;
  --radio-pill: 999px;
  --sombra: 0 4px 16px rgba(10, 8, 4, 0.08);
  --sombra-panel: 0 30px 60px -20px rgba(0, 0, 0, 0.5), 0 10px 25px rgba(0, 0, 0, 0.22);
  --transicion: 150ms ease;

  --sidebar-ancho: 280px;
  --sidebar-ancho-colapsado: 76px;
  --topbar-alto: 76px;
}

html, body { height: 100%; }

a { color: var(--enlace); text-decoration: underline; }
a:hover, a:focus { color: var(--color-primario); }

body {
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  margin: 0;
  /* Lienzo de marca: negro/carbón NEUTRO (sin tinte café) como base. En vez
     de solo dos brillos dorados arriba, ahora hay una capa de grano muy
     sutil (le quita el look "plano de pantalla" y le da textura, como un
     panel premium) más varios resplandores a distintas alturas y tamaños
     (dorado arriba, un brillo neutro tenue abajo) para que se sienta con
     profundidad en vez de un degradado de dos puntos nada más. Este mismo
     fondo cubre TODO el layout — sidebar/header incluidos — para que se
     sientan una sola superficie continua, no cajas separadas. */
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.035 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E") repeat,
    radial-gradient(1300px 700px at 10% -12%, rgba(212, 175, 55, 0.30) 0%, rgba(212, 175, 55, 0) 55%),
    radial-gradient(950px 560px at 92% -8%, rgba(212, 175, 55, 0.17) 0%, rgba(212, 175, 55, 0) 50%),
    radial-gradient(1500px 800px at 50% 118%, rgba(255, 255, 255, 0.045) 0%, rgba(255, 255, 255, 0) 60%),
    linear-gradient(180deg, #17171c 0%, #0b0b0d 45%, #030304 100%);
  background-attachment: fixed;
  color: var(--texto);
  -webkit-text-size-adjust: 100%;
}

body.modal-abierto { overflow: hidden; }

img, svg { max-width: 100%; }

/* --- Encabezado: dos piezas, como en la referencia ---
   1) .topbar: barra superior de ANCHO COMPLETO (logo a la izquierda, avatar
      del usuario a la derecha) — siempre visible arriba de todo.
   2) header: en celular es la fila de navegación justo debajo de la topbar;
      en pantallas grandes (≥960px) se convierte en la barra lateral fija
      (sidebar) con la etiqueta "Sistema de Folios" + los botones de módulo.
   Ninguna de las dos tiene un color de fondo "de caja" sólido — son overlays
   translúcidos con desenfoque sobre el mismo lienzo oscuro del <body>, para
   que todo se sienta una sola superficie continua. */

.topbar {
  position: sticky;
  top: 0;
  z-index: 25;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 1.25rem;
  background: rgba(10, 8, 5, 0.6);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.header-marca {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-width: 0;
}

.header-marca img {
  height: 70px;
  width: 70px;
  object-fit: contain;
  flex-shrink: 0;
}

.header-marca strong {
  font-size: 1.2rem;
  font-weight: 800;
  letter-spacing: 0.015em;
  color: var(--color-primario);
  white-space: nowrap;
}

header {
  color: var(--chrome-texto-fuerte);
  padding: 0.85rem 1.25rem 1.1rem;
}

.nav-etiqueta-fila {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}

/* Etiqueta de sección sobre la navegación (equivalente a "MÓDULOS" en Secure
   Cloud): letra grande y con peso para que la navegación destaque como
   sección propia, no solo texto suelto. Solo aplica en modo sidebar
   (pantallas grandes) — en celular el nav ya es autoexplicativo y el
   espacio es limitado. */
.nav-etiqueta { display: none; }

@media (min-width: 960px) {
  .nav-etiqueta { display: block; }
  .nav-etiqueta span {
    display: inline-block;
    font-size: 1.6rem;
    line-height: 1.15;
    font-weight: 800;
    letter-spacing: 0.005em;
    color: var(--chrome-texto-fuerte);
    padding-bottom: 0.4rem;
    border-bottom: 3px solid var(--color-primario);
  }
  body.sidebar-colapsado .nav-etiqueta { display: none; }
  body.sidebar-colapsado .nav-etiqueta-fila { justify-content: center; }
}

nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: center;
  margin-top: 1rem;
}

/* Los enlaces de navegación se ven como botones reales (con contorno sutil
   propio, no solo en :hover) — el mismo criterio que los módulos de Secure
   Cloud: letra más grande y con peso para que "hagan sentido de su función",
   con bastante espacio interno, y un contorno tenue que da separación sin
   sentirse como piezas sueltas. */
nav a {
  display: inline-flex;
  align-items: center;
  gap: 0.65rem;
  color: var(--chrome-texto);
  text-decoration: none;
  font-size: 0.98rem;
  font-weight: 700;
  white-space: nowrap;
  padding: 0.85rem 1.05rem;
  border-radius: var(--radio-chico);
  background: rgba(255, 255, 255, 0.065);
  border: 1px solid var(--chrome-borde);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  transition: background var(--transicion), color var(--transicion), border-color var(--transicion);
}

/* Mismo caso que form.grid-2[hidden] más abajo: "nav a" (display: inline-flex)
   tiene más especificidad que el [hidden] { display: none } nativo del
   navegador, así que sin esto, un <a class="solo-admin" hidden> (Usuarios,
   Confirmaciones, Auditoría) se queda visible para cualquier rol aunque JS
   sí le haya puesto correctamente el atributo hidden — se detectó al
   verificar que Auditoría (Paso 37) apareciera SOLO para superadmin. */
nav a[hidden] { display: none; }

nav a svg { width: 21px; height: 21px; flex-shrink: 0; }

nav a:hover, nav a:focus {
  background: var(--chrome-hover);
  color: var(--color-primario);
}

/* Página actual dentro de la navegación: pastilla con tinte dorado, para que
   se note claramente cuál sección está activa (igual que en Secure Cloud). */
nav a.nav-activo {
  background: var(--chrome-activo-bg);
  border-color: rgba(212, 175, 55, 0.4);
  color: var(--color-primario);
}

/* Chevron al final de cada botón de navegación (solo en modo sidebar, como
   en Secure Cloud) — un detalle pequeño pero que refuerza que son botones
   de módulo, no simples enlaces de texto. */
@media (min-width: 960px) {
  nav a::after {
    content: "\203A";
    margin-left: auto;
    font-size: 1.2rem;
    line-height: 1;
    opacity: 0.55;
  }
  body.sidebar-colapsado nav a::after { display: none; }
}

.sidebar-toggle {
  display: none; /* solo visible en modo sidebar (pantallas grandes) */
  background: transparent;
  border: 1px solid var(--chrome-borde);
  color: var(--chrome-texto-suave);
  border-radius: var(--radio-chico);
  width: 2.1rem;
  height: 2.1rem;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  transition: background var(--transicion), color var(--transicion);
}

.sidebar-toggle:hover {
  background: var(--chrome-hover);
  color: var(--color-primario);
}

.sidebar-toggle svg {
  transition: transform var(--transicion);
}

/* --- Menú del usuario (avatar), en la barra superior ---
   No es decorativo: es un botón real que abre un menú con acciones de la
   cuenta (cambiar mi contraseña, salir) — se inyecta por JS (ver
   inicializarMenuUsuario en app-shell.js) para no repetir el mismo bloque en
   las 4 páginas autenticadas. Vive en .topbar, arriba a la derecha, igual
   que en Secure Cloud — no escondido al fondo de la navegación. */

/* Agrupa avatar + campana como un solo hijo de .topbar (ver
   obtenerTopbarDerecha en app-shell.js) — evita que la campana quede
   "flotando" centrada cuando .topbar tiene 3 hijos en vez de 2. */
.topbar-derecha { display: flex; align-items: center; gap: 0.65rem; flex-shrink: 0; }

.usuario-menu-envoltura { position: relative; flex-shrink: 0; }

.usuario-actual {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  background: transparent;
  border: none;
  padding: 0.3rem 0.4rem;
  border-radius: var(--radio-chico);
  cursor: pointer;
  color: var(--chrome-texto-fuerte);
  font-family: inherit;
  text-align: left;
  transition: background var(--transicion);
}

.usuario-actual:hover { background: var(--chrome-hover); }

.avatar-iniciales {
  flex-shrink: 0;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--color-primario), var(--color-primario-oscuro));
  color: var(--color-primario-texto);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  font-size: 0.85rem;
  border: 2px solid rgba(255, 255, 255, 0.45);
}

.usuario-info {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.usuario-nombre {
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--chrome-texto-fuerte);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 11rem;
}

.usuario-rol {
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--color-primario);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.usuario-dropdown {
  position: absolute;
  top: calc(100% + 0.6rem);
  right: 0;
  min-width: 220px;
  background: var(--bg-panel);
  border-radius: var(--radio-chico);
  box-shadow: var(--sombra-panel);
  overflow: hidden;
  z-index: 30;
}

.usuario-dropdown[hidden] { display: none; }

.usuario-dropdown button {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 0.75rem 0.9rem;
  font-size: 0.88rem;
  font-weight: 600;
  font-family: inherit;
  color: var(--texto-fuerte);
  cursor: pointer;
  white-space: nowrap;
}

.usuario-dropdown button:hover { background: var(--bg-panel-alt); }
.usuario-dropdown button + button { border-top: 1px solid var(--borde); }

/* --- Campana de notificaciones (Paso 34) ---
   Mismo patrón que el menú de usuario: se inyecta por JS en .topbar (ver
   inicializarCampanaNotificaciones en app-shell.js), a la izquierda del
   avatar, con una insignia roja con el número de no leídas. */

.notificaciones-envoltura { position: relative; flex-shrink: 0; }

.btn-campana {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.4rem;
  height: 2.4rem;
  background: transparent;
  border: none;
  border-radius: 50%;
  cursor: pointer;
  color: var(--chrome-texto-fuerte);
  transition: background var(--transicion);
}

.btn-campana:hover { background: var(--chrome-hover); }

.badge-campana {
  position: absolute;
  top: 0.1rem;
  right: 0.1rem;
  min-width: 1.1rem;
  height: 1.1rem;
  padding: 0 0.25rem;
  border-radius: 999px;
  background: var(--peligro-texto, #c0392b);
  color: #fff;
  font-size: 0.65rem;
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}

.badge-campana[hidden] { display: none; }

.notificaciones-dropdown {
  position: absolute;
  top: calc(100% + 0.6rem);
  right: 0;
  min-width: 300px;
  max-width: 360px;
  max-height: 400px;
  overflow-y: auto;
  background: var(--bg-panel);
  border-radius: var(--radio-chico);
  box-shadow: var(--sombra-panel);
  z-index: 30;
}

.notificaciones-dropdown[hidden] { display: none; }

.notificacion-item {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.7rem 0.9rem;
  text-decoration: none;
  border-bottom: 1px solid var(--borde);
  color: var(--texto-fuerte);
}

.notificacion-item:hover { background: var(--bg-panel-alt); }
.notificacion-item--no-leida { background: var(--info-bg); }

.notificacion-mensaje { font-size: 0.85rem; font-weight: 600; }
.notificacion-fecha { font-size: 0.72rem; color: var(--chrome-texto-fuerte); opacity: 0.7; }

.notificaciones-vacio {
  padding: 1rem;
  font-size: 0.85rem;
  text-align: center;
  color: var(--texto-tenue, #8a7f6a);
}

@media (max-width: 480px) {
  /* En celular muy angosto solo se muestra el avatar (sin nombre/rol), para
     no saturar la barra superior junto con el logo. */
  .usuario-info { display: none; }
}

/* --- Layout --- */

main {
  margin: 0;
  padding: 1.5rem 1.75rem 2.5rem;
  width: 100%;
}

/* El login trae su propio <main style="max-width:420px; margin-top:3rem;">
   inline. Ese inline solo pisa margin-top (no es el shorthand "margin"), así
   que sin esta regla se queda con margin-left/right en 0 (heredado de la
   regla genérica de arriba) y la tarjeta se ve pegada a la izquierda en vez
   de centrada. Esto centra horizontalmente solo en el login, sin tocar el
   layout de las páginas con sidebar. */
body.pagina-login main {
  margin-left: auto;
  margin-right: auto;
}

@media (min-width: 960px) {
  body {
    padding-left: var(--sidebar-ancho);
    padding-top: var(--topbar-alto);
    transition: padding-left 180ms ease;
  }
  body.sidebar-colapsado { padding-left: var(--sidebar-ancho-colapsado); }

  /* El login (.pagina-login) no tiene topbar/sidebar — es solo la tarjeta
     centrada. Sin este reset, heredaba el padding del resto de las páginas
     y la tarjeta terminaba centrada dentro del espacio "restante" en vez del
     viewport completo: se veía descentrada en vez de centrada de verdad. */
  body.pagina-login { padding-left: 0; padding-top: 0; }

  .topbar {
    position: fixed;
    inset: 0 0 auto 0;
    height: var(--topbar-alto);
    padding: 0 1.75rem;
    /* Antes esto era "background: none", pensado para que se viera
       exactamente igual que el lienzo oscuro del <body> detrás, sin costura
       con el sidebar. El problema: al ser realmente transparente, en cuanto
       el contenido tenía más de una pantalla de alto, el panel BLANCO se
       "asomaba" detrás de la topbar fija al hacer scroll — ahí es donde el
       avatar se veía mezclado con el panel. La solución es pintar aquí el
       MISMO fondo del <body> (mismo grano + resplandores + degradado) pero
       de forma opaca y propia, para que nunca se vea nada de lo que hay
       scrolleado debajo, sin perder la sensación de "misma superficie" con
       el sidebar. Si el fondo del <body> cambia, hay que replicar el cambio
       aquí también (son las dos únicas copias). */
    background:
      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.035 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E") repeat,
      radial-gradient(1300px 700px at 10% -12%, rgba(212, 175, 55, 0.30) 0%, rgba(212, 175, 55, 0) 55%),
      radial-gradient(950px 560px at 92% -8%, rgba(212, 175, 55, 0.17) 0%, rgba(212, 175, 55, 0) 50%),
      radial-gradient(1500px 800px at 50% 118%, rgba(255, 255, 255, 0.045) 0%, rgba(255, 255, 255, 0) 60%),
      linear-gradient(180deg, #17171c 0%, #0b0b0d 45%, #030304 100%);
    background-attachment: fixed;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  header {
    position: fixed;
    inset: var(--topbar-alto) auto 0 0;
    width: var(--sidebar-ancho);
    height: calc(100vh - var(--topbar-alto));
    display: flex;
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    padding: 1.5rem 1.1rem;
    overflow-y: auto;
    transition: width 180ms ease;
  }

  body.sidebar-colapsado header { width: var(--sidebar-ancho-colapsado); }

  .sidebar-toggle {
    display: inline-flex;
  }

  nav {
    flex-direction: column;
    align-items: stretch;
    gap: 0.6rem;
    /* Marco general que envuelve TODO el bloque de módulos, además del
       contorno individual de cada botón — igual que se pidió: una tarjeta
       que agrupa Folios/Nuevo folio/Usuarios/Exportar Excel y los separa
       visualmente del resto del sidebar, sin dejar de sentirse una sola
       superficie (el fondo del marco es apenas un tono más claro que el
       lienzo de atrás, no una caja sólida distinta). */
    padding: 0.6rem;
    border: 1px solid var(--chrome-borde);
    border-radius: var(--radio);
    background: rgba(255, 255, 255, 0.02);
  }

  body.sidebar-colapsado nav {
    padding: 0.6rem 0.4rem;
  }

  nav a, nav .btn {
    width: 100%;
    justify-content: flex-start;
  }

  body.sidebar-colapsado .nav-label {
    display: none;
  }

  body.sidebar-colapsado nav a,
  body.sidebar-colapsado nav .btn {
    justify-content: center;
    padding-left: 0;
    padding-right: 0;
  }

  body.sidebar-colapsado .sidebar-toggle svg { transform: rotate(180deg); }
}

/* --- Panel único de contenido ---
   .card: panel independiente de una sola pieza (lo usa únicamente el login).
   .panel + .panel-seccion: el patrón que usan folios/folio-nuevo/folio-
   detalle/usuarios — TODO el contenido de una pantalla vive dentro de un
   solo panel flotante; las distintas secciones (p. ej. "Filtros" y "Listado
   de folios") ya no son cajas independientes con su propio borde y sombra,
   solo se separan entre sí con una línea divisoria sutil. Así se lee como
   un sistema único, no como piezas sueltas. */

.card, .panel {
  background: var(--bg-panel);
  border-radius: var(--radio-panel);
  box-shadow: var(--sombra-panel);
}

.card { padding: 1.75rem; margin-bottom: 1.5rem; }
/* Antes .panel tenía overflow:hidden para redondear las esquinas de sus
   .panel-seccion hijas — pero eso también recorta el popup nativo de
   cualquier <select> dentro del panel cuando abre cerca del borde inferior
   (se veía "mochado", ver conversación con Sal). El redondeo se logra igual
   poniéndolo directamente en la primera/última sección, sin necesidad de
   recortar nada. */
.panel { margin-bottom: 1.5rem; }

.panel-seccion { padding: 1.75rem; border-radius: inherit; }
.panel-seccion:first-child { border-radius: var(--radio-panel) var(--radio-panel) 0 0; }
.panel-seccion:last-child { border-radius: 0 0 var(--radio-panel) var(--radio-panel); }
.panel-seccion:only-child { border-radius: var(--radio-panel); }
.panel-seccion + .panel-seccion { border-top: 1px solid var(--borde); }

/* Sección visualmente "apagada" cuando no hay nada que hacer ahí (ej.
   "Agregar pago" en un folio cancelado o ya liquidado) — antes solo se
   ocultaba el formulario y quedaba un mensaje suelto en una tarjeta blanca
   normal, sin sentirse realmente bloqueada. */
.panel-seccion--bloqueada {
  background: var(--bg-panel-alt);
  opacity: 0.72;
}
.panel-seccion--bloqueada h2 .icono-bloqueado {
  width: 16px;
  height: 16px;
  opacity: 0.55;
}

.card h1, .card h2,
.panel-seccion h1, .panel-seccion h2 {
  margin-top: 0;
  color: var(--texto-fuerte);
}

.card h2, .panel-seccion h2 {
  font-size: 1.15rem;
  font-weight: 800;
  margin-bottom: 1.1rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* --- Formularios --- */

form label {
  display: block;
  margin-top: 0.85rem;
  margin-bottom: 0.3rem;
  font-weight: 700;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--texto-suave);
}

/* El modal de prompt genérico (confirmarAccion/mostrarPrompt) no vive dentro
   de un <form>, así que estos estilos también aplican dentro de .modal-cuerpo
   para que ese input no se vea "crudo" sin estilo. */
form input, form select, form textarea,
.modal-cuerpo input, .modal-cuerpo select, .modal-cuerpo textarea {
  width: 100%;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--borde);
  border-radius: var(--radio-chico);
  font-size: 1rem; /* evita zoom automático en iOS al enfocar */
  font-family: inherit;
  background: var(--bg-input);
  color: var(--texto-fuerte);
  transition: border-color var(--transicion), box-shadow var(--transicion);
}

form input::placeholder, form textarea::placeholder,
.modal-cuerpo input::placeholder { color: var(--texto-suave); opacity: 0.6; }

form input:focus, form select:focus, form textarea:focus,
.modal-cuerpo input:focus, .modal-cuerpo select:focus, .modal-cuerpo textarea:focus {
  outline: none;
  border-color: var(--color-primario);
  box-shadow: 0 0 0 3px rgba(201, 162, 39, 0.22);
}

/* --- Campo de contraseña con botón de mostrar/ocultar (ícono de ojo) --- */

.campo-password {
  position: relative;
}

.campo-password input {
  padding-right: 2.6rem;
}

.btn-toggle-password {
  position: absolute;
  right: 0.35rem;
  top: 50%;
  transform: translateY(-50%);
  background: transparent;
  border: none;
  color: var(--texto-suave);
  cursor: pointer;
  padding: 0.35rem;
  border-radius: var(--radio-chico);
  display: flex;
  align-items: center;
  justify-content: center;
}

.btn-toggle-password:hover { color: var(--color-primario); }
.btn-toggle-password[hidden] { display: none; }

form.grid-2 {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0 1rem;
}

/* La regla de arriba (display: grid) tiene más especificidad que el
   [hidden] { display: none } que trae el navegador por default, así que sin
   esto, un <form class="grid-2" hidden> se queda visible por error — pasaba
   con "Agregar pago" cuando un folio quedaba liquidado. */
form.grid-2[hidden] { display: none; }

@media (min-width: 640px) {
  form.grid-2 { grid-template-columns: 1fr 1fr; }
  form.grid-2 .col-completa { grid-column: 1 / -1; }
}

/* Selector de país (bandera + lada) + número, para los campos de
   WhatsApp/celular (Paso 38b) — el select se queda del ancho de su
   contenido, el input del número ocupa el resto. */
.grupo-telefono {
  display: flex;
  gap: 0.5rem;
}
.grupo-telefono select {
  flex: 0 0 auto;
  width: auto;
  max-width: 12rem;
}
.grupo-telefono input {
  flex: 1 1 auto;
  min-width: 0;
}

.acciones-form {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 1.25rem;
}

/* --- Sistema de botones --- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  height: 2.5rem;
  padding: 0 1.1rem;
  border-radius: var(--radio-chico);
  border: 1px solid transparent;
  font-size: 0.92rem;
  font-weight: 700;
  font-family: inherit;
  cursor: pointer;
  text-decoration: none;
  transition: background var(--transicion), border-color var(--transicion), filter var(--transicion), transform var(--transicion);
  white-space: nowrap;
}

.btn:hover { filter: brightness(1.08); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.65; cursor: not-allowed; filter: none; transform: none; }

/* Spinner chico para meter dentro de un botón mientras dura una acción
   asíncrona (ej. crear usuario, que espera a que se mande el correo de
   bienvenida) — así la persona ve que el sistema sigue trabajando y no le
   sigue picando al botón. */
.spinner-boton {
  display: inline-block;
  width: 0.9rem;
  height: 0.9rem;
  border: 2px solid rgba(255, 255, 255, 0.4);
  border-top-color: currentColor;
  border-radius: 50%;
  animation: spinner-boton-giro 0.7s linear infinite;
}

@keyframes spinner-boton-giro {
  to { transform: rotate(360deg); }
}

.btn-primary { background: var(--color-primario); color: var(--color-primario-texto); }
.btn-primary:hover { background: #d8b23a; }

.btn-secondary { background: var(--color-secundario-bg); color: var(--texto-fuerte); border-color: var(--borde); }
.btn-secondary:hover { background: var(--color-secundario-bg-hover); }

.btn-danger { background: var(--color-peligro); color: #fff; }
.btn-danger:hover { background: var(--color-peligro-hover); }

/* .btn-ghost solo se usa dentro del chrome (nav): "Instalar app" / "Salir". */
.btn-ghost { background: transparent; color: var(--chrome-texto); border-color: var(--chrome-borde); }
.btn-ghost:hover { background: var(--chrome-hover); color: var(--color-primario); }

.btn-sm { height: 2rem; padding: 0 0.75rem; font-size: 0.82rem; }

.btn[hidden] { display: none; }

td .btn + .btn { margin-left: 0.4rem; }

/* --- Tablas --- */

.tabla-scroll {
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

table {
  width: 100%;
  border-collapse: collapse;
  min-width: 520px;
}

thead th {
  text-align: left;
  padding: 0.65rem 0.6rem;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--texto-suave);
  background: var(--bg-panel-alt);
  border-bottom: 1px solid var(--borde);
  white-space: nowrap;
}

tbody td {
  padding: 0.65rem 0.6rem;
  border-bottom: 1px solid var(--borde);
  white-space: nowrap;
  font-size: 0.92rem;
  color: var(--texto);
}

tbody tr:hover { background: var(--bg-panel-alt); }
tbody tr:last-child td { border-bottom: none; }

/* Encabezados de tabla que se pueden ordenar A-Z/Z-A con un clic (ver
   folios.html) — la flechita solo aparece en la columna activa; el resto
   solo se ve clicable al pasar el mouse, para no saturar la tabla. */
.th-ordenable {
  cursor: pointer;
  user-select: none;
}
.th-ordenable:hover { color: var(--texto); }
.th-ordenable::after {
  content: "";
  display: inline-block;
  width: 0.6em;
  margin-left: 0.3em;
  opacity: 0.35;
}
.th-ordenable--asc::after { content: "▲"; opacity: 1; color: var(--color-primario-oscuro); }
.th-ordenable--desc::after { content: "▼"; opacity: 1; color: var(--color-primario-oscuro); }

/* --- Badges de estatus --- */

.badge-pill {
  display: inline-block;
  padding: 0.22rem 0.7rem;
  border-radius: var(--radio-pill);
  font-size: 0.76rem;
  font-weight: 700;
  white-space: nowrap;
}

.badge-activo.badge-pill { background: var(--exito-bg); color: var(--exito-texto); }
.badge-cancelado.badge-pill { background: var(--peligro-bg); color: var(--peligro-texto); }
.badge-pendiente.badge-pill { background: var(--alerta-bg); color: var(--alerta-texto); }

/* --- Calendario de disponibilidad (26-ago-2026) --- */
/* Compartido entre folio-nuevo.html/folio-detalle.html (para elegir fecha)
   y el botón "Ver disponibilidad" de folios.html (solo consulta). Colores
   reutilizan los mismos tokens que los badges de arriba, para que un día
   "lleno" se vea con el mismo rojo que un folio cancelado, etc.

   27-ago-2026: Sal reportó que se veía "muy plano" — se le dio aspecto de
   tarjeta propia (fondo/borde/sombra) en vez de flotar directo sobre el
   panel blanco, y los días llevan un borde de acento + más peso de letra
   para que se distingan mejor unos de otros. */

.calendario-disponibilidad {
  max-width: 340px;
  background: var(--bg-panel);
  border: 1px solid var(--borde);
  border-radius: var(--radio-chico);
  padding: 1rem;
  box-shadow: 0 14px 32px -18px rgba(20, 16, 8, 0.35), 0 2px 6px rgba(20, 16, 8, 0.06);
}

.calendario-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.4rem;
  margin-bottom: 0.7rem;
  font-size: 0.95rem;
}

/* Selects de mes/año (reemplazan el texto fijo "agosto 2026") — permiten
   saltar directo a cualquier mes/año sin darle a "‹ ›" repetidas veces,
   que era la queja principal de Sal sobre el calendario. */
.calendario-selector-mes-anio {
  display: flex;
  gap: 0.35rem;
  flex: 1 1 auto;
  min-width: 0;
  justify-content: center;
}

.calendario-selector-mes-anio select {
  border: 1px solid var(--borde-fuerte);
  border-radius: var(--radio-chico);
  background: var(--bg-input);
  color: var(--texto-fuerte);
  font-weight: 800;
  font-size: 0.86rem;
  padding: 0.3rem 0.4rem;
  cursor: pointer;
  min-width: 0;
}

.calendario-selector-mes-anio select:focus {
  outline: none;
  border-color: var(--color-primario);
  box-shadow: 0 0 0 3px rgba(201, 162, 39, 0.22);
}

.calendario-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 0.3rem;
  text-align: center;
}

.calendario-dias-semana {
  margin-bottom: 0.3rem;
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--texto-suave);
}

.dia-calendario {
  aspect-ratio: 1;
  border: 1px solid transparent;
  border-radius: var(--radio-chico);
  font-size: 0.82rem;
  font-weight: 700;
  cursor: pointer;
  background: var(--color-secundario-bg);
  color: var(--texto-fuerte);
  transition: filter var(--transicion), transform var(--transicion);
}

.dia-calendario:hover:not(.dia-pasado) { filter: brightness(1.08); transform: translateY(-1px); }
.dia-calendario.dia-pasado { cursor: default; opacity: 0.35; }

.dia-calendario.dia-libre { background: var(--exito-bg); color: var(--exito-texto); border-color: rgba(30, 122, 52, 0.28); }
.dia-calendario.dia-parcial { background: var(--alerta-bg); color: var(--alerta-texto); border-color: rgba(138, 75, 15, 0.3); }
.dia-calendario.dia-lleno { background: var(--peligro-bg); color: var(--peligro-texto); border-color: rgba(163, 35, 27, 0.3); }

/* Día de hoy: referencia visual aunque no esté seleccionado, para orientarse
   más rápido dentro del mes. */
.dia-calendario.dia-hoy:not(.dia-seleccionado) {
  box-shadow: inset 0 0 0 2px var(--color-primario-oscuro);
}

.dia-calendario.dia-seleccionado {
  outline: 2px solid var(--color-primario);
  outline-offset: 2px;
}

.calendario-leyenda {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 0.7rem;
  font-size: 0.76rem;
  color: var(--texto-suave);
}

.calendario-leyenda span { display: inline-flex; align-items: center; gap: 0.3rem; }

.punto-leyenda {
  display: inline-block;
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
}
.punto-leyenda.dia-libre { background: var(--exito-bg); }
.punto-leyenda.dia-parcial { background: var(--alerta-bg); }
.punto-leyenda.dia-lleno { background: var(--peligro-bg); }

/* Selector de fecha tipo "popover" (27-ago-2026): antes el calendario vivía
   siempre desplegado dentro del formulario, empujando todo lo de abajo y
   sintiéndose invasivo. Ahora el campo de texto se comporta como el trigger
   de un date-picker — el calendario aparece flotando justo debajo solo
   mientras se está eligiendo la fecha, y se cierra solo al elegir un día o
   al hacer clic fuera. Ver vincularSelectorFechaPopover() en app-shell.js. */
.campo-fecha-evento {
  position: relative;
}

.campo-selector-fecha {
  cursor: pointer;
  background: var(--bg-input);
  max-width: 22rem;
}

/* position:fixed y top/left se fijan por JS (ver vincularSelectorFechaPopover),
   porque el popover se reubica como hijo directo de <body> para que ningún
   contenedor con scroll/overflow (ej. .modal) pueda recortarlo. */
.calendario-popover {
  display: none;
  z-index: 1000;
  width: 320px;
  max-width: calc(100vw - 2.5rem);
}

.calendario-popover.abierto {
  display: block;
  animation: calendario-popover-aparece 120ms ease;
}

@keyframes calendario-popover-aparece {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}


/* --- Mensajes inline --- */

.texto-ayuda {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.8rem;
  color: var(--texto-tenue, #8a7f6a);
}

.mensaje-error {
  background: var(--peligro-bg);
  color: var(--peligro-texto);
  padding: 0.65rem 0.9rem;
  border-radius: var(--radio-chico);
  margin-top: 0.85rem;
  font-size: 0.88rem;
}

.mensaje-ok {
  background: var(--exito-bg);
  color: var(--exito-texto);
  padding: 0.65rem 0.9rem;
  border-radius: var(--radio-chico);
  margin-top: 0.85rem;
  font-size: 0.88rem;
}

.aviso-info {
  background: var(--info-bg);
  color: var(--info-texto);
  padding: 0.75rem 1rem;
  border-radius: var(--radio-chico);
  font-size: 0.9rem;
}

.aviso-folio-siguiente {
  background: var(--info-bg);
  color: var(--info-texto);
  padding: 0.6rem 0.9rem;
  border-radius: var(--radio-chico);
  font-size: 0.85rem;
  font-weight: 600;
  margin: 0 0 1.1rem;
}

/* --- Grid de datos del folio (detalle) --- */

/* Grid fijo (no auto-fit): con 8 bloques de estadística, auto-fit dejaba
   huecos vacíos en la última fila cuando no se llenaba una columna completa.
   Con columnas fijas (2 en celular, 4 en pantallas más anchas) siempre se
   llenan filas completas sin espacio muerto. */
.resumen-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.85rem;
  margin: 0.25rem 0 1.25rem;
}

@media (min-width: 640px) {
  .resumen-grid { grid-template-columns: repeat(4, 1fr); }
}

.stat-bloque {
  background: var(--bg-panel-alt);
  border-radius: var(--radio-chico);
  padding: 0.75rem 0.9rem;
}

.stat-bloque .stat-label {
  display: block;
  font-size: 0.7rem;
  color: var(--texto-suave);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.stat-bloque .stat-valor {
  display: block;
  font-size: 1.05rem;
  font-weight: 800;
  margin-top: 0.25rem;
  color: var(--texto-fuerte);
  overflow-wrap: anywhere;
}

/* Desglose de IVA (para el contador): el monto capturado ya incluye el 16%,
   así que este renglón chico solo muestra hacia atrás cuánto es IVA y
   cuánto es subtotal — no es un valor nuevo, es el mismo número explicado. */
.stat-bloque .stat-subvalor {
  display: block;
  font-size: 0.68rem;
  font-weight: 600;
  margin-top: 0.3rem;
  color: var(--texto-suave);
  overflow-wrap: anywhere;
}
/* Bug real encontrado en Paso 32: el "display: block" de arriba (2 clases)
   pesa más que la regla nativa "[hidden] { display: none }" (1 atributo), así
   que un .stat-subvalor con el atributo hidden="" (como #folio-saldo-nota
   cuando el saldo NO es a favor del cliente) se seguía viendo en pantalla
   aunque en el DOM sí estuviera marcado como oculto. Esta regla, con más
   especificidad, restaura el ocultamiento real. */
.stat-bloque .stat-subvalor[hidden] {
  display: none;
}

/* Los "números de impacto" (más grandes y con más peso, como en Secure
   Cloud) solo aplican desde tablet en adelante: en celular, con solo 2
   columnas, un tamaño tan grande hace que montos como "$25,000.00" se
   corten contra el borde del bloque en vez de verse bien. */
@media (min-width: 640px) {
  .stat-bloque .stat-valor { font-size: 1.4rem; }
}

/* Bloques con tinte de color con significado (igual criterio que los
   stat-cards de Secure Cloud): el monto pactado lleva el acento dorado de
   marca, "total pagado" siempre es un logro (verde), y "saldo pendiente"
   cambia de verde a ámbar según si ya se liquidó o sigue pendiente (esto
   último se decide con JS, ver folio-detalle.html). */
.stat-bloque--dorado { background: rgba(212, 175, 55, 0.14); }
.stat-bloque--dorado .stat-valor { color: var(--color-primario-oscuro); }

.stat-bloque--exito { background: var(--exito-bg); }
.stat-bloque--exito .stat-valor { color: var(--exito-texto); }

.stat-bloque--alerta { background: var(--alerta-bg); }
.stat-bloque--alerta .stat-valor { color: var(--alerta-texto); }

.stat-bloque--peligro { background: var(--peligro-bg); }
.stat-bloque--peligro .stat-valor { color: var(--peligro-texto); }

/* Saldo A FAVOR del cliente (20-ago-2026): distinto de "pendiente" (ámbar,
   el cliente debe) y de "liquidado" (verde, ya no debe nada) — aquí es la
   casa la que le debe algo al cliente (p. ej. se bajó el monto total
   después de ya tener pagos). Un tono azul/informativo, no una alarma. */
.stat-bloque--info { background: var(--info-bg); }
.stat-bloque--info .stat-valor { color: var(--info-texto); }

/* --- Pagos cancelados (20-ago-2026): ya NO se borran, se marcan como
   cancelados con motivo obligatorio y se quedan visibles (tachados) en la
   tabla, para no perder el rastro contable. Ver folio-detalle.html. --- */
.fila-pago-cancelado td {
  color: var(--texto-suave);
  text-decoration: line-through;
  text-decoration-color: var(--peligro-texto);
}
.fila-pago-cancelado .nota-cancelado {
  display: block;
  text-decoration: none;
  font-size: 0.72rem;
  color: var(--peligro-texto);
  font-weight: 600;
  margin-top: 0.2rem;
}

/* --- Modales --- */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(2, 2, 2, 0.75);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  z-index: 100;
}

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

.modal {
  background: var(--bg-panel);
  border-radius: var(--radio-panel);
  box-shadow: var(--sombra-panel);
  width: 100%;
  max-width: 560px;
  max-height: 90vh;
  overflow-y: auto;
  animation: modal-aparece 150ms ease;
}

.modal.modal-chico { max-width: 420px; }

@keyframes modal-aparece {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.1rem 1.4rem;
  border-bottom: 1px solid var(--borde);
}

.modal-header h3 {
  margin: 0;
  font-size: 1.05rem;
  color: var(--texto-fuerte);
}

.modal-cerrar {
  background: none;
  border: none;
  font-size: 1.4rem;
  line-height: 1;
  color: var(--texto-suave);
  cursor: pointer;
  padding: 0.2rem 0.4rem;
  border-radius: var(--radio-chico);
}

.modal-cerrar:hover { background: var(--bg-panel-alt); color: var(--texto-fuerte); }

.modal-cuerpo { padding: 1.4rem; color: var(--texto); }

.acciones-modal {
  justify-content: flex-end;
}

@media (max-width: 480px) {
  main { margin: 0 auto; padding: 1rem 0.75rem 1.5rem; }
  .card, .panel-seccion { padding: 1.1rem; }
  header { padding: 0.6rem 0.85rem; }
  .acciones-modal { flex-direction: column-reverse; }
  .acciones-modal .btn { width: 100%; }

  /* En celular los bloques de estadística quedan en 2 columnas muy angostas
     (ver .resumen-grid); un valor en negritas de 10-11 caracteres como
     "$25,000.00" apenas cabe, así que aquí se reduce el tamaño y el relleno
     interno para que no se corte ni se parta a la mitad de un número. */
  .resumen-grid { gap: 0.6rem; }
  .stat-bloque { padding: 0.6rem 0.65rem; }
  .stat-bloque .stat-valor { font-size: 0.92rem; }
}

/* --- Página de login: se queda con el look oscuro original ---
   El resto de la app pasó a contenido claro (chrome oscuro + panel único
   claro, estilo Secure Cloud), pero el login no tiene sidebar/nav — es solo
   la tarjeta de marca (.card), y esa se ve mejor oscura y elegante, como
   antes. El fondo del <body> ya es oscuro en todas las páginas, así que
   aquí solo hace falta redefinir los tokens de "contenido" para que la
   tarjeta en sí (no el fondo) se quede oscura. */
body.pagina-login {
  --bg-panel: #201a10;
  --bg-panel-alt: #2c2416;
  --bg-input: #150f09;
  --borde: #4a3f24;
  --borde-fuerte: #6b5a2e;

  --texto: #e9e4d9;
  --texto-fuerte: #ffffff;
  --texto-suave: #a89e88;

  --color-secundario-bg: #3a3527;
  --color-secundario-bg-hover: #4a4433;

  /* El dorado oscuro (--enlace default) casi no se lee sobre esta tarjeta
     oscura — aquí usamos el dorado brillante, el mismo del botón "Entrar". */
  --enlace: var(--color-primario);

  /* Mismo fondo de marca que el resto de la app (ver regla `body` arriba),
     más un resplandore extra centrado justo detrás de donde cae la tarjeta
     — así ya no se siente "un cuadro solo en un fondo enorme": hay un halo
     de luz propio detrás de la tarjeta, como si la iluminara, en vez de
     flotar en un vacío parejo. */
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.035 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E") repeat,
    radial-gradient(720px 720px at 50% 40%, rgba(212, 175, 55, 0.22) 0%, rgba(212, 175, 55, 0) 62%),
    radial-gradient(1300px 700px at 10% -12%, rgba(212, 175, 55, 0.30) 0%, rgba(212, 175, 55, 0) 55%),
    radial-gradient(950px 560px at 92% -8%, rgba(212, 175, 55, 0.17) 0%, rgba(212, 175, 55, 0) 50%),
    radial-gradient(1500px 800px at 50% 118%, rgba(255, 255, 255, 0.045) 0%, rgba(255, 255, 255, 0) 60%),
    linear-gradient(180deg, #17171c 0%, #0b0b0d 45%, #030304 100%);
  background-attachment: fixed;
}
