/* ============================================================
   AppsHub UAI — Estructura
   Contenedor, encabezado, secciones y grillas.
   ============================================================ */

.envoltorio {
    max-width: var(--ancho-contenido);
    margin: 0 auto;
    padding: var(--space-6) var(--space-6) 56px;
}

/* ── Encabezado ──────────────────────────────────────────── */

.encabezado {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
    margin-bottom: 22px;
}

.marca {
    display: flex;
    align-items: center;
    gap: 14px;
}

.marca h1 {
    margin: 0;
    font-size: 22px;
    font-weight: var(--weight-semibold);
    letter-spacing: .2px;
}

.marca .subtitulo {
    margin: 2px 0 0;
    font-size: 13.5px;
    color: var(--text-muted);
}

/* **Sin `position: relative`.** La tenía de cuando el ícono del tema se
   dibujaba encimado sobre el `<select>` con `position: absolute`; ese ícono ya
   no existe y nada más acá adentro se posiciona contra esta caja.

   Dejarla no era inocuo: convertía a `.usuario` en el bloque contenedor del
   panel del selector de tema, y como esta caja mide lo que miden sus cuatro
   botones, el panel anclado a su borde derecho arrancaba en −32px cuando el
   encabezado envolvía. Medio panel fuera de la pantalla, y sin scroll que lo
   alcance porque flota. */
.usuario {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    /* Envuelve porque sin JavaScript el selector de tema muestra además su
       botón «Aplicar» —que el script esconde—, y esos 40px de más sacaban la
       fila fuera de la pantalla en 375px: la página entera quedaba con scroll
       horizontal. Con JS todo entra en una línea y esto no cambia nada. */
    flex-wrap: wrap;
    justify-content: flex-end;
}

/* Texto introductorio. Discreto a propósito: quien entra por primera vez lo lee
   una vez, pero quien usa el hub a diario lo tiene delante siempre.

   El margen superior compite con el inferior del encabezado y gana el mayor —se
   colapsan, no se suman—, por eso 30 da 30 y no 52. */
.intro {
    margin: 30px 0 34px;
    font-size: var(--text-base);
    line-height: 1.55;
    color: var(--text-muted);
}

/* ── Buscador ────────────────────────────────────────────────
   Más aire abajo que entre secciones (34px): separa la zona de encabezado y
   búsqueda del contenido, en vez de dejar el campo flotando a igual distancia
   de todo. */
.barra-busqueda {
    margin-bottom: var(--space-10);
}

/* ── Secciones ───────────────────────────────────────────── */

.seccion {
    margin-bottom: 34px;
}

/* `flex-wrap` porque acá adentro no vive sólo el título: en `/gestion/usuarios`
   comparte fila con el buscador, y sin envolver su botón «Buscar» queda fuera de
   la pantalla en un teléfono — 47px afuera a 375px, 102px a 320px. Es la misma
   razón por la que `.encabezado` y `.nav-gestion` ya lo llevan. */
.seccion-titulo {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin: 0 0 14px;
}

/* 16.5px: por encima de los títulos de tarjeta (16px). El título de la sección
   tiene que pesar más que el de sus tarjetas, no menos. */
.seccion-titulo h2 {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0;
    font-size: 16.5px;
    font-weight: var(--weight-semibold);
}

.seccion-titulo h2 .ti {
    width: 17px;
    height: 17px;
    color: var(--acento-texto);
}

/* ── Grillas ─────────────────────────────────────────────── */

.grilla {
    display: grid;
    gap: var(--space-4);
}

.grilla-tarjetas {
    grid-template-columns: repeat(auto-fill, minmax(264px, 1fr));
}

/* Columnas más anchas que cuando el tile era sólo el nombre: con una línea de
   descripción, 208px dejaba el texto en cuatro renglones. */
.grilla-enlaces {
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: var(--space-3);
}

/* ── Mampostería ─────────────────────────────────────────────
   Cada tarjeta ocupa sólo su altura real en vez de estirarse hasta la más alta
   de su fila. Filas implícitas de 4px y un span por tarjeta que calcula el JS.

   El row-gap pasa a 0 y lo repone el margen inferior de la tarjeta —el span ya
   lo incluye—, así el sobrante por redondeo es de 0 a 3px por tarjeta.

   Sin JS las tarjetas se ven igual de bien, sólo que alineadas por fila: el
   `.mamposteria` lo agrega el script, así que este bloque no llega a aplicar. */
.mamposteria {
    grid-auto-rows: 4px;
    row-gap: 0;
    align-items: start;
}

.mamposteria .tarjeta,
.mamposteria .enlace {
    height: auto;
}

/* El margen va en el hijo DIRECTO de la grilla y no en `.tarjeta`: según el
   caso ese hijo es la tarjeta, el envoltorio de una bloqueada con contacto, o
   el contenedor de área de un reporte. Apuntar a la tarjeta dejaría el margen
   adentro del envoltorio, donde el span calculado no lo cuenta y las filas se
   solapan. */
.mamposteria.grilla-tarjetas > * {
    margin-bottom: var(--space-4);
}

.mamposteria.grilla-enlaces > * {
    margin-bottom: var(--space-3);
}

/* El margen de la última fila se sumaba al cierre de la sección (34px pasaban a
   50px). Se descuenta acá para que la distancia entre secciones la fije sólo
   `.seccion`. El `:not(:empty)` evita el tirón cuando la grilla no tiene nada
   —una categoría de reportes vacía—. */
.mamposteria.grilla-tarjetas:not(:empty) {
    margin-bottom: calc(var(--space-4) * -1);
}

.mamposteria.grilla-enlaces:not(:empty) {
    margin-bottom: calc(var(--space-3) * -1);
}

/* ── Pie ─────────────────────────────────────────────────── */

.pie {
    margin-top: 44px;
    padding-top: var(--space-6);
    border-top: 1px solid var(--border-default);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
}

.pie .logo-uai {
    height: 66px;
    width: auto;
    max-width: 100%;
    opacity: .95;
}

/* Sello de publicación. Sirve para saber si alguien está viendo una copia
   cacheada: se le pregunta qué figura acá.

   Sin opacidad: a 11px, atenuar un gris que ya es discreto dejaba el contraste
   en 3.06:1, debajo del 4.5:1 que pide WCAG. */
.pie-version {
    margin: 0;
    font-size: var(--text-xs);
    letter-spacing: .3px;
    color: var(--text-muted);
}

.pie-separador {
    margin: 0 7px;
}

/* ── Estados vacíos ──────────────────────────────────────── */

.estado-vacio {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-10) 0 var(--space-4);
    color: var(--text-muted);
    font-size: var(--text-md);
    text-align: center;
}

.estado-vacio .ti {
    width: 36px;
    height: 36px;
    opacity: .45;
}

/* La línea de abajo del estado vacío: a quién pedirle acceso, dónde está el
   calendario oficial. Vive acá y no en las hojas de módulo porque la usan
   horarios, biblioteca y agenda, y la agenda no carga ninguna de las dos. */
.vacio-detalle {
    margin-top: var(--space-3);
    font-size: var(--text-sm);
    color: var(--text-muted);
    text-align: center;
}

/* Dentro de un modal es otra cosa. La versión de arriba está pensada para una
   página vacía —un bloque centrado con aire alrededor, que ahí se lee como «no
   hay nada» y no como «falta algo»—, y en un modal de 560px, debajo de un
   título alineado a la izquierda, esos 40px de sangría superior y el centrado
   se leen como un párrafo mal puesto.

   Acá es una frase más del texto, así que se comporta como tal: al ras de la
   izquierda, con el ícono al lado de su primera línea y no encima. */
.modal .estado-vacio {
    flex-direction: row;
    align-items: flex-start;
    padding: 0;
    text-align: left;
    font-size: var(--text-base);
    line-height: 1.5;
}

.modal .estado-vacio .ti {
    width: 18px;
    height: 18px;
    margin-top: 3px;
    flex: none;
}

@media (max-width: 640px) {
    .envoltorio {
        padding: var(--space-4) var(--space-4) var(--space-10);
    }

    .encabezado {
        gap: var(--space-3);
    }
}
