/* =============================================================
   app.css — CSS UNIFICADO del Sistema Tickets
   Incluye: tokens, reset, layout, login, alertas,
            notificaciones, dashboard, sidebar,
            componentes genéricos, dark mode
   ============================================================= */

/* ─────────────────────────────────────────────
   1. TOKENS / VARIABLES
   ───────────────────────────────────────────── */
:root {
  /* Paleta de marca */
  --c-primary:       #02377d;
  --c-primary-dark:  #011f4b;
  --c-primary-light: #1a56c4;
  --c-accent:        #ec3237;
  --c-secondary:     #98cb59;

  /* Modo claro (default) */
  --c-bg:            #f0f4f8;
  --c-surface:       #ffffff;
  --c-surface-2:     #f8fafc;
  --c-text:          #1e293b;
  --c-text-muted:    #64748b;
  --c-border:        #e2e8f0;
  --c-border-focus:  #3b82f6;

  /* Alertas / estados */
  --c-error-bg:      #fef2f2;
  --c-error-border:  #fca5a5;
  --c-error-text:    #991b1b;
  --c-warning-bg:    #fffbeb;
  --c-warning-border:#fcd34d;
  --c-warning-text:  #92400e;
  --c-info-bg:       #eff6ff;
  --c-info-border:   #93c5fd;
  --c-info-text:     #1e40af;
  --c-success-bg:    #f0fdf4;
  --c-success-border:#86efac;
  --c-success-text:  #166534;

  /* Sombras */
  --shadow-sm:   0 1px 3px rgba(0,0,0,.08);
  --shadow-md:   0 4px 16px rgba(0,0,0,.12);
  --shadow-lg:   0 8px 32px rgba(0,0,0,.16);

  /* Bordes */
  --radius-sm:   6px;
  --radius-md:   10px;
  --radius-lg:   16px;
  --radius-xl:   24px;

  /* Tipografía */
  --font-body:    'Inter', system-ui, sans-serif;
  --font-display: 'Space Grotesk', sans-serif;
  --fz-xs:       11px;
  --fz-sm:       13px;
  --fz-base:     15px;
  --fz-lg:       18px;
  --fz-xl:       24px;
  --fz-2xl:      32px;

  /* Transición global */
  --transition:  0.2s ease;

  /* Notificaciones */
  --notif-width: 340px;

  /* Dashboard / Sidebar */
  --sidebar-w:         240px;
  --sidebar-w-collapsed: 64px;
  --sidebar-bg:        var(--c-primary);
  --sidebar-bg-dark-strip: var(--c-primary-dark);
  --topbar-h:          56px;
}

/* ─── Dark mode ─── */
body.dark-mode {
  --c-bg:            #0b1120;
  --c-surface:       #131c2e;
  --c-surface-2:     #1a2540;
  --c-text:          #e2e8f0;
  --c-text-muted:    #94a3b8;
  --c-border:        #1e3352;
  --c-border-focus:  #60a5fa;

  --c-error-bg:      #2d0a0a;
  --c-error-border:  #7f1d1d;
  --c-error-text:    #fca5a5;
  --c-warning-bg:    #2d1a00;
  --c-warning-border:#92400e;
  --c-warning-text:  #fcd34d;
  --c-info-bg:       #0d1f3c;
  --c-info-border:   #1e40af;
  --c-info-text:     #93c5fd;
  --c-success-bg:    #0a1f0a;
  --c-success-border:#166534;
  --c-success-text:  #86efac;

  --sidebar-bg:        #0d1527;
  --sidebar-bg-dark-strip: #080f1c;
}

/* ─────────────────────────────────────────────
   2. RESET & BASE
   BUG CORREGIDO: box-sizing no estaba definido
   en dashboard.css — los paneles con padding se
   desbordaban en viewports angostos.
   ───────────────────────────────────────────── */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
}

body {
  font-family: var(--font-body);
  font-size: var(--fz-base);
  background: var(--c-bg);
  color: var(--c-text);
  min-height: 100vh;
  /* BUG CORREGIDO: body tenía display:flex + align/justify center
     en app.css original, lo que rompía el layout del dashboard
     (sidebar + main). Ahora solo el login usa esa distribución,
     aplicada via .body--login. */
  transition: background var(--transition), color var(--transition);
  position: relative;
}

/* Variante de body para la página de login */
body.body--login {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ─────────────────────────────────────────────
   3. BOTÓN DE TEMA (esquina superior derecha)
   ───────────────────────────────────────────── */
.theme-toggle {
  position: fixed;
  top: 14px;
  right: 14px;
  z-index: 1100;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px 13px;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: 50px;
  color: var(--c-text);
  font-family: var(--font-body);
  font-size: var(--fz-sm);
  font-weight: 500;
  cursor: pointer;
  transition: all var(--transition);
  box-shadow: var(--shadow-sm);
}

.theme-toggle:hover {
  border-color: var(--c-primary-light);
  color: var(--c-primary-light);
}

.theme-toggle .t-icon {
  width: 16px;
  height: 16px;
  display: block;
}

/* ─────────────────────────────────────────────
   4. LOGIN LAYOUT
   ───────────────────────────────────────────── */
.login-container {
  display: flex;
  width: min(900px, 95vw);
  min-height: 520px;
  background: var(--c-surface);
  border-radius: var(--radius-xl);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
  border: 1px solid var(--c-border);
  transition: background var(--transition), border-color var(--transition);
}

.login-left {
  flex: 1;
  background: linear-gradient(150deg, var(--c-primary) 0%, var(--c-primary-dark) 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 48px 40px;
  position: relative;
  overflow: hidden;
}

.login-left::before,
.login-left::after {
  content: '';
  position: absolute;
  border-radius: 50%;
  opacity: 0.08;
  background: #ffffff;
}
.login-left::before {
  width: 300px; height: 300px;
  top: -80px; right: -80px;
}
.login-left::after {
  width: 200px; height: 200px;
  bottom: -60px; left: -60px;
}

.login-left-inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  color: white;
}

.login-left-inner img {
  max-width: 80px;
  margin-bottom: 20px;
  filter: brightness(0) invert(1);
}

.login-left-inner h1 {
  font-family: var(--font-display);
  font-size: var(--fz-2xl);
  font-weight: 700;
  line-height: 1.2;
  margin-bottom: 12px;
  letter-spacing: -0.5px;
}

.login-left-inner p {
  font-size: var(--fz-sm);
  opacity: 0.75;
  line-height: 1.6;
}

/* [RETIRADO 2026-07-28] Aquí vivía .login-dots: tres puntitos bajo el
   subtítulo del panel izquierdo, el primero blanco y los otros dos al
   40 %. Es la firma visual de un indicador de carrusel, así que
   anunciaban diapositivas navegables que nunca existieron. Se quitaron
   los estilos y el marcado de login.php. Sin usos en ningún otro
   archivo — verificado con grep antes de borrarlos. */

.login-right {
  flex: 1.1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 48px;
}

.login-right-inner {
  width: 100%;
  max-width: 360px;
}

.login-right-inner h2 {
  font-family: var(--font-display);
  font-size: var(--fz-xl);
  font-weight: 700;
  color: var(--c-text);
  margin-bottom: 4px;
  letter-spacing: -0.3px;
}

.login-subtitle {
  font-size: var(--fz-sm);
  color: var(--c-text-muted);
  margin-bottom: 24px;
}

/* ─────────────────────────────────────────────
   5. ALERTAS INLINE
   ───────────────────────────────────────────── */
.alert {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 14px;
  border-radius: var(--radius-md);
  border: 1px solid;
  margin-bottom: 20px;
  font-size: var(--fz-sm);
  line-height: 1.5;
  animation: alertIn 0.25s ease;
}

@keyframes alertIn {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}

.alert--error   { background: var(--c-error-bg);   border-color: var(--c-error-border);   color: var(--c-error-text);   }
.alert--warning { background: var(--c-warning-bg);  border-color: var(--c-warning-border);  color: var(--c-warning-text);  }
.alert--info    { background: var(--c-info-bg);     border-color: var(--c-info-border);     color: var(--c-info-text);    }
.alert--success { background: var(--c-success-bg);  border-color: var(--c-success-border);  color: var(--c-success-text); }

.alert__icon::before { font-size: 15px; }
.alert--error   .alert__icon::before { content: '⚠'; }
.alert--warning .alert__icon::before { content: '⏱'; }
.alert--info    .alert__icon::before { content: 'ℹ'; }
.alert--success .alert__icon::before { content: '✓'; }

.alert-js { display: none; }
.alert-js.is-visible { display: flex; }

/* ─────────────────────────────────────────────
   6. CAMPOS DE FORMULARIO
   ───────────────────────────────────────────── */
.field { margin-bottom: 18px; }

.field label {
  display: block;
  font-size: var(--fz-sm);
  font-weight: 500;
  color: var(--c-text-muted);
  margin-bottom: 6px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.field__wrap {
  position: relative;
  display: flex;
  align-items: center;
}

.field__icon {
  position: absolute;
  left: 12px;
  color: var(--c-text-muted);
  display: flex;
  align-items: center;
  pointer-events: none;
}

.field__eye {
  position: absolute;
  right: 10px;
  background: none;
  border: none;
  padding: 6px;
  cursor: pointer;
  color: var(--c-text-muted);
  width: auto;
  display: flex;
  align-items: center;
  border-radius: var(--radius-sm);
  transition: color var(--transition);
}
.field__eye:hover { color: var(--c-primary-light); background: transparent; }

.field__wrap input {
  width: 100%;
  padding: 11px 12px 11px 38px;
  border: 1.5px solid var(--c-border);
  border-radius: var(--radius-md);
  background: var(--c-surface-2);
  color: var(--c-text);
  font-family: var(--font-body);
  font-size: var(--fz-base);
  transition: border-color var(--transition), background var(--transition), box-shadow var(--transition);
  outline: none;
}

.field__wrap input[type="password"],
.field__wrap input[name="contrasena"] { padding-right: 42px; }

.field__wrap input:focus {
  border-color: var(--c-border-focus);
  background: var(--c-surface);
  box-shadow: 0 0 0 3px rgba(59,130,246,.15);
}

.field__wrap input::placeholder { color: var(--c-text-muted); opacity: 0.7; }

.field.has-error .field__wrap input { border-color: var(--c-error-border); }
.field.has-error .field__wrap input:focus { box-shadow: 0 0 0 3px rgba(252,165,165,.2); }

.field__error {
  display: block;
  font-size: var(--fz-xs);
  color: var(--c-error-text);
  margin-top: 5px;
  min-height: 16px;
}

/* ─────────────────────────────────────────────
   7. BOTONES
   ───────────────────────────────────────────── */
.btn-primary {
  width: 100%;
  padding: 13px;
  border: none;
  border-radius: var(--radius-md);
  background: linear-gradient(135deg, var(--c-primary-light), var(--c-primary));
  color: white;
  font-family: var(--font-body);
  font-size: var(--fz-base);
  font-weight: 600;
  cursor: pointer;
  transition: all var(--transition);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 6px;
  letter-spacing: 0.01em;
  box-shadow: 0 2px 8px rgba(2,55,125,.3);
}
.btn-primary:hover:not(:disabled) { transform: translateY(-1px); box-shadow: 0 4px 14px rgba(2,55,125,.4); }
.btn-primary:active:not(:disabled) { transform: translateY(0); }
.btn-primary:disabled { opacity: 0.65; cursor: not-allowed; }

/* Botón secundario (outline) */
.btn-secondary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 16px;
  border: 1.5px solid var(--c-border);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--c-text);
  font-family: var(--font-body);
  font-size: var(--fz-sm);
  font-weight: 500;
  cursor: pointer;
  transition: all var(--transition);
}
.btn-secondary:hover { border-color: var(--c-primary-light); color: var(--c-primary-light); }

/* Spinner de carga */
/* .btn-loader es el CONTENEDOR (flex) que agrupa el círculo + texto.
   Solo .spinner gira — el texto "Guardando..." permanece estático.
   Antes: .btn-loader era el círculo mismo, lo que hacía girar TODO
   su contenido (ícono + texto completo del botón). */
.btn-loader {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}
.spinner {
  width: 14px; height: 14px;
  border: 2px solid rgba(255,255,255,0.35);
  border-top-color: white;
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
  flex-shrink: 0;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ─────────────────────────────────────────────
   8. FOOTER DEL LOGIN
   ───────────────────────────────────────────── */
.login-footer {
  margin-top: 28px;
  font-size: var(--fz-xs);
  color: var(--c-text-muted);
  text-align: center;
}

/* ─────────────────────────────────────────────
   9. SISTEMA DE NOTIFICACIONES (TOAST)
   ───────────────────────────────────────────── */
.notif-container {
  position: fixed;
  top: 20px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 9999;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  pointer-events: none;
  width: min(var(--notif-width), 92vw);
}

.notif {
  width: 100%;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  padding: 14px 16px;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  pointer-events: all;
  animation: notifIn 0.35s cubic-bezier(0.34,1.56,0.64,1);
  transition: opacity 0.25s ease, transform 0.25s ease;
  position: relative;
  overflow: hidden;
}

.notif.is-leaving {
  opacity: 0;
  transform: translateY(-10px) scale(0.97);
}

@keyframes notifIn {
  from { opacity: 0; transform: translateY(-16px) scale(0.95); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.notif__progress {
  position: absolute;
  bottom: 0; left: 0;
  height: 3px;
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  animation: notifProgress linear forwards;
}
@keyframes notifProgress { from { width: 100%; } to { width: 0%; } }

.notif__icon {
  width: 32px; height: 32px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  font-size: 14px;
  font-weight: 700;
}

.notif__body { flex: 1; min-width: 0; }

.notif__title {
  font-weight: 600;
  font-size: var(--fz-sm);
  color: var(--c-text);
  margin-bottom: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.notif__msg {
  font-size: var(--fz-xs);
  color: var(--c-text-muted);
  line-height: 1.5;
}

.notif__img {
  width: 100%;
  border-radius: var(--radius-sm);
  margin-top: 8px;
  max-height: 120px;
  object-fit: cover;
  display: block;
}

.notif__close {
  background: none;
  border: none;
  padding: 2px 4px;
  cursor: pointer;
  color: var(--c-text-muted);
  font-size: 16px;
  line-height: 1;
  border-radius: var(--radius-sm);
  transition: color var(--transition);
  width: auto;
  flex-shrink: 0;
  align-self: flex-start;
}
.notif__close:hover { color: var(--c-text); background: transparent; }

.notif--success { border-left: 3px solid #22c55e; }
.notif--success .notif__icon { background: var(--c-success-bg); color: var(--c-success-text); }
.notif--success .notif__progress { background: #22c55e; }

.notif--error { border-left: 3px solid #ef4444; }
.notif--error .notif__icon { background: var(--c-error-bg); color: var(--c-error-text); }
.notif--error .notif__progress { background: #ef4444; }

.notif--warning { border-left: 3px solid #f59e0b; }
.notif--warning .notif__icon { background: var(--c-warning-bg); color: var(--c-warning-text); }
.notif--warning .notif__progress { background: #f59e0b; }

.notif--info { border-left: 3px solid #3b82f6; }
.notif--info .notif__icon { background: var(--c-info-bg); color: var(--c-info-text); }
.notif--info .notif__progress { background: #3b82f6; }

.notif__actions { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }

.notif__action-btn {
  padding: 5px 12px;
  border-radius: var(--radius-sm);
  font-size: var(--fz-xs);
  font-weight: 500;
  cursor: pointer;
  border: 1px solid var(--c-border);
  background: var(--c-surface-2);
  color: var(--c-text);
  transition: all var(--transition);
  width: auto;
}
.notif__action-btn:hover { border-color: var(--c-primary-light); color: var(--c-primary-light); }
.notif__action-btn--primary { background: var(--c-primary); color: white; border-color: var(--c-primary); }
.notif__action-btn--primary:hover { background: var(--c-primary-dark); border-color: var(--c-primary-dark); color: white; }

/* ─── Panel de notificaciones (bandeja lateral) ─── */
/* BUG NUEVO: el sistema original solo tenía toasts efímeros.
   Se agrega una bandeja persistente (bell) para comunicados
   globales/por usuario que no deben perderse. */
.notif-bell-wrapper {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.notif-bell-btn {
  background: none;
  border: none;
  cursor: pointer;
  color: rgba(255,255,255,0.85);
  width: 36px;
  height: 36px;
  padding: 0;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  transition: color var(--transition), background var(--transition);
  position: relative;
}
.notif-bell-btn:hover { color: white; background: rgba(255,255,255,0.12); }

/* Badge de conteo: antes ocupaba casi tanto espacio como el propio botón,
   lo que hacía que el usuario intentara darle clic al número en vez de a
   la campana. Ahora es más pequeño, no intercepta clics (pointer-events:
   none) y el botón (36x36) mantiene un área de toque cómoda independiente
   del tamaño del badge. */
.notif-bell-badge {
  position: absolute;
  top: 1px; right: 1px;
  min-width: 14px; height: 14px;
  padding: 0 3px;
  background: var(--c-accent);
  color: white;
  font-size: 9px;
  font-weight: 700;
  border-radius: 50px;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  border: 2px solid var(--c-surface);
  pointer-events: none;
}
.notif-bell-badge[hidden] { display: none; }

.notif-panel {
  position: fixed;
  top: 0; right: 0;
  width: 360px;
  max-width: 100vw;
  height: 100vh;
  background: var(--c-surface);
  border-left: 1px solid var(--c-border);
  box-shadow: var(--shadow-lg);
  z-index: 1200;
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.notif-panel.is-open { transform: translateX(0); }

.notif-panel__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--c-border);
  flex-shrink: 0;
}

.notif-panel__title {
  font-family: var(--font-display);
  font-size: var(--fz-base);
  font-weight: 600;
  color: var(--c-text);
  display: flex;
  align-items: center;
  gap: 8px;
}

.notif-panel__close {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--c-text-muted);
  font-size: 20px;
  line-height: 1;
  padding: 4px;
  border-radius: var(--radius-sm);
  transition: color var(--transition);
}
.notif-panel__close:hover { color: var(--c-text); }

.notif-panel__actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 20px;
  border-bottom: 1px solid var(--c-border);
  flex-shrink: 0;
}

.notif-panel__mark-all {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--c-primary-light);
  font-size: var(--fz-xs);
  font-weight: 500;
  padding: 0;
  transition: opacity var(--transition);
}
.notif-panel__mark-all:hover { opacity: 0.75; }

/* Variante de color para acciones destructivas (ej. "Limpiar bandeja").
   Antes se aplicaba con estilo inline directamente en el HTML del dashboard;
   ahora es una clase reutilizable disponible para cualquier módulo. */
.notif-panel__mark-all--danger {
  color: var(--c-error-text, #b91c1c);
}

.notif-panel__filter {
  display: flex;
  gap: 4px;
}

.notif-filter-btn {
  background: none;
  border: 1px solid var(--c-border);
  border-radius: 50px;
  padding: 3px 10px;
  font-size: var(--fz-xs);
  font-weight: 500;
  cursor: pointer;
  color: var(--c-text-muted);
  transition: all var(--transition);
}
.notif-filter-btn.active,
.notif-filter-btn:hover {
  background: var(--c-primary);
  border-color: var(--c-primary);
  color: white;
}

.notif-panel__list {
  flex: 1;
  overflow-y: auto;
  padding: 8px 0;
}

.notif-panel__empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  gap: 12px;
  color: var(--c-text-muted);
  font-size: var(--fz-sm);
  padding: 40px;
  text-align: center;
}

.notif-item {
  display: flex;
  gap: 12px;
  padding: 14px 20px;
  cursor: pointer;
  transition: background var(--transition);
  position: relative;
  border-bottom: 1px solid var(--c-border);
}
.notif-item:last-child { border-bottom: none; }
.notif-item:hover { background: var(--c-surface-2); }

.notif-item.is-unread {
  background: color-mix(in srgb, var(--c-primary) 4%, var(--c-surface));
}
.notif-item.is-unread::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--c-primary-light);
  border-radius: 0 2px 2px 0;
}

.notif-item__icon {
  width: 36px; height: 36px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  font-size: 15px;
}

.notif-item__content { flex: 1; min-width: 0; }

.notif-item__title {
  font-size: var(--fz-sm);
  font-weight: 600;
  color: var(--c-text);
  margin-bottom: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.notif-item__msg {
  font-size: var(--fz-xs);
  color: var(--c-text-muted);
  line-height: 1.5;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.notif-item__img {
  width: 100%;
  border-radius: var(--radius-sm);
  margin-top: 6px;
  max-height: 90px;
  object-fit: cover;
}

.notif-item__time {
  font-size: var(--fz-xs);
  color: var(--c-text-muted);
  white-space: nowrap;
  margin-top: 4px;
}

/* Botón de descarte individual dentro de un .notif-item.
   Antes se generaba con un objeto de estilos inline en JS (dashboard.php);
   ahora es una clase reutilizable, consistente en cualquier módulo. */
.notif-dismiss-btn {
  position: absolute;
  top: 6px; right: 6px;
  background: transparent;
  border: none;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  color: var(--c-text-muted);
  opacity: 0.5;
  transition: opacity 0.15s, background var(--transition);
  padding: 2px 6px;
  border-radius: var(--radius-sm);
}
.notif-dismiss-btn:hover {
  opacity: 1;
  background: var(--c-surface-2, rgba(0,0,0,0.06));
}

.notif-panel__overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.3);
  z-index: 1199;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
}
.notif-panel__overlay.is-open { opacity: 1; pointer-events: all; }

/* ─────────────────────────────────────────────
   10. DASHBOARD LAYOUT
   BUG CORREGIDO: el layout original usaba
   margin-left en .main-content para compensar
   el sidebar, pero no usaba variables CSS, por
   lo que el valor estaba hardcoded y se
   desincronizaba al cambiar el ancho del sidebar.
   Ahora se usa una variable CSS --sidebar-w.
   ───────────────────────────────────────────── */
.app-layout {
  display: flex;
  min-height: 100vh;
}

/* ─── Sidebar ─── */
.sidebar {
  width: var(--sidebar-w);
  background: var(--sidebar-bg);
  color: #fff;
  display: flex;
  flex-direction: column;
  position: fixed;
  top: 0; left: 0; bottom: 0;
  z-index: 1000;
  transition: width 0.28s cubic-bezier(0.4,0,0.2,1);
  overflow: hidden;
  /* BUG CORREGIDO: overflow-y:auto en el sidebar original
     causaba que la scrollbar apareciera sobre el contenido
     cuando estaba colapsado. Se reemplaza por overflow:hidden
     en el sidebar y overflow-y:auto solo en .sidebar__menu-wrap */
}

.sidebar.collapsed { width: var(--sidebar-w-collapsed); }

/* ─────────────────────────────────────────────
   MODO SIDEBAR OCULTO (overlay-only)
   Usado por vistas inmersivas (ej. organigrama.php)
   vía $sidebar_oculto en sidebar.php. Reutiliza el
   mismo patrón transform/overlay que ya existe para
   móvil, pero aplicado también en escritorio.
   ───────────────────────────────────────────── */
.sidebar--overlay {
  transform: translateX(-100%);
  transition: transform 0.28s cubic-bezier(0.4,0,0.2,1);
}
.sidebar--overlay.is-open { transform: translateX(0); }

.sidebar--overlay ~ .main-wrapper { margin-left: 0 !important; }
.sidebar--overlay ~ .main-wrapper .topbar { left: 0 !important; }
.sidebar--overlay ~ .main-wrapper .form-progress-track { left: 0 !important; }

/* El botón de menú (normalmente solo visible en móvil) se muestra
   también en escritorio cuando el sidebar está en modo oculto, para
   que el usuario pueda abrirlo como panel flotante si lo necesita. */
.sidebar--overlay ~ .main-wrapper .topbar__menu-btn { display: flex; }


/* Scrollable area inside sidebar (menu only) */
.sidebar__menu-wrap {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  /* Custom scrollbar */
  scrollbar-width: thin;
  scrollbar-color: rgba(255,255,255,0.15) transparent;
}
.sidebar__menu-wrap::-webkit-scrollbar { width: 4px; }
.sidebar__menu-wrap::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 2px; }

.sidebar__header {
  display: flex;
  align-items: center;
  padding: 0 16px;
  height: var(--topbar-h);
  background: var(--sidebar-bg-dark-strip);
  flex-shrink: 0;
  gap: 10px;
  /* BUG CORREGIDO: en el original el logo y el botón
     de toggle estaban en orden inverso (toggle a la
     izquierda, logo a la derecha). Se corrige. */
}

.sidebar__logo {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 0;
  overflow: hidden;
}

.sidebar__logo-icon {
  flex-shrink: 0;
  width: 32px; height: 32px;
  background: rgba(255,255,255,0.12);
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
}

.sidebar__logo-text {
  font-family: var(--font-display);
  font-size: var(--fz-sm);
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  transition: opacity 0.2s ease, max-width 0.28s ease;
  max-width: 160px;
  letter-spacing: -0.2px;
}
.sidebar.collapsed .sidebar__logo-text { opacity: 0; max-width: 0; }

.sidebar__toggle {
  background: none;
  border: none;
  color: rgba(255,255,255,0.7);
  cursor: pointer;
  padding: 6px;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  flex-shrink: 0;
  transition: color var(--transition), background var(--transition);
}
.sidebar__toggle:hover { color: white; background: rgba(255,255,255,0.12); }

/* User info block */
.sidebar__user {
  padding: 12px 16px;
  border-bottom: 1px solid rgba(255,255,255,0.1);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  overflow: hidden;
}

.sidebar__avatar {
  width: 32px; height: 32px;
  border-radius: 50%;
  background: rgba(255,255,255,0.15);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fz-sm);
  font-weight: 600;
  flex-shrink: 0;
  color: white;
}

.sidebar__user-info {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  transition: opacity 0.2s ease, max-width 0.28s ease;
  max-width: 160px;
}
.sidebar.collapsed .sidebar__user-info { opacity: 0; max-width: 0; }

.sidebar__username {
  font-size: var(--fz-sm);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: white;
}

.sidebar__roles {
  font-size: var(--fz-xs);
  color: rgba(255,255,255,0.55);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Navigation menu */
.sidebar__nav { padding: 8px 0; }

.nav-section-label {
  font-size: var(--fz-xs);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.35);
  padding: 12px 18px 4px;
  white-space: nowrap;
  overflow: hidden;
  transition: opacity 0.2s ease;
}
.sidebar.collapsed .nav-section-label { opacity: 0; }

.nav-item {
  list-style: none;
}

.nav-link,
.nav-toggle {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 10px 16px;
  background: none;
  border: none;
  color: rgba(255,255,255,0.75);
  text-decoration: none;
  font-family: var(--font-body);
  font-size: var(--fz-sm);
  font-weight: 500;
  cursor: pointer;
  border-radius: 0;
  transition: color var(--transition), background var(--transition);
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  position: relative;
}

.nav-link:hover,
.nav-toggle:hover {
  color: white;
  background: rgba(255,255,255,0.1);
}

.nav-link.is-active,
.nav-link.is-active:hover {
  color: white;
  background: rgba(255,255,255,0.15);
}
/* Active indicator stripe */
.nav-link.is-active::before {
  content: '';
  position: absolute;
  left: 0; top: 6px; bottom: 6px;
  width: 3px;
  background: var(--c-secondary);
  border-radius: 0 2px 2px 0;
}

.nav-icon {
  width: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  font-size: 15px;
}

.nav-label {
  flex: 1;
  transition: opacity 0.2s ease;
}
.sidebar.collapsed .nav-label { opacity: 0; }

/* BUG CORREGIDO: el chevron en el original usaba
   transform:rotate vía clase .rotated que se aplicaba
   con JS. Si el JS fallaba, el chevron no rotaba.
   Ahora se usa CSS con el atributo aria-expanded. */
.nav-chevron {
  margin-left: auto;
  flex-shrink: 0;
  transition: transform 0.25s ease, opacity 0.2s ease;
  font-size: 11px;
  color: rgba(255,255,255,0.4);
}
.nav-toggle[aria-expanded="true"] .nav-chevron { transform: rotate(180deg); }
.sidebar.collapsed .nav-chevron { opacity: 0; }

/* Tooltip when collapsed */
.nav-link[data-tooltip],
.nav-toggle[data-tooltip] {
  position: relative;
}
.nav-link[data-tooltip]::after,
.nav-toggle[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  left: calc(var(--sidebar-w-collapsed) + 4px);
  top: 50%;
  transform: translateY(-50%);
  background: var(--c-primary-dark);
  color: white;
  font-size: var(--fz-xs);
  font-weight: 500;
  padding: 5px 10px;
  border-radius: var(--radius-sm);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease;
  z-index: 1500;
}
.sidebar.collapsed .nav-link[data-tooltip]:hover::after,
.sidebar.collapsed .nav-toggle[data-tooltip]:hover::after {
  opacity: 1;
}

/* Submenú */
.nav-submenu {
  list-style: none;
  overflow: hidden;
  max-height: 0;
  transition: max-height 0.3s ease;
  background: rgba(0,0,0,0.15);
}
.nav-submenu.is-open { max-height: 400px; }
.sidebar.collapsed .nav-submenu { display: none; }

.nav-submenu .nav-link {
  padding-left: 46px;
  font-size: var(--fz-xs);
  color: rgba(255,255,255,0.6);
}
.nav-submenu .nav-link:hover { color: white; }
.nav-submenu .nav-link.is-active { color: white; }

/* Divider in sidebar */
.nav-divider {
  height: 1px;
  background: rgba(255,255,255,0.08);
  margin: 6px 0;
}

/* Logout button at bottom */
.sidebar__footer {
  border-top: 1px solid rgba(255,255,255,0.08);
  flex-shrink: 0;
}

.logout-btn {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 13px 16px;
  background: none;
  border: none;
  color: rgba(255,255,255,0.6);
  font-family: var(--font-body);
  font-size: var(--fz-sm);
  font-weight: 500;
  cursor: pointer;
  transition: color var(--transition), background var(--transition);
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
}
.logout-btn:hover {
  color: white;
  background: rgba(236, 50, 55, 0.2);
}
.logout-btn .nav-label { transition: opacity 0.2s ease; }
.sidebar.collapsed .logout-btn .nav-label { opacity: 0; }

/* ─────────────────────────────────────────────
   11. TOPBAR
   BUG NUEVO: el original no tenía topbar,
   lo que dejaba el header pegado al sidebar sin
   ningún espacio visual para breadcrumbs, búsqueda
   o controles rápidos.
   ───────────────────────────────────────────── */
.topbar {
  position: fixed;
  top: 0;
  left: var(--sidebar-w);
  right: 0;
  height: var(--topbar-h);
  background: var(--c-surface);
  border-bottom: 1px solid var(--c-border);
  display: flex;
  align-items: center;
  padding: 0 20px;
  gap: 12px;
  z-index: 900;
  transition: left 0.28s cubic-bezier(0.4,0,0.2,1), background var(--transition), border-color var(--transition);
}
.sidebar.collapsed ~ .main-wrapper .topbar { left: var(--sidebar-w-collapsed); }

/* Mobile menu button (only visible on small screens) */
.topbar__menu-btn {
  display: none;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--c-text-muted);
  padding: 6px;
  border-radius: var(--radius-sm);
  transition: color var(--transition);
}
.topbar__menu-btn:hover { color: var(--c-text); }

.topbar__breadcrumb {
  flex: 1;
  font-size: var(--fz-sm);
  color: var(--c-text-muted);
  display: flex;
  align-items: center;
  gap: 6px;
  overflow: hidden;
}

.topbar__breadcrumb-page {
  color: var(--c-text);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.topbar__right {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ─────────────────────────────────────────────
   12. MAIN CONTENT
   ───────────────────────────────────────────── */
.main-wrapper {
  flex: 1;
  /* BUG CORREGIDO: margin-left fijo → uso de variable */
  margin-left: var(--sidebar-w);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  transition: margin-left 0.28s cubic-bezier(0.4,0,0.2,1);
}
.sidebar.collapsed ~ .main-wrapper { margin-left: var(--sidebar-w-collapsed); }

.main-content {
  flex: 1;
  padding: calc(var(--topbar-h) + 24px) 24px 24px;
}

/* ─────────────────────────────────────────────
   12b. APP FOOTER
   Pie de página compartido, impreso por
   plantillas/layout/footer.php al final de
   .main-wrapper. Se queda pegado abajo porque
   .main-content ya tiene flex:1 dentro de un
   .main-wrapper en columna.
   ───────────────────────────────────────────── */
.app-footer {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 24px;
  border-top: 1px solid var(--c-border);
  background: var(--c-surface);
  font-size: var(--fz-xs);
  color: var(--c-text-muted);
}

.app-footer__left {
  display: flex;
  align-items: center;
  gap: 8px;
}

.app-footer__version {
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--c-bg-subtle, rgba(0,0,0,0.05));
  font-weight: 600;
  letter-spacing: 0.02em;
}

.app-footer__right {
  display: flex;
  align-items: center;
  gap: 16px;
}

.app-footer__link {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--c-text-muted);
  text-decoration: none;
  transition: color var(--transition);
}
.app-footer__link:hover { color: var(--c-primary-light); }

/* Contacto del desarrollador: deliberadamente discreto — solo un ícono
   pequeño con baja opacidad; el correo va en el atributo title (tooltip). */
.app-footer__dev {
  display: flex;
  align-items: center;
  color: var(--c-text-muted);
  opacity: 0.35;
  text-decoration: none;
  transition: opacity var(--transition);
}
.app-footer__dev:hover { opacity: 0.9; }

/* ─────────────────────────────────────────────
   12c. MODO FORMULARIO
   Impreso por plantillas/layout/form_topbar.php.
   Identidad sutil: píldora "Formulario" + barra de
   progreso de 3px bajo el topbar. El stepper lateral
   (.form-stepper, .form-section, .form-layout) YA
   existe en assets/css/form.css — este componente lo
   reutiliza tal cual, no lo duplica aquí.
   ───────────────────────────────────────────── */

/* Píldora junto al botón Guardar, junto a las acciones del topbar */
.form-topbar-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  font-weight: 600;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--c-info-bg, #e6f1fb);
  color: var(--c-info-text, #0c447c);
  white-space: nowrap;
}

/* Barra de progreso: fija, pegada justo debajo del topbar (igual
   patrón fixed que .topbar, para no quedar oculta tras el contenido).
   Respeta el ancho del sidebar colapsado igual que .topbar. */
.form-progress-track {
  position: fixed;
  top: var(--topbar-h);
  left: var(--sidebar-w);
  right: 0;
  height: 3px;
  background: var(--c-border);
  overflow: hidden;
  z-index: 899;
  transition: left 0.28s cubic-bezier(0.4,0,0.2,1);
}
.sidebar.collapsed ~ .main-wrapper .form-progress-track { left: var(--sidebar-w-collapsed); }
.form-progress-track__bar {
  height: 100%;
  width: 0%;
  background: var(--c-accent);
  transition: width 0.25s ease;
}

/* .main-content en modo formulario necesita 3px extra de padding-top
   para no quedar debajo de la barra de progreso fija. .form-layout ya
   trae su propio max-width/centrado (form.css), así que aquí solo se
   ajusta el padding superior del contenedor estándar. */
.main-content--form { padding-top: calc(var(--topbar-h) + 3px + 24px); }


/* ─────────────────────────────────────────────
   13. TARJETAS (CARDS)
   ───────────────────────────────────────────── */
.card {
  background: var(--c-surface);
  border-radius: var(--radius-lg);
  border: 1px solid var(--c-border);
  box-shadow: var(--shadow-sm);
  transition: background var(--transition), border-color var(--transition);
}

.card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--c-border);
}

.card__title {
  font-family: var(--font-display);
  font-size: var(--fz-base);
  font-weight: 600;
  color: var(--c-text);
  display: flex;
  align-items: center;
  gap: 8px;
}

.card__body { padding: 20px; }

/* ─── Stat card (KPI) ─── */
.stat-card {
  background: var(--c-surface);
  border-radius: var(--radius-lg);
  border: 1px solid var(--c-border);
  padding: 20px;
  display: flex;
  align-items: flex-start;
  gap: 14px;
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--transition), transform var(--transition), background var(--transition);
  cursor: default;
}
.stat-card:hover { box-shadow: var(--shadow-md); transform: translateY(-1px); }

.stat-card__icon {
  width: 44px; height: 44px;
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  flex-shrink: 0;
}

.stat-card__body { flex: 1; min-width: 0; }

.stat-card__label {
  font-size: var(--fz-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--c-text-muted);
  margin-bottom: 4px;
}

.stat-card__value {
  font-family: var(--font-display);
  font-size: var(--fz-xl);
  font-weight: 700;
  color: var(--c-text);
  line-height: 1;
}

.stat-card__delta {
  font-size: var(--fz-xs);
  font-weight: 500;
  margin-top: 4px;
  display: flex;
  align-items: center;
  gap: 3px;
}
.stat-card__delta--up   { color: var(--c-success-text); }
.stat-card__delta--down { color: var(--c-error-text); }
.stat-card__delta--neutral { color: var(--c-text-muted); }

/* Color variants for stat icon */
.stat-icon--blue    { background: var(--c-info-bg);    color: var(--c-info-text); }
.stat-icon--green   { background: var(--c-success-bg); color: var(--c-success-text); }
.stat-icon--red     { background: var(--c-error-bg);   color: var(--c-error-text); }
.stat-icon--yellow  { background: var(--c-warning-bg); color: var(--c-warning-text); }
.stat-icon--primary { background: color-mix(in srgb, var(--c-primary) 10%, transparent); color: var(--c-primary-light); }

/* ─────────────────────────────────────────────
   14. GRID PARA DASHBOARD
   ───────────────────────────────────────────── */
.dash-grid {
  display: grid;
  gap: 20px;
}
.dash-grid--2 { grid-template-columns: repeat(2, 1fr); }
.dash-grid--3 { grid-template-columns: repeat(3, 1fr); }
.dash-grid--4 { grid-template-columns: repeat(4, 1fr); }
.dash-grid--auto { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }

/* ─────────────────────────────────────────────
   15. TABLAS GENÉRICAS
   ───────────────────────────────────────────── */
.table-wrap {
  overflow-x: auto;
  border-radius: var(--radius-md);
  border: 1px solid var(--c-border);
}

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fz-sm);
}

.table th {
  background: var(--c-surface-2);
  color: var(--c-text-muted);
  font-size: var(--fz-xs);
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 10px 14px;
  text-align: left;
  border-bottom: 1px solid var(--c-border);
  white-space: nowrap;
}

.table td {
  padding: 11px 14px;
  border-bottom: 1px solid var(--c-border);
  color: var(--c-text);
  vertical-align: middle;
}
.table tr:last-child td { border-bottom: none; }
.table tbody tr:hover { background: var(--c-surface-2); }

/* ─────────────────────────────────────────────
   16. UTILIDADES GENÉRICAS
   ───────────────────────────────────────────── */
.truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.divider  { height: 1px; background: var(--c-border); margin: 20px 0; }

.badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 50px;
  font-size: var(--fz-xs);
  font-weight: 600;
  gap: 4px;
}
.badge--success { background: var(--c-success-bg); color: var(--c-success-text); }
.badge--error   { background: var(--c-error-bg);   color: var(--c-error-text);   }
.badge--warning { background: var(--c-warning-bg); color: var(--c-warning-text); }
.badge--info    { background: var(--c-info-bg);    color: var(--c-info-text);    }
.badge--neutral { background: var(--c-surface-2);  color: var(--c-text-muted);   }

:focus-visible { outline: 2px solid var(--c-border-focus); outline-offset: 2px; }

/* Skeleton loader */
.skeleton {
  background: linear-gradient(90deg, var(--c-border) 25%, var(--c-surface-2) 50%, var(--c-border) 75%);
  background-size: 200% 100%;
  animation: skeleton-shine 1.4s ease infinite;
  border-radius: var(--radius-sm);
}
@keyframes skeleton-shine {
  0%   { background-position: 200% center; }
  100% { background-position: -200% center; }
}

/* ─────────────────────────────────────────────
   17. RESPONSIVE
   ───────────────────────────────────────────── */
@media (max-width: 1024px) {
  .dash-grid--4 { grid-template-columns: repeat(2, 1fr); }
  .dash-grid--3 { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 768px) {
  /* Sidebar: overlay en móvil */
  .sidebar {
    transform: translateX(-100%);
    width: var(--sidebar-w) !important; /* ignorar collapsed en móvil */
    transition: transform 0.28s cubic-bezier(0.4,0,0.2,1);
  }
  .sidebar.is-open { transform: translateX(0); }

  /* Main ocupa todo el ancho en móvil */
  .main-wrapper { margin-left: 0 !important; }

  .topbar { left: 0 !important; }

  .topbar__menu-btn { display: flex; }

  /* Overlay cuando sidebar está abierto en móvil */
  .sidebar-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.4);
    z-index: 999;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.28s ease;
  }
  .sidebar-overlay.is-visible { opacity: 1; pointer-events: all; }

  .dash-grid--4,
  .dash-grid--3,
  .dash-grid--2 { grid-template-columns: 1fr; }

  .main-content { padding: calc(var(--topbar-h) + 16px) 16px 16px; }

  /* Footer: apilar en columna y ocultar el texto largo del link de soporte,
     igual que ya se hace con los botones del topbar en móvil. */
  .app-footer {
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    padding: 12px 16px;
  }
  .app-footer__right { width: 100%; justify-content: space-between; }
}


@media (max-width: 640px) {
  /* Login responsive */
  .login-container {
    flex-direction: column;
    border-radius: var(--radius-lg);
  }
  .login-left { padding: 32px 24px; min-height: 180px; }
  .login-left-inner h1 { font-size: var(--fz-xl); }
  .login-right { padding: 32px 24px; }
  .login-right-inner { max-width: 100%; }
  .theme-toggle { top: 12px; right: 12px; padding: 6px 10px; font-size: var(--fz-xs); }
}

.login-footer-signature {
    bottom:-100px;
    margin-top: 10px;
    margin-bottom: 0px;
    text-align: center;

    font-size: var(--fz-xs);
    color: var(--c-text-muted);
    opacity: 0.75;

    letter-spacing: 0.4px;
    border-top: 1px solid var(--c-border);
    padding-top: 5px;
}
/* ─────────────────────────────────────────────
   [NUEVO 2026-07-02] ESTADO DE SISTEMAS
   Selector en el header (topbar) + chips reutilizables
   para los paneles de disponibilidad.
   Ver services/EstadoSistemasService.php (catálogo),
   plantillas/layout/topbar.php (render) y
   assets/js/app.js → EstadoSistemasModule (interacción).
   ───────────────────────────────────────────── */
.estado-sis { position: relative; }

/* [FIX 2026-07-02] El botón usaba colores blancos fijos (rgba(255,255,255,…))
   asumiendo un topbar oscuro. Pero .topbar usa background: var(--c-surface),
   que en modo claro es blanco — el texto blanco sobre fondo blanco quedaba
   invisible (solo se veía en modo oscuro, donde --c-surface sí es oscuro).
   Se corrige usando las mismas variables de tema que .theme-toggle
   (var(--c-surface)/var(--c-border)/var(--c-text)), que ya se adaptan solas
   a modo claro/oscuro. */
.estado-sis__btn {
  display: flex;
  align-items: center;
  gap: 7px;
  height: 36px;
  padding: 0 12px 0 10px;
  border-radius: 999px;
  border: 1px solid var(--c-border);
  background: var(--c-surface);
  color: var(--c-text);
  font-size: var(--fz-xs);
  font-weight: 600;
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition), color var(--transition);
  width: auto;
}
.estado-sis__btn:hover { border-color: var(--c-primary-light); background: var(--c-surface-2); }
.estado-sis__btn i[aria-hidden] { font-size: 12px; }
.estado-sis__chevron { font-size: 9px !important; opacity: .6; transition: transform var(--transition); }
.estado-sis.is-open .estado-sis__chevron { transform: rotate(180deg); }

.estado-sis__dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
  /* Anillo con el color de fondo del propio botón (no blanco fijo) para
     que el punto resalte igual en modo claro y oscuro. */
  box-shadow: 0 0 0 2px var(--c-surface);
}

.estado-sis__menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 210px;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-md);
  box-shadow: 0 12px 28px rgba(0,0,0,.18);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  z-index: 400;
}
.estado-sis__menu[hidden] { display: none; }

.estado-sis__opt {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  background: none;
  border: none;
  color: var(--c-text);
  font-size: var(--fz-sm);
  font-weight: 500;
  cursor: pointer;
  text-align: left;
  width: 100%;
}
.estado-sis__opt:hover { background: var(--c-surface-2); }
.estado-sis__opt.is-active { background: var(--c-info-bg); color: var(--c-primary); font-weight: 600; }
.estado-sis__opt i[aria-hidden] { font-size: 12px; width: 16px; text-align: center; opacity: .85; }

@media (max-width: 640px) {
  .estado-sis__btn span:not(.estado-sis__dot) { display: none; }
  .estado-sis__btn .estado-sis__chevron { display: none; }
  .estado-sis__btn { padding: 0 8px; width: 36px; justify-content: center; }
}

/* ── Chip de estado reutilizable ──
   Usado en tickets/dashboard_tickets.php (panel visible a toda la
   empresa) y tickets/dashboard_sistemas.php (tarjeta de técnico,
   panel del Coordinador). */
.estado-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: var(--fz-xs);
  font-weight: 600;
  white-space: nowrap;
}
.estado-chip__dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }

.personal-sistemas-panel {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding: 14px 16px;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-lg);
  margin-bottom: 16px;
}
.personal-sistemas-panel__title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--fz-sm);
  font-weight: 700;
  color: var(--c-text-muted);
  width: 100%;
  margin-bottom: 2px;
}
.personal-sistemas-card {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border: 1px solid var(--c-border);
  border-radius: var(--radius-md);
  background: var(--c-surface-2);
}
.personal-sistemas-card__nombre { font-size: var(--fz-sm); font-weight: 600; color: var(--c-text); }
.personal-sistemas-card__meta { font-size: 11px; color: var(--c-text-muted); }

/* ═════════════════════════════════════════════════════════════════════
   [NUEVO 2026-07-28] LOGIN · MOSAICO DE MARCA
   ═════════════════════════════════════════════════════════════════════
   Pinta las figuras de Grupo Micro-Tec detrás del login: en la página y
   dentro del panel izquierdo.

   NO HAY NI UN COLOR ESCRITO AQUÍ. Todo entra por variables que genera
   services/TemaLogin.php y que login.php escribe en un <style> del
   <head>, después de este archivo. Cambiar de propuesta o de temporada
   no toca este CSS.

   Las variables, y de qué se encarga cada una:
     --login-pagina           color de fondo de la página
     --login-capa-pagina      mosaico de la página (data URI, o none)
     --login-tam-pagina       tamaño del azulejo de la página
     --login-anclaje          scroll | fixed  (ver más abajo)
     --login-panel-color      velo semitransparente del panel izquierdo
     --login-capa-panel       mosaico o figura del panel
     --login-panel-tam        tamaño del azulejo del panel
     --login-panel-tam-movil  el mismo, encogido para celular
     --login-panel-repetir    repeat | no-repeat
     --login-panel-anclaje    scroll | fixed
     --login-circulos         block para recuperar los círculos originales

   TODAS llevan valor por omisión en el var(), así que si TemaLogin no
   emitiera nada —archivo borrado, PHP con error— el login se ve como
   siempre en vez de quedar en blanco.

   ── POR QUÉ background-attachment: fixed ──
   Es lo que produce el cruce. Anclado al viewport, el mosaico de la
   página y el del panel coinciden pixel a pixel, así que las figuras se
   ven partidas justo en el filo del panel y cambian de color al pasar de
   un lado al otro. Con el valor normal (scroll) cada capa arrancaría en
   su propia esquina y no cuadrarían nunca. No hace falta calcular el
   desplazamiento del panel ni escuchar el resize: el navegador lo
   sostiene solo.

   ── POR QUÉ EL VELO VA EN background-color Y EL MOSAICO EN -image ──
   Dentro de un elemento, el color de fondo se pinta DEBAJO de las
   imágenes de fondo. Y el panel entero está por encima del fondo de la
   página. Así queda el orden que se busca, de abajo hacia arriba:
   mosaico de la página → velo del panel → mosaico del panel → texto.
   El velo es semitransparente a propósito: deja asomar el mosaico de la
   página, y por eso la figura se ve continua aunque cambie de tono.
   ═════════════════════════════════════════════════════════════════════ */

body.body--login {
  background-color: var(--login-pagina, var(--c-bg));
  background-image:  var(--login-capa-pagina, none);
  background-repeat: repeat;
  background-size:   var(--login-tam-pagina, auto);
  background-position: center center;
  /* [CORRECCIÓN 2026-07-28] El respaldo era `fixed`. Ver la nota del
     mismo día en la regla de .login-left, unas líneas más abajo. */
  background-attachment: var(--login-anclaje, scroll);
}

/* Se usa .body--login como prefijo para ganarle por especificidad al
   degradado de la sección 4 sin tener que editarla: si algún día se
   quita este bloque, el login vuelve solo a su aspecto anterior. */
.body--login .login-left {
  background-color: var(--login-panel-color, var(--c-primary));
  background-image:  var(--login-capa-panel, none);
  background-repeat: var(--login-panel-repetir, repeat);
  background-size:   var(--login-panel-tam, auto);
  background-position: center center;
  /* [CORRECCIÓN 2026-07-28] El respaldo era `fixed`, y eso rompía en
     silencio la propuesta 0.

     Esa propuesta no declara --login-panel-anclaje, así que caía en el
     respaldo. Con `fixed`, el área donde se pinta un fondo pasa a ser el
     VIEWPORT en vez de la caja del elemento: el degradado original del
     panel se dibujaba a lo ancho de toda la pantalla y el panel enseñaba
     nada más la rebanada que le tocaba. O sea que la propuesta que
     existe justamente para verse "como estaba antes" no se veía como
     estaba antes.

     `scroll` es el valor normal de CSS. Las propuestas que necesitan el
     cruce piden `fixed` explícitamente desde TemaLogin, así que no
     pierden nada. */
  background-attachment: var(--login-panel-anclaje, scroll);
}

/* Los dos círculos blancos decorativos del diseño original. Con mosaico
   se enciman con las figuras y ensucian el panel, así que por omisión se
   apagan; la propuesta 0-actual-produccion los vuelve a encender poniendo
   --login-circulos en block. */
.body--login .login-left::before,
.body--login .login-left::after {
  display: var(--login-circulos, none);
}

/* ── Celular y tablets angostas (≤640 px) ──
   Aquí la tarjeta ya se apila —lo hace la media query de la sección de
   responsive, más arriba— y pasan tres cosas:

   1. El mosaico de la página se retira. Con la tarjeta ocupando el 95 %
      del ancho solo quedarían dos franjas de 2.5 vw a los lados: no se
      leería como patrón, sino como suciedad en el borde.
   2. El del panel se encoge al 74 %. A tamaño de escritorio, dos figuras
      llenarían la franja de 150 px de alto y tampoco se leerían.
   3. Se pasa a anclaje scroll. Sin mosaico en la página no hay nada con
      lo que alinearse, y de paso se esquiva el fallo conocido de
      background-attachment: fixed en Safari de iOS, que lo ignora y
      además provoca saltos al hacer scroll. */
@media (max-width: 640px) {
  body.body--login { background-image: none; }

  .body--login .login-left {
    background-size: var(--login-panel-tam-movil, auto);
    background-attachment: scroll;
  }
}

/* Quien pide más contraste en el sistema operativo normalmente lo hace
   porque las marcas de agua le estorban para leer. Se le quitan las dos. */
@media (prefers-contrast: more) {
  body.body--login,
  .body--login .login-left { background-image: none; }
}

/* ═════════════════════════════════════════════════════════════════════
   [NUEVO 2026-07-28] LOGIN · FOTOGRAFÍA DESENFOCADA (propuesta 5)
   ═════════════════════════════════════════════════════════════════════
   Solo actúa cuando el <body> lleva .body--login-foto, clase que pone
   TemaLogin::clasesBody() si la propuesta activa declara "foto".

   POR QUÉ EN UN PSEUDO-ELEMENTO Y NO EN EL PROPIO FONDO
   ----------------------------------------------------
   filter: blur() no se puede aplicar a un background-image por separado:
   afecta al elemento COMPLETO, contenido incluido. Puesto sobre .login-
   left, el logo y "Sistema de Tickets" saldrían borrosos junto con la
   foto. La única forma de desenfocar solo el fondo es pintarlo en una
   capa aparte, detrás del contenido, y desenfocar esa capa.

   Se desborda a propósito (inset negativo). Un blur de N px difumina los
   bordes hacia adentro y dejaría un halo claro en el perímetro; sacando
   la capa más allá del recorte, ese halo cae fuera de lo visible.
   ═════════════════════════════════════════════════════════════════════ */

.body--login-foto::before {
  content: '';
  position: fixed;
  /* Los cuatro lados por separado, no `inset`. Es el mismo criterio que
     abajo, donde mezclar la forma corta con lados sueltos ya provocó un
     fallo. */
  top: -60px;
  right: -60px;
  bottom: -60px;
  left: -60px;
  z-index: -1;
  /* Igual que en el panel: el velo va como primera capa de la pila, por
     encima de la foto. Aquí no es cuestión de contraste de texto sino de
     jerarquía — en modo oscuro la foto deja el lienzo más luminoso que la
     tarjeta y esta parece hundida. TemaLogin lo pone en transparent para
     el modo claro y en negro al 60 % para el oscuro. */
  background-image:
    linear-gradient(var(--login-foto-velo-pagina, transparent),
                    var(--login-foto-velo-pagina, transparent)),
    var(--login-foto-pagina, none);
  background-size: cover;
  background-position: center center;
  filter: blur(var(--login-blur-pagina, 24px));
}

/* z-index -1 pinta encima del fondo del <body> pero debajo de la
   tarjeta, que es justo el orden que hace falta. */
.body--login-foto .login-left::before {
  display: block;
  /* [CORRECCIÓN 2026-07-28] Aquí había `inset: -40px` seguido de
     `top: auto; right: auto`. `inset` es la forma corta de los cuatro
     lados, así que esas dos líneas de después le devolvían dos de ellos
     a auto: quedaba anclado solo por abajo y por la izquierda, con
     width y height en auto, y el pseudo-elemento colapsaba a 0×0. La
     foto del panel NO se dibujaba. Se escriben los cuatro lados uno por
     uno para que no vuelva a pasar. */
  top: -40px;
  right: -40px;
  bottom: -40px;
  left: -40px;
  /* Se anulan las medidas del círculo decorativo de la sección 4, que
     ocupa este mismo pseudo-elemento en las demás propuestas. */
  width: auto;
  height: auto;
  border-radius: 0;
  opacity: 1;
  z-index: 0;
  background-color: transparent;
  /* [CORRECCIÓN 2026-07-28] El velo se movió AQUÍ, como primera capa
     encima de la foto.

     Estaba en background-color de .login-left, y eso no funcionaba: un
     pseudo-elemento es una caja hija y se pinta POR ENCIMA del fondo de
     su padre. La foto tapaba el velo, de modo que el logo blanco y los
     textos quedaban directamente sobre la imagen, sin nada que
     garantizara su contraste — justo al revés de lo que decía la
     documentación.

     Un degradado de un solo color es la forma estándar de meter un color
     plano en la pila de background-image, que sí admite varias capas y
     donde la primera queda arriba. */
  background-image:
    linear-gradient(var(--login-panel-color, rgba(2,55,125,.8)),
                    var(--login-panel-color, rgba(2,55,125,.8))),
    var(--login-foto-panel, none);
  background-size: cover;
  background-position: center center;
  filter: blur(var(--login-blur-panel, 12px));
}

/* El segundo círculo no tiene equivalente aquí: se apaga. */
.body--login-foto .login-left::after { display: none; }

/* El contenido del panel tiene que quedar por encima de la foto. Ya
   lleva position: relative y z-index: 1 de la sección 4; se sube para no
   depender de que ese valor no cambie. */
.body--login-foto .login-left-inner { z-index: 2; }

/* Con la foto detrás, el velo del panel deja de ser solo un tinte: es lo
   que garantiza que el logo blanco se lea pase lo que pase con la
   imagen. Por eso no se aclara aquí; llega casi opaco desde el tema. */

@media (max-width: 640px) {
  /* En celular la foto de la página se retira: la tarjeta ocupa el 95 %
     del ancho, así que solo quedarían dos franjas borrosas de 2.5 vw. Y
     un blur a pantalla completa es de lo más caro que se le puede pedir
     a la GPU de un teléfono de gama baja en el arranque. */
  .body--login-foto::before { display: none; }
  .body--login-foto .login-left::before { filter: blur(8px); }
}

/* Un desenfoque es decorativo por definición. Quien pide más contraste
   no lo necesita compitiendo con el texto. */
@media (prefers-contrast: more) {
  .body--login-foto::before,
  .body--login-foto .login-left::before { display: none; }
}

/* ─────────────────────────────────────────────────────────────────────
   [NUEVO 2026-07-28] LOGIN · BOTÓN "ENTRAR" PLANO

   .btn-primary trae un degradado (sección 7). En el login era el único
   elemento con degradado de toda la pantalla —el del panel izquierdo lo
   reemplazó el velo plano del mosaico—, así que llamaba la atención por
   la razón equivocada.

   SE ACOTA AL LOGIN A PROPÓSITO. .btn-primary se usa en otros 8
   archivos: tickets, horarios, facturación, los modales de aviso y de
   términos. Aplanarlo en todos lados es una decisión de sistema de
   diseño y merece su propia ronda, no colarse en un cambio del login.
   ───────────────────────────────────────────────────────────────────── */

.body--login .btn-primary {
  background: var(--c-primary);
  /* La sombra navy sobre la tarjeta blanca ancla el botón; a 0.22 en vez
     de 0.3 porque sin el degradado el botón ya pesa más de por sí. */
  box-shadow: 0 2px 8px rgba(2, 55, 125, .22);
}

.body--login .btn-primary:hover:not(:disabled) {
  background: var(--c-primary-light);
}

/* En oscuro el navy #02377d contra la tarjeta #131c2e queda apagado para
   ser la acción principal de la pantalla. Se sube al azul claro de la
   paleta: #1a56c4 es 9.3× más luminoso que la tarjeta y da 6.62:1 con el
   texto blanco. El hover #2f6bd8 da 4.98:1 — sigue pasando AA, que es el
   piso que había que respetar al aclararlo. */
body.dark-mode.body--login .btn-primary {
  background: var(--c-primary-light);
  box-shadow: none;
}
body.dark-mode.body--login .btn-primary:hover:not(:disabled) {
  background: #2f6bd8;
}

/* ─────────────────────────────────────────────────────────────────────
   [NUEVO 2026-07-28] LOGIN · SUBTÍTULO SIN opacity

   .login-left-inner p usaba opacity: 0.75 (sección 4).

   Seamos precisos, porque el matiz importa: pasar a rgba() NO elimina la
   dependencia del fondo —un color con alfa también se compone contra lo
   que haya detrás—. Lo que se gana es otra cosa:

     · opacity atenúa el ELEMENTO ENTERO y crea contexto de apilamiento.
       Si mañana ese <p> lleva un fondo, un borde o un hijo, todo se
       apaga con él. Con color solo se tiñe el texto.
     · Semánticamente, un texto atenuado es un color de texto, no una
       propiedad de composición del elemento.

   Y el riesgo que motivó el cambio queda acotado por otra vía: los velos
   de los cuatro temas son oscuros por obligación —el logo solo existe en
   blanco—, así que el contraste está medido y no se va a ir de rango al
   cambiar de temporada:

     oscuro   institucional 7.85  ·  madre 5.86  ·  orgullo 6.26  ·  navidad 6.80
     claro    institucional 6.63  ·  madre 4.88  ·  orgullo 5.14  ·  navidad 5.60

   El peor caso es 4.88:1, por encima del 4.5 que pide AA. Si alguna vez
   se agrega un tema con velo claro, este número hay que recalcularlo.
   ───────────────────────────────────────────────────────────────────── */

.body--login .login-left-inner p {
  opacity: 1;
  color: rgba(255, 255, 255, .78);
}
