  /* ---------------------------------------------------------------------------
     TOKENS. Todo valor que se repita en más de un lugar vive acá y solo acá.
     La regla al tocar el CSS: no escribir un px suelto de tipografía, radio o
     espaciado — elegir el token más cercano. Si ninguno sirve, es señal de que
     falta discutir el token, no de que ese componente necesita su propio valor.
     --------------------------------------------------------------------------- */
  :root {
    /* Color */
    --bg: #171B22;
    --bg-panel: #1E232C;
    --bg-panel-2: #242A35;
    --border: #333A46;
    --text: #EDEAE3;
    --text-muted: #8E96A3;
    --brand-red: #D42E31;
    --turno-color: #E8B23D;
    /* Rojo claro, legible sobre fondo oscuro: para texto y bordes de peligro. */
    --danger-text: #F87171;
    /* Rojo apagado, para superficies y líneas (ausente, baja, hover destructivo).
       Estaba escrito a mano como #C0574A en 6 reglas distintas. */
    --danger-line: #C0574A;
    /* Texto sobre un fondo de acento (amarillo o rojo pleno). El oscuro es el
       mismo --bg; iba hardcodeado como #171B22 en 6 lugares. */
    --on-accent: #171B22;
    --on-brand: #fff;
    /* Halo del campo enfocado. Es --turno-color con alfa; va como color literal
       porque CSS no permite derivar un rgba() de una variable sin color-mix. */
    --focus-ring: rgba(232, 178, 61, 0.28);

    /* Tipografía. Cuatro tamaños de contenido, uno de control y dos de display:
       los saltos son de 2px para arriba, así la jerarquía se ve de verdad en vez
       de depender de diferencias de 1px que nadie percibe. */
    --fs-xs: 11px;   /* badges, hints, metadatos, encabezados de tabla */
    --fs-sm: 13px;   /* texto secundario, etiquetas, botones */
    --fs-md: 15px;   /* texto base, nombres de alumno */
    --fs-lg: 22px;   /* títulos de modal */
    --fs-xl: 32px;   /* h1 y números grandes de estadísticas */
    /* Los controles de formulario van SIEMPRE a 16px: por debajo de eso, iOS hace
       zoom automático al enfocar el campo y descoloca la página entera. Es la
       única razón por la que este valor existe aparte de la escala. */
    --fs-control: 16px;

    /* Espaciado: múltiplos de 4. */
    --sp-1: 4px;
    --sp-2: 8px;
    --sp-3: 12px;
    --sp-4: 16px;
    --sp-5: 24px;
    --sp-6: 32px;

    /* Radios: chico para controles, mediano para paneles y tarjetas, píldora
       para badges. Nada más. */
    --r-sm: 6px;
    --r-md: 12px;
    --r-pill: 999px;

    /* Alto mínimo de un control que se toca con el dedo. */
    --control-h: 44px;

    /* Padding de botón. Tres formas, no quince: normal, compacto, y "la altura la
       pone la fila" (para los que se estiran dentro de un flex o tienen height
       propio, donde agregar padding vertical los desalinearía de su vecino). */
    --pad-btn: 10px 16px;
    --pad-btn-sm: 7px 12px;
    --pad-btn-x: 0 16px;
    /* Padding de campo de texto: mismo alto visual que --pad-btn, con algo menos
       de aire lateral porque el texto arranca pegado al borde izquierdo. */
    --pad-field: 10px 12px;
  }
  * { box-sizing: border-box; }
  /* Reserva SIEMPRE el lugar de la barra de scroll vertical, aparezca o no.
     Sin esto, el ancho útil de la ventana cambia 15px entre una sección que necesita
     scroll y otra que no, y como .wrap está centrado con margin:0 auto, todo el
     contenido se corría 8px al cambiar de sede. Se nota sobre todo en pantallas altas
     (4K), donde la mayoría de las secciones entran enteras y solo la más larga no.
     En mobile no cuesta nada: ahí las barras son flotantes y no ocupan lugar. */
  html { scrollbar-gutter: stable; }
  body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    /* Única declaración de la tipografía de texto en todo el proyecto: de acá la
       heredan el resto de los elementos y, vía el reset de abajo, los controles. */
    font-family: 'Public Sans', sans-serif;
    -webkit-font-smoothing: antialiased;
    border-top: 5px solid var(--brand-red);
  }
  /* Los controles de formulario no heredan la tipografía del body por defecto: usan
     la del sistema salvo que se les diga lo contrario. Declararlo una vez acá es lo
     que permite que ningún componente vuelva a repetir font-family (antes estaba
     escrito a mano en 25 reglas). Solo la familia: el tamaño y el peso los sigue
     eligiendo cada componente. */
  button, input, select, textarea, optgroup {
    font-family: inherit;
  }

  /* ---------------------------------------------------------------------------
     Un solo lugar decide cómo se ve un campo en toda la app. Cada componente que
     lo usa agrega únicamente lo que le es propio (si crece, si lleva margen, si
     puede achicarse), nunca cómo se ve. Antes este mismo bloque estaba copiado a
     mano en 8 reglas, y las copias habían derivado entre sí: cuatro tamaños de
     letra distintos (13/14/15/16) y cuatro paddings distintos para campos que
     el usuario percibe como el mismo elemento.
     La lista de selectores es larga a propósito: no hay build ni clases nuevas
     que agregar al HTML, así que el "primitivo" se arma agrupando los selectores
     que ya existen. Al sumar un campo nuevo, se agrega acá su selector.
     --------------------------------------------------------------------------- */
  .search-row input,
  .add-row input[type="text"],
  .field input,
  .float-field input,
  .field-grid input,
  .field-grid textarea,
  .field-grid select,
  .form-field input,
  .form-field textarea,
  .move-form select,
  .rename-input,
  .request-match-select {
    width: 100%;
    background: var(--bg-panel-2);
    border: 1px solid var(--border);
    color: var(--text);
    /* Ver --fs-control: por debajo de 16px, iOS hace zoom al enfocar. */
    font-size: var(--fs-control);
    padding: var(--pad-field);
    border-radius: var(--r-sm);
  }
  .search-row input::placeholder,
  .add-row input[type="text"]::placeholder,
  .field input::placeholder,
  .field-grid input::placeholder,
  .field-grid textarea::placeholder,
  .form-field input::placeholder,
  .form-field textarea::placeholder,
  .rename-input::placeholder {
    color: var(--text-muted);
  }
  /* Antes solo el nombre en el panel de Staff marcaba el foco; el resto de los
     campos no daba ninguna señal de cuál estaba activo, que es justo lo que
     necesita quien navega con teclado o con el teclado del celular abierto. */
  .search-row input:focus,
  .add-row input[type="text"]:focus,
  .field input:focus,
  .float-field input:focus,
  .field-grid input:focus,
  .field-grid textarea:focus,
  .field-grid select:focus,
  .form-field input:focus,
  .form-field textarea:focus,
  .move-form select:focus,
  .rename-input:focus,
  .pases-busca:focus,
  .request-match-select:focus {
    outline: none;
    border-color: var(--turno-color);
    /* El halo iba como sombra de 2px SIN desenfoque. Una sombra con blur 0 no se lee
       como resplandor: se lee como un SEGUNDO borde pegado al primero, y encima con el
       radio 2px más grande, así que cada campo enfocado mostraba dos anillos
       concéntricos. Con desenfoque vuelve a ser lo que quería ser: una iluminación
       alrededor del único borde, que es el del campo. */
    box-shadow: 0 0 7px 1px var(--focus-ring);
  }
  /* ---------------------------------------------------------------------------
     PRIMITIVO: superficie interactiva.
     Un velo del propio color de contenido que aparece al pasar y se refuerza al
     presionar. El hover no existe en un celular, así que el :active es lo que de
     verdad confirma que el toque llegó. Va en ::before porque .cal-day ya usa
     ::after para el punto de "sin clase".
     Al sumar un botón nuevo, se agrega su selector acá.
     --------------------------------------------------------------------------- */
  .login-btn, .modal-save-btn, .add-row button, .request-apply-btn,
  .grade-add-btn, .confirm-day-btn, .pases-open-btn, .pase-fila, .day-lock-btn, .move-btn, .logout-btn,
  .today-btn, .request-dismiss-btn, .linked-section-unlink-btn, .role-option,
  .icon-btn, .requests-toggle, .date-nav-btn, .modal-close, .date-display,
  .belt-chip, .suggestion-row, .text-btn, .history-toggle, .bajas-toggle,
  .password-toggle, .tab, .cal-day:not(:disabled),
  .student-list:not(.is-locked) .student-row:not(.is-baja) {
    position: relative;
  }
  .login-btn::before, .modal-save-btn::before, .add-row button::before,
  .request-apply-btn::before, .grade-add-btn::before, .confirm-day-btn::before,
  .pases-open-btn::before, .pase-fila::before,
  .day-lock-btn::before, .move-btn::before, .logout-btn::before, .today-btn::before,
  .request-dismiss-btn::before, .linked-section-unlink-btn::before, .role-option::before,
  .icon-btn::before, .requests-toggle::before, .date-nav-btn::before, .modal-close::before,
  .date-display::before, .belt-chip::before, .suggestion-row::before, .text-btn::before,
  .history-toggle::before, .bajas-toggle::before, .password-toggle::before, .tab::before,
  .cal-day:not(:disabled)::before,
  .student-list:not(.is-locked) .student-row:not(.is-baja)::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: currentColor;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s ease;
  }
  .login-btn:hover::before, .modal-save-btn:hover::before, .add-row button:hover::before,
  .request-apply-btn:hover::before, .grade-add-btn:hover::before, .confirm-day-btn:hover::before,
  .pases-open-btn:hover::before, .pase-fila:hover::before,
  .day-lock-btn:hover::before, .move-btn:hover::before, .logout-btn:hover::before,
  .today-btn:hover::before, .request-dismiss-btn:hover::before,
  .linked-section-unlink-btn:hover::before, .role-option:hover::before, .icon-btn:hover::before,
  .requests-toggle:hover::before, .date-nav-btn:hover::before, .modal-close:hover::before,
  .date-display:hover::before, .belt-chip:hover::before, .suggestion-row:hover::before,
  .text-btn:hover::before, .history-toggle:hover::before, .bajas-toggle:hover::before,
  .password-toggle:hover::before, .tab:hover::before, .cal-day:not(:disabled):hover::before,
  .student-list:not(.is-locked) .student-row:not(.is-baja):hover::before {
    opacity: 0.08;
  }
  .login-btn:active::before, .modal-save-btn:active::before, .add-row button:active::before,
  .request-apply-btn:active::before, .grade-add-btn:active::before, .confirm-day-btn:active::before,
  .pases-open-btn:active::before, .pase-fila:active::before,
  .day-lock-btn:active::before, .move-btn:active::before, .logout-btn:active::before,
  .today-btn:active::before, .request-dismiss-btn:active::before,
  .linked-section-unlink-btn:active::before, .role-option:active::before, .icon-btn:active::before,
  .requests-toggle:active::before, .date-nav-btn:active::before, .modal-close:active::before,
  .date-display:active::before, .belt-chip:active::before, .suggestion-row:active::before,
  .text-btn:active::before, .history-toggle:active::before, .bajas-toggle:active::before,
  .password-toggle:active::before, .tab:active::before, .cal-day:not(:disabled):active::before,
  .student-list:not(.is-locked) .student-row:not(.is-baja):active::before {
    opacity: 0.18;
  }
  /* Un solo anillo de foco para todo lo que se puede tocar, en vez del que trae
     cada navegador por su cuenta. */
  .login-btn:focus-visible, .modal-save-btn:focus-visible, .add-row button:focus-visible,
  .request-apply-btn:focus-visible, .grade-add-btn:focus-visible, .confirm-day-btn:focus-visible,
  .pases-open-btn:focus-visible, .pase-fila:focus-visible,
  .day-lock-btn:focus-visible, .move-btn:focus-visible, .logout-btn:focus-visible,
  .today-btn:focus-visible, .request-dismiss-btn:focus-visible,
  .linked-section-unlink-btn:focus-visible, .role-option:focus-visible, .icon-btn:focus-visible,
  .requests-toggle:focus-visible, .date-nav-btn:focus-visible, .modal-close:focus-visible,
  .date-display:focus-visible, .belt-chip:focus-visible, .suggestion-row:focus-visible,
  .text-btn:focus-visible, .history-toggle:focus-visible, .bajas-toggle:focus-visible,
  .password-toggle:focus-visible, .tab:focus-visible, .cal-day:focus-visible,
  .footer-links a:focus-visible, .page-back-link:focus-visible {
    outline: 2px solid var(--turno-color);
    outline-offset: 2px;
  }
  @media (prefers-reduced-motion: reduce) {
    .login-btn::before, .modal-save-btn::before, .icon-btn::before, .tab::before,
    .cal-day:not(:disabled)::before, .student-row::before {
      transition: none;
    }
  }

  .brand-tag {
    font-family: 'Barlow Semi Condensed', sans-serif;
    font-weight: 700;
    font-size: var(--fs-md);
    color: var(--brand-red);
    margin: 0 0 4px;
  }
  .wrap {
    max-width: 640px;
    margin: 0 auto;
    padding: 28px 18px 60px;
  }
  h1 {
    font-family: 'Barlow Semi Condensed', sans-serif;
    font-weight: 700;
    font-size: var(--fs-xl);
    letter-spacing: 0.2px;
    margin: 0 0 4px;
  }
  .subtitle {
    color: var(--text-muted);
    font-size: var(--fs-md);
    margin: 0 0 24px;
  }
  .tabs {
    display: flex;
    gap: 4px;
    border-bottom: 1px solid var(--border);
    overflow-x: auto;
    margin-bottom: 18px;
  }
  .tabs[hidden] { display: none !important; }
  .tabs::-webkit-scrollbar { display: none; }
  /* Difuminado en el borde por donde quedan pestañas escondidas: es la señal de que la
     tira se desliza. Lo pone el JS según si desborda y hacia qué lado, así que en una
     tira que entra entera no aparece nada. */
  .tabs.fade-der {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 36px), transparent 100%);
    mask-image: linear-gradient(to right, #000 calc(100% - 36px), transparent 100%);
  }
  .tabs.fade-izq {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 36px);
    mask-image: linear-gradient(to right, transparent 0, #000 36px);
  }
  .tabs.fade-izq.fade-der {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 36px, #000 calc(100% - 36px), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, #000 36px, #000 calc(100% - 36px), transparent 100%);
  }
  .tab {
    background: none;
    border: none;
    color: var(--text-muted);
    font-size: var(--fs-md);
    font-weight: 500;
    padding: 10px 4px;
    margin-right: 20px;
    cursor: pointer;
    white-space: nowrap;
    border-bottom: 2px solid transparent;
    transition: color 0.15s ease, border-color 0.15s ease;
  }
  .tab:hover { color: var(--text); }
  .tab.active { color: var(--text); }
  #sedeTabs .tab.active { border-color: var(--brand-red); }
  .tab-divider {
    align-self: stretch;
    width: 1px;
    background: var(--border);
    margin: 6px 12px 6px 2px;
    flex-shrink: 0;
  }
  #turnoTabs .tab {
    display: flex;
    align-items: center;
    gap: 7px;
  }
  #turnoTabs .tab .dot {
    width: 9px; height: 9px; border-radius: 50%;
    background: var(--dot-color, var(--text-muted));
    opacity: 0.55;
  }
  #turnoTabs .tab.active .dot { opacity: 1; }
  #turnoTabs .tab.active { border-color: var(--dot-color, var(--turno-color)); }

  .toolbar {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin: 22px 0 14px;
  }
  .date-nav {
    display: grid;
    grid-template-columns: 44px minmax(0, 1fr) 44px auto;
    gap: 6px;
    width: 100%;
  }
  .date-nav-btn {
    width: 44px;
    height: 44px;
    flex-shrink: 0;
    background: var(--bg-panel-2);
    border: 1px solid var(--border);
    color: var(--text);
    border-radius: var(--r-sm);
    font-size: var(--fs-lg);
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
  }
  .date-nav-btn svg { width: 20px; height: 20px; display: block; }
  .date-display {
    width: 100%;
    min-width: 0;
    height: 44px;
    background: var(--bg-panel-2);
    border: 1px solid var(--border);
    color: var(--text);
    font-size: var(--fs-md);
    font-weight: 600;
    border-radius: var(--r-sm);
    cursor: pointer;
  }
  .date-display.open {
    border-color: var(--turno-color);
  }
  .today-btn {
    height: 44px;
    background: none;
    border: 1px solid var(--border);
    color: var(--text-muted);
    border-radius: var(--r-sm);
    font-size: var(--fs-sm);
    font-weight: 600;
    padding: var(--pad-btn-x);
    cursor: pointer;
  }
  .today-btn:hover { color: var(--text); }

  .cal-panel {
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    padding: 12px;
  }
  .cal-panel[hidden] { display: none !important; }
  .cal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 10px;
  }
  .cal-head span {
    font-size: var(--fs-md);
    font-weight: 600;
  }
  .cal-weekdays,
  .cal-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 4px;
  }
  .cal-weekdays span {
    text-align: center;
    font-size: var(--fs-xs);
    color: var(--text-muted);
    padding: 4px 0 8px;
  }
  .cal-day {
    height: 40px;
    border: none;
    background: transparent;
    color: var(--text);
    border-radius: var(--r-sm);
    font-size: var(--fs-md);
    cursor: pointer;
  }
  .cal-day.empty {
    visibility: hidden;
    pointer-events: none;
  }
  .cal-day.today:not(.selected) {
    box-shadow: inset 0 0 0 1px var(--turno-color);
  }
  .cal-day.selected {
    background: var(--turno-color);
    color: var(--on-accent);
    font-weight: 700;
  }
  .cal-day.future,
  .cal-day:disabled {
    color: var(--text-muted);
    opacity: 0.35;
    cursor: not-allowed;
  }
  /* Día en el que esta sede no entrena. Se distingue del día futuro: aquel todavía
     puede llegar a usarse, este no va a estar disponible nunca para esta sede. El
     punto debajo del número deja ver de un vistazo qué días entrena cada sede sin
     tener que descubrirlo tocando. */
  .cal-day.no-class:not(.future) {
    opacity: 0.25;
    position: relative;
  }
  .cal-day.no-class:not(.future)::after {
    content: '';
    position: absolute;
    left: 50%;
    bottom: 6px;
    width: 3px;
    height: 3px;
    margin-left: -1.5px;
    border-radius: 50%;
    background: currentColor;
  }
  .date-nav-btn:disabled {
    color: var(--text-muted);
    opacity: 0.35;
    cursor: not-allowed;
  }

  .counter {
    font-size: var(--fs-sm);
    color: var(--text-muted);
  }
  .counter strong { color: var(--text); font-weight: 600; }
  .save-status {
    font-size: var(--fs-sm);
    color: var(--turno-color);
    min-width: 70px;
    text-align: right;
    opacity: 0;
    transition: opacity 0.25s ease;
  }
  .save-status.show { opacity: 1; }
  /* Un guardado que no vuelve (típicamente, sin conexión). El color lo pone el JS junto
     con el texto; acá va lo que el mensaje necesita para leerse: más peso, alineado a la
     izquierda y sin el ancho mínimo pensado para un "Guardado" de una palabra. */
  .save-status.is-warning {
    font-weight: 600;
    text-align: left;
    min-width: 0;
    flex: 1 1 100%;
  }

  .search-row { margin-bottom: 10px; }

  /* La lista es una tarjeta de tres piezas: el contador arriba, las filas en el medio
     y la leyenda al pie. El redondeo vive en los extremos, así que las filas no llevan
     ninguno y el borde de arriba y abajo lo ponen la cabecera y la leyenda. */
  .list-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    /* El aviso de "sin conexión" es una frase larga: sin esto le comería el ancho al
       contador en pantallas angostas. Así baja a su propio renglón. */
    flex-wrap: wrap;
    padding: 9px 14px;
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-bottom: 0;
    border-radius: var(--r-sm) var(--r-sm) 0 0;
  }
  .student-list {
    display: flex;
    flex-direction: column;
    gap: 1px;
    background: var(--border);
    border-left: 1px solid var(--border);
    border-right: 1px solid var(--border);
  }
  /* Cargando, o la búsqueda no encontró a nadie: lo que sigue a la cabecera no son las
     filas sino uno de estos bloques, así que se sueldan igual en vez de dejar la
     cabecera flotando sola. */
  #listArea > .empty-state,
  #listArea > .loading {
    border-top: 0;
    border-radius: 0 0 var(--r-sm) var(--r-sm);
  }
  #listArea > .loading {
    border: 1px solid var(--border);
    border-top: 0;
    padding: 20px 14px;
  }
  /* Pegada al pie de la lista: se lee como nota de la misma tarjeta, no le saca lugar
     a la cabecera y no empuja las filas hacia abajo. */
  /* El padding izquierdo es mayor que el del resto de la tarjeta a propósito: la fila
     lleva un borde de color de 4px que la leyenda no tiene, y su marcador mide 21px
     contra los 17px de acá. Sumado da 6px, que es lo que hay que correr para que los
     marcadores de la leyenda queden en la MISMA columna que los de la lista.
     El gap de 12px (y no 18) es lo que permite que los tres ítems sigan entrando en una
     sola línea hasta 320px, ya con ese padding extra y con scrollbar-gutter reservado. */
  .list-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 12px;
    padding: 9px 14px 9px 20px;
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: 0 0 var(--r-sm) var(--r-sm);
    font-size: var(--fs-xs);
    color: var(--text-muted);
  }
  .list-legend > span { display: flex; align-items: center; gap: 7px; }
  .list-legend .check-box { width: 17px; height: 17px; }
  .list-legend .check-box::after { width: 9px; height: 9px; }
  .list-legend .check-box.is-absent::after { width: 11px; height: 3px; }
  /* Mientras llega otra sección, la lista anterior se queda en su lugar pero apagada y
     sin poder tocarse. Evita que el bloque se desplome a una línea y vuelva a crecer. */
  #listArea.is-loading {
    opacity: 0.45;
    pointer-events: none;
    transition: opacity 0.12s ease;
  }
  /* Encabezado de letra en listas largas. Se pega arriba mientras su grupo está en
     pantalla, así al desplazarte siempre sabés en qué letra estás sin leer los nombres.
     Lleva el fondo de la PÁGINA y no el de un panel: con fondo de panel tenía la misma
     estructura visual que una fila y se confundía con un alumno más. Así se lee como un
     hueco que separa grupos, que es lo que es. */
  .letra-grupo {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--bg);
    color: var(--turno-color);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: 0.06em;
    padding: 5px 14px;
  }
  .student-row {
    display: flex;
    align-items: center;
    gap: 12px;
    min-height: 48px;
    background: var(--bg-panel);
    padding: 12px 8px 12px 14px;
    border-left: 4px solid var(--turno-color);
    cursor: pointer;
    user-select: none;
  }
  /* Marcador de estado, copiado de cómo el maestro anota en su lista de papel: un punto
     para el presente y un anillo alrededor del punto para el que trajo el pase, que es
     como arranca todo el mundo. El ausente es una raya, no otro anillo, para que las
     tres formas se distingan sin depender del color (WCAG 1.4.1).
     El punto mide 11px pero la caja sigue midiendo 21px: el área táctil no cambió. */
  .check-box {
    width: 21px; height: 21px;
    flex-shrink: 0;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
  }
  .check-box::after {
    content: '';
    width: 11px; height: 11px;
    border-radius: 50%;
    background: var(--turno-color);
    transition: width 0.15s ease, height 0.15s ease, background 0.15s ease;
  }
  .check-box.has-pass::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: 50%;
    border: 2px solid var(--turno-color);
  }
  .student-row.is-absent {
    border-left-color: var(--danger-line);
    opacity: 0.7;
  }
  /* La segunda mitad de cada par es para la leyenda, que dibuja los mismos marcadores
     fuera de una fila. */
  .student-row.is-absent .check-box::after,
  .check-box.is-absent::after {
    width: 13px; height: 3px;
    border-radius: 2px;
    background: var(--danger-line);
  }
  .student-row.is-absent .check-box::before,
  .check-box.is-absent::before { display: none; }
  /* Solo el nombre: el tachado dice "esta persona faltó hoy", no anula su graduación.
     Estaba en .student-name, que es el contenedor de todo, así que la línea cruzaba
     también la pastilla del cinturón. */
  .student-row.is-absent .student-name-text {
    text-decoration: line-through;
    text-decoration-color: var(--danger-line);
  }
  /* Dos líneas: el nombre se queda con la primera entera y la graduación baja a la
     segunda. Antes iban todos en la misma línea envuelta, así que la pastilla del
     cinturón caía donde terminara el nombre y nunca formaba columna. */
  .student-name {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 5px;
  }
  /* Antes esto cortaba con "…" porque el nombre competía por la línea con las pastillas.
     Ya no comparte la línea con nada, así que puede envolver: en un celular de 390px un
     nombre largo perdía 33px, y en una lista de asistencia el nombre es justamente el
     dato que no se puede perder. Se limita a dos renglones para que ninguna fila se
     dispare de alto. */
  .student-name-text {
    font-size: var(--fs-md);
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    overflow-wrap: break-word;
  }
  .student-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 6px;
    min-width: 0;
  }
  .row-actions {
    display: flex;
    flex-shrink: 0;
    gap: 2px;
  }
  .icon-btn {
    background: none;
    border: none;
    color: var(--text-muted);
    font-size: var(--fs-md);
    line-height: 1;
    cursor: pointer;
    padding: 8px 7px;
    border-radius: var(--r-sm);
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  .icon-btn svg { width: 17px; height: 17px; display: block; }
  .icon-btn:hover { color: var(--text); }
  .icon-btn.delete:hover { color: var(--danger-line); }

  .empty-state {
    padding: 26px 14px;
    background: var(--bg-panel);
    border: 1px dashed var(--border);
    border-radius: var(--r-sm);
    color: var(--text-muted);
    font-size: var(--fs-md);
    text-align: center;
  }

  .add-row {
    display: flex;
    gap: 8px;
    margin-top: 14px;
  }
  /* Sin esto, poner .add-row.hidden = true no hace absolutamente nada: el display:flex
     de la regla de arriba le gana al display:none que el navegador aplica por `hidden`.
     Arranca oculta y la muestra applyRoleUI() solo si el rol corresponde. */
  .add-row[hidden] { display: none; }
  .add-row input[type="text"] {
    flex: 1;
    /* Sin esto, un input con flex:1 igual se niega a achicarse más allá de un mínimo
       basado en su propio contenido (el "min-width:auto" automático de flexbox) — a
       320px de ancho eso ya alcanzaba para que el botón "Agregar" de al lado se saliera
       de la pantalla. min-width:0 le da permiso a este input, y solo a este, a ceder
       todo el espacio que haga falta antes que el botón. */
    min-width: 0;
  }
  .add-row button {
    background: var(--turno-color);
    color: var(--on-accent);
    border: none;
    font-weight: 600;
    font-size: var(--fs-md);
    padding: var(--pad-btn-x);
    border-radius: var(--r-sm);
    cursor: pointer;
  }

  .existing-suggestions {
    margin-top: 8px;
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    padding: 8px;
  }
  .suggestions-title {
    font-size: var(--fs-sm);
    color: var(--text-muted);
    margin: 0 0 6px;
  }
  .suggestion-row {
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
    background: var(--bg-panel-2);
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    padding: 8px 10px;
    margin-bottom: 6px;
    color: var(--text);
    font-size: var(--fs-sm);
    cursor: pointer;
    text-align: left;
  }
  .suggestion-row:last-child { margin-bottom: 0; }
  .suggestion-row:hover { border-color: var(--turno-color); }
  .suggestion-section { color: var(--text-muted); font-size: var(--fs-xs); white-space: nowrap; }

  /* El historial cierra la página: la línea lo separa de la lista en vez de dejarlo
     suelto justo debajo del campo de agregar alumno. */
  .history-section {
    margin-top: 26px;
    padding-top: 8px;
    border-top: 1px solid var(--border);
  }
  .history-toggle {
    background: none;
    border: none;
    color: var(--text-muted);
    font-size: var(--fs-sm);
    cursor: pointer;
    padding: 11px 12px;
    border-radius: var(--r-sm);
    text-decoration: underline;
    text-underline-offset: 3px;
  }
  .history-toggle:hover { color: var(--text); }

  /* Enlaces de un lado y la versión del otro, con una línea que separa el pie del
     contenido. Es la misma estructura que .header-bar arriba (marca a un lado, botones
     al otro), así la página queda enmarcada igual arriba que abajo.
     Antes esto era una sola línea corrida de items separados por puntos, centrada, a
     11px y con opacity .6. Esa opacidad era el problema real: mezclaba el gris con el
     fondo y dejaba el contraste en 2.93:1 — por debajo del 4.5:1 que pide WCAG AA para
     texto chico, e incluso del 3:1 del mínimo relajado. El mismo color SIN opacidad da
     5.79:1, así que no aportaba nada y costaba legibilidad. */
  .app-footer {
    margin-top: 40px;
    padding-top: 16px;
    border-top: 1px solid var(--border);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 10px 20px;
    font-size: var(--fs-xs);
    color: var(--text-muted);
  }
  .footer-links {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 18px;
  }
  /* Sin puntos separadores: el espacio ya separa, y los puntos hacían que en pantallas
     angostas un nombre se partiera a la mitad ("Link / para familias"). */
  .footer-links a {
    color: var(--text-muted);
    font-size: var(--fs-sm);
    text-decoration: none;
  }
  .footer-links a:hover {
    color: var(--turno-color);
    text-decoration: underline;
  }
  .footer-meta { white-space: nowrap; }
  .history-panel {
    margin-top: 12px;
    display: none;
  }
  .history-panel.open { display: block; }
  .history-month {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 8px;
  }
  .history-month span {
    font-size: var(--fs-md);
    font-weight: 600;
  }
  .history-row {
    border-bottom: 1px solid var(--border);
    font-size: var(--fs-sm);
    color: var(--text-muted);
  }
  .history-row-head {
    display: flex;
    align-items: center;
    gap: 4px;
  }
  .history-row-main {
    display: flex;
    justify-content: space-between;
    flex: 1;
    min-width: 0;
    background: none;
    border: none;
    color: inherit;
    font: inherit;
    padding: 11px 2px;
    cursor: pointer;
    text-align: left;
  }
  .history-row-main span:last-child { color: var(--text); }
  .history-row-delete { flex-shrink: 0; }
  .history-detail {
    padding: 0 2px 12px;
    display: none;
  }
  .history-row.open .history-detail { display: block; }
  .history-detail p {
    margin: 0 0 6px;
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--text);
  }
  .history-detail ul {
    margin: 0 0 10px;
    padding-left: 18px;
  }
  .history-detail li { margin: 2px 0; }
  .loading { color: var(--text-muted); font-size: var(--fs-md); padding: 20px 2px; }

  .tabs-disabled {
    pointer-events: none;
    opacity: 0.5;
  }
  /* El título ("Registro de asistencia", ~443px de ancho natural) más los botones de
     cuenta (188px) no entran juntos en una sola fila salvo en pantallas anchas — y como
     .wrap tiene un techo de 640px a propósito (para verse igual en mobile y desktop),
     ese ancho generoso nunca llega. Ponerlos a competir por la misma fila terminaba en
     dos problemas distintos según el ancho: el título aplastado a pocas líneas, o (ya
     evitando eso) los botones "flotando" solos con un hueco vacío al lado.
     La salida no es la fila, es no compartirla: "marinotkd" + los tres botones son
     cortos y sí entran juntos en cualquier ancho realista, así que van en su propia
     barra angosta arriba (.header-bar) — donde además la esquina superior derecha es
     justo donde se espera encontrar "Cerrar sesión". El título grande queda solo,
     debajo, con todo el ancho para él y sin competir con nada. */
  .header-bar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 14px;
  }
  /* El margen de abajo en .brand-tag es para cuando queda solo, apilado arriba de un
     h1 (login, estadísticas). Acá comparte fila con los botones vía align-items:center,
     y ese margen desalinearía el texto un pelo respecto de ellos. */
  .header-bar .brand-tag { margin: 0; }
  .header-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
  }
  .logout-btn {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text-muted);
    padding: var(--pad-btn-sm);
    border-radius: var(--r-sm);
    font-size: var(--fs-sm);
    cursor: pointer;
    white-space: nowrap;
  }
  .requests-toggle {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    flex-shrink: 0;
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text-muted);
    border-radius: var(--r-sm);
    cursor: pointer;
    padding: 0;
  }
  .requests-toggle[hidden] { display: none !important; }
  .requests-toggle:hover { color: var(--text); }
  .requests-toggle svg { width: 16px; height: 16px; display: block; }
  .requests-badge {
    position: absolute;
    top: -6px;
    right: -6px;
    background: var(--brand-red);
    color: var(--on-brand);
    font-size: var(--fs-xs);
    font-weight: 700;
    border-radius: var(--r-pill);
    min-width: 16px;
    height: 16px;
    line-height: 16px;
    text-align: center;
    padding: 0 3px;
  }
  /* #loginArea pasó de ser la tarjeta a ser el área que la centra. Antes la tarjeta
     tenía margin:60px auto y quedaba pegada arriba: medido en una pantalla de 900px,
     65px de aire arriba contra 410px abajo. De ahí la sensación de caja flotando.
     El -5px descuenta el borde rojo del body, que suma alto y si no aparecería una
     barra de scroll vertical en una pantalla que entra justo. */
  .login-box {
    min-height: calc(100vh - 5px);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px 18px;
  }
  .login-card {
    width: 100%;
    max-width: 380px;
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    padding: 32px 26px;
  }
  .login-logo {
    display: block;
    width: 64px;
    height: 64px;
    margin: 0 auto 16px;
    border-radius: var(--r-md);
  }
  .login-card h1 {
    font-size: 26px;
    text-align: center;
    margin: 0 0 4px;
  }
  .login-card .subtitle {
    text-align: center;
    margin: 0 0 24px;
  }
  /* ---------------------------------------------------------------------------
     Campo con etiqueta flotante (el patrón del login de Google). La etiqueta
     arranca dentro del campo y, al enfocarlo o si ya tiene contenido, sube y se
     apoya sobre el borde. Todo con CSS, sin JavaScript.
     La clave es el placeholder=" " (un espacio) del HTML: es lo que permite usar
     :placeholder-shown como detector de "campo vacío". Sin ese espacio el selector
     nunca cambia y la etiqueta se queda abajo tapando lo que se escribe.
     --------------------------------------------------------------------------- */
  .float-field {
    position: relative;
    margin-bottom: 16px;
  }
  /* Fondo transparente y solo contorno, como los campos de Google: así el fondo que
     queda detrás de la etiqueta flotante es el de la tarjeta, y el recorte sobre el
     borde calza. Con el relleno --bg-panel-2 de los demás campos, la etiqueta habría
     quedado con un color distinto arriba y abajo de la línea. */
  .float-field input {
    background: transparent;
  }
  .float-field label {
    position: absolute;
    left: 9px;
    top: 50%;
    transform: translateY(-50%);
    padding: 0 4px;
    background: var(--bg-panel);
    color: var(--text-muted);
    font-size: var(--fs-control);
    line-height: 1;
    pointer-events: none;
    transition: top 0.15s ease, font-size 0.15s ease, color 0.15s ease;
  }
  .float-field input:focus + label,
  .float-field input:not(:placeholder-shown) + label {
    top: 0;
    font-size: var(--fs-xs);
    color: var(--turno-color);
  }
  /* Sin foco, una etiqueta ya subida vuelve al gris: el acento marca dónde estás
     parado, no se reparte entre todos los campos completados. */
  .float-field input:not(:focus):not(:placeholder-shown) + label {
    color: var(--text-muted);
  }

  /* El botón de ver/ocultar va dentro del campo. El padding derecho del input le
     reserva el lugar para que el texto escrito nunca quede debajo del botón. */
  .float-field:has(.password-toggle) input { padding-right: 64px; }
  .password-toggle {
    position: absolute;
    right: 5px;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    color: var(--text-muted);
    font-size: var(--fs-sm);
    font-weight: 600;
    padding: 8px 10px;
    border-radius: var(--r-sm);
    cursor: pointer;
  }
  .password-toggle:hover { color: var(--text); }
  .field {
    margin-bottom: 16px;
    display: flex;
    flex-direction: column;
    gap: 6px;
  }
  .field label {
    font-size: var(--fs-sm);
    color: var(--text-muted);
  }
  .login-btn {
    width: 100%;
    background: var(--brand-red);
    color: var(--on-brand);
    border: none;
    font-weight: 600;
    font-size: var(--fs-md);
    padding: var(--pad-btn);
    border-radius: var(--r-sm);
    cursor: pointer;
    margin-top: 8px;
  }
  .text-btn {
    display: block;
    width: 100%;
    margin-top: 12px;
    background: none;
    border: none;
    color: var(--text-muted);
    font-size: var(--fs-sm);
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 3px;
  }
  .error-msg {
    color: var(--danger-text);
    font-size: var(--fs-sm);
    margin-top: 14px;
    text-align: center;
    min-height: 18px;
  }
  .error-msg.ok { color: var(--turno-color); }

/* Ficha médica / contacto y examenes de grado */
/* Las tres pastillas que pueden aparecer al lado del nombre comparten el mismo
   comportamiento frente a la falta de espacio: en vez de desbordar la fila, se
   recortan con puntos suspensivos. Antes tenían flex-shrink:0, así que un
   cinturón de nombre largo ("Blanco punta amarilla", 131px) sobre una pantalla
   angosta se salía de su celda y quedaba dibujado POR DEBAJO de los botones de
   acción — medido: 38px de invasión a 320px, 8px a 350px, y recién a partir de
   360px dejaba de pasar. Recortar es preferible a superponer: el texto cortado
   se sigue leyendo entero al abrir la ficha, un botón tapado no se puede tocar. */
.belt-badge,
.badge-baja {
  min-width: 0;
  flex-shrink: 1;
}
/* El recorte va en el TEXTO, no en la pastilla: .belt-badge es un contenedor flex
   (lleva la cinta al lado del nombre) y text-overflow no funciona sobre un flex. */
.badge-baja,
.belt-badge-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.belt-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--text-muted);
  background: var(--bg-panel-2);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  padding: 2px 8px;
  vertical-align: middle;
}
/* La cinta del cinturón: una barrita con su color real, para poder reconocerlo de
   un vistazo sin leer. Los cinturones "punta" llevan los dos colores con un corte
   limpio, como la cinta de verdad — no un degradé: al fundir rojo con negro el
   medio da un marrón que no es ningún cinturón, y una cinta real no se desvanece.
   El corte no va al 50%: el color base manda y la punta es el remate, igual que
   en el cinturón físico. */
.belt-ribbon {
  flex-shrink: 0;
  width: 18px;
  height: 9px;
  border-radius: 2px;
  background: linear-gradient(
    90deg,
    var(--cinta) 0 var(--corte-cinta, 62%),
    var(--cinta-punta) var(--corte-cinta, 62%) 100%
  );
  /* El cinturón negro es casi del color del panel: sin este canto desaparecería.
     Al blanco le da un borde definido y a los demás no les molesta. */
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.25);
}
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  z-index: 200;
}
.modal-overlay[hidden] { display: none; }
.modal {
  position: fixed;
  inset: 0;
  z-index: 201;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0;
}
.modal[hidden] { display: none; }
.modal-panel {
  width: 100%;
  max-width: 480px;
  max-height: 88vh;
  overflow-y: auto;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--r-md) var(--r-md) 0 0;
  padding: 20px;
}
@media (min-width: 560px) {
  .modal { align-items: center; }
  .modal-panel { border-radius: var(--r-md); }
}
.modal-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 14px;
}
.modal-head h2 {
  font-family: 'Barlow Semi Condensed', sans-serif;
  font-size: var(--fs-lg);
  margin: 0;
}
.modal-close {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  margin: -8px -8px 0 0;
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: var(--fs-lg);
  line-height: 1;
  cursor: pointer;
  padding: 0;
  border-radius: var(--r-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.modal-close svg { width: 20px; height: 20px; display: block; }
.modal-close:hover { color: var(--text); }
/* Antes usaba var(--text-muted), el mismo gris que .move-hint (el texto de ayuda que
   suele ir justo debajo): se distinguían solo por ser negrita y 1px más grande, algo
   que en el celular cuesta notar de un vistazo. Un título de subsección tiene que
   contrastar contra el texto normal de verdad, no solo por peso — mismo criterio que
   ya se aplicó al nombre en el panel de Staff y al nombre en la tarjeta de cumpleaños. */
.modal-section-title {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text);
  margin: 18px 0 10px;
}
.modal-section-title:first-of-type { margin-top: 0; }
.field-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  container-type: inline-size;
}
.field-grid .field-full { grid-column: 1 / -1; }
/* Este span aparece/desaparece según se sepa o no la edad (al elegir una fecha de
   nacimiento). Reservar el renglón siempre, con o sin texto, evita que la celda
   cambie de alto de golpe cuando aparece — ver la nota en ".field-grid label" de
   abajo sobre por qué esto ya no depende de alinear la fila entera por el fondo. */
.field-hint {
  display: block;
  font-size: var(--fs-xs);
  line-height: 14px;
  min-height: 14px;
  color: var(--turno-color);
  margin-top: 4px;
}
.field-hint[hidden] { visibility: hidden; }
/* min-height reserva 2 líneas siempre, wrappee el texto o no — es la manera correcta
   de resolver la desalineación entre inputs de la misma fila cuando una etiqueta es
   más larga que la otra ("Teléfono de contacto habitual (opcional)" vs "Obra social
   (opcional)"). Un intento anterior lo resolvía alineando la FILA ENTERA por el fondo
   (align-items:end en .field-grid), pero eso rompía cualquier campo que tuviera algo
   extra debajo del input (como el hint de edad de fichaBirthDate): esa celda quedaba
   más alta que su vecina, y la vecina se corría entera hacia abajo para acompañarla.
   Reservando el alto en la ETIQUETA en vez de en la fila, cada input arranca siempre
   a la misma altura por sí solo, sin importar qué tenga (o no tenga) al lado. */
.field-grid label,
.form-field label {
  /* flex + align-items:flex-end pega el texto al FONDO de esos 29px reservados —
     si no, una etiqueta corta ("Teléfono") deja el texto arriba y un hueco vacío
     antes de llegar a su input, mientras la de al lado, si ocupa las 2 líneas de
     verdad, no tiene ese hueco. Así las dos quedan pegadas a su input por igual,
     sea cual sea su largo. */
  /* Importante: si el label tiene más de un nodo adentro (ej: texto + un
     <span> como "(opcional)"), hay que envolver TODO el contenido en un único
     <span> hijo (ver ficha-familia.html). Si no, flex trata el texto y el
     span como dos ítems separados: se come el espacio entre ambos y, si el
     texto envuelve a 2 líneas en pantallas angostas, el span queda flotando
     lejos en vez de acompañar la palabra anterior. */
  display: flex;
  align-items: flex-end;
  font-size: var(--fs-sm);
  line-height: 14.5px;
  color: var(--text-muted);
  margin-bottom: 4px;
}
/* La reserva de 2 renglones solo sirve cuando una etiqueta REALMENTE envuelve, y eso
   depende del ancho de la grilla, no del de la ventana (el modal de ficha es angosto
   aunque la pantalla sea grande; por eso va @container y no @media). Cuando las
   columnas son anchas ninguna envuelve, y reservar de más dejaba 14px muertos arriba
   de cada etiqueta: se notaba sobre todo debajo de un título de sección, que quedaba
   igual de lejos de sus campos que de la sección anterior.
   El corte es 500px porque a 484px la etiqueta más larga ("Teléfono de contacto
   habitual (opcional)") todavía envuelve y a 524px ya no: medido, no estimado. Si
   algún día se agrega una etiqueta más larga hay que volver a medir este número —
   el síntoma sería que dos campos de la misma fila dejan de estar alineados. */
@container (max-width: 500px) {
  .field-grid label,
  .form-field label {
    min-height: 29px;
  }
}
.field-grid textarea,
.form-field textarea {
  resize: vertical;
  min-height: 54px;
}
.modal-save-btn {
  width: 100%;
  margin-top: 14px;
  background: var(--turno-color);
  color: var(--on-accent);
  border: none;
  font-weight: 600;
  font-size: var(--fs-md);
  padding: var(--pad-btn);
  border-radius: var(--r-sm);
  cursor: pointer;
}

/* Pestañas dentro del modal de ficha (Contacto y salud / Graduaciones / Sección):
   reusa el estilo de .tabs/.tab, pero más compacto y sin scroll horizontal. */
.ficha-tabs {
  margin: 0 0 16px;
  gap: 2px;
}
.ficha-tabs .tab {
  font-size: var(--fs-sm);
  padding: 8px 4px;
  margin-right: 16px;
}
.ficha-tabs .tab.active { border-color: var(--turno-color); }
.ficha-tab-panel[hidden] { display: none; }

.current-belt-card {
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 12px 14px;
  margin-bottom: 16px;
}
.current-belt-card-label {
  font-size: var(--fs-sm);
  color: var(--text-muted);
}
.current-belt-card strong {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: var(--fs-md);
  color: var(--turno-color);
}
.grade-form-card {
  background: var(--bg-panel-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 14px;
  margin-bottom: 18px;
}
.grade-form-card .modal-section-title { margin-top: 0; }
/* La card de "Registrar nuevo examen" cuenta como :first-of-type de su panel,
   así que el título de abajo (Historial) necesita recuperar el margen manualmente. */
.grade-form-card + .modal-section-title { margin-top: 18px; }
/* Grilla de columnas iguales, no una fila que envuelve: con flex cada chip toma el
   ancho de su texto y el borde derecho de cada renglón queda dentado (medido: entre
   117px y 123px según el código y si el gup tiene uno o dos dígitos). En grilla los
   once quedan exactamente del mismo ancho y las columnas alineadas, que es lo que
   hace que se lea como una lista ordenada y no como un montón de pastillas sueltas.
   auto-fit acomoda 2 o 3 columnas según el ancho disponible. */
.belt-chips {
  display: grid;
  /* El piso de 130px es el ancho que necesita el chip más largo ("BL 10.º GUP") con su
     cinta y su padding: con menos, el texto se partía en dos renglones en pantallas de
     320px. Puesto así, en esas pantallas la grilla baja sola a una columna en vez de
     apretar dos. */
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 6px;
  margin-top: 8px;
}
/* Los chips son el selector rápido de cinturón dentro de la ficha: acá el color
   trabaja más fuerte que en la lista, porque el objetivo es encontrar el cinturón
   correcto entre once de un vistazo, no leer once nombres. La cinta es más grande
   y el borde toma un tinte del propio color del cinturón. */
.belt-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  color: var(--text-muted);
  font-size: var(--fs-sm);
  padding: 6px 12px 6px 8px;
  border-radius: var(--r-pill);
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}
/* Navegadores viejos ignoran esta línea y se quedan con el borde neutro de arriba. */
.belt-chip { border-color: color-mix(in srgb, var(--cinta, var(--border)) 45%, var(--border)); }
.belt-chip .belt-ribbon { width: 22px; height: 11px; }
.belt-chip:hover {
  color: var(--text);
  border-color: var(--cinta, var(--turno-color));
}
.grade-form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-bottom: 16px;
  container-type: inline-size;
}
.grade-form .form-field-full { grid-column: 1 / -1; }
.grade-add-btn {
  grid-column: 1 / -1;
  background: var(--bg-panel-2);
  border: 1px solid var(--turno-color);
  color: var(--text);
  font-weight: 600;
  font-size: var(--fs-sm);
  padding: var(--pad-btn);
  border-radius: var(--r-sm);
  cursor: pointer;
}
.grade-history-list { display: flex; flex-direction: column; gap: 8px; }
.grade-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border);
  border-left: 3px solid var(--turno-color);
  border-radius: var(--r-sm);
  padding: 8px 10px;
  font-size: var(--fs-sm);
}
.grade-row-main { flex: 1; min-width: 0; }
.grade-delete-btn { flex-shrink: 0; }
.grade-date { color: var(--text-muted); margin-left: 8px; font-size: var(--fs-sm); }
.grade-examiner, .grade-notes { color: var(--text-muted); font-size: var(--fs-sm); margin-top: 3px; }
.move-hint {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin: 0 0 10px;
}
.move-form select { margin-bottom: 10px; }
/* Dos columnas mientras entren; si no, una sola apilada. Con dos columnas fijas,
   "Pasar a esa sección" se partía en dos renglones en pantallas de 320px y dejaba
   los dos botones de distinto aspecto. auto-fit resuelve el caso angosto sin
   media query y sin tener que achicarle el padding al botón. */
.move-actions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 8px;
}
.move-btn {
  font-weight: 600;
  font-size: var(--fs-sm);
  padding: var(--pad-btn-sm);
  border-radius: var(--r-sm);
  cursor: pointer;
  background: var(--bg-panel-2);
  border: 1px solid var(--border);
  color: var(--text);
}
.move-btn-link { border-color: var(--turno-color); }
.move-btn-move { border-color: var(--danger-text); color: var(--danger-text); }
.move-status {
  margin: 10px 0 0;
  font-size: var(--fs-sm);
  font-weight: 600;
}

/* Modal compacto para editar el nombre: mismo overlay/panel que la ficha, pero angosto. */
.modal-compact .modal-panel { max-width: 360px; }
.rename-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 16px;
  margin-top: 16px;
}
.rename-actions .text-btn { display: inline-block; width: auto; margin: 0; }
.rename-save-btn { width: auto; margin: 0; padding: var(--pad-btn); }
.linked-sections-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 16px;
}
.linked-sections-list[hidden] { display: none; }
.linked-section-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 8px 10px;
  font-size: var(--fs-sm);
}
.linked-section-row.is-current { border-color: var(--turno-color); }
.linked-section-current-tag {
  font-size: var(--fs-xs);
  color: var(--turno-color);
  margin-left: 6px;
}
.linked-section-unlink-btn {
  background: none;
  border: 1px solid var(--danger-text);
  color: var(--danger-text);
  font-weight: 600;
  font-size: var(--fs-xs);
  padding: var(--pad-btn-sm);
  border-radius: var(--r-sm);
  cursor: pointer;
  white-space: nowrap;
}

.request-card {
  background: var(--bg-panel-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 12px;
  margin-bottom: 12px;
}
.request-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 8px;
}
.request-section { font-size: var(--fs-sm); color: var(--text-muted); white-space: nowrap; }
.request-fields { font-size: var(--fs-sm); margin-bottom: 8px; }
.request-field { margin-bottom: 3px; }
.request-field span { color: var(--text-muted); margin-right: 6px; }
.request-consent { font-size: var(--fs-sm); color: var(--turno-color); margin: 0 0 10px; }
.request-invalid { font-size: var(--fs-sm); color: var(--danger-text); margin: 0 0 10px; }
.request-match { display: flex; gap: 8px; margin-bottom: 8px; }
.request-match-select {
  flex: 1;
  min-width: 0;
  /* Único override del primitivo: la tarjeta de la ficha pendiente ya es
     --bg-panel-2, así que el campo usa el fondo más oscuro para no fundirse con ella. */
  background: var(--bg-panel);
}
.request-apply-btn {
  background: var(--turno-color); color: var(--on-accent); border: none; font-weight: 600;
  font-size: var(--fs-sm); padding: var(--pad-btn-x); border-radius: var(--r-sm); cursor: pointer; white-space: nowrap;
}
.request-apply-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.request-dismiss-btn {
  background: none; border: 1px solid var(--border); color: var(--text-muted);
  font-size: var(--fs-sm); padding: var(--pad-btn-sm); border-radius: var(--r-sm); cursor: pointer;
}
.request-dismiss-btn:hover { color: var(--danger-line); border-color: var(--danger-line); }

/* Presente sin pase */
.icon-btn.nopaper-btn.active { color: var(--turno-color); }
.icon-btn.nopaper-btn:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

/* Alumnos dados de baja: siguen existiendo (con su ficha y su historial), pero se
   ocultan de la lista salvo que se pidan ver con el toggle. */
.badge-baja {
  display: inline-block;
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--danger-text);
  background: transparent;
  border: 1px solid var(--danger-text);
  border-radius: var(--r-pill);
  padding: 1px 7px;
  vertical-align: middle;
}
.student-row.is-baja {
  opacity: 0.55;
  border-left-color: var(--text-muted);
  cursor: default;
}
/* Un alumno de baja no está ni presente ni ausente: no se le toma asistencia. Va un
   círculo VACÍO, no un punto lleno — un punto lleno, aunque sea gris, se lee como
   "presente" ahora que ese es el marcador de presente. */
.student-row.is-baja .check-box::after {
  width: 13px; height: 13px;
  background: transparent;
  border: 2px solid var(--text-muted);
}
.student-row.is-baja .check-box::before { display: none; }
.bajas-toggle {
  display: block;
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: var(--fs-sm);
  cursor: pointer;
  padding: 12px;
  margin-bottom: 2px;
  border-radius: var(--r-sm);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.bajas-toggle[hidden] { display: none; }
.bajas-toggle:hover { color: var(--text); }

/* Modal de confirmación propio (reemplaza al confirm() del navegador). Va por encima
   de los demás modales, que son z-index 200/201. */
.confirm-overlay { z-index: 300; }
.confirm-modal { z-index: 301; }
.confirm-modal #confirmText {
  font-size: var(--fs-md);
  line-height: 1.5;
  margin: 0;
  color: var(--text);
}
.confirm-ok-btn { background: var(--brand-red); color: var(--on-brand); }

/* Panel de Staff y Permisos */
/* El subtítulo del modal llevaba el margen en un style inline. */
#staffModal .subtitle { margin-bottom: 14px; }
.staff-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.staff-row {
  display: flex;
  flex-direction: column;
  gap: 10px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 12px;
}
.staff-info {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
/* Por defecto se ve como el título de la tarjeta (sin caja, negrita, más grande) y
   solo revela que es editable al pasar el mouse o enfocarlo. Antes tenía fondo y
   borde permanentes, y se leía como un campo de formulario más, no como el dato
   principal de la fila — perdía en la comparación contra la pastilla de rol, que
   por su color termina siendo lo primero que se lee si nada más pesa visualmente. */
.staff-name-input {
  width: 100%;
  background: transparent;
  border: 1px solid transparent;
  color: var(--text);
  font-size: var(--fs-control);
  font-weight: 700;
  padding: 6px 8px;
  border-radius: var(--r-sm);
  transition: background 0.15s ease, border-color 0.15s ease;
}
.staff-name-input:hover:not(:disabled) { background: var(--bg-panel); border-color: var(--border); }
.staff-name-input:focus { outline: none; background: var(--bg-panel); border-color: var(--turno-color); }
.staff-name-input::placeholder { color: var(--text-muted); font-weight: 400; }
.staff-name-input:disabled { opacity: 0.6; }
.staff-email {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  padding: 0 8px;
  /* anywhere y no break-all: parte el correo solo cuando de verdad no entra, en vez de
     cortarlo en cualquier letra aunque haya lugar. */
  overflow-wrap: anywhere;
}
/* Un nombre sin cargar no debe leerse como si la persona se llamara así. */
.staff-name-input.sin-nombre::placeholder { font-style: italic; opacity: 0.75; }
.staff-role {
  display: flex;
  flex-direction: column;
  gap: 6px;
  /* La línea separa "quién es" (nombre + mail) de "qué puede hacer" (rol):
     antes las cuatro líneas de la tarjeta se leían como una sola lista plana. */
  padding-top: 10px;
  border-top: 1px solid var(--border);
}
/* El control de rol y su descripción van en la MISMA línea, y con la misma altura tenga
   selector o rol fijo: así todas las tarjetas del listado miden igual. Antes la de un
   maestro quedaba 23px más baja que la de un instructor y el listado se veía dentado. */
.rol-linea {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 10px;
  min-height: 34px;
}
/* Selector de rol tipo "segmentado": dos opciones a la vista, se toca la que se quiere,
   en vez del <select> nativo que obliga a desplegar para ver qué había. Compacto a
   propósito: esto marca un estado, no es un botón de acción. */
.role-segmented {
  display: flex;
  flex-shrink: 0;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  padding: 3px;
}
.role-option {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: var(--fs-xs);
  font-weight: 600;
  padding: 5px 12px;
  border-radius: var(--r-pill);
  cursor: pointer;
}
.role-option:hover:not(.active):not(:disabled) { color: var(--text); }
.role-option.active {
  background: var(--turno-color);
  color: var(--on-accent);
}
.role-option:disabled { opacity: 0.6; cursor: default; }
.role-desc { font-size: var(--fs-xs); color: var(--text-muted); min-width: 0; }

/* El rol más alto no es una alerta: mismo peso visual que el selector, en gris con borde.
   En rojo de marca era lo primero que se leía de todo el panel. */
/* Misma geometría que el selector y entre sí, para que todas las filas midan igual.
   Solo cambian los colores. El borde va en los dos aunque uno sea del color del fondo:
   sin él, las dos insignias tendrían alturas distintas. */
.staff-role-badge,
.dev-badge {
  flex-shrink: 0;
  font-size: var(--fs-xs);
  font-weight: 700;
  border-radius: var(--r-pill);
  padding: 6px 14px;
}
.staff-role-badge {
  color: var(--text);
  background: var(--bg-panel);
  border: 1px solid var(--text-muted);
}
.dev-badge {
  background: var(--brand-red);
  color: var(--on-brand);
  border: 1px solid var(--brand-red);
}

/* Qué puede hacer cada rol. Cerrado por defecto: quien ya los conoce no lo necesita, y
   quien duda lo abre una vez. */
.roles-ayuda {
  background: var(--bg-panel-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 10px 12px;
  margin-bottom: 14px;
}
.roles-ayuda summary {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
}
.roles-tabla { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
.rol-fila { display: grid; grid-template-columns: 76px 1fr; gap: 10px; align-items: start; }
.rol-nombre {
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--turno-color);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding-top: 1px;
}
.rol-texto { font-size: var(--fs-xs); color: var(--text-muted); line-height: 1.55; }
.rol-texto b { color: var(--text); font-weight: 600; }

/* Los dos botones que escriben la asistencia del día comparten renglón. El de "marcar
   todos como presentes" se lleva el 70% porque es el de todos los días; el de pases es
   el ocasional. Se ocultan juntos: los dos escriben el día (ver renderDayLock). */
.day-actions {
  display: flex;
  gap: 8px;
  margin: 2px 0 14px;
}
.day-actions[hidden] { display: none; }

/* Confirmar el día aunque nadie haya faltado */
.confirm-day-btn {
  display: block;
  /* 70% del renglón, y el de pases se queda con lo que sobra. Va como flex-basis en
     porcentaje y no como proporción (flex: 7 / flex: 3): con basis 0 el reparto es del
     espacio LIBRE y el relleno de cada botón se suma después, así que daba 64/33. */
  flex: 0 0 70%;
  min-width: 0;
  background: transparent;
  border: 1px solid var(--turno-color);
  color: var(--turno-color);
  font-weight: 600;
  font-size: var(--fs-sm);
  padding: var(--pad-btn);
  border-radius: var(--r-sm);
  cursor: pointer;
}
/* Mismo tratamiento que su vecino: contorno, no relleno. Relleno era el único botón
   macizo de toda la pantalla y se leía como de otra app. Lo que los distingue es el
   ancho y la etiqueta, no el color. */
.pases-open-btn {
  display: block;
  flex: 1 1 auto;
  min-width: 0;
  background: transparent;
  border: 1px solid var(--turno-color);
  color: var(--turno-color);
  font-weight: 600;
  font-size: var(--fs-sm);
  padding: var(--pad-btn);
  border-radius: var(--r-sm);
  cursor: pointer;
}
/* .confirm-day-btn define su propio display, así que sin esto el atributo hidden que le
   pone renderDayLock() no lo ocultaría (gana el display de la clase por especificidad). */
.confirm-day-btn[hidden] { display: none; }

/* ---------------------------------------------------------------------------
   Modo "Pases del día". Se carga el taco de papeles de una vez: se marca a quien
   trajo el suyo y al cerrar el resto queda "sin pase" en una sola escritura.
   --------------------------------------------------------------------------- */
.pases-busca {
  width: 100%;
  background: var(--bg-panel-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: inherit;
  /* 16px: por debajo de eso, iOS hace zoom solo al enfocar el campo y descoloca todo. */
  font-size: var(--fs-control);
  padding: var(--pad-btn);
  margin-bottom: 10px;
}
/* Alto acotado para que el aviso y los botones del cierre entren siempre en pantalla:
   si la lista empujara el "Listo" fuera de la vista habría que scrollear para terminar. */
.pases-lista {
  max-height: 46vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
/* Mismo tratamiento que .letra-grupo en la lista de asistencia: fondo de página, para que
   el renglón se lea como un hueco que separa grupos y no como una fila más. */
.pase-letra {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--bg);
  color: var(--turno-color);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 5px 14px;
}
.pase-fila {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  /* 52px: acá no hay íconos al lado con los que competir, así que la fila entera es el
     área táctil y llega al mínimo recomendado para mobile, cosa que la fila de la lista
     de asistencia (22px por botón) no cumple. */
  min-height: 52px;
  background: var(--bg-panel);
  border: 0;
  border-left: 4px solid transparent;
  padding: 8px 14px;
  font-family: inherit;
  font-size: var(--fs-md);
  color: var(--text);
  text-align: left;
  cursor: pointer;
}
/* .pase-fila define su propio display, así que sin esto el atributo hidden del filtro no
   la ocultaría (gana el display de la clase por especificidad). */
.pase-fila[hidden] { display: none; }
.pase-letra[hidden] { display: none; }
/* El tilde se centra con flex sobre el viewBox del SVG. La primera versión lo posicionaba
   con left/top a mano y quedaba corrido: el dibujo no está centrado en su propia caja. */
.pase-marca {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 1.5px dashed var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--on-accent);
}
.pase-marca svg {
  width: 13px;
  height: 13px;
  display: block;
  stroke-width: 3;
  opacity: 0;
  transition: opacity 0.12s ease;
}
.pase-fila.is-con-pase { border-left-color: var(--turno-color); }
.pase-fila.is-con-pase .pase-marca {
  background: var(--turno-color);
  border: 1.5px solid var(--turno-color);
}
.pase-fila.is-con-pase .pase-marca svg { opacity: 1; }
.pase-fila.is-con-pase .pase-nombre { color: var(--text-muted); }
.pase-nombre {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pase-cinto {
  flex-shrink: 0;
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--text-muted);
}
.pases-sin-resultado {
  padding: 18px 14px;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}
.pases-sin-resultado[hidden] { display: none; }
.pases-aviso {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  line-height: 1.55;
  margin-top: 10px;
}
.pases-aviso b { color: var(--turno-color); font-weight: 700; }
.pases-acciones {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 14px;
}

/* Días que no son hoy: la asistencia queda en solo lectura hasta desbloquear ese día a
   mano. El error que evita es retroceder en el calendario sin darse cuenta y, al tocar
   un nombre, crear el registro de una clase que nunca existió (marcar una ausencia
   escribe taken:true, y deja a todos los demás como presentes). El cartel además sirve
   de señal permanente de que uno no está parado en el día de hoy. */
.day-lock {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-left: 3px solid var(--text-muted);
  border-radius: var(--r-md);
  padding: 10px 12px;
  margin: 2px 0 14px;
}
.day-lock[hidden] { display: none; }
.day-lock-text {
  font-size: var(--fs-sm);
  line-height: 1.5;
  color: var(--text-muted);
  min-width: 0;
}
.day-lock-btn {
  flex-shrink: 0;
  background: none;
  border: 1px solid var(--turno-color);
  color: var(--turno-color);
  font-size: var(--fs-sm);
  font-weight: 600;
  padding: var(--pad-btn-sm);
  border-radius: var(--r-sm);
  cursor: pointer;
}
/* Ya desbloqueado: el cartel no desaparece, cambia de tono. Sigue haciendo falta el
   recordatorio de que lo que se está tocando no es el día de hoy. */
.day-lock.is-editing { border-left-color: var(--danger-text); }
.day-lock.is-editing .day-lock-text { color: var(--danger-text); }

/* En solo lectura las filas no deben *parecer* tocables, aunque el guard de
   isDayEditable() ya impida la escritura. */
.student-list.is-locked .student-row { cursor: default; }

/* Página de estadísticas (estadisticas.html): reusa .wrap/.header-top/.login-box.
   Se usa sobre todo en PC/notebook, así que esta página (y solo esta) tiene más
   ancho que el resto de la app, que se mantiene angosta a propósito para mobile. */
.stats-page .wrap { max-width: 1100px; }
.page-back-link {
  display: inline-block;
  color: var(--turno-color);
  font-size: var(--fs-sm);
  text-decoration: none;
  margin-bottom: 20px;
}
.page-back-link:hover { text-decoration: underline; }
.stat-card {
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 16px;
  margin-bottom: 24px;
}
.birthday-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.birthday-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border);
  border-left: 3px solid var(--turno-color);
  border-radius: var(--r-sm);
  padding: 10px 12px;
  font-size: var(--fs-md);
}
.birthday-row.is-today { border-left-color: var(--brand-red); }
.birthday-row-date { color: var(--text-muted); font-size: var(--fs-sm); white-space: nowrap; }
/* El nombre no tenía ningún peso propio: mismo tamaño/color casi que la sección de
   abajo y la fecha al costado, así que no había título claro en la fila. */
.birthday-row-name { display: flex; flex-direction: column; gap: 2px; min-width: 0; font-weight: 600; color: var(--text); }
.birthday-row-section { font-size: var(--fs-xs); color: var(--text-muted); font-weight: 400; }
.status-baja { color: var(--danger-text); font-weight: 600; }

/* Tiles del resumen (alumnos por sede / asistencia de hoy / fichas pendientes) */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
  margin-bottom: 24px;
}
.stat-tile {
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.stat-tile-label { font-size: var(--fs-sm); color: var(--text-muted); }
.stat-tile-value {
  font-family: 'Barlow Semi Condensed', sans-serif;
  font-size: var(--fs-xl);
  font-weight: 700;
}
.stat-subrows { margin-top: 8px; display: flex; flex-direction: column; gap: 4px; }
.stat-subrow {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}
.stat-subrow-warn { color: var(--danger-text); font-weight: 600; }
.stat-subrow-ok { font-size: var(--fs-sm); color: var(--turno-color); }

/* Vista de alumnos: tabla ancha, pensada para PC; scrollea horizontal si no entra.
   La barra se deja bien visible (no la fina/oculta por default) para que se note
   que hay más columnas hacia la derecha (contacto de emergencia, salud, etc). */
.roster-table-wrap {
  overflow-x: auto;
  scrollbar-width: auto;
  scrollbar-color: var(--text-muted) var(--bg-panel-2);
  padding-bottom: 4px;
}
.roster-table-wrap::-webkit-scrollbar { height: 12px; }
.roster-table-wrap::-webkit-scrollbar-track { background: var(--bg-panel-2); border-radius: var(--r-sm); }
.roster-table-wrap::-webkit-scrollbar-thumb { background: var(--text-muted); border-radius: var(--r-sm); border: 2px solid var(--bg-panel-2); }
.roster-table-wrap::-webkit-scrollbar-thumb:hover { background: var(--text); }
.roster-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-sm);
}
.roster-table th {
  text-align: left;
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border);
  padding: 8px 10px;
  white-space: nowrap;
}
.roster-table td {
  padding: 9px 10px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
/* La celda de cinturón en la tabla de alumnos usa la misma cinta que la lista y la
   ficha, para que el color signifique lo mismo en las tres pantallas. */
.belt-cell {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.roster-table tbody tr:hover { background: var(--bg-panel-2); }
.roster-table td.empty-state, .roster-table td.loading { white-space: normal; }
.muted { color: var(--text-muted); }
