/* ==========================================================================
   GRADANN · 2. DISPOSICIÓN
   Barra superior, menú lateral, área de contenido, panel lateral, modal y
   avisos. Sólo estructura: lo que va dentro son componentes.
   ========================================================================== */

/* ==========================================================================
   Barra superior — azul muy oscuro, fija, con la marca a la izquierda
   ========================================================================== */

.barra {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: var(--e2);
  min-height: var(--barra-alto);
  padding: 0 var(--e3);
  background: var(--marino-barra);
  color: var(--marino-texto);
}

.barra__logo {
  flex: 0 0 auto;
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: .08em;
  color: var(--texto-inv);
  text-decoration: none;
}
.barra__logo:hover { text-decoration: none; }
.barra__logo span { color: var(--rojo); }

/* Selector de establecimiento */
.barra__estab select {
  min-width: 9ch;
  padding: var(--e0) var(--e1);
  border: 1px solid var(--marino-borde);
  border-radius: var(--radio-chico);
  background: var(--marino);
  color: var(--texto-inv);
  font-weight: 600;
}

/* Búsqueda global: se lleva el espacio sobrante pero nunca baja de 14ch. */
.barra__buscar {
  position: relative;
  flex: 1 1 auto;
  min-width: 14ch;
  max-width: 62ch;
}
/* `.barra .barra__buscar input` y no `.barra__buscar input` a secas.
 *
 * `componentes.css` viste TODOS los campos con `input[type="search"]`, que pesa
 * 0,1,1 —igual que `.barra__buscar input`— y se enlaza DESPUÉS, así que ganaba
 * y le ponía fondo blanco. Pero sólo pisaba el fondo: el `color` seguía siendo
 * el blanco de aquí. Resultado medido: fondo rgb(255,255,255) y texto
 * rgb(255,255,255). Se escribía y no se veía nada; el usuario sólo sabía que
 * algo había entrado porque al pulsar Enter salía el resultado.
 *
 * Con la clase del padre delante pesa 0,2,1 y gana por especificidad, no por
 * orden de archivo. Es la misma trampa que el hover de los botones (D-079) y
 * la del campo que pide un dato (D-097); aquí se declara TODO lo visual junto
 * para que no vuelva a quedarse media regla ganando y media perdiendo.
 * Ver DECISIONES.md · D-101.
 */
.barra .barra__buscar input {
  width: 100%;                     /* excepción permitida: columna acotada */
  height: auto;
  padding: var(--e1) var(--e2);
  border: 1px solid var(--marino-borde);
  border-radius: var(--radio-chico);
  background: var(--marino);
  color: var(--texto-inv);
}
.barra .barra__buscar input:focus { border-color: var(--marino-tenue); }
.barra .barra__buscar input::placeholder { text-transform: none; color: #8AA0BC; }

/* Escaneo: campo y botón de cámara pegados */
/* Selector de dos clases a propósito: la regla .scan vive en componentes.css,
   que se enlaza DESPUÉS, así que con .barra__escaneo a secas la variante de la
   barra perdía y le entraba el hueco entre la caja y la cámara. Aquí gana por
   especificidad, no por orden de archivo. */
.barra .barra__escaneo {
  display: flex;
  align-items: stretch;
  gap: 0;
  border: 1px solid var(--marino-borde);
  border-radius: var(--radio-chico);
  overflow: hidden;
}
/* Lo mismo que arriba: con la clase del padre gana por especificidad. Sin ella
   el campo de escaneo de la barra salía blanco sobre blanco. D-101. */
.barra .barra__escaneo input {
  width: 18ch;
  height: auto;
  padding: var(--e1) var(--e2);
  border: 0;
  border-radius: 0;
  background: var(--marino);
  color: var(--texto-inv);
}
.barra .barra__escaneo input:focus { background: var(--marino-2); }
.barra .barra__escaneo input::placeholder { color: #8AA0BC; }
.barra__escaneo .btn {
  border: 0;
  border-left: 1px solid var(--marino-borde);
  border-radius: 0;
}

.barra__solo-movil { display: none; }

.barra__derecha { display: flex; align-items: center; gap: var(--e2); margin-left: auto; }

/* Campana con su contador */
.campana {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--e0);
  padding: var(--e1) var(--e2);
  border: 1px solid var(--marino-borde);
  border-radius: var(--radio-chico);
  background: transparent;
  color: var(--marino-texto);
  cursor: pointer;
  text-decoration: none;
}
.campana:hover { background: var(--marino); color: var(--texto-inv); text-decoration: none; }
.campana__contador {
  display: inline-grid;
  place-items: center;
  min-width: 1.5em;
  height: 1.5em;
  padding: 0 .35em;
  border-radius: 999px;
  background: var(--rojo);
  color: var(--texto-inv);
  font-size: .72rem;
  font-weight: 700;
}

.barra__usuario {
  display: inline-flex;
  align-items: center;
  gap: var(--e0);
  color: var(--marino-texto);
  font-weight: 500;
  white-space: nowrap;
}

.barra__menu-btn { display: none; }

/* ==========================================================================
   Menú lateral — azul profundo, con iconos y secciones
   ========================================================================== */

.disposicion { display: flex; min-height: calc(100dvh - var(--barra-alto)); }

/* El menú es su propia columna, pegada y con su propio scroll.
   Antes era un bloque más del flujo: medía 1079 px en una ventana de 768, así
   que «Configuración» y «Usuarios» quedaban por debajo del borde y sólo se
   alcanzaban si la PÁGINA tenía contenido largo que hiciera scroll. De ahí que
   las opciones parecieran aparecer y desaparecer según la sección, y que la
   franja azul terminara a distinta altura en cada pantalla.
   Ver DECISIONES.md · D-086. */
.menu {
  flex: 0 0 var(--menu-ancho);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: var(--barra-alto-real, var(--barra-alto));
  align-self: flex-start;
  /* `dvh` y no `vh`: en el móvil, al abrir el teclado el navegador encoge la
     ventana pero `100vh` sigue valiendo lo de antes, así que el menú —y todo lo
     que dependa de esa unidad— se queda más alto que la pantalla y descuadra la
     página. `dvh` es la altura DINÁMICA y se ajusta sola. Ver D-092. */
  height: calc(100dvh - var(--barra-alto-real, var(--barra-alto)));
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-top: var(--e1);
  padding-bottom: var(--e3);
  background: var(--marino);
  color: var(--marino-texto);
  --tabla-sticky-top: 0px;
}

/* Barra de desplazamiento discreta: sobre el azul profundo, la del sistema
   canta demasiado. */
.menu::-webkit-scrollbar { width: 8px; }
.menu::-webkit-scrollbar-thumb { background: var(--marino-2); border-radius: 999px; }
.menu::-webkit-scrollbar-track { background: transparent; }

/* Cabecera de grupo: es un botón, porque se puede plegar. */
.menu__grupo {
  display: flex;
  align-items: center;
  gap: var(--e1);
  width: 100%;
  padding: var(--e3) var(--e3) var(--e0);
  border: 0;
  background: transparent;
  color: var(--marino-tenue);
  font: inherit;
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .09em;
  text-transform: uppercase;
  text-align: left;
  cursor: pointer;
}
.menu__grupo:hover { color: var(--texto-inv); }
.menu__grupo__texto { flex: 1 1 auto; }
.menu__grupo .icono {
  width: 1em;
  height: 1em;
  transition: transform .12s ease-out;
}
.menu__grupo[aria-expanded="true"] .icono { transform: rotate(90deg); }

/* Con el dedo, la cabecera de grupo es lo que se pulsa para plegar: 25 px no
   se aciertan. La regla va aquí, junto al componente, y no en un bloque táctil
   aparte (ver componentes.css · .btn--chico y DECISIONES.md · D-084). */
@media (pointer: coarse) {
  .menu__grupo { min-height: 44px; padding-top: var(--e2); padding-bottom: var(--e1); }
}

/* Cuando el grupo está plegado y la pantalla actual vive dentro, se marca:
   se respeta la decisión de plegarlo, pero sin perder el «usted está aquí». */
.menu__grupo--contiene-activo[aria-expanded="false"] { color: var(--texto-inv); }
.menu__grupo--contiene-activo[aria-expanded="false"]::after {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--rojo);
}

/* Las entradas de un grupo. `hidden` las quita del flujo cuando se pliega, así
   que el menú encoge de verdad y no queda un hueco. */
.menu__lista { display: flex; flex-direction: column; }
.menu__lista[hidden] { display: none; }

.menu__item {
  display: flex;
  align-items: center;
  gap: var(--e2);
  padding: var(--e1) var(--e3);
  color: var(--marino-texto);
  text-decoration: none;
  border-left: 3px solid transparent;
}
.menu__item:hover { background: var(--marino-2); color: var(--texto-inv); text-decoration: none; }

/* Una entrada del menú es un rótulo, no un párrafo: NUNCA envuelve. Si algún
   día un nombre no cabe, se recorta con puntos suspensivos y se ve al instante
   —hay que arreglarlo ensanchando o acortando—, en vez de partirse en dos
   líneas y descuadrar el menú en silencio. `tests/auditar_ui.js` lo vigila. */
.menu__item > span:not(.menu__atajo) {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Entrada de una fase que todavía no llega: se ve, se entiende que existirá,
   y no se puede pulsar. Mejor que un enlace que devuelve 404. */
.menu__item--pendiente { opacity: .45; cursor: default; }
.menu__item--pendiente:hover { background: transparent; color: var(--marino-texto); }
.menu__item--pendiente .menu__atajo { font-size: .68rem; text-transform: uppercase; letter-spacing: .06em; }

.menu__item.activo {
  background: var(--rojo);
  border-left-color: var(--rojo-oscuro);
  color: var(--texto-inv);
  font-weight: 600;
}

.menu__item .icono { opacity: .8; }
.menu__item.activo .icono { opacity: 1; }

/* El atajo de teclado, discreto y a la derecha. */
.menu__atajo { margin-left: auto; font-size: .78rem; opacity: .65; }

/* ==========================================================================
   Contenido
   ========================================================================== */

.contenido {
  flex: 1 1 auto;
  min-width: 0;
  padding: var(--e3) var(--e4) var(--e5);
}

.pagina__cabecera {
  display: flex;
  align-items: center;
  gap: var(--e2);
  flex-wrap: wrap;
  margin-bottom: var(--e3);
}
.pagina__cabecera h1 { margin: 0; }
.pagina__cabecera .acciones {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;   /* sin esto, en móvil arrastra la página de lado */
  gap: var(--e1);
}

/* ==========================================================================
   Panel lateral (drawer)
   ========================================================================== */

.drawer-fondo {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: rgba(13, 39, 69, .35);
  opacity: 0;
  pointer-events: none;
  transition: opacity .14s ease-out;
}
.drawer-fondo.abierto { opacity: 1; pointer-events: auto; }

.drawer {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  z-index: 51;
  width: min(46rem, 100vw);
  display: flex;
  flex-direction: column;
  background: var(--superficie);
  border-left: 1px solid var(--borde);
  transform: translateX(100%);
  transition: transform .16s ease-out;
}
.drawer.abierto { transform: translateX(0); }
.drawer--nivel2 { width: min(38rem, 100vw); box-shadow: -1px 0 0 var(--borde); }

.drawer__cabecera {
  display: flex;
  align-items: center;
  gap: var(--e2);
  padding: var(--e2) var(--e3);
  background: var(--marino);
  color: var(--texto-inv);
}
.drawer__titulo { flex: 1 1 auto; min-width: 0; margin: 0; font-size: 1rem; font-weight: 600; }
.drawer__cerrar {
  border: 0;
  background: transparent;
  color: var(--marino-texto);
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
  padding: var(--e0) var(--e1);
  border-radius: var(--radio-chico);
}
.drawer__cerrar:hover { background: var(--marino-2); color: var(--texto-inv); }

.drawer__cuerpo {
  flex: 1 1 auto;
  overflow-y: auto;
  /* Sin relleno ARRIBA: el área de relleno de un contenedor con scroll no se
     recorta, y las filas se verían pasar por esa franja por encima de la
     cabecera pegajosa. El aire lo pone el espaciador, que sí se va con el
     scroll. Ver DECISIONES.md · D-068. */
  padding: 0 var(--e3) var(--e3);
  --tabla-sticky-top: 0px;
}
.drawer__cuerpo::before { content: ''; display: block; height: var(--e3); }

.drawer__acciones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e1);
  padding: var(--e2) var(--e3);
  border-top: 1px solid var(--borde);
  background: var(--superficie-2);
}

/* ==========================================================================
   Modal — sólo para confirmaciones destructivas
   ========================================================================== */

.modal-fondo {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: var(--e3);
  background: rgba(13, 39, 69, .4);
}

.modal {
  width: min(30rem, 100%);
  background: var(--superficie);
  border-radius: var(--radio);
  overflow: hidden;
}
.modal__cabecera {
  padding: var(--e2) var(--e3);
  background: var(--marino);
  color: var(--texto-inv);
  font-weight: 600;
}
.modal__cuerpo {
  padding: 0 var(--e3) var(--e3);
  --tabla-sticky-top: 0px;
}
.modal__cuerpo::before { content: ''; display: block; height: var(--e3); }
.modal__pie {
  display: flex;
  gap: var(--e1);
  justify-content: flex-end;
  padding: var(--e2) var(--e3);
  border-top: 1px solid var(--borde);
  background: var(--superficie-2);
}

/* ==========================================================================
   Avisos flotantes (toasts) — nunca alert()
   ========================================================================== */

.toasts {
  position: fixed;
  right: var(--e3);
  bottom: var(--e3);
  z-index: 70;
  display: flex;
  flex-direction: column;
  gap: var(--e1);
  max-width: min(28rem, calc(100vw - 2 * var(--e3)));
}

.toast {
  display: flex;
  align-items: flex-start;
  gap: var(--e1);
  padding: var(--e2) var(--e3);
  border-radius: var(--radio-chico);
  border-left: 3px solid var(--borde-fuerte);
  background: var(--superficie);
  box-shadow: var(--sombra);
}
.toast--ok    { border-left-color: var(--verde); }
.toast--error { border-left-color: var(--rojo); }
.toast--aviso { border-left-color: var(--ambar); }

/* ==========================================================================
   Adaptación a pantallas estrechas
   ========================================================================== */

@media (max-width: 1100px) {
  /* 14.5rem y no 13: el rótulo más largo —«Plano de ubicaciones»— necesita
     204 px y 13rem daban 195, porque `1rem` aquí son 15 px y no 16 (el tamaño
     base es un clamp que depende del ancho). Con 13rem, SEIS entradas partían
     en dos líneas y el menú entero cambiaba de alto al cruzar el punto de
     corte, que es el salto que se veía. Ver DECISIONES.md · D-091. */
  :root { --menu-ancho: 14.5rem; }
  .contenido { padding: var(--e3) var(--e3) var(--e5); }
}

@media (max-width: 820px) {
  .barra { flex-wrap: wrap; row-gap: var(--e1); padding-bottom: var(--e1); }
  .barra__buscar { order: 10; flex-basis: 100%; max-width: none; }
}

@media (max-width: 700px) {
  .barra__menu-btn {
    display: inline-flex;
    align-items: center;
    padding: var(--e1);
    border: 1px solid var(--marino-borde);
    border-radius: var(--radio-chico);
    background: transparent;
    color: var(--marino-texto);
    cursor: pointer;
  }

  .menu {
    position: fixed;
    /* Empieza BAJO la barra, no en 0: con `top: 0` y z-index 45 el menú se
       pintaba encima de su propio botón (la barra es 40) y el dedo pulsaba la
       cabecera de grupo que quedara debajo en vez de cerrar. Funcionaba por
       JavaScript y no con el dedo. Ver DECISIONES.md · D-092. */
    top: var(--barra-alto-real, var(--barra-alto));
    bottom: 0;
    left: 0;
    /* Por debajo de la barra (40) y por encima del contenido. */
    z-index: 38;
    width: min(17rem, 82vw);
    /* Altura EXPLÍCITA. Con `top` + `bottom` + `height: auto` el navegador
       resolvía la altura del CONTENIDO —1214 px en una ventana de 893— y el
       menú se salía por abajo. */
    bottom: auto;
    height: calc(100dvh - var(--barra-alto-real, var(--barra-alto)));
    overflow-y: auto;
    transform: translateX(-100%);
    transition: transform .14s ease-out;
  }
  .menu.abierto { transform: translateX(0); }

  /* La barra, en una sola línea: menú · logo · buscar · escanear · avisos.
     Lo demás vive en el pie del menú (lo mueve app.js) o se convierte en
     icono. Antes eran cuatro líneas y 155 px de alto. */
  .barra { flex-wrap: nowrap; padding-bottom: 0; }
  .barra__solo-movil { display: inline-flex; }
  .barra__buscar { display: none; }
  .barra__derecha { margin-left: auto; gap: var(--e0); }
  .barra__derecha .campana span:not(.campana__contador) { display: none; }
  /* En el teléfono se queda SÓLO el botón de cámara: el campo de escribir el
     código ocupaba 145 px de los 390 y hacía desbordar la barra 23 px, que a su
     vez sacaba de la pantalla el panel de los desplegables. Y sobra: no hay
     pistola conectada a un móvil, F2 no existe, y todas las pantallas
     operativas traen su propio campo de escaneo con su cámara. */
  .barra__escaneo input { display: none; }
  .barra__escaneo { border: 0; }
  .barra__logo { font-size: 1rem; }

  /* El pie del menú recoge lo que se saca de la barra en el móvil. Va pegado
     abajo para que no quede flotando a media lista. */
  .menu__pie {
    margin-top: auto;
    display: flex;
    flex-direction: column;
    gap: var(--e1);
    padding: var(--e3);
    border-top: 1px solid var(--marino-borde);
  }
  .menu__pie .barra__usuario { color: var(--marino-texto); font-size: .9rem; }
  .menu__pie select { width: 100%; }
  .menu__pie form { width: 100%; }
  .menu__pie .campana { width: 100%; justify-content: flex-start; }

  /* El velo: cierra al tocarlo y deja claro que el fondo no está activo.
     Es el mismo recurso que el panel lateral ya usaba bien. */
  .menu-fondo {
    position: fixed;
    inset: var(--barra-alto-real, var(--barra-alto)) 0 0 0;
    z-index: 37;
    background: rgba(13, 39, 69, .35);
    opacity: 0;
    pointer-events: none;
    transition: opacity .14s ease-out;
  }
  .menu-fondo.abierto { opacity: 1; pointer-events: auto; }
}

@media (max-width: 600px) {
  .barra__usuario { display: none; }
  .barra__escaneo input { width: 12ch; }
  .drawer { width: 100vw; }
}

@media (max-width: 480px) {
  .barra__buscar { width: 100%; }   /* única excepción permitida al 100 % */
  .pagina__cabecera .acciones { width: 100%; }
}

/* Pantalla táctil: objetivos de 44 px */
@media (pointer: coarse) {
  /* Sube el token y con él suben input, select y botón a la vez: una sola
     línea en vez de una regla por tipo, y siguen midiendo todos lo mismo. */
  :root { --control-alto: 44px; }

  .btn { padding: var(--e1) var(--e3); }
  table.tabla tbody tr { height: 2.6em; }
  .menu__item { padding: var(--e2) var(--e3); }
  .barra__menu-btn,
  .campana,
  .barra .scan__camara,
  .drawer__cerrar { min-height: 44px; min-width: 44px; }
}

/* ==========================================================================
   2.9  Ingreso al sistema
   --------------------------------------------------------------------------
   Esta pantalla NO TENÍA NI UNA REGLA. El marcado usaba `.login`,
   `.login__caja`, `.login__marca`, `.login__cuerpo` y `.login__pie`, y ninguna
   de las cinco existía en ninguna hoja: lo que se veía era el flujo por
   defecto del navegador con la tipografía de `base.css`. De ahí que «se viera
   feo» y que en el celular el formulario apareciera pegado arriba.

   El fondo lo pone la imagen de marca, que ya trae el logotipo arriba: hay una
   apaisada para la pantalla grande y una vertical para el teléfono, porque
   recortar la apaisada en un móvil deja el logotipo fuera de cuadro.

   POR QUÉ NO SE CENTRA VERTICALMENTE. Centrar con `vh`, `dvh` o `flex` sobre
   toda la altura hace que al abrir el teclado del teléfono —que se come media
   pantalla— la tarjeta salte hacia arriba y el campo que se está tecleando se
   vaya debajo del teclado. Aquí la tarjeta cuelga de un desplazamiento desde
   ARRIBA: el teclado puede abrirse y cerrarse las veces que quiera, que la
   tarjeta no se mueve, y si no cabe la página se desplaza como cualquier otra.
   Ver DECISIONES.md · D-101.
   ========================================================================== */

.login {
  /* `vh` aquí es seguro: sólo garantiza que el fondo cubra la pantalla. Al
     abrir el teclado esta caja se encoge, pero la tarjeta cuelga de arriba y
     no se mueve — que es justo lo que se quería evitar. */
  min-height: 100vh;
  padding: 0 var(--e3) var(--e5);
  /* El desplazamiento que deja libre el logotipo NO se elige a ojo: se calcula
     a partir de dónde acaba en la imagen y de cómo la escala `cover`.
     Medido sobre el archivo: el logotipo de la apaisada (1600×900) termina al
     24,0 % del alto.

     `cover` escala por el lado que domine, y eso cambia con la pantalla:
       · pantalla ALTA  → manda el alto: el logotipo acaba en 0,240 × alto  → 24vh
       · pantalla ANCHA → manda el ancho: 0,240 × (900/1600) × ancho        → 13,5vw
     Se toma el mayor de los dos con `max()` y se le suma un respiro. Con
     `clamp()` en `rem` esto salía mal, porque la raíz tipográfica del proyecto
     es fluida: 12rem son 165 px en un móvil y no los 192 que se suponían, y la
     tarjeta se comía el subtítulo del logotipo. */
  padding-top: max(29vh, 17vw);
  background-image: url('/assets/img/login-pc.jpg');
  background-size: cover;
  /* `top center` y no `center`: el logotipo vive arriba en la imagen y tiene
     que quedar arriba pase lo que pase con la proporción de la pantalla. */
  background-position: top center;
  background-repeat: no-repeat;
  /* Nada de `fixed`: en iOS provoca saltos y repintados al desplazar. */
  background-attachment: scroll;
  background-color: var(--marino-barra);   /* si la imagen no carga */
}

@media (max-width: 700px) {
  .login {
    background-image: url('/assets/img/login-movil.jpg');
    /* La vertical mide 900×1600 y su logotipo acaba al 21,1 % del alto:
         alto  manda → 0,211 × alto              → 21vh
         ancho manda → 0,211 × (1600/900) × ancho → 37,5vw
       (lo segundo pasa con el teléfono en horizontal, donde la pantalla es
       más ancha que alta y `cover` escala por el ancho). */
    padding-top: max(26vh, 43vw);
    padding-left: var(--e2);
    padding-right: var(--e2);
  }
}

.login__caja {
  width: min(24rem, 100%);
  margin: 0 auto;
  background: var(--superficie);
  border: 1px solid var(--borde-fuerte);
  border-radius: var(--radio);
  overflow: hidden;
}

/* La marca ya está EN la imagen del fondo, así que no se pinta dos veces: se
   conserva en el documento sólo para quien usa lector de pantalla.
   El ocultado va con `solo-lectura-pantalla`, la clase que el proyecto ya tiene
   en `base.css`, y NO con una copia hecha aquí: la auditoría reconoce esa clase
   y sabe que no debe medirla. Con la copia, la sonda de texto veía un bloque de
   1 px con 268 px de texto dentro y lo cantaba como recorte en las tres
   anchuras. Una convención que existe y no se usa es una convención que
   estorba. */

.login__cuerpo {
  display: flex;
  flex-direction: column;
  gap: var(--e3);
  padding: var(--e4);
}

.login__cuerpo form {
  display: flex;
  flex-direction: column;
  gap: var(--e3);
}

/* Los campos ocupan la tarjeta entera: es la excepción que el propio pliego
   admite —una columna acotada—, y aquí la columna son 24rem. */
.login__cuerpo .campo input { width: 100%; }

.login__cuerpo .btn { width: 100%; }

.login__pie {
  padding: var(--e2) var(--e4) var(--e3);
  border-top: 1px solid var(--borde);
  background: var(--superficie-2);
  color: var(--texto-3);
  font-size: .78rem;
  letter-spacing: .04em;
  text-align: center;
}

/* El título de la tarjeta: dice a qué se entra, que en la imagen no está. */
.login__titulo {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--texto);
}
.login__titulo small {
  display: block;
  margin-top: var(--e0);
  font-size: .82rem;
  font-weight: 400;
  color: var(--texto-2);
}
