/* ══════════════════════════════════════════════════════

   EDUPAGO — SISTEMA DE DISEÑO (CRM moderno · limpio · profesional)

   Marca: navy #282d65 · green #49af54 · lime #bdcf00 (acento)

   Modo claro por defecto · modo oscuro pulido disponible

══════════════════════════════════════════════════════ */



*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }



:root {
  /* ══ MARCA EDUPAGO (se conserva para splash, logo y acentos de estado) ══ */
  --navy:        #282d65;
  --navy-dark:   #1c2050;
  --navy-light:  #3a4080;
  --lime:        #bdcf00;
  --lime-dark:   #8a9a00;
  --lime-glow:   rgba(189,207,0,0.14);
  --green:       #49af54;
  --green-dark:  #3a9446;
  --green-glow:  rgba(73,175,84,0.12);

  /* ══ PALETA "CONCURED" — muestreada del video de referencia ══
     Violeta primario · cian · magenta. Cambia SOLO estas 3 líneas
     para re-teñir el sistema completo.                            */
  --violet:       #6c5af0;
  --violet-dark:  #5846dd;
  --violet-light: #8e80f4;
  --cyan:         #4fd8f0;
  --magenta:      #d860f0;

  --violet-soft:  #f1effd;   /* pill de nav activo / fondos teñidos */
  --cyan-soft:    #e8faff;
  --magenta-soft: #fbeeff;

  /* --grad-brand:  linear-gradient(115deg, #4fd8f0 0%, #6c5af0 55%, #d860f0 100%);
  --grad-cool:   linear-gradient(135deg, #4fd8f0 0%, #6c5af0 100%);
  --grad-warm:   linear-gradient(135deg, #6c5af0 0%, #d860f0 100%); */

  --grad-brand:  linear-gradient(#6c5af0);
  --grad-cool:   linear-gradient(#6c5af0);
  --grad-warm:   linear-gradient(#6c5af0);

  /* ══ MODO CLARO (por defecto) ══ */
  --bg-main:      #f4f5f9;   /* lienzo lavanda de la página */
  --bg-solid:     #ffffff;
  --bg-surface:   #ffffff;   /* tarjetas */
  --bg-surface-2: #fafafd;
  --bg-panel:     #ffffff;   /* panel principal (lienzo del contenido) */
  --bg-sidebar:   transparent;

  /* ══ SIDEBAR (ahora claro, integrado al lienzo) ══ */
  --side-bg:        transparent;
  --side-bg-2:      transparent;
  --side-border:    rgba(23,26,44,0.06);
  --side-ink:       #1e2430;   /* texto principal (oscuro sobre claro) */
  --side-ink-2:     #5b6273;
  --side-ink-3:     #8d94a5;
  --side-hover:     rgba(108,90,240,0.06);
  --side-active-bg: var(--violet-soft);

  /* ══ BORDES Y SUPERFICIES ══ */
  --border-glow:   #ebecf3;
  --border-active: var(--violet);
  --glass-light:   #f5f6fa;
  --glass-hover:   #eeeff6;
  --glass-dark:    #fafbfd;
  --shadow-glass:  0 1px 2px rgba(23,26,44,.03), 0 2px 8px rgba(23,26,44,.04);
  --shadow-md:     0 4px 14px rgba(23,26,44,.07), 0 2px 5px rgba(23,26,44,.04);
  --shadow-lg:     0 18px 44px rgba(23,26,44,.13);
  --shadow-violet: 0 8px 22px rgba(108,90,240,.28);

  /* ══ TEXTO ══ */
  --ink:    #1e2430;
  --ink-2:  #414958;
  --ink-3:  #697182;   /* 4.90:1 sobre blanco — cumple WCAG AA */
  --ink-4:  #8f97a6;   /* solo para texto decorativo/iconos */

  /* ══ ACENTOS ══ */
  --accent:          var(--violet);
  --accent-2:        var(--cyan);
  --accent-gradient: var(--grad-cool);
  --btn-gradient:    var(--violet);
  --accent-glow:     rgba(108,90,240,0.09);
  --on-accent:       #ffffff;

  /* ══ SEMÁNTICOS ══ */
  --amber:       #d97706;
  --amber-glow:  rgba(217,119,6,0.10);
  --red:         #e5484d;
  --red-glow:    rgba(229,72,77,0.09);
  --purple:      var(--violet);
  --purple-glow: rgba(108,90,240,0.10);

  /* ══ LAYOUT ══ */
  /* Alias de compatibilidad. Varias vistas usan estos nombres, que nunca
     existieron como token: sin fallback la propiedad quedaba anulada, y con
     fallback aplicaban un color oscuro en modo claro. Definirlos aquí corrige
     13 usos en Alumnos, Caja, Escuelas, Usuarios y Recordatorios. */
  --bg-2:    var(--bg-surface-2);
  --border:  var(--border-glow);
  --glass:   var(--glass-light);
  --ink-1:   var(--ink);
  --surface: var(--bg-surface);

  --radius-sm:    9px;
  --radius:       12px;
  --radius-lg:    18px;
  --radius-panel: 26px;   /* esquina del panel blanco principal */
  --radius-pill:  999px;
  --blur:      blur(12px);
  --font:      'DM Sans', system-ui, -apple-system, sans-serif;
  --mono:      'JetBrains Mono', ui-monospace, monospace;
  --sidebar-w: 244px;
  --header-h:  66px;
}

/* ══ MODO OSCURO (adaptado a la misma paleta) ══ */
:root[data-theme="dark"] {
  /* Escala de elevación: cuanto más "arriba" está el elemento, más claro.
     Sin esto, panel y tarjetas quedan del mismo tono y las tarjetas se pierden. */
  --bg-main:      #0b0d18;   /* lienzo — el más oscuro */
  --bg-panel:     #13162b;   /* panel principal */
  --bg-surface:   #1b1f39;   /* tarjetas — elevadas sobre el panel */
  --bg-solid:     #1b1f39;
  --bg-surface-2: #232848;
  --bg-sidebar:   transparent;

  --border-glow:   rgba(255,255,255,0.09);
  --border-active: var(--violet-light);
  --glass-light:   rgba(255,255,255,0.05);
  --glass-hover:   rgba(255,255,255,0.08);
  --glass-dark:    rgba(0,0,0,0.28);
  --shadow-glass:  0 1px 2px rgba(0,0,0,.34), inset 0 1px 0 rgba(255,255,255,.04);
  --shadow-md:     0 8px 22px rgba(0,0,0,.45), inset 0 1px 0 rgba(255,255,255,.06);
  --shadow-lg:     0 22px 52px rgba(0,0,0,.62);
  --shadow-violet: 0 8px 26px rgba(108,90,240,.45);

  --ink:    #eef1f8;
  --ink-2:  #c4cad8;
  --ink-3:  #8b93a7;
  --ink-4:  #69798f;

  --side-bg:        transparent;
  --side-bg-2:      transparent;
  --side-border:    rgba(255,255,255,0.07);
  --side-ink:       #eef1f8;
  --side-ink-2:     #9aa1bb;
  --side-ink-3:     #6b7290;
  --side-hover:     rgba(255,255,255,0.05);
  --side-active-bg: rgba(108,90,240,0.20);

  --violet-soft:  rgba(108,90,240,0.18);
  --cyan-soft:    rgba(79,216,240,0.14);
  --magenta-soft: rgba(216,96,240,0.14);

  --accent:          var(--violet-light);
  /* El degradado cian daba 1.69:1 con texto blanco. Este mantiene 4.6:1+ */
  --btn-gradient:    linear-gradient(135deg, #6c5af0 0%, #9a4fe0 100%);
  --accent-glow:     rgba(108,90,240,0.18);
  --on-accent:       #ffffff;
  --amber:       #fbbf24;
  --red:         #f87171;
  --purple:      var(--violet-light);
}



html, body {

  height:100%; font-family:var(--font);

  background:var(--bg-main); color:var(--ink);

  font-size:14px; line-height:1.55;

  -webkit-font-smoothing:antialiased;

  text-rendering:optimizeLegibility;

  overflow:hidden;

}



/* ── LAYOUT ── */

/* 100vh en móviles se calcula sobre el viewport MÁS GRANDE posible (barra de
   direcciones/toolbar colapsada) -- mientras esa barra sigue visible (el caso
   normal), el alto real disponible es MENOR que 100vh, así que .app queda más
   alto que la pantalla real. Como .app tiene overflow:hidden, lo que cae fuera
   de esa franja no se puede alcanzar ni haciendo scroll. (El bug puntual del
   botón "Cobrar" de Caja recortado en móvil resultó ser otra cosa -- ver el
   comentario de .payment-methods más abajo -- pero este mismo problema de
   fondo puede recortar cualquier otro contenido cerca del borde inferior en
   cualquier pantalla, así que vale la pena corregirlo aquí también.)
   100dvh (dynamic viewport height) sí se ajusta a la barra visible; se
   declara después de 100vh para que sea solo un override en navegadores que
   lo soportan y no rompa nada donde no. */
#root { height:100vh; height:100dvh; }

.app  { display:flex; height:100vh; height:100dvh; overflow:hidden; background:var(--bg-main); }



/* ── SIDEBAR ── */

.sidebar {

  width:var(--sidebar-w); flex-shrink:0;

  background:transparent;

  display:flex; flex-direction:column; overflow:hidden;

  border-right:none;

}

.sidebar-brand { padding:22px 24px 20px; border-bottom:none; }

.brand-logo    { display:flex; align-items:center; gap:11px; }

.brand-icon {

  width:38px; height:38px;

  background:var(--grad-brand);

  border-radius:50%; display:flex; align-items:center; justify-content:center;

  font-size:17px; 
  /* box-shadow:var(--shadow-violet);  */
  color:#fff; flex-shrink:0; font-weight:800;

}

.brand-name { font-size:16.5px; font-weight:700; color:var(--side-ink); letter-spacing:-.3px; }

.brand-sub  { font-size:10px; color:var(--side-ink-3); margin-top:1px; font-weight:600; text-transform:uppercase; letter-spacing:.7px; }



.sidebar-nav { flex:1; overflow-y:auto; padding:14px 12px; }

/* El logo es un JPEG rectangular: con width:100% el borde derecho quedaba
   recto. Se recorta a un ancho propio y se redondean las 4 esquinas. */
.sidebar-brand img {
  border-radius:12px; display:block; width:auto; max-width:100%;
  object-fit:contain; object-position:left center;
}

.sidebar-nav::-webkit-scrollbar { width:0; }

.nav-section { font-size:10px; font-weight:700; letter-spacing:.9px; text-transform:uppercase; color:var(--side-ink-3); padding:18px 12px 8px; }

.nav-item {

  display:flex; align-items:center; gap:12px; padding:12px 24px;

  margin:0 -12px 2px; border-radius:0; cursor:pointer;

  transition:background .15s, color .15s;

  color:var(--side-ink-2); font-size:13.5px; font-weight:500;

  position:relative;

}

.nav-item:hover { background:var(--side-hover); color:var(--side-ink); }

.nav-item.active {

  background:var(--side-active-bg); color:var(--side-ink); font-weight:600;

}

.nav-item.active .nav-icon { color:var(--violet); }

.nav-item.active::before {

  content:''; position:absolute; right:0; top:0;

  width:3px; height:100%; border-radius:0; background:var(--violet);

}

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

.nav-badge {

  margin-left:auto; background:var(--red);

  color:#fff; font-size:10px; font-weight:700;

  padding:2px 7px; border-radius:20px;

  min-width:20px; height:20px; display:flex; align-items:center; justify-content:center;

}



.sidebar-footer { border-top:1px solid var(--side-border); padding:14px 12px; }

.user-card { display:flex; align-items:center; gap:11px; padding:8px 10px; border-radius:var(--radius); cursor:default; transition:background .15s; }

.user-card:hover { background:var(--side-hover); }

.avatar {

  width:34px; height:34px; border-radius:9px; display:flex; align-items:center;

  justify-content:center; font-size:12px; font-weight:700; flex-shrink:0; font-family:var(--mono);

}

.avatar-super  { background:var(--grad-warm) !important; color:#fff !important; }

.avatar-admin  { background:var(--grad-cool) !important; color:#fff !important; }

.avatar-cajero { background:linear-gradient(135deg, var(--green), var(--green-dark)) !important; color:#fff !important; }

/* views/components/MenuPerfil.js arma la clase como 'avatar-' + user.rol, así
   que el nombre real que llega es avatar-superadmin (no avatar-super) y existen
   uno por cada rol. Sin estas reglas, todos los roles salvo admin y cajero
   pintaban el avatar sin color de fondo — incluido el superadmin, porque
   .avatar-super de arriba nunca llega a coincidir. Los colores son los mismos
   de ROL_INFO en views/Usuarios.js, para que la credencial y la etiqueta del
   rol se vean como la misma cosa en todo el sistema. */
.avatar-superadmin   { background:var(--grad-warm) !important; color:#fff !important; }
.avatar-familia      { background:linear-gradient(135deg, #a855f7, #7e22ce) !important; color:#fff !important; }
.avatar-distribuidor { background:linear-gradient(135deg, #84cc16, #4d7c0f) !important; color:#fff !important; }
.avatar-contador     { background:linear-gradient(135deg, #0891b2, #0e7490) !important; color:#fff !important; }
.avatar-soporte      { background:linear-gradient(135deg, #6366f1, #4338ca) !important; color:#fff !important; }
.avatar-provision    { background:linear-gradient(135deg, #f97316, #c2410c) !important; color:#fff !important; }
.avatar-tesoreria    { background:linear-gradient(135deg, #0d9488, #0f766e) !important; color:#fff !important; }
.avatar-promotor     { background:linear-gradient(135deg, #8b5cf6, #6d28d9) !important; color:#fff !important; }

.user-info { flex:1; min-width:0; }

.user-name { font-size:13px; font-weight:600; color:var(--side-ink); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }

.user-role { font-size:11px; color:var(--side-ink-3); margin-top:1px; }

.logout-btn { font-size:16px; color:var(--side-ink-3); cursor:pointer; padding:6px; border-radius:8px; transition:all .15s; border:none; background:none; display:flex; align-items:center; }

.logout-btn:hover { color:var(--red); background:var(--red-glow); }



/* ── MAIN ── */

.main {
  flex:1; display:flex; flex-direction:column; overflow:hidden;
  background:var(--bg-panel);
  border-radius:var(--radius-panel) 0 0 0;
  border-left:1px solid var(--border-glow);
  border-top:1px solid var(--border-glow);
  margin-top:14px;
}

.topbar {

  height:var(--header-h); background:transparent;

  border-bottom:none; display:flex; align-items:center;

  padding:0 30px; gap:14px; flex-shrink:0;

}

/* Era un bloque plano: el SVG del icono caía a su propia línea y el título
   quedaba debajo. Como fila flex, el icono se alinea a la izquierda del texto. */
.topbar-title   {
  font-size:16px; font-weight:700; color:var(--ink); letter-spacing:-.2px;
  display:flex; align-items:center; gap:9px; min-width:0;
}
.topbar-title > span { display:inline-flex; align-items:center; flex-shrink:0; }
.topbar-title svg { display:block; }

.topbar-actions { margin-left:auto; display:flex; align-items:center; gap:10px; }

.content { flex:1; overflow-y:auto; padding:14px 30px 34px; animation:fadeIn .25s ease-out; }

.content::-webkit-scrollbar { width:8px; }

.content::-webkit-scrollbar-thumb { background:var(--border-glow); border-radius:20px; }

.content::-webkit-scrollbar-thumb:hover { background:var(--ink-4); }

@keyframes fadeIn { from{opacity:0;transform:translateY(4px)} to{opacity:1;transform:none} }



/* ── BOTONES ── */

.btn {

  display:inline-flex; align-items:center; justify-content:center; gap:7px;

  padding:10px 18px; border-radius:var(--radius-pill); font-size:13px;

  font-weight:600; cursor:pointer; border:1px solid transparent;

  transition:all .15s; font-family:var(--font); white-space:nowrap; line-height:1;

}

.btn-primary {

  background:var(--btn-gradient); color:var(--on-accent);

  /* box-shadow:var(--shadow-violet); */

}

.btn-primary:hover { filter:brightness(1.08); box-shadow:var(--shadow-md); }

.btn-secondary { background:var(--bg-solid); border-color:var(--border-glow); color:var(--ink-2); box-shadow:0 1px 2px rgba(16,24,40,.04); }

.btn-secondary:hover { background:var(--glass-light); color:var(--ink); border-color:var(--ink-4); }

.btn-ghost { background:transparent; color:var(--ink-3); padding:8px; border:none; border-radius:var(--radius-sm); cursor:pointer; font-family:var(--font); }

.btn-ghost:hover { background:var(--glass-light); color:var(--ink); }



.emoji-picker-grid {

  display:grid; grid-template-columns:repeat(6,1fr); gap:2px;

  max-height:170px; overflow-y:auto; padding-right:2px;

}

.emoji-picker-item {

  font-size:20px; background:transparent; border:none; cursor:pointer;

  border-radius:var(--radius-sm); padding:5px 0; line-height:1;

  transition:background .12s;

}

.emoji-picker-item:hover { background:var(--bg-surface-2); }

.btn-danger { background:var(--red-glow); color:var(--red); border-color:rgba(220,38,38,.2); }

.btn-danger:hover { background:rgba(220,38,38,.16); }

.btn-success { background:var(--green-glow); color:var(--green-dark); border-color:rgba(47,158,68,.22); }

:root[data-theme="dark"] .btn-success { color:var(--green); }

.btn-success:hover { background:rgba(47,158,68,.16); }

.btn-sm { padding:6px 11px !important; font-size:12px !important; border-radius:var(--radius-sm) !important; }

.btn:disabled { opacity:.5; cursor:not-allowed; filter:none!important; box-shadow:none!important; }



/* ── CARDS ── */

.card {

  background:var(--bg-surface);

  border:1px solid var(--border-glow); border-radius:var(--radius-lg);

  padding:24px; box-shadow:var(--shadow-glass);

}

.card-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:18px; gap:12px; }

.card-title  { font-size:15px; font-weight:700; color:var(--ink); letter-spacing:-.2px; }

.card-sub    { font-size:12px; color:var(--ink-3); margin-top:2px; }



/* ── STAT CARDS ── */

.stats-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(210px,1fr)); gap:18px; margin-bottom:24px; }

.dash-split-grid { display:grid; grid-template-columns:1fr 320px; gap:20px; }
/* Por default un elemento de grid no se encoge por debajo del ancho mínimo
   de su contenido (min-width:auto) -- la tabla de "Cobros recientes" y la
   fila dona+leyenda de "Por método de pago" podían así forzar sus columnas
   (incluidos los 320px fijos) a crecer más de la cuenta y desbordar toda la
   fila. min-width:0 deja que .table-wrap (que ya tiene su propio scroll
   horizontal) y el flex-wrap de la dona hagan su trabajo dentro del ancho
   real de la columna, en vez de ensancharla. */
.dash-split-grid > .card { min-width:0; }

.stat-card {

  background:var(--bg-surface);

  border:1px solid var(--border-glow); border-radius:var(--radius-lg);

  padding:20px 22px; display:flex; flex-direction:column; gap:6px;

  position:relative; overflow:hidden;

  box-shadow:var(--shadow-glass); transition:transform .2s, box-shadow .2s, border-color .2s;

}

.stat-card:hover { transform:translateY(-3px); box-shadow:var(--shadow-md); border-color:#dcdcea; }

/* Jerarquía del video: icono arriba, número grande, etiqueta debajo.
   Se reordena por CSS — el marcado de las vistas no se toca. */
.stat-card > .stat-icon  { order:1; }
.stat-card > .stat-value { order:2; }
.stat-card > .stat-label { order:3; }
.stat-card > .stat-meta  { order:4; }
/* Cualquier otro hijo (por ejemplo una mini gráfica) va al final, no al inicio:
   sin esto quedaría con order:0 y saltaría arriba del icono. */
.stat-card > *           { order:5; }
.stat-card > .stat-icon  { order:1; }
.stat-card > .stat-value { order:2; }
.stat-card > .stat-label { order:3; }
.stat-spark              { order:5; margin-top:10px; width:100%; }

.stat-icon  {
  width:44px; height:44px; border-radius:14px; display:flex; align-items:center;
  justify-content:center; font-size:19px; margin-bottom:10px; color:var(--violet);
  background:var(--violet-soft);
}

.stat-label { font-size:12.5px; color:var(--ink-3); font-weight:500; text-transform:none; letter-spacing:0; }

.stat-value {
  font-size:29px; font-weight:700; color:var(--ink); letter-spacing:-1.1px;
  font-family:var(--font); font-variant-numeric:tabular-nums; line-height:1.12;
  /* .stat-card tiene overflow:hidden (para el degradado/borde redondeado de
     la tarjeta destacada) -- sin esto, un monto grande (varios millones)
     se recortaba en silencio en vez de pasar a una segunda línea. */
  overflow-wrap:anywhere;
}

.stat-meta  { font-size:12px; color:var(--ink-3); }

.stat-meta.up   { color:var(--green-dark); font-weight:600; }

.stat-meta.down { color:var(--red); font-weight:600; }



/* ── TABLAS ── */

.table-wrap { overflow-x:auto; }

table { width:100%; border-collapse:collapse; }

th {
  padding:11px 14px; text-align:left; font-size:11.5px; font-weight:600;
  color:var(--ink-3); text-transform:none; letter-spacing:.2px;
  background:var(--glass-light); border-bottom:none; white-space:nowrap;
}
th:first-child { border-radius:var(--radius-sm) 0 0 var(--radius-sm); }
th:last-child  { border-radius:0 var(--radius-sm) var(--radius-sm) 0; }

td { padding:13px 14px; border-bottom:1px solid var(--border-glow); font-size:13px; color:var(--ink-2); vertical-align:middle; transition:background .12s; }

tr:last-child td { border-bottom:none; }

tbody tr { transition:background .12s; }

tbody tr:hover td { background:var(--violet-soft); color:var(--ink); }



/* ── BADGES ── */

.badge        { display:inline-flex; align-items:center; gap:4px; padding:4px 11px; border-radius:var(--radius-pill); font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.3px; }

.badge-green  { background:var(--green-glow); color:var(--green-dark); border:1px solid rgba(47,158,68,.2); }

.badge-lime   { background:var(--lime-glow);  color:var(--lime-dark); border:1px solid rgba(189,207,0,.25); }

.badge-amber  { background:var(--amber-glow); color:var(--amber); border:1px solid rgba(217,119,6,.2); }

.badge-red    { background:var(--red-glow);   color:var(--red); border:1px solid rgba(220,38,38,.2); }

.badge-blue   { background:var(--accent-glow); color:var(--violet-dark); border:1px solid rgba(108,90,240,.20); }

.badge-purple { background:var(--magenta-soft); color:#a83fc0; border:1px solid rgba(216,96,240,.22); }

.badge-gray   { background:var(--glass-light); color:var(--ink-3); border:1px solid var(--border-glow); }

:root[data-theme="dark"] .badge-green { color:var(--green); }

:root[data-theme="dark"] .badge-blue  { color:#818ccc; }



/* ── FORMULARIOS ── */

.form-group { margin-bottom:16px; }

.form-label { display:block; font-size:12px; font-weight:600; color:var(--ink-2); margin-bottom:6px; }

.form-input, .form-select {

  width:100%; padding:10px 13px; background:var(--bg-solid);

  border:1px solid var(--border-glow); border-radius:var(--radius);

  color:var(--ink); font-size:13.5px; font-family:var(--font);

  transition:border-color .15s, box-shadow .15s; outline:none;

}

.form-input:focus, .form-select:focus {

  border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-glow);

}

.form-input::placeholder { color:var(--ink-4); }

.form-select {

  appearance:none;

  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23697386' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");

  background-repeat:no-repeat; background-position:right 12px center;

  padding-right:32px; cursor:pointer;

}

.form-select option { background:var(--bg-solid); color:var(--ink); }



/* ── MODAL ── */

.modal-backdrop {

  position:fixed; inset:0; background:rgba(16,19,40,.45); backdrop-filter:blur(4px);

  display:flex; align-items:center; justify-content:center; z-index:1000;

  animation:fadeIn .15s ease; padding:20px;

  /* Con el modal abierto, el fondo seguía desplazándose por detrás: el backdrop
     es position:fixed y se queda quieto, pero .content (que es el contenedor
     con scroll de toda la app) nunca se bloqueaba, así que la rueda o el gesto
     sobre el overlay movían la página de atrás y daba la sensación de "salirse"
     del modal. No existe bloqueo de scroll en ningún lado del sistema.

     overflow-y:auto convierte al overlay en su propio contenedor de scroll, y
     overscroll-behavior:contain evita que ese scroll se ENCADENE al de atrás.
     Como .modal tiene max-height:90vh, el overlay casi nunca desborda: lo que
     se gana es justamente cortar el encadenamiento. Y si algún día un modal
     crece de más, ahora se recorre dentro del overlay en vez de quedar fuera
     de alcance. Va en la clase compartida, así que cubre los 21 modales del
     sistema y no solo el de Facturación, que fue donde se reportó. */
  overflow-y:auto; overscroll-behavior:contain;

}

.modal {

  background:var(--bg-surface); border:1px solid var(--border-glow);

  border-radius:var(--radius-panel); width:100%; max-width:480px; max-height:90vh;

  overflow:hidden; display:flex; flex-direction:column;

  box-shadow:var(--shadow-lg);

  animation:slideUp .2s ease;

}

.modal-lg  { max-width:680px; }

@keyframes slideUp { from{transform:translateY(16px);opacity:0} to{transform:none;opacity:1} }

.modal-header { display:flex; align-items:center; justify-content:space-between; padding:18px 22px; border-bottom:1px solid var(--border-glow); flex-shrink:0; }

.modal-title  { font-size:16px; font-weight:700; color:var(--ink); }

.modal-body   { padding:20px 22px; overflow-y:auto; flex:1; }

.modal-footer { display:flex; justify-content:flex-end; gap:10px; padding:16px 22px; border-top:1px solid var(--border-glow); flex-shrink:0; background:var(--bg-main); }



/* ── LOGIN ── */

.login-screen {

  height:100vh; display:flex; align-items:center; justify-content:center;

  position:relative; overflow:hidden; padding-bottom:5rem;

  /* ── Colores ORIGINALES de EduPago, aislados del rediseño ──
     Redefinir los tokens aquí re-tiñe solo el login: el resto
     del sistema sigue con la paleta del video.                */

  --accent:       var(--lime);

  --accent-glow:  rgba(189,207,0,0.18);

  --btn-gradient: linear-gradient(135deg, #bdcf00 0%, #2f9e44 100%);

  --border-glow:  rgba(255,255,255,0.07);

  --bg-solid:     #272c52;

  --bg-surface:   #272c52;

  --ink:    #eef1f8;

  --ink-2:  #c4cad8;

  --ink-3:  #8b93a7;

  --ink-4:  #69798f;

  background:#272c64;

}

.login-screen::before {

  content:''; position:absolute; inset:0;

  background:

    radial-gradient(ellipse 60% 60% at 80% 15%, rgba(189,207,0,.10) 0%, transparent 60%),

    radial-gradient(ellipse 60% 60% at 15% 85%, rgba(73,175,84,.10) 0%, transparent 60%);

  pointer-events:none;

}

.login-card {

  width:390px; background:#272c52; border:1px solid rgba(255,255,255,.07);

  border-radius:var(--radius-lg); padding:34px 32px; box-shadow:0 16px 40px rgba(0,0,0,.5);

  animation:slideUp .3s ease; position:relative; z-index:1;

}

.login-logo    { text-align:center; margin-bottom:30px; }

.login-brand   { font-size:28px; font-weight:800; color:var(--ink); letter-spacing:-1px; }

.login-tagline { font-size:12px; color:var(--green); margin-top:5px; font-weight:600; letter-spacing:.3px; }

.login-label   { display:block; font-size:12px; font-weight:600; color:var(--ink-2); margin-bottom:6px; }

.login-input {

  width:100%; padding:11px 14px; 

  /*background:var(--bg-solid);*/

  border:1px solid var(--border-glow); border-radius:var(--radius);

  /*color:var(--ink);*/

  font-size:14px; font-family:var(--font); outline:none;

  transition:border-color .15s, box-shadow .15s;

}

.login-input:focus { border-color:var(--accent); 

box-shadow:0 0 0 3px var(--accent-glow); 

}

.login-btn {

  width:100%; margin-top:20px; padding:13px;

  background:var(--btn-gradient);

  color:#fff; font-size:14px; font-weight:700; border:none; border-radius:var(--radius);

  cursor:pointer; transition:all .15s; font-family:var(--font);

  letter-spacing:.2px; box-shadow:0 4px 14px rgba(47,158,68,.28);

}

.login-btn:hover:not(:disabled) { filter:brightness(1.1); box-shadow:var(--shadow-md); }

.login-btn:disabled { opacity:.6; cursor:not-allowed; }

.login-demo { display:flex; gap:8px; margin-top:16px; }

.demo-pill {

  flex:1; padding:8px 6px; background:var(--bg-solid); border:1px solid var(--border-glow);

  border-radius:var(--radius-sm); cursor:pointer; font-size:11px;

  font-weight:500; color:var(--ink-2); text-align:center; transition:all .15s;

  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;

}

.demo-pill:hover { background:var(--accent-glow); border-color:var(--accent); color:var(--accent); }

.login-error { color:var(--red); font-size:12px; margin-top:8px; }



/* ── POS / CAJA ── */

.pos-layout { display:grid; grid-template-columns:1fr 360px; gap:20px; height:100%; min-height:600px; }

.pos-products { display:flex; flex-direction:column; background:var(--bg-surface); border:1px solid var(--border-glow); border-radius:var(--radius-lg); overflow:hidden; box-shadow:var(--shadow-glass); }

.pos-header { display:flex; align-items:center; gap:10px; padding:14px 16px; border-bottom:1px solid var(--border-glow); flex-shrink:0; }

.pos-products-grid { flex:1; min-height:0; overflow-y:auto; padding:14px; display:grid; grid-template-columns:repeat(auto-fill,minmax(140px,1fr)); gap:10px; align-content:start; }



.product-card {

  background:var(--bg-solid); border:1px solid var(--border-glow);

  border-radius:var(--radius); padding:14px 12px; cursor:pointer;

  transition:all .15s; text-align:center;

}

.product-card:hover { border-color:var(--violet); transform:translateY(-3px); box-shadow:var(--shadow-md); }

.product-emoji { font-size:26px; margin-bottom:7px; }

.product-name  { font-size:12.5px; font-weight:600; color:var(--ink); margin-bottom:3px; line-height:1.3; }

.product-type  { font-size:10.5px; color:var(--ink-3); margin-bottom:5px; text-transform:capitalize; }

.product-price { font-size:13.5px; font-weight:700; color:var(--violet); font-variant-numeric:tabular-nums; }

:root[data-theme="dark"] .product-price { color:var(--violet-light); }



.pos-cart { display:flex; flex-direction:column; background:var(--bg-surface); border:1px solid var(--border-glow); border-radius:var(--radius-lg); overflow:hidden; box-shadow:var(--shadow-glass); }

.cart-header { padding:14px 16px; border-bottom:1px solid var(--border-glow); flex-shrink:0; }

.cart-customer {

  display:flex; align-items:center; gap:10px; padding:9px 12px;

  background:var(--glass-light); border:1px solid var(--border-glow);

  border-radius:var(--radius-sm); cursor:pointer; margin-top:8px;

  transition:all .15s;

}

.cart-customer:hover { background:var(--glass-hover); border-color:var(--accent); }

.cart-items { flex:1; min-height:0; overflow-y:auto; padding:10px 12px; }

.cart-item { display:flex; align-items:center; gap:10px; padding:9px 10px; border-radius:var(--radius-sm); border-bottom:1px solid var(--border-glow); }

.cart-item:last-child { border-bottom:none; }

.cart-item-info  { flex:1; min-width:0; }

.cart-item-name  { font-size:13px; font-weight:500; color:var(--ink); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }

.cart-item-qty   { display:flex; align-items:center; gap:6px; margin-top:3px; }

.qty-btn { width:22px; height:22px; border-radius:5px; border:1px solid var(--border-glow); background:var(--bg-solid); color:var(--ink); cursor:pointer; font-size:14px; display:flex; align-items:center; justify-content:center; transition:all .12s; line-height:1; font-family:var(--mono); }

.qty-btn:hover { background:var(--accent-glow); border-color:var(--accent); color:var(--accent); }

.qty-num { font-size:12.5px; font-weight:600; font-family:var(--mono); min-width:18px; text-align:center; }

.cart-item-price { font-size:13.5px; font-weight:700; font-family:var(--mono); color:var(--ink); }

.cart-remove { font-size:12px; color:var(--ink-4); cursor:pointer; display:block; margin-top:3px; transition:color .12s; text-align:right; }

.cart-remove:hover { color:var(--red); }



.cart-totals { padding:12px 16px; border-top:1px solid var(--border-glow); border-bottom:1px solid var(--border-glow); flex-shrink:0; }

.totals-row  { display:flex; justify-content:space-between; align-items:center; margin-bottom:4px; font-size:13px; color:var(--ink-3); }

.totals-total { font-size:18px; font-weight:800; font-family:var(--mono); color:var(--ink); }

.text-mono { font-family:var(--mono); }



.payment-methods { display:flex; gap:7px; padding:12px 14px; border-bottom:1px solid var(--border-glow); flex-shrink:0; flex-wrap:wrap; }

.pay-method {

  flex:1; min-width:60px; display:flex; flex-direction:column; align-items:center; gap:4px;

  padding:8px 6px; border-radius:var(--radius-sm); border:1px solid var(--border-glow);

  cursor:pointer; transition:all .15s; font-size:11px; font-weight:600; color:var(--ink-3);

  background:var(--bg-solid);

}

.pay-method:hover { background:var(--glass-light); color:var(--ink); }

.pay-method.selected { background:var(--accent-glow); border-color:var(--accent); color:var(--accent); }

.pm-icon { font-size:18px; }

.checkout-btn {

  /* flex-shrink:0 — .pos-cart es un flex column con max-height y
     overflow:hidden. Sin esto, el botón es lo primero que se comprime cuando
     el contenido no cabe, y lo que sobra queda recortado e inalcanzable. */
  flex-shrink:0;
  margin:12px; padding:13px; background:var(--btn-gradient);

  color:#fff; font-size:14px; font-weight:700; border:none;

  border-radius:var(--radius); cursor:pointer; transition:all .15s;

  font-family:var(--font); box-shadow:0 1px 2px rgba(16,24,40,.12);

}

.checkout-btn:hover:not(:disabled) { filter:brightness(1.1); box-shadow:var(--shadow-md); }

.checkout-btn:disabled { opacity:.45; cursor:not-allowed; filter:none; box-shadow:none; }



/* ── SEARCH BAR ── */

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

/* Fila de filtros/chips (rangos de fecha, métodos de pago, estados, roles...):
   en vez de quedar amontonados a la izquierda, se reparten uniformemente
   ocupando todo el ancho disponible. Si no caben a un tamaño legible
   (min-width), en vez de encogerse hasta ser ilegibles, el renglón se
   convierte en un carrusel horizontal — mismo comportamiento que ya se usa
   en los métodos de pago del carrito de Caja. */
.chip-row-fill {
  display: flex;
  width: 100%;
  min-width: 0;
  max-width: 100%;
  gap: 8px;
  flex-wrap: nowrap;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.chip-row-fill::-webkit-scrollbar { display: none; }
.chip-row-fill > * { flex: 1 1 110px; text-align: center; white-space: nowrap; }
/* .pill-group (definido más abajo) trae inline-flex + wrap; con esta doble
   clase gana la especificidad y el renglón se comporta igual que los demás. */
.chip-row-fill.pill-group { display: flex; flex-wrap: nowrap; }

/* Leyenda "Jerarquía de permisos y roles autorizados" en Mi equipo: los
   badges de rol tienen texto de distinto largo (Admin vs Cajero vs
   Superadmin), lo que dejaba la descripción de cada uno arrancando en un
   punto distinto y se veía disparejo. Se les da un ancho mínimo parejo para
   que todas las descripciones alineen en la misma columna. */
.rol-legend-badge { min-width: 100px; justify-content: center; }

.search-bar .search-icon { position:absolute; left:14px; top:50%; transform:translateY(-50%); font-size:15px; pointer-events:none; color:var(--ink-4); }

.search-bar input {

  width:100%; padding:9px 14px 9px 38px; background:var(--bg-solid);

  border:1px solid var(--border-glow); border-radius:var(--radius-sm);

  color:var(--ink); font-size:13px; font-family:var(--font); outline:none;

  transition:border-color .15s, box-shadow .15s;

}

.search-bar input:focus { border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-glow); }

.search-bar input::placeholder { color:var(--ink-4); }



/* ── EMPTY STATE ── */

.empty-state { text-align:center; padding:40px 20px; }

.empty-icon  { font-size:38px; margin-bottom:10px; opacity:.4; }
.empty-icon > svg { display:block; margin:0 auto; }

/* Botones de pago de Mi suscripción (tarjeta / efectivo): cada uno ocupa el
   ancho disponible; si no caben lado a lado, se apilan a ancho completo. */
.susc-pagos > .btn { flex:1 1 220px; justify-content:center; }

.empty-text  { font-size:14px; color:var(--ink-3); font-weight:500; }

.empty-sub   { font-size:12px; color:var(--ink-4); margin-top:4px; }



/* ── TICKET ��─ */

.ticket { background:#fff; color:#000; padding:18px 16px; border-radius:var(--radius); font-family:var(--mono); font-size:12px; max-width:280px; margin:0 auto; box-shadow:var(--shadow-md); }

.ticket-divider { border:none; border-top:1px dashed #ccc; margin:8px 0; }



/* ── SPEI BOX ── */

.spei-box {

  background:var(--grad-warm);

  border-radius:var(--radius-lg); padding:22px; margin:12px 0;

  border:none; 
  /* box-shadow:var(--shadow-violet); */

}

.clabe-display { font-family:var(--mono); font-size:22px; font-weight:700; color:#fff; letter-spacing:3px; text-align:center; padding:12px; background:rgba(0,0,0,.2); border-radius:var(--radius); margin:12px 0; }

.spei-info-row { display:flex; justify-content:space-between; align-items:center; margin-bottom:6px; font-size:12px; }

.spei-label  { color:rgba(255,255,255,.55); font-size:10px; text-transform:uppercase; letter-spacing:.5px; }

.spei-value  { color:#fff; font-weight:600; font-family:var(--mono); font-size:13px; }

.copy-btn { display:flex; align-items:center; gap:6px; padding:8px 14px; background:rgba(255,255,255,.12); border:1px solid rgba(255,255,255,.2); border-radius:var(--radius-sm); color:#fff; font-size:12px; font-weight:700; cursor:pointer; transition:all .15s; font-family:var(--font); width:100%; justify-content:center; }

.copy-btn:hover  { background:rgba(255,255,255,.2); }

.copy-btn.copied { background:rgba(47,158,68,.35); border-color:rgba(47,158,68,.6); color:#fff; }



/* ── QR CoDi ── */

.codi-qr { width:180px; height:180px; margin:0 auto; background:#fff; border-radius:var(--radius); padding:10px; display:flex; align-items:center; justify-content:center; box-shadow:var(--shadow-glass); }

.qr-svg  { width:100%; height:100%; }



/* ── SPINNER ── */

.spinner { display:inline-block; width:18px; height:18px; border:2px solid rgba(255,255,255,.35); border-top-color:#fff; border-radius:50%; animation:spin .7s linear infinite; }

@keyframes spin { to { transform:rotate(360deg); } }



/* ── THEME TOGGLE ── */

.theme-toggle {
  padding:9px 11px; border-radius:var(--radius-pill); border:1px solid var(--border-glow);
  background:var(--bg-surface); cursor:pointer; font-size:15px; color:var(--ink-3);
  transition:all .15s; display:flex; align-items:center; line-height:1;
}

/* Antes esta regla era `display:none`, así que el usuario no podía cambiar de tema. */
.theme-toggle:hover { background:var(--violet-soft); color:var(--violet); border-color:var(--violet); }

/* ── SWITCH (toggle deslizante) ── */
/* Uso: <label className="switch"><input type="checkbox" checked={...} onChange={...} /><span className="switch-track"></span></label> */
.switch { position:relative; display:inline-block; width:40px; height:22px; flex-shrink:0; cursor:pointer; }
.switch input { position:absolute; opacity:0; width:100%; height:100%; margin:0; cursor:pointer; }
.switch-track {
  position:absolute; inset:0; background:var(--border-glow); border-radius:var(--radius-pill);
  transition:background .15s;
}
.switch-track::before {
  content:''; position:absolute; top:2px; left:2px; width:18px; height:18px;
  background:#fff; border-radius:50%; box-shadow:0 1px 3px rgba(16,24,40,.25);
  transition:transform .15s;
}
.switch input:checked + .switch-track { background:var(--violet); }
.switch input:checked + .switch-track::before { transform:translateX(18px); }
.switch input:disabled + .switch-track { opacity:.5; cursor:not-allowed; }
.switch input:focus-visible + .switch-track { outline:2px solid var(--violet); outline-offset:2px; }

/* ── MOBILE MENU BUTTON + DRAWER BACKDROP (ocultos en escritorio) ── */

.mobile-menu-btn {

  display:none; align-items:center; justify-content:center;

  width:38px; height:38px; flex-shrink:0;

  border:1px solid var(--border-glow); border-radius:var(--radius-sm);

  background:var(--bg-solid); color:var(--ink-2); cursor:pointer;

  transition:all .15s; padding:0; margin-right:2px;

}

.mobile-menu-btn:hover { background:var(--glass-light); color:var(--ink); }

.nav-backdrop { display:none; }



/* ── PROGRESS BAR ── */

.progress-bar  { height:6px; background:var(--glass-light); border-radius:10px; overflow:hidden; margin-top:8px; }

.progress-fill { height:100%; border-radius:10px; transition:width 1s ease; background:var(--grad-cool); }



/* ── VERIF ROW ── */

.verif-row { display:flex; align-items:center; gap:8px; padding:10px 12px; background:var(--glass-light); border-radius:var(--radius-sm); margin-top:10px; }

.verif-dot { width:8px; height:8px; border-radius:50%; flex-shrink:0; }

.verif-dot.pulse { animation:pulse 1.5s infinite; }

@keyframes pulse { 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:.5;transform:scale(.8)} }



/* ── Toast animation ── */

@keyframes slideIn {

  from { opacity:0; transform:translateY(-10px) translateX(10px); }

  to   { opacity:1; transform:translateY(0) translateX(0); }

}



/* ══════════════════════════════════════════════════════

   RESPONSIVE LAYOUT — funciona en cualquier dispositivo

   · ≥ 1025px  → sidebar fijo completo (256px)

   · 769–1024  → sidebar fijo (un poco más angosto) + grids fluidos

   · ≤ 768px   → sidebar como drawer deslizable + botón de menú

══════════════════════════════════════════════════════ */



/* ── Asegura tablas y bloques anchos con scroll horizontal limpio ── */

.table-wrap { -webkit-overflow-scrolling: touch; }



/* ── TABLETS / PANTALLAS MEDIANAS (769–1024px) ── */

@media (max-width: 1024px) and (min-width: 769px) {

  :root { --sidebar-w: 220px; }

  .content { padding: 22px; }

  .pos-layout { grid-template-columns: 1fr 320px; }

}



/* ── TABLETS GRANDES Y MENORES: POS apilado (≤900px) ── */

@media (max-width: 900px) {

  .pos-layout {

    grid-template-columns: 1fr;

    min-height: auto;

    height: auto;

  }

  .pos-cart { max-height: 60vh; max-height: 60dvh; }

  /* Seguro contra el bug del botón "Cobrar" que no aparece (reportado en un
     OnePlus 15: "no aparece ni bajando hasta el fondo"). La regla base pone
     .pos-cart en overflow:hidden, así que todo lo que no cabe dentro del tope
     de altura NO se recorta y ya: queda INALCANZABLE, sin scroll que lo pueda
     traer. Y el botón es el último hijo de esa columna, así que es siempre lo
     primero que se pierde.
     El tope de altura y el scroll interno de .cart-items se quedan como están
     (son el arreglo documentado más abajo, y siguen siendo lo que evita que el
     bloque de cobro se vaya al fondo de la página). Esto solo cambia qué pasa
     cuando aun así no cabe — medido: con el aviso de bloqueo de SPEI visible, o
     con el escalado de texto de Android por encima del 100%, el bloque fijo se
     pasa del tope y hoy el botón desaparece sin remedio. Con overflow-y:auto se
     alcanza deslizando en vez de perderse. */
  .pos-cart { overflow-y: auto; }

  .stats-grid { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }

}



/* ── MÓVILES Y TABLETS VERTICALES (≤768px): SIDEBAR COMO DRAWER ── */

@media (max-width: 768px) {

  /* El layout deja de reservar espacio para el sidebar */

  .app { display: block; height: 100vh; height: 100dvh; overflow: hidden; }



  /* Botón hamburguesa visible en la topbar */

  .mobile-menu-btn { display: flex; }



  /* Sidebar fuera de pantalla, entra al activar el menú */

  .sidebar {

    position: fixed;

    top: 0;

    left: 0;

    height: 100%;

    width: 270px;

    max-width: 82vw;

    z-index: 300;

    transform: translateX(-100%);

    transition: transform .25s ease;

    box-shadow: var(--shadow-lg);

  }

  /* En modo drawer sí necesita fondo sólido y borde */

  .sidebar { background: var(--bg-surface); border-right: 1px solid var(--border-glow); }

  /* El panel principal ocupa todo el ancho: sin esquina redondeada ni margen */

  .main { border-radius:0; border-left:none; border-top:none; margin-top:0; height:100vh; height:100dvh; }

  .topbar  { padding: 0 16px; }

  .content { padding: 4px 16px 22px; }

  /* La barra de acento vuelve al borde izquierdo dentro del drawer */

  .nav-item.active::before { right:auto; left:0; }


  .app.nav-open .sidebar { transform: translateX(0); }



  /* Fondo oscuro detrás del drawer */

  .nav-backdrop {

    display: block;

    position: fixed;

    inset: 0;

    background: rgba(16, 19, 40, .5);

    backdrop-filter: blur(2px);

    z-index: 200;

    opacity: 0;

    pointer-events: none;

    transition: opacity .25s ease;

  }

  .app.nav-open .nav-backdrop { opacity: 1; pointer-events: auto; }



  /* La zona principal ocupa todo el ancho */

  .main { height: 100vh; height: 100dvh; width: 100%; }



  .topbar { padding: 0 14px; height: 58px; gap: 10px; }

  .topbar-title { font-size: 15px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  .content { padding: 16px; }

  .card { padding: 16px; }

  .dash-split-grid { grid-template-columns: 1fr; }



  /* Modales a ancho casi completo */

  .modal { max-width: 95%; max-height: 88vh; }

  .modal-lg { max-width: 95%; }

  .modal-header { padding: 14px 16px; }

  .modal-body { padding: 16px; }

  .modal-footer { padding: 14px 16px; }



  /* Login */

  .login-card { width: 100%; max-width: 380px; padding: 28px 22px; }

  .login-brand { font-size: 24px; }

}



/* ── MÓVILES ESTÁNDAR (≤560px) ── */

@media (max-width: 560px) {

  .stats-grid { grid-template-columns: repeat(2, 1fr); gap: 12px; }

  /* En 2 columnas cada tarjeta queda muy angosta (~150px) -- el monto en
     $29px se acercaba al borde o se recortaba con montos grandes (millones).
     Se reduce tarjeta y tipografía para que el texto siempre quepa. */
  .stat-card { padding: 14px 14px; gap: 4px; }
  .stat-value { font-size: 21px; letter-spacing: -.4px; }
  .stat-icon { width: 36px; height: 36px; border-radius: 11px; font-size: 16px; margin-bottom: 6px; }
  .stat-label, .stat-meta { font-size: 11.5px; }



  /* "Nuevo cobro" pasa a botón de ícono para ahorrar espacio */

  .topbar-actions .btn-primary { font-size: 0 !important; gap: 0 !important; padding: 9px !important; }



  .card-header { flex-wrap: wrap; }

  /* Antes se quitaba el tope de altura aquí (max-height: none), así que en
     un carrito con varios conceptos el bloque de cobro (totales, métodos de
     pago y el botón "Cobrar") quedaba muy abajo, después de desplazarse por
     toda la lista de conceptos y de artículos del carrito. Con el bug de
     flexbox de cart-items ya corregido (min-height:0 arriba), un tope de
     altura vuelve a ser seguro: solo la lista de artículos hace scroll
     interno y el botón de cobrar queda siempre visible en pantalla.
     dvh además de vh por lo mismo que #root/.app arriba: 56vh de un
     viewport "grande" deja menos alto REAL del que parece en pantallas
     donde la barra del navegador sigue visible. La causa concreta de que
     "Cobrar" se recortara era otra (ver .payment-methods abajo), pero de
     todos modos vale usar el alto real aquí. */
  .pos-cart { max-height: 68vh; max-height: 74dvh; }

  /* Los métodos de pago en UNA sola fila con scroll horizontal (22-sep-2026).
     Esta era la causa real de que el botón "Cobrar" se recortara en celular
     vertical, y el tope de altura de arriba solo era el mensajero.

     .pay-method es flex:1 con min-width:60px. En un celular de 360px quedan
     332px útiles, y seis métodos piden 6*60 + 5*7 de gap = 395px. Con
     flex-wrap:wrap se envolvían a dos o tres filas de ~53px cada una, así que
     la parte fija de .pos-cart (header + totales + métodos + botón) crecía
     hasta pasarse de los 56vh. Como .pos-cart tiene overflow:hidden, lo que
     sobraba no se podía alcanzar con scroll: simplemente se recortaba, y lo
     último en la columna es justo el botón de cobrar.

     En una sola fila los métodos ocupan ~53px fijos en vez de 160, y el
     usuario los desliza de lado — que es el gesto natural en móvil. */
  .payment-methods {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .payment-methods::-webkit-scrollbar { display: none; }
  /* flex:0 0 auto en vez de flex:1 — con flex:1 y nowrap se comprimirían
     todos hasta volverse ilegibles en vez de desbordar y poder deslizarse. */
  .pay-method { flex: 0 0 auto; min-width: 74px; }

}



/* ── MÓVILES PEQUEÑOS (≤420px) ── */

@media (max-width: 420px) {

  .topbar-title { font-size: 14px; }

  .topbar-actions { gap: 6px; }

  .content { padding: 12px; }



  /* Antes 1fr (una tarjeta por fila) — a partir del 27-sep-2026, a pedido,
     se dejan siempre 2 columnas (como ya se ve en "Mi equipo") incluso en
     celulares chicos: una sola columna obligaba a scrollear mucho para ver
     4 tarjetas de resumen, y en 2 columnas el contenido (ícono, cifra,
     etiqueta corta) cabe perfectamente bien. */
  .stats-grid { grid-template-columns: repeat(2, 1fr); }



  .pos-products-grid { grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); }

  .product-emoji { font-size: 22px; }

  .product-name { font-size: 11.5px; }



  .ticket { max-width: 100%; }

}



/* ── PORTAL FAMILIA: header y pestañas responsive ── */

.pf-tabs {

  overflow-x: auto;

  -webkit-overflow-scrolling: touch;

  scrollbar-width: none;

}

.pf-tabs::-webkit-scrollbar { display: none; }

.pf-grid-2 { display: grid; grid-template-columns: 1fr 1fr; }
.pf-grid-1-2 { display: grid; grid-template-columns: 1fr 2fr; }

@media (max-width: 480px) {
  .pf-grid-2, .pf-grid-1-2 { grid-template-columns: 1fr; }
}



@media (max-width: 640px) {

  .pf-header {

    height: auto !important;

    min-height: 60px;

    flex-wrap: wrap !important;

    row-gap: 6px;

    padding: 10px 14px !important;

  }

  .pf-header-actions {

    gap: 8px !important;

  }

  /* El badge de la escuela y el subtítulo "Portal Familiar" no caben

     en una pantalla chica — se ocultan y solo queda lo esencial:

     logo, nombre corto, avatar y botón de salir. */

  .pf-header-badge { display: none !important; }

  .pf-header-user-sub { display: none !important; }

  .pf-header-user-name {

    max-width: 110px !important;

    overflow: hidden !important;

    text-overflow: ellipsis !important;

    white-space: nowrap !important;

  }

}



@media (max-width: 420px) {
  .pf-header-user-name { max-width: 78px !important; }
}

/* ══════════════════════════════════════════════════════
   COMPONENTES NUEVOS — tomados del video de referencia
   Son opcionales: se activan añadiendo la clase en la vista.
══════════════════════════════════════════════════════ */

/* ── Encabezado de sección ("Project statistics" en el video) ── */
.section-title {
  font-size:23px; font-weight:700; color:var(--ink);
  letter-spacing:-.7px; display:flex; align-items:center; gap:9px; margin-bottom:18px;
}

/* ── Grupo de píldoras / filtros segmentados (30 días · 90 días · …) ── */
.pill-group { display:inline-flex; align-items:center; gap:8px; flex-wrap:wrap; }
.pill {
  padding:9px 18px; border-radius:var(--radius-pill); font-size:12.5px; font-weight:600;
  border:1px solid var(--border-glow); background:var(--bg-surface); color:var(--ink-3);
  cursor:pointer; transition:all .15s; font-family:var(--font); white-space:nowrap; line-height:1;
}
.pill:hover { color:var(--ink); border-color:#dcdcea; }
.pill.active {
  background:var(--violet); border-color:var(--violet); color:#fff;
  /* box-shadow:var(--shadow-violet); */
}

/* ── Tarjeta destacada con degradado (la tarjeta "Bounce Rate") ── */
.stat-card.is-featured {
  background:var(--grad-brand); border:none; color:#fff;
  /* box-shadow:0 10px 28px rgba(108,90,240,.30); */
}
.stat-card.is-featured .stat-value { color:#fff; }
.stat-card.is-featured .stat-label { color:rgba(255,255,255,.88); }
.stat-card.is-featured .stat-meta  { color:rgba(255,255,255,.80); }
.stat-card.is-featured .stat-icon  { background:rgba(255,255,255,.22); color:#fff; }
.stat-card.is-featured:hover { border-color:transparent; }

/* ── Indicador de variación arriba a la derecha (▲ 1.2% / ▼ 2.1%) ── */
.stat-delta {
  position:absolute; top:20px; right:22px;
  font-size:12px; font-weight:700; display:inline-flex; align-items:center; gap:4px;
}
.stat-delta.up   { color:var(--green); }
.stat-delta.down { color:var(--red); }
.stat-delta::before { content:'▲'; font-size:8px; }
.stat-delta.down::before { content:'▼'; }
.stat-card.is-featured .stat-delta { color:#fff; }

/* ── Tintes para los azulejos de icono ── */
.stat-icon.tint-cyan    { background:var(--cyan-soft);    color:#1aa8c4; }
.stat-icon.tint-magenta { background:var(--magenta-soft); color:#b845d0; }
.stat-icon.tint-green   { background:var(--green-glow);   color:var(--green-dark); }
.stat-icon.tint-amber   { background:var(--amber-glow);   color:var(--amber); }
.stat-icon.tint-red     { background:var(--red-glow);     color:var(--red); }

/* ── Barras de desplazamiento discretas ── */
.content::-webkit-scrollbar-thumb,
.sidebar-nav::-webkit-scrollbar-thumb { background:#dcdde8; border-radius:20px; }
* { scrollbar-width:thin; scrollbar-color:#dcdde8 transparent; }

/* ── Encabezado de sección: apila filtros en pantallas angostas ── */
@media (max-width: 768px) {
  .section-title {
    flex-direction:column; align-items:flex-start; gap:12px;
    font-size:20px; margin-bottom:16px;
  }
  .section-title .pill-group { margin-left:0 !important; width:100%; }
  .pill { padding:8px 14px; font-size:12px; }
}


/* ══════════════════════════════════════════════════════
   AJUSTES ESPECÍFICOS DEL MODO OSCURO
   Detalles que no traducen automáticamente desde el modo claro.
══════════════════════════════════════════════════════ */

/* ── El drawer móvil debe usar el panel, no la tarjeta ── */
@media (max-width: 768px) {
  :root[data-theme="dark"] .sidebar { background:var(--bg-panel); }
}

/* ── El panel se separa del lienzo con un borde superior sutil ── */
:root[data-theme="dark"] .main {
  border-left-color:rgba(255,255,255,.07);
  border-top-color:rgba(255,255,255,.07);
}

/* ── Tarjeta destacada: en oscuro el degradado a full brillo deslumbra ── */
:root[data-theme="dark"] .stat-card.is-featured {
  /* background:linear-gradient(115deg, #2ea8c4 0%, #5946d8 55%, #a845c0 100%); */
  background:linear-gradient(#5946d8);
  /* box-shadow:0 10px 30px rgba(108,90,240,.35); */
}

/* ── Azulejos de icono: tintes translúcidos en vez de pasteles sólidos ── */
:root[data-theme="dark"] .stat-icon              { background:rgba(108,90,240,.18); color:var(--violet-light); }
:root[data-theme="dark"] .stat-icon.tint-cyan    { background:rgba(79,216,240,.15);  color:#6fdcf0; }
:root[data-theme="dark"] .stat-icon.tint-magenta { background:rgba(216,96,240,.15);  color:#e08cf5; }
:root[data-theme="dark"] .stat-icon.tint-green   { background:rgba(73,175,84,.16);   color:#5fc96c; }
:root[data-theme="dark"] .stat-icon.tint-amber   { background:rgba(217,119,6,.16);   color:#f0a63a; }
:root[data-theme="dark"] .stat-icon.tint-red     { background:rgba(229,72,77,.16);   color:#f2777a; }

/* ── Píldora inactiva: la superficie de tarjeta la haría desaparecer ── */
:root[data-theme="dark"] .pill { background:var(--bg-surface-2); }
:root[data-theme="dark"] .pill:hover { border-color:rgba(255,255,255,.20); }

/* ── Hover de fila: el violeta sólido es demasiado fuerte sobre oscuro ── */
:root[data-theme="dark"] tbody tr:hover td { background:rgba(108,90,240,.14); }

/* ── Cabecera de tabla sobre la superficie elevada ── */
:root[data-theme="dark"] th { background:rgba(255,255,255,.045); color:var(--ink-3); }

/* ── Botón secundario: necesita contraste contra la tarjeta ── */
:root[data-theme="dark"] .btn-secondary { background:var(--bg-surface-2); border-color:var(--border-glow); color:var(--ink-2); }
:root[data-theme="dark"] .btn-secondary:hover { background:rgba(255,255,255,.10); color:var(--ink); }

/* ── Campos de formulario ── */
:root[data-theme="dark"] .form-input,
:root[data-theme="dark"] .form-select,
:root[data-theme="dark"] .search-bar input { background:var(--bg-main); color:var(--ink); }

/* ── Barras de desplazamiento ── */
:root[data-theme="dark"] .content::-webkit-scrollbar-thumb,
:root[data-theme="dark"] .sidebar-nav::-webkit-scrollbar-thumb { background:rgba(255,255,255,.14); }
:root[data-theme="dark"] * { scrollbar-color:rgba(255,255,255,.14) transparent; }

/* ── Transición al cambiar de tema (se respeta reduced-motion) ── */
@media (prefers-reduced-motion: no-preference) {
  .app, .main, .sidebar, .card, .stat-card, .topbar, .modal {
    transition:background-color .22s ease, border-color .22s ease, color .22s ease;
  }
}

/* ── Portal familiar: pestañas desplazables sin barra visible ──
   .pf-tabs también trae la clase .pill-group (display:inline-flex), y
   ninguna regla de .pf-tabs fijaba su propio `display` -- así que ganaba
   inline-flex, que se ajusta a su CONTENIDO (shrink-to-fit) en vez de al
   ancho real disponible. Con flexWrap:'nowrap' puesto en línea y cada
   pestaña con flexShrink:0 (ver PortalFamilia.js), la barra terminaba
   renderizando a su ancho completo (~680px) sin importar la pantalla,
   saliéndose de .pf-body/.pf-root -- overflow-x:auto nunca llegaba a
   activarse porque la caja nunca quedó más angosta que su contenido. Ese
   sobrante se volvía scroll horizontal de TODO el portal (.pf-root), y al
   tocar una pestaña el foco del botón hacía que el navegador scrolleara ese
   contenedor para mostrarla -- dejando toda la pantalla corrida a la
   izquierda sin forma de regresarla (overflowX:'hidden' bloquea el scroll
   manual, no el programático). display:flex + width:100% fuerza a la barra
   a quedarse en el ancho real de su columna, para que el desbordamiento
   quede DENTRO de ella (donde overflow-x:auto sí puede scrollearlo). */
.pf-tabs { display:flex; width:100%; max-width:100%; min-width:0; overflow-x:auto; -webkit-overflow-scrolling:touch; scrollbar-width:none; padding-bottom:2px; }
.pf-tabs::-webkit-scrollbar { display:none; }
@media (max-width: 640px) {
  .pf-header-user-name, .pf-header-user-sub { display:none; }
  .pf-header .badge { display:none; }
}

/* ── Familias: fila de familia y fila de hijo ──
   Cada fila agrupa [avatar+nombre] aparte de [saldo/badge/botones] en dos
   bloques flex. En escritorio ambos caben en una sola línea. En móvil, sin
   esta regla, el bloque de acciones (que no se puede encoger) seguía
   compitiendo por la misma línea que el nombre, aplastándolo palabra por
   palabra -- aquí se fuerza a bajar a su propia línea de ancho completo, y
   ya con ese ancho real disponible, sus propios botones envuelven bien
   entre ellos en vez de recortarse contra el borde de la tarjeta. */
@media (max-width: 640px) {
  .fam-row-actions { flex-basis: 100%; justify-content: flex-end; margin-top: 4px; }
}

/* ── Portal familiar: las pestañas Facturas y Configuración quedan como
   hermanas de .pf-body en el árbol, así que se salían del contenedor.
   Esta regla les da el mismo ancho y sangrado sin reestructurar el JSX. ── */
.pf-root > div:not(.pf-header):not(.pf-body) {
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 clamp(18px,3vw,30px) clamp(18px,3vw,30px);
}

/* ══ Buscador de etiquetas (Familias · Alumnos) ══ */
.buscador-etiquetas {
  display:flex; align-items:center; flex-wrap:wrap; gap:7px;
  padding:8px 12px; min-height:42px; cursor:text;
  background:var(--bg-surface); border:1px solid var(--border-glow);
  border-radius:var(--radius); transition:border-color .15s, box-shadow .15s;
}
.buscador-etiquetas:focus-within { border-color:var(--violet); box-shadow:0 0 0 3px var(--accent-glow); }
.be-icono { color:var(--ink-4); display:inline-flex; align-items:center; flex-shrink:0; }
.be-chip {
  display:inline-flex; align-items:center; gap:6px;
  padding:4px 6px 4px 11px; border-radius:var(--radius-pill);
  background:var(--violet-soft); color:var(--violet-dark);
  font-size:12.5px; font-weight:600; max-width:230px;
}
.be-chip > span { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.be-chip-x {
  border:none; background:rgba(108,90,240,.16); color:inherit; cursor:pointer;
  width:17px; height:17px; border-radius:50%; flex-shrink:0; line-height:1;
  font-size:13px; display:flex; align-items:center; justify-content:center;
  font-family:var(--font); padding:0;
}
.be-chip-x:hover { background:var(--violet); color:#fff; }
.be-input {
  flex:1; min-width:130px; border:none; outline:none; background:transparent;
  font-family:var(--font); font-size:13.5px; color:var(--ink); padding:3px 0;
}
.be-input::placeholder { color:var(--ink-4); }
.be-limpiar {
  border:none; background:transparent; color:var(--ink-3); cursor:pointer;
  font-size:11.5px; font-weight:600; font-family:var(--font); padding:4px 6px;
  border-radius:var(--radius-sm); flex-shrink:0;
}
.be-limpiar:hover { background:var(--glass-light); color:var(--ink); }
.be-ayuda { font-size:11.5px; color:var(--ink-4); margin-top:6px; }
:root[data-theme="dark"] .be-chip { background:rgba(108,90,240,.22); color:#b6a9fa; }

/* ══ Menú de perfil (pie del sidebar) ══ */
.menu-perfil {
  position:absolute; bottom:calc(100% + 8px); left:0; right:0; z-index:220;
  background:var(--bg-surface); border:1px solid var(--border-glow);
  border-radius:var(--radius); box-shadow:var(--shadow-lg);
  padding:6px; animation:slideUp .15s ease;
}
/* Variante para cuando el menú cuelga de una BARRA SUPERIOR en vez del pie del
   sidebar (paneles de contador, provisión y tesorería). La regla de arriba abre
   hacia ARRIBA con bottom:calc(100% + 8px), que es lo correcto abajo del todo,
   pero en una barra superior lo empuja fuera del contenedor: como esos paneles
   ahora tienen su propio overflow para poder hacer scroll, lo que se salía por
   arriba quedaba recortado y tapado por el contenido.
   left:auto + right:0 lo alinea contra el borde derecho del disparador (que es
   donde vive el menú en esas barras), y el min-width evita que quede tan
   angosto como el botón: la regla base usa left:0;right:0 porque en el sidebar
   el disparador ocupa todo el ancho. */
.menu-perfil-abajo {
  top:calc(100% + 8px); bottom:auto; left:auto; right:0;
  min-width:220px; animation:fadeIn .15s ease;
}

.menu-perfil-item {
  width:100%; display:flex; align-items:center; gap:10px;
  padding:9px 11px; border:none; background:transparent; cursor:pointer;
  border-radius:var(--radius-sm); font-family:var(--font); font-size:13px;
  color:var(--ink-2); text-align:left; transition:background .12s, color .12s;
}
.menu-perfil-item:hover { background:var(--violet-soft); color:var(--violet); }
.menu-perfil-item.peligro:hover { background:var(--red-glow); color:var(--red); }
.menu-perfil-sep { height:1px; background:var(--border-glow); margin:5px 2px; }
.user-card { border-radius:var(--radius); transition:background .12s; }
.user-card:hover { background:var(--side-hover); }

/* ══ Ficha técnica (credencial de alumno / tutor) ══ */
.ficha-tecnica { overflow:hidden; }
.ficha-top {
  position:relative; padding:26px 24px 20px; text-align:center;
  background:var(--grad-brand); color:#fff;
}
.ficha-cerrar {
  position:absolute; top:12px; right:12px; width:28px; height:28px;
  border:none; border-radius:50%; cursor:pointer; color:#fff;
  background:rgba(255,255,255,.20); display:flex; align-items:center; justify-content:center;
}
.ficha-cerrar:hover { background:rgba(255,255,255,.34); }
.ficha-foto {
  width:96px; height:96px; margin:0 auto 13px; border-radius:18px;
  overflow:hidden; background:#fff; padding:3px;
  box-shadow:0 6px 20px rgba(0,0,0,.22);
}
.ficha-foto > * { border-radius:15px; }
.ficha-nombre { font-size:18px; font-weight:700; letter-spacing:-.4px; line-height:1.25; }
.ficha-sub { font-size:12.5px; opacity:.9; margin-top:3px; }
.ficha-escuela {
  font-size:11px; opacity:.78; margin-top:9px; text-transform:uppercase; letter-spacing:.5px;
}
.ficha-datos { display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:14px; }
.ficha-dato-k {
  font-size:10.5px; color:var(--ink-4); text-transform:uppercase;
  letter-spacing:.4px; margin-bottom:2px;
}
.ficha-dato-v { font-size:13px; color:var(--ink); word-break:break-word; }

/* ══ Paginador reutilizable ══ */
.paginador {
  display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  padding:14px 2px 4px; margin-top:4px;
}
.pag-info { font-size:12.5px; color:var(--ink-3); }
.pag-nav  { display:flex; align-items:center; gap:4px; margin-left:auto; }
.pag-btn {
  min-width:32px; height:32px; padding:0 9px; border-radius:var(--radius-sm);
  border:1px solid var(--border-glow); background:var(--bg-surface);
  color:var(--ink-2); font-size:12.5px; font-weight:600; cursor:pointer;
  font-family:var(--font); transition:all .12s;
}
.pag-btn:hover:not(:disabled) { border-color:var(--violet); color:var(--violet); }
.pag-btn:disabled { opacity:.4; cursor:not-allowed; }
.pag-btn.activo {
  background:var(--violet); border-color:var(--violet); color:#fff;
}
.pag-elipsis { color:var(--ink-4); padding:0 2px; font-size:12.5px; }
.pag-tam {
  height:32px; padding:0 9px; border-radius:var(--radius-sm);
  border:1px solid var(--border-glow); background:var(--bg-surface);
  color:var(--ink-3); font-size:12px; font-family:var(--font); cursor:pointer;
}
@media (max-width: 640px) {
  .pag-nav { margin-left:0; width:100%; justify-content:center; }
  .pag-tam { margin-left:auto; }
}

/* ══ Animación de espera (esqueleto de carga) ══ */
.sk-envoltura { animation: skEntra .18s ease both; }
@keyframes skEntra { from { opacity: 0 } to { opacity: 1 } }

.sk-bloque {
  position: relative;
  overflow: hidden;
  background: var(--glass-light);
}
/* Barrido de luz: una banda diagonal que recorre el bloque. Da sensación de
   avance sin el mareo de un círculo girando indefinidamente. */
.sk-bloque::after {
  content: '';
  position: absolute; inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg,
    transparent 0%,
    rgba(255,255,255,.55) 50%,
    transparent 100%);
  animation: skBarrido 1.4s ease-in-out infinite;
}
:root[data-theme="dark"] .sk-bloque::after {
  background: linear-gradient(90deg,
    transparent 0%,
    rgba(255,255,255,.09) 50%,
    transparent 100%);
}
@keyframes skBarrido { 100% { transform: translateX(100%) } }

.sk-grid {
  display: grid; gap: 16px; margin-bottom: 20px;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.sk-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-glow);
  border-radius: var(--radius-lg);
  padding: 18px;
}
.sk-panel {
  background: var(--bg-surface);
  border: 1px solid var(--border-glow);
  border-radius: var(--radius-lg);
  padding: 18px 20px 8px;
}
.sk-panel-head {
  display: flex; align-items: center; justify-content: space-between;
  padding-bottom: 16px; margin-bottom: 6px;
  border-bottom: 1px solid var(--border-glow);
}
.sk-fila {
  display: flex; align-items: center; gap: 18px;
  padding: 15px 0; border-bottom: 1px solid var(--border-glow);
}
.sk-fila:last-child { border-bottom: none; }

/* Variante compacta: tres puntos que laten por turnos */
.sk-simple {
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 14px; padding: 46px 20px;
}
.sk-puntos { display: flex; gap: 7px; }
.sk-puntos span {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--violet);
  animation: skLatido 1.1s ease-in-out infinite;
}
.sk-puntos span:nth-child(2) { animation-delay: .16s; }
.sk-puntos span:nth-child(3) { animation-delay: .32s; }
@keyframes skLatido {
  0%, 70%, 100% { transform: scale(.7); opacity: .35 }
  35%           { transform: scale(1);  opacity: 1 }
}
.sk-texto { font-size: 13px; color: var(--ink-3); }

/* Si el sistema pide menos movimiento, se quitan las animaciones y quedan
   los bloques estáticos: sigue comunicando que algo está cargando. */
@media (prefers-reduced-motion: reduce) {
  .sk-bloque::after { animation: none; opacity: 0; }
  .sk-puntos span   { animation: none; opacity: .6; }
  .sk-envoltura     { animation: none; }
}

/* ── Transición al cambiar de sección (23-sep-2026) ──────────────────────
   Un fundido corto con un desplazamiento mínimo hacia arriba. Se dispara
   sola en cada cambio de vista porque assets/js/app.js le pone key={view}
   al contenedor .content, y eso hace que React lo remonte.

   Nació para la guía de primer uso, que va llevando al usuario de sección en
   sección: sin transición, cada salto se siente como un corte seco y cuesta
   seguir dónde quedaste. Aplica a toda la navegación, no solo a la guía.

   Los 14px de desplazamiento y los 260ms son deliberadamente discretos: lo
   que se busca es hilar una sección con la siguiente, no llamar la atención
   sobre la animación misma. */
@keyframes content-entra {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}
/* fill-mode "backwards" y NO "both": con "both" el elemento conserva el estado
   final del keyframe, y ese estado es transform:translateY(0). Un transform
   distinto de "none" -aunque sea el identidad- convierte a .content en el
   bloque contenedor de todos sus descendientes position:fixed. Resultado: cada
   .modal-backdrop del sistema dejaba de estar fijo a la ventana y se colocaba
   respecto a .content, que esta desplazado; el modal aparecia arriba y fuera
   de alcance. Con "backwards" se conserva solo el estado inicial ANTES de
   arrancar (que es lo que evita el parpadeo) y al terminar el transform vuelve
   a "none". El resultado visual es identico: la animacion ya terminaba en
   translateY(0), que es exactamente donde queda el elemento sin transform. */
.content-anim { animation: content-entra .26s cubic-bezier(.22,.61,.36,1) backwards; }

/* Quien pidió menos movimiento recibe solo el fundido — el desplazamiento es
   justo lo que molesta con sensibilidad vestibular. */
@media (prefers-reduced-motion: reduce) {
  @keyframes content-entra {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
  .content-anim { animation-duration: .14s; }
}

/* ── Ilustración de la guía de primer uso (23-sep-2026) ──────────────────
   Cada paso de la guía trae un boceto de cómo se ve esa sección CON datos.
   La animación se dispara sola en cada paso porque el componente le pone
   key={actual.id} al contenedor, y eso hace que React lo remonte.

   Existe porque la guía corre en el PRIMER ingreso, cuando el colegio no
   tiene ni un alumno: llevar al usuario a la sección real le enseñaría una
   pantalla vacía. El dibujo muestra la forma de la pantalla funcionando. */
@keyframes guia-ilu-entra {
  from { opacity: 0; transform: translateY(6px) scale(.985); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.guia-ilustracion { animation: guia-ilu-entra .3s cubic-bezier(.22,.61,.36,1) both; }

@media (prefers-reduced-motion: reduce) {
  @keyframes guia-ilu-entra { from { opacity: 0; } to { opacity: 1; } }
  .guia-ilustracion { animation-duration: .15s; }
}

/* ══════════════════════════════════════════════════════════════════════
   RESPONSIVE — RONDA DE AJUSTES MÓVIL (27-sep-2026)
   Ajustes de estilo pedidos por Leo tras revisar Admin en móvil: topbar,
   saludo del dashboard, gráficas de dona, tarjetas de "Conceptos" en Caja,
   tarjetas de familia y grid de roles en Mi Equipo. Todo va detrás de
   media queries — en escritorio no cambia nada.
   ══════════════════════════════════════════════════════════════════════ */

@media (max-width: 768px) {
  /* Aviso de "cobros pendientes" junto al nombre de la sección: en escritorio
     cabe bien, pero en la topbar angosta de un celular empuja al botón de
     "Nuevo cobro" y al de tema fuera de vista o los deja pegados. Se oculta
     solo aquí — el aviso real (con el detalle de cada cobro) ya vive también
     dentro del dashboard, así que no se pierde información, solo se quita el
     duplicado que estorba en la barra superior. */
  .topbar-pendientes { display: none !important; }
}

@media (max-width: 600px) {
  /* Subtítulo del saludo del dashboard ("Instituto X · Plan Y · sábado ...").
     En escritorio fluye bien en una sola línea; en celular el badge del plan
     y la fecha quedaban pegados sin separación clara. Se apila en líneas
     cortas, cada dato en su propio renglón. */
  .dash-greeting-meta {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
  }
  .dash-greeting-meta > * { margin-right: 0 !important; }
  .dash-escuela-line { display: flex; align-items: center; flex-wrap: wrap; gap: 4px; }

  /* Dona de "Por método de pago" + su leyenda: en escritorio van una junto a
     la otra a la izquierda; en celular se ven mejor centradas y, si no caben
     lado a lado, apiladas una encima de la otra. */
  .donut-row {
    justify-content: center;
    text-align: center;
  }
  .donut-row > svg,
  .donut-row > div:first-child { margin: 0 auto; }

  /* Aviso de "Cobros pendientes" (con el ícono de warning) dentro de la
     tarjeta de método de pago: se centra en vez de ir pegado a la izquierda. */
  .dash-pendientes-titulo { justify-content: center; }
  .dash-pendientes-item { text-align: center; }

  /* Tarjeta "Tu plan" (plan + barra de alumnos + planteles): en escritorio
     van los 3 bloques en una fila; en celular, al envolverse con flex-wrap,
     quedaban con anchos dispares y se sentían "flotando" sin relación entre
     sí. Se apilan, cada uno ocupando el ancho completo, con una línea sutil
     separando alumnos de planteles para que se lean como dos datos
     distintos y no como uno solo cortado. */
  .dash-plan-card { flex-direction: column; align-items: stretch !important; gap: 12px !important; }
  .dash-plan-alumnos { width: 100%; min-width: 0 !important; }
  .dash-plan-alumnos > div { max-width: none !important; }
  .dash-plan-planteles {
    width: 100%;
    padding-top: 10px;
    border-top: 1px dashed var(--glass-light);
  }
}

/* ── Tablas sin scroll horizontal en móvil ──────────────────────────────
   Técnica de "tabla → tarjetas": se oculta el <thead> y cada <td> se
   convierte en una fila propia con su etiqueta a la izquierda (tomada del
   atributo data-label) y el valor a la derecha. Requiere que el <td>
   correspondiente traiga data-label="Nombre de columna" — se está agregando
   vista por vista; donde todavía no está, la tabla simplemente sigue
   mostrando scroll horizontal como hasta ahora (no se rompe nada). */
@media (max-width: 700px) {
  .table-wrap.cards-mobile { overflow-x: visible; }
  .table-wrap.cards-mobile table,
  .table-wrap.cards-mobile thead,
  .table-wrap.cards-mobile tbody,
  .table-wrap.cards-mobile th,
  .table-wrap.cards-mobile td,
  .table-wrap.cards-mobile tr { display: block; width: 100%; }

  .table-wrap.cards-mobile thead { display: none; }

  .table-wrap.cards-mobile tbody tr {
    border: 1px solid var(--border-glow);
    border-radius: var(--radius-md, 10px);
    margin-bottom: 10px;
    padding: 10px 12px;
    background: var(--bg-surface);
  }

  .table-wrap.cards-mobile tbody tr:last-child { margin-bottom: 0; }

  .table-wrap.cards-mobile td {
    padding: 8px 0 !important;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    text-align: right;
    padding: 8px 0;
    border: none;
    border-bottom: 1px solid var(--glass-light);
    white-space: normal !important;
    max-width: none !important;
  }

  .table-wrap.cards-mobile tr td:last-child { border-bottom: none; }

  .table-wrap.cards-mobile td[data-label]::before {
    content: attr(data-label);
    font-size: 10.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .4px;
    color: var(--ink-3);
    text-align: left;
    flex-shrink: 0;
  }

  /* La primera celda de cada tarjeta (normalmente el nombre/folio — lo que
     identifica esa fila) se destaca un poco: sirve de "título" de la
     tarjeta y ayuda a ubicarse antes de leer el resto de los datos. */
  .table-wrap.cards-mobile tr td:first-child {
    padding-top: 2px;
    padding-bottom: 10px;
    margin-bottom: 2px;
    border-bottom: 1px dashed var(--glass-light);
  }
  .table-wrap.cards-mobile tr td:first-child::before {
    color: var(--ink-2);
  }

  /* Celdas sin data-label (ej. una sola con colSpan para "sin resultados")
     no muestran etiqueta ni el layout de dos columnas. */
  .table-wrap.cards-mobile td:not([data-label]) {
    display: block;
    text-align: left;
  }

  /* Botón de acción único dentro de una tarjeta (ej. "Generar CFDI",
     "Emitir complemento" en Facturación): ocupa todo el ancho de la tarjeta
     en vez de quedar pegado a un lado. Solo aplica cuando el botón es hijo
     directo de la celda — los grupos de varios botones (dentro de un div)
     no se tocan. */
  .table-wrap.cards-mobile td:not([data-label]) > .btn,
  .table-wrap.cards-mobile td[data-label="Acción"] > .btn {
    width: 100%;
    justify-content: center;
    margin-top: 4px;
  }
  .table-wrap.cards-mobile td[data-label="Acción"] { display: block; }
  .table-wrap.cards-mobile td[data-label="Acción"]::before { display: none; }
}

@media (max-width: 640px) {
  /* Filtros de Gastos (proveedor, forma de pago, fecha inicio/fin): en vez
     de amontonarse en 4 renglones sueltos, se acomodan en pares de 2
     columnas (proveedor+forma de pago arriba, fechas abajo, una a cada
     lado) y el texto seleccionado va centrado en los selects. */
  .gastos-filtros { display: grid !important; grid-template-columns: 1fr 1fr; gap: 10px !important; }
  .gastos-filtros select,
  .gastos-filtros input { width: 100% !important; text-align-last: center; text-align: center; }

  /* Filtros de Historial de cobros: el select tenía marginLeft:auto para ir
     pegado a la derecha en escritorio; en celular va en su propia fila. */
  /* Recordatorios: el select "Ordenar" baja a su propia fila, ancho completo.
     Reportes: el botón Exportar baja debajo del selector de periodo. */
  .recordatorios-controles { flex-direction: column; align-items: stretch !important; }
  .recordatorios-controles select { width: 100% !important; margin-left: 0 !important; }
  .reportes-periodo-bar { flex-direction: column; align-items: stretch !important; }
  .reportes-periodo-bar > .btn { width: 100%; justify-content: center; }
  .usuarios-filtros { flex-direction: column; align-items: stretch !important; }
  .usuarios-filtros > .chip-row-fill { width: 100% !important; flex: 0 0 auto !important; }
  .usuarios-filtros .search-bar { margin-left: 0 !important; min-width: 0 !important; width: 100%; }
  .cobros-filtros { flex-direction: column; }
  .cobros-filtros select { width: 100% !important; margin-left: 0 !important; }

  /* Barra de "Conceptos" en Caja. Causa raíz de que el buscador no ocupara todo el ancho: .pos-header es
     una fila flex y dejaba el bloque título+buscador y las categorías LADO A
     LADO. En celular se apilan: título, buscador (ancho completo), categorías. */
  .pos-header { flex-direction: column; align-items: stretch !important; gap: 8px; }
  .pos-header > * { width: 100%; min-width: 0; }
  .pos-header-top {
    flex-direction: column;
    align-items: stretch !important;
  }
  .pos-header-top .search-bar { width: 100%; }

  /* Chips de categoría ("Todos", "Por categoría", "Descuentos"...): en vez
     de envolverse en un bloque de varias filas que empuja el contenido hacia
     abajo, se deslizan en una sola fila horizontal — mismo patrón que ya se
     usa abajo para los métodos de pago del carrito. */
  .pos-cat-tabs {
    flex-wrap: nowrap !important;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-bottom: 2px;
  }
  .pos-cat-tabs::-webkit-scrollbar { display: none; }
  .pos-cat-tabs > button { flex: 0 0 auto; }

  /* Fila de acciones de cada tarjeta de familia (editar, añadir estudiante,
     ficha, vincular): en escritorio caben junto al badge de saldo; en
     celular se veían amontonadas. Bajan a su propia fila, en dos columnas. */
  .fam-row-actions { flex-direction: column; align-items: stretch !important; }
  .fam-actions-buttons {
    width: 100%;
    display: grid !important;
    grid-template-columns: repeat(2, 1fr);
    gap: 6px;
  }
  .fam-actions-buttons > button { width: 100%; }

  /* Flechita de expandir/contraer la tarjeta de familia: al apilar todo en
     columna quedaba pegada abajo a la izquierda. Se manda al lado derecho,
     que es donde se espera un indicador de "abrir/cerrar". */
  .fam-chevron { display: flex; justify-content: flex-end; width: 100%; }

  /* Saldo/"Al corriente" de cada hijo dentro de la ficha de familia: se
     centra en vez de quedar pegado a la izquierda al apilarse. */
  .fam-hijo-saldo { display: flex; justify-content: center; width: 100%; text-align: center; }
}

@media (max-width: 480px) {
  /* Tarjetas de rol en "Mi equipo" (Admin/Cajero/Familia...): con una sola
     columna cada tarjeta ocupa todo el ancho y obliga a bajar mucho para
     ver las demás. Se mantienen en 2 columnas también en celulares chicos —
     el contenido (ícono, número, etiqueta) cabe bien así. */
  .roles-grid { grid-template-columns: repeat(2, 1fr) !important; }
}

/* ══════════════════════════════════════════════════════════════════════
   SUPER ADMIN — Colegios / Inquilinos (27-sep-2026)
   La rejilla de colegios tenía 4 columnas fijas en línea (sin clase), así que
   en tablet/celular las tarjetas quedaban aplastadas. Se baja a 2 columnas y
   luego a 1. Los !important son necesarios porque el valor viene inline.
   ══════════════════════════════════════════════════════════════════════ */
@media (max-width: 1100px) {
  .esc-grid { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
}
@media (max-width: 640px) {
  .esc-grid { grid-template-columns: minmax(0, 1fr) !important; gap: 12px !important; }
  /* Buscador a todo el ancho; los dos selects (plan / estado) lado a lado. */
  .esc-filtros { display: grid !important; grid-template-columns: 1fr 1fr; gap: 8px !important; }
  .esc-filtros input { grid-column: 1 / -1; max-width: none !important; width: 100%; }
  .esc-filtros select { max-width: none !important; width: 100%; text-align-last: center; }
  /* Paginación + "Alta escuela": el botón a ancho completo debajo. */
  .esc-acciones { width: 100%; }
  .esc-acciones > .btn { flex: 1 1 100%; justify-content: center; }
}

@media (max-width: 640px) {
  /* Super Admin — filtros: selects/botones de Métricas Globales, Logs y
     Comisiones ocupan el ancho completo en vez de quedar pegados a un lado. */
  .sr-filtros { display: grid !important; grid-template-columns: 1fr; gap: 8px !important; width: 100%; }
  .sr-filtros select { width: 100% !important; text-align-last: center; }
  .sr-filtros > .btn { width: 100%; justify-content: center; }
  .logs-filtro { width: 100%; text-align-last: center; }
  .card-header:has(> .logs-filtro) { flex-direction: column; align-items: stretch; gap: 10px; }
  .com-busqueda { flex-direction: column; align-items: stretch !important; }
  .com-busqueda > .search-bar { max-width: none !important; width: 100%; }
  .com-busqueda > .btn { width: 100%; justify-content: center; }
}

/* ══════════════════════════════════════════════════════════════════════
   PORTAL FAMILIA — ajustes móviles (27-sep-2026)
   1) No dejaba llegar al final de ninguna sección: .pf-root trae
      height:100vh EN LÍNEA, y en celular 100vh es más alto que la pantalla
      visible (la barra del navegador tapa la parte de abajo), así que el
      último tramo quedaba fuera de alcance. #root y .app ya usan 100dvh;
      aquí faltaba. El !important es necesario porque el valor viene inline.
   2) Cabecera: en celular las acciones (tema, avatar, guía, salir) caían a un
      segundo renglón pegadas a la izquierda. Ahora se quedan en la MISMA fila,
      a la derecha, como botones redondos parejos.
   ══════════════════════════════════════════════════════════════════════ */
.pf-root { height: 100vh !important; height: 100dvh !important; }
.pf-body { padding-bottom: calc(clamp(18px,3vw,30px) + env(safe-area-inset-bottom, 0px) + 24px) !important; }

@media (max-width: 640px) {
  .pf-header { flex-wrap: nowrap !important; padding: 8px 12px !important; gap: 8px !important; }
  .pf-header > div:first-child { flex: 1 1 auto; min-width: 0; }
  .pf-header-actions { margin-left: auto; flex: 0 0 auto; gap: 6px !important; }
  .pf-header-user-name { display: none !important; }
  .pf-header-actions .theme-toggle,
  .pf-header-actions .btn-ghost {
    width: 36px; height: 36px; padding: 0 !important; flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid var(--border-glow) !important; border-radius: 50%;
    background: var(--bg-surface-2, var(--glass-light));
  }
}

/* Portal Familia: el logo ya dice "Paga la Escuela" y "Portal familiar" se
   sobreentiende, así que en celular el texto repetido junto al logo se quita
   para dar espacio a los botones de la derecha (tema, avatar, guía, salir). */
@media (max-width: 640px) {
  .pf-brand-text { display: none !important; }
}
