/* ==========================================================================
   Ágora — hoja de estilo única.

   Traduce el prototipo «Ágora Acceso» a CSS servido desde el propio dominio: no
   hay estilos en línea (la Content-Security-Policy no los admite) ni recursos de
   terceros.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Tipografías. Todas se sirven desde este mismo dominio, nunca desde un CDN.
   «swap» evita el texto invisible mientras cargan: se ve antes con la
   tipografía del sistema y luego se sustituye.

   Myriad Pro es la tipografía institucional y la que lleva todo el texto. Son
   los mismos ficheros que usa UNIVERSA, convertidos de OTF a WOFF2: el mismo
   contorno, la mitad de bytes (286 kB -> 155 kB) y un formato que entiende
   cualquier navegador de los últimos diez años.

   Aviso para quien mire los metadatos: MyriadPro-Semibold declara «Myriad Pro
   Light» como familia heredada (name ID 1). Es el apaño de Adobe para encajar
   seis estilos en los cuatro huecos del enlazado clásico —el fichero es
   Semibold de verdad: usWeightClass 600 y nombre PostScript MyriadPro-Semibold—
   y aquí da igual, porque @font-face ignora los nombres internos y manda el
   font-weight declarado abajo.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: 'Myriad Pro';
  src: url("../fonts/myriad-pro-regular.6831f77fdd0e.woff2") format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Myriad Pro';
  src: url("../fonts/myriad-pro-semibold.f802386ad94b.woff2") format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Myriad Pro';
  src: url("../fonts/myriad-pro-bold.0cf926538095.woff2") format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* Newsreader se queda solo para los titulares («Ágora» de la cabecera y de la
   portada, y el título del panel): es el gesto del prototipo y Myriad Pro no lo
   sustituye. Es variable, así que un fichero por subconjunto cubre 400-600. */
@font-face {
  font-family: 'Newsreader';
  src: url("../fonts/newsreader-latin.0a301b6df4c7.woff2") format('woff2');
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Newsreader';
  src: url("../fonts/newsreader-latin-ext.40f89707c10e.woff2") format('woff2');
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --------------------------------------------------------------------------
   Paleta y medidas
   -------------------------------------------------------------------------- */
:root {
  /* Azul institucional de la Universidad, el mismo que UNIVERSA y el resto de
     aplicaciones del vicerrectorado (allí: --primary-color y --primary-dark). */
  --azul: #223d71;          /* color principal: botón, enlaces, foco */
  --azul-oscuro: #1a2f57;   /* titulares, pie y estados :hover */
  --azul-claro: #e9eef7;    /* fondos suaves sobre el azul principal */
  --acento: #B4873A;        /* dorado del prototipo, para los detalles */

  --texto: #16273F;
  --texto-medio: #44546E;
  --texto-suave: #55647E;
  --texto-tenue: #7383A0;
  --texto-nota: #6B7A93;

  --fondo: #F4F5F8;
  --superficie: #FFFFFF;
  --borde: #DDE2EA;
  --borde-cabecera: #E1E5EC;
  --borde-suave: #E7EAF0;

  --alto-cabecera: 78px;
  --alto-pie: 64px;         /* hueco que reserva el pie fijo, como en UEZ */
  --margen-lateral: 48px;

  /* Un solo alto para todo lo que se pulsa o se rellena. Va como altura fija y
     no como relleno vertical porque un botón y un campo con el mismo relleno no
     miden lo mismo: cada uno interpreta a su manera la altura de línea, y en una
     fila de controles esos dos o tres píxeles de diferencia se ven como un error
     de montaje. */
  --alto-control: 38px;
  /* Los extremos redondos de la cabecera del calendario, para toda la interfaz:
     ningún control queda con esquinas vivas. */
  --radio-control: 999px;
  /* Las cajas pequeñas que no son controles (avisos, notas, celdas). */
  --radio-caja: 10px;

  /* Serif solo para los titulares; Myriad Pro para todo lo demás. */
  --serif: 'Newsreader', Georgia, 'Times New Roman', serif;
  /* La misma cola de reservas que UEZ, para que las dos aplicaciones también se
     parezcan cuando Myriad Pro no llegue a cargar. */
  --sans: 'Myriad Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
          'Helvetica Neue', Arial, sans-serif;
}

/* --------------------------------------------------------------------------
   Base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  font-family: var(--sans);
  color: var(--texto);
  background: var(--fondo);
  -webkit-font-smoothing: antialiased;
  /* El pie está fuera del flujo (position: fixed, como en UEZ), así que hay que
     reservarle el hueco a mano o taparía el final del contenido. */
  padding-bottom: var(--alto-pie);
}

/* Los controles de formulario no heredan la tipografía del documento: el
   navegador les pone la suya (en macOS, System UI) si no se le dice lo contrario.
   Sin esta regla, la mitad de la interfaz —botones, campos, desplegables— se
   saldría de Myriad Pro, que es la tipografía de la Universidad. Está aquí y no
   repetida en cada control para que un control nuevo no pueda nacer fuera.
   Lo mismo vale para el texto de las pistas de los campos y para las opciones de
   un <select>, que en algunos navegadores tampoco heredan. */
button, input, select, textarea, optgroup, option { font-family: inherit; }
::placeholder { font-family: inherit; }

a { color: var(--azul); text-decoration: none; }
a:hover { color: var(--azul-oscuro); text-decoration: underline; }

/* Un foco visible y uniforme en toda la interfaz: es la única pista de
   navegación para quien usa el teclado. */
:focus-visible {
  outline: 2px solid var(--azul);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Enlace de salto: oculto hasta que recibe el foco. */
.salto-contenido {
  position: absolute;
  left: 12px;
  top: -60px;
  z-index: 100;
  padding: 10px 18px;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio-control);
  font-size: 14px;
  font-weight: 600;
  transition: top 140ms ease;
}
.salto-contenido:focus { top: 12px; }

/* --------------------------------------------------------------------------
   Marca de agua: el sello de la Universidad, como en el resto de aplicaciones
   del vicerrectorado. Está en base.html, así que acompaña a todas las vistas.

   Fijo a la esquina de la ventana y no absoluto dentro del contenido: así se
   queda quieto aunque la página tenga scroll y no depende de la altura de cada
   vista. El contenido (cabecera, main, pie) lleva z-index propio y pasa por
   encima; el sello no intercepta el ratón.
   -------------------------------------------------------------------------- */
.sello {
  position: fixed;
  right: -90px;
  bottom: -120px;
  z-index: 0;
  width: 780px;
  height: auto;
  opacity: 0.55;
  pointer-events: none;
  user-select: none;
}

/* --------------------------------------------------------------------------
   Cabecera
   -------------------------------------------------------------------------- */
.cabecera {
  flex-shrink: 0;
  height: var(--alto-cabecera);
  padding: 0 var(--margen-lateral);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 32px;
  background: var(--superficie);
  border-bottom: 1px solid var(--borde-cabecera);
  position: relative;
  z-index: 10;
}

.marca {
  display: flex;
  align-items: center;
  gap: 22px;
  color: inherit;
}
.marca:hover { text-decoration: none; }

.marca-logo { display: block; width: 176px; height: auto; }
.marca-separador { width: 1px; height: 34px; background: var(--borde); }

.marca-texto { display: flex; flex-direction: column; gap: 1px; }
.marca-nombre {
  font-family: var(--serif);
  font-size: 25px;
  font-weight: 500;
  letter-spacing: 0.06em;
  line-height: 1;
  color: var(--azul-oscuro);
}
/* Sesión iniciada (panel) */
.sesion { display: flex; align-items: center; gap: 16px; }
.sesion-usuario { font-size: 14.5px; font-weight: 600; color: var(--texto-medio); }
.boton-salir {
  display: inline-flex;
  align-items: center;
  height: var(--alto-control);
  padding: 0 16px;
  border: 1px solid var(--borde);
  border-radius: var(--radio-control);
  background: var(--superficie);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--azul-oscuro);
  cursor: pointer;
  transition: background 140ms ease;
}
.boton-salir:hover { background: var(--azul-claro); }

/* Distintivo del rol: solo aparece con ACRI o Administrador (el rol de serie no
   distingue a nadie, así que no lleva etiqueta). */
.sesion-rol {
  padding: 4px 10px;
  border-radius: var(--radio-control);
  background: var(--azul-claro);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--azul);
  white-space: nowrap;
}

/* Acceso al panel de administración: mismo tamaño que «Cerrar sesión», pero
   relleno, porque lleva a algún sitio en lugar de deshacer la sesión. */
.boton-cabecera {
  display: inline-flex;
  align-items: center;
  height: var(--alto-control);
  padding: 0 16px;
  border: 1px solid var(--azul);
  border-radius: var(--radio-control);
  background: var(--azul);
  font-size: 13.5px;
  font-weight: 600;
  color: #FFFFFF;
  white-space: nowrap;
  transition: background 140ms ease;
}
.boton-cabecera:hover { background: var(--azul-oscuro); color: #FFFFFF; text-decoration: none; }

/* --------------------------------------------------------------------------
   Pantalla de acceso
   -------------------------------------------------------------------------- */
.acceso {
  flex: 1;
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  /* Aclarado hacia la esquina superior derecha, como en el prototipo. Va sobre
     el sello, así que el gesto del prototipo se conserva: la marca de agua se
     desvanece en la esquina clara y aparece en la de abajo. */
  background: radial-gradient(120% 90% at 88% 0%, #FFFFFF 0%, rgba(255, 255, 255, 0) 62%);
}
/* Variante centrada (SIR3 simulado): la tarjeta va sola en el centro, con aire
   garantizado respecto a la cabecera y el pie. «safe center» evita que un
   viewport bajo recorte la parte superior de la tarjeta. */
.acceso-centrado {
  justify-content: center;
  align-items: safe center;
  padding: 32px var(--margen-lateral);
}

.acceso-rejilla {
  position: relative;
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 80px var(--margen-lateral);
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: 88px;
  align-items: center;
}

/* ---- Columna izquierda: presentación ---- */
.antetitulo {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 26px;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--azul);
}
.antetitulo-regla { width: 34px; height: 2px; background: var(--acento); }
.antetitulo-tenue { margin: 0; color: var(--texto-tenue); }

.presentacion-titulo {
  margin: 0;
  font-family: var(--serif);
  font-size: 96px;
  font-weight: 400;
  line-height: 0.94;
  letter-spacing: -0.01em;
  color: var(--azul-oscuro);
}
/* ---- Columna derecha: tarjeta de acceso ---- */
.tarjeta-acceso {
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: 6px;
  box-shadow: 0 18px 44px -28px rgba(26, 47, 87, 0.28);
  overflow: hidden;
}
.tarjeta-estrecha { width: 100%; max-width: 460px; }

/* Versión condensada de la tarjeta (SIR3 simulado): mismos elementos con menos
   aire vertical, para que el formulario completo quepa en la vista sin scroll. */
.tarjeta-compacta .tarjeta-cuerpo { padding: 28px 32px 22px; }
.tarjeta-compacta .tarjeta-titulo { font-size: 23px; }
.tarjeta-compacta .tarjeta-entrada { margin: 10px 0 20px; font-size: 14.5px; line-height: 1.5; }
.tarjeta-compacta .aviso { margin: 12px 0 0; padding: 9px 12px; font-size: 13px; }
.tarjeta-compacta .campo { margin-bottom: 14px; }
.tarjeta-compacta .campo input { padding: 10px 12px; font-size: 15px; }
.tarjeta-compacta .tarjeta-pie { margin-top: 20px; padding-top: 16px; }

/* Filo superior bicolor: dorado corto sobre azul. */
.tarjeta-filo { display: flex; height: 3px; background: var(--azul); }
.tarjeta-filo::before { content: ''; width: 76px; background: var(--acento); }

.tarjeta-cuerpo { padding: 40px 40px 30px; }

.tarjeta-titulo {
  margin: 10px 0 0;
  font-size: 27px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--azul-oscuro);
}
.tarjeta-entrada {
  margin: 12px 0 30px;
  font-size: 15.5px;
  line-height: 1.6;
  color: var(--texto-suave);
  text-wrap: pretty;
}

/* Botón de acceso institucional */
.boton-sso {
  width: 100%;
  height: 58px;
  padding: 0 20px;
  display: flex;
  align-items: center;
  gap: 14px;
  border: none;
  border-radius: var(--radio-control);
  background: var(--azul);
  color: #FFFFFF;
  font-size: 16.5px;
  font-weight: 700;
  cursor: pointer;
  transition: background 140ms ease, transform 140ms ease;
}
.boton-sso:hover { background: var(--azul-oscuro); }
.boton-sso:active { transform: translateY(1px); }
.boton-sso:disabled { cursor: progress; background: var(--azul-oscuro); }

.boton-sso-icono {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.boton-sso-etiqueta { flex: 1; text-align: left; }
.boton-sso-flecha { font-size: 18px; opacity: 0.75; }

.boton-compacto { height: 52px; margin-top: 8px; }

/* Estado «redirigiendo»: el icono se sustituye por un giro. Lo activa
   static/js/acceso.js al enviar el formulario. */
.boton-sso.esta-ocupado .boton-sso-icono svg { display: none; }
.boton-sso.esta-ocupado .boton-sso-icono::after {
  content: '';
  width: 18px;
  height: 18px;
  border: 2px solid rgba(255, 255, 255, 0.35);
  border-top-color: #FFFFFF;
  border-radius: 50%;
  animation: ag-giro 700ms linear infinite;
}
@keyframes ag-giro { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .boton-sso.esta-ocupado .boton-sso-icono::after { animation-duration: 2.5s; }
  * { scroll-behavior: auto; }
}

.nota-conexion {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 16px 0 0;
  font-size: 13.5px;
  color: var(--texto-nota);
}
.nota-candado { flex-shrink: 0; }

.tarjeta-pie {
  margin-top: 28px;
  padding-top: 22px;
  border-top: 1px solid var(--borde-suave);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  font-size: 14px;
}
.enlace-tenue { color: var(--texto-tenue); }
.enlace-tenue:hover { color: var(--azul-oscuro); }

/* ---- Avisos (mensajes de Django) ----
   La caja de color («.aviso») vale por sí sola dentro de una página —la nota de
   «solo desarrollo local» del SIR3 simulado es una—, y flotando sobre la
   interfaz cuando va dentro de «.avisos», que es lo que pinta base.html con los
   mensajes de Django. */
.aviso {
  margin: 0 0 10px;
  padding: 11px 16px;
  border-radius: var(--radio-caja);
  font-size: 14px;
  line-height: 1.45;
}
.aviso:last-child { margin-bottom: 0; }
.aviso-warning { background: #FBF3E4; color: #7A5716; border: 1px solid #E9D8B4; }
.aviso-error   { background: #FDF1F1; color: #8C2E33; border: 1px solid #F0D2D3; }
.aviso-success { background: #EDF6F2; color: #1F5C4C; border: 1px solid #C9E2D8; }
.aviso-info    { background: #EEF2F9; color: #223d71; border: 1px solid #D3DEEE; }

/* Pila de avisos: fija bajo la cabecera, a la derecha y por encima de todo lo
   demás (el pie fijo llega a z-index 1000). Al estar fuera del flujo, un mensaje
   ya no desplaza la página al aparecer ni al retirarse. */
.avisos {
  position: fixed;
  top: calc(var(--alto-cabecera) + 16px);
  right: var(--margen-lateral);
  z-index: 1200;
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: min(400px, calc(100vw - 2 * var(--margen-lateral)));
  margin: 0;
  padding: 0;
  list-style: none;
  /* La columna ocupa una franja alta de la ventana aunque los avisos sean
     bajitos: sin esto se quedaría con los clics de lo que hay debajo. Cada
     aviso vuelve a admitirlos. */
  pointer-events: none;
}

.avisos .aviso {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 0;
  padding: 12px 14px;
  box-shadow: 0 16px 36px -20px rgba(26, 47, 87, 0.5);
  animation: aviso-entra 200ms ease both;
}
.aviso-texto { flex: 1; min-width: 0; overflow-wrap: anywhere; }

/* El botón de cerrar lo pone static/js/avisos.js. */
.aviso-cerrar {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  font-size: 17px;
  line-height: 1;
  opacity: 0.6;
  cursor: pointer;
  transition: opacity 140ms ease, background 140ms ease;
}
.aviso-cerrar:hover { opacity: 1; background: rgba(26, 47, 87, 0.08); }

/* Al retirarse, el aviso se va por donde vino y deja de contar para la pila:
   los que queden debajo suben sin salto. */
.avisos .aviso.se-va {
  animation: aviso-sale 180ms ease forwards;
  pointer-events: none;
}

@keyframes aviso-entra {
  from { opacity: 0; transform: translateY(-10px); }
  to   { opacity: 1; transform: none; }
}
@keyframes aviso-sale {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(-10px); }
}
@media (prefers-reduced-motion: reduce) {
  .avisos .aviso,
  .avisos .aviso.se-va { animation-duration: 1ms; }
}

/* ---- Campos de formulario (SIR3 simulado) ---- */
.campo { margin-bottom: 18px; }
/* «.campo-etiqueta» es para lo que no es un control único al que apuntar con un
   <label>, como el selector de día: ahí la etiqueta es un <span> y quien la
   reclama es el botón, con aria-labelledby. */
.campo label,
.campo-etiqueta {
  display: block;
  margin-bottom: 7px;
  font-size: 13px;
  font-weight: 600;
  color: var(--texto-medio);
}
.campo input {
  width: 100%;
  padding: 12px 18px;
  border: 1px solid var(--borde);
  border-radius: var(--radio-control);
  background: var(--superficie);
  font-size: 15.5px;
  color: var(--texto);
  transition: border-color 140ms ease, box-shadow 140ms ease;
}
.campo input:focus {
  outline: none;
  border-color: var(--azul);
  box-shadow: 0 0 0 3px rgba(34, 61, 113, 0.14);
}

/* Lo que falla en un campo se marca en el propio campo, con el borde en rojo, y
   no con un texto debajo: el texto empujaba el formulario hacia abajo y, en un
   diálogo con varios campos, un envío en blanco lo estiraba entero y movía de
   sitio los botones que se acababan de pulsar.

   El aviso se escribe igual (static/js/validacion.js) pero solo para lectores de
   pantalla: es un «role=alert», así que se oye al enviar y se queda anclado al
   campo por «aria-describedby» hasta que se corrige. El color no es la única
   señal —para quien no lo distingue— porque el campo va además con
   «aria-invalid», que es lo que anuncian los lectores de pantalla. */
.campo-error {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.campo input[aria-invalid="true"] { border-color: #D8A3A6; }
/* Los desplegables llevan un <select> escondido detrás de un botón propio (ver
   static/js/selector.js): el marcado va al botón, que es lo que se ve. */
.selector:has([aria-invalid="true"]) .selector-boton { border-color: #D8A3A6; }
.campo input[aria-invalid="true"]:focus {
  border-color: #C0787C;
  box-shadow: 0 0 0 3px rgba(140, 46, 51, 0.14);
}
.adjuntar:has([aria-invalid="true"]) { border-color: #D8A3A6; }

/* --------------------------------------------------------------------------
   Panel principal: saludo y calendario
   -------------------------------------------------------------------------- */
/* El panel es una columna que reparte la altura disponible: la cabecera mide lo
   suyo y el calendario absorbe el resto, de modo que todo cabe en la vista sin
   scroll sea cual sea la altura de la ventana.

   Para que el reparto ocurra, el body necesita una altura DEFINIDA en esta
   página: con el min-height:100vh general el body crece con el contenido y el
   flex nunca comprime nada. dvh y no vh por las barras móviles de iOS/Android. */
.pagina-panel {
  height: 100vh;
  height: 100dvh;
}

.panel {
  flex: 1;
  min-height: 0;
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 30px var(--margen-lateral) 26px;
}

.panel-cabecera { margin-bottom: 22px; }
.panel-cabecera .antetitulo { margin-bottom: 12px; }

.panel-saludo {
  margin: 0;
  font-family: var(--serif);
  font-size: 40px;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--azul-oscuro);
}

/* Solo para lectores de pantalla (el «(hoy)» del calendario). */
.visualmente-oculto {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---- Calendario ---- */
.calendario {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: 6px;
  box-shadow: 0 18px 44px -28px rgba(26, 47, 87, 0.28);
  overflow: hidden;
}

.calendario-cabecera {
  /* relative: es el ancla del desplegable de mes y año, que se abre debajo. */
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 22px 28px;
  border-bottom: 1px solid var(--borde-suave);
}
.calendario-mes {
  margin: 0;
  font-family: var(--serif);
  font-size: 26px;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--azul-oscuro);
}

/* El título del mes es un botón (abre el desplegable de mes y año), pero se
   viste de título: hereda la letra del h2 que lo envuelve y solo insinúa que se
   puede pulsar con el fondo al pasar por encima y una flechita al lado. En la
   vista de lista se apaga —el título dice «Convocatorias»— y entonces ni fondo
   ni flecha: vuelve a ser un título a secas. */
.calendario-mes-boton {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-left: -8px;
  padding: 2px 8px;
  border: 0;
  border-radius: 6px;
  background: none;
  font: inherit;
  letter-spacing: inherit;
  color: inherit;
  cursor: pointer;
  transition: background 140ms ease;
}
.calendario-mes-boton:hover:not(:disabled),
.calendario-mes-boton[aria-expanded="true"] { background: var(--azul-claro); }
.calendario-mes-boton:disabled { cursor: default; }
.calendario-mes-flecha { flex-shrink: 0; color: var(--texto-tenue); transition: transform 140ms ease; }
.calendario-mes-boton[aria-expanded="true"] .calendario-mes-flecha {
  transform: rotate(180deg);
  color: var(--azul);
}
.calendario-mes-boton:disabled .calendario-mes-flecha { display: none; }

/* ---- Desplegable de mes y año ----
   Un panel bajo el título con dos columnas: los doce meses en rejilla y los
   años en una lista con scroll. Lo llena y lo gobierna panel.js; la opción
   que se está viendo va marcada en azul sólido, como en el conmutador de
   vista: es un estado, no un botón más. */
.calendario-selector {
  position: absolute;
  top: calc(100% + 8px);
  left: 22px;
  z-index: 30;
  display: flex;
  gap: 20px;
  padding: 16px 18px;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: 6px;
  box-shadow: 0 24px 48px -24px rgba(26, 47, 87, 0.35);
}
.calendario-selector[hidden] { display: none; }

.calendario-selector-titulo {
  margin: 0 0 8px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--texto-tenue);
}
.calendario-selector-meses {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 4px;
}
.calendario-selector-anyos {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 76px;
  max-height: 168px;
  overflow-y: auto;
  padding-right: 2px;
}
.calendario-selector-opcion {
  padding: 6px 12px;
  border: 0;
  border-radius: 5px;
  background: none;
  font-size: 13.5px;
  font-weight: 600;
  text-transform: capitalize;
  font-variant-numeric: tabular-nums;
  color: var(--texto-medio);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}
.calendario-selector-opcion:hover { background: var(--azul-claro); color: var(--azul); }
.calendario-selector-opcion.es-activa { background: var(--azul); color: #FFFFFF; }

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

/* Redondos y de borde tenue: en la cabecera del calendario conviven tres o cuatro
   controles seguidos, y con esquinas vivas y borde marcado se leen como una tira
   de cajas. El borde solo tiene que insinuar el área que se pulsa; quien manda es
   el fondo azul claro al pasar por encima. */
.calendario-boton {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: var(--alto-control);
  height: var(--alto-control);
  padding: 0 10px;
  border: 1px solid var(--borde-suave);
  border-radius: var(--radio-control);
  background: var(--superficie);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--azul-oscuro);
  cursor: pointer;
  transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
}
.calendario-boton:hover {
  background: var(--azul-claro);
  border-color: var(--azul-claro);
  color: var(--azul);
}
.calendario-boton-hoy { padding: 0 16px; }

/* Cabecera de la semana (lun … dom) */
.calendario-semana {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  padding: 0 12px;
  border-bottom: 1px solid var(--borde-suave);
}
.calendario-dia-semana {
  padding: 12px 10px 10px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-align: right;
  color: var(--texto-tenue);
}

/* Rejilla de días: 6 filas que se reparten la altura que deja libre la cabecera
   del panel, así el mes completo queda a la vista sin scroll.

   El mínimo de la fila es el alto que necesita una celda con un evento dentro
   (número + pastilla + relleno). Sin él, las filas se encogían hasta donde
   hiciera falta y en ventanas normales la pastilla quedaba cortada por la mitad
   —el evento estaba pintado, pero no se veía—. Cuando ni con el mínimo cabe el
   mes, la rejilla hace scroll: mejor eso que esconder lo que hay en el mes. */
.calendario-rejilla {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  grid-auto-rows: minmax(66px, 1fr);
  gap: 5px;
  padding: 10px;
}

.calendario-celda {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 3px;
  min-height: 0;
  padding: 6px;
  border-radius: 5px;
  background: var(--fondo);
  overflow: hidden;
  transition: background 140ms ease;
}
.calendario-celda:hover { background: var(--azul-claro); }
.calendario-celda.es-otro-mes { background: transparent; }
.calendario-celda.es-otro-mes .calendario-numero { color: var(--texto-tenue); opacity: 0.55; }

.calendario-numero {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  min-width: 28px;
  height: 28px;
  border-radius: 50%;
  font-size: 14px;
  font-weight: 600;
  color: var(--texto-medio);
}

/* Hoy: número en un círculo azul y celda ligeramente teñida. */
.calendario-celda.es-hoy { background: var(--azul-claro); }
.calendario-celda.es-hoy .calendario-numero {
  background: var(--azul);
  color: #FFFFFF;
  font-weight: 700;
}

/* Eventos: pastillas a lo ancho de la celda, con el filo dorado del prototipo.
   Son enlaces a la convocatoria, así que se declaran también como bloque: un <a>
   en línea no respetaría ni el ancho de la celda ni el recorte del texto. */
.calendario-evento {
  display: block;
  align-self: stretch;
  flex-shrink: 0;
  padding: 2px 7px;
  border-left: 3px solid var(--acento);
  border-radius: 3px;
  background: var(--superficie);
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--azul-oscuro);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
a.calendario-evento:hover { background: var(--azul-claro); text-decoration: none; }
/* La hora, delante del título y algo más tenue: informa sin robarle el sitio. */
.calendario-evento-hora { font-variant-numeric: tabular-nums; color: var(--texto-tenue); }

/* Suspendida: tachada y apagada, pero en su día y en su sitio. Sigue siendo un
   enlace —su pantalla es la que cuenta que no se celebra— y por eso no se le
   quita el color del todo: tiene que parecer algo a lo que se puede entrar.
   El «(suspendida)» que se oye lo pone panel.js: un tachado no se lee. */
.calendario-evento.es-suspendida {
  border-left-color: var(--borde);
  color: var(--texto-suave);
  text-decoration: line-through;
}

/* Movida: no se tacha. El tachado es de las suspendidas —lo que no se celebra—
   y una movida sí se celebra, en otro día; con las dos tachadas había que leer
   la letra pequeña para saber cuál era cuál. Así que esta se marca al revés: el
   contorno a rayas (el hueco que deja algo que ya no está aquí) y, donde iría
   la hora, una flecha con el día al que se fue, que la pone panel.js junto con
   el «(movida al …)» que se oye. */
.calendario-evento.es-movida {
  /* El contorno entero, a rayas y fino, en lugar del filo grueso de las demás:
     así sigue siendo una cápsula como sus vecinas —el filo dorado a puntos
     parecía tres motas sueltas al lado del texto— y a la vez se ve que lo que
     hay aquí es la marca de algo que ya no está. El dorado, aclarado: el del
     resto (--acento) es para un trazo de 3px, y a 1px y a rayas pesa demasiado. */
  border: 1px dashed #C9AC77;
  /* El hueco de dentro, igual que en las demás, contando que el filo pasa de
     3px a 1px: el texto queda a la misma altura y a la misma distancia. */
  padding: 1px 7px 1px 9px;
  color: var(--texto-medio);
  font-weight: 500;
}
/* La flecha y su día, en azul y en negrita: es lo que hay que leer de la
   pastilla, y en dorado sobre blanco no daría el contraste a este tamaño. */
.calendario-evento-destino {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--azul);
}

/* Arrastrar a otro día (solo quien puede convocar; lo activa panel.js). La
   pastilla lo insinúa con el cursor, la que está en vuelo se atenúa para que se
   vea el calendario de debajo, y la celda sobre la que se pasa se enciende: es
   la respuesta a «¿dónde va a caer esto si lo suelto?». */
.calendario-evento.se-puede-mover { cursor: grab; }
.calendario-evento.esta-arrastrando { opacity: 0.45; }
.calendario-celda.es-destino {
  background: var(--azul-claro);
  box-shadow: inset 0 0 0 2px var(--azul);
}

/* El botón de convocar convive con las flechas del mes: la misma altura y la
   misma forma redondeada que ellas, para que la fila se lea como un grupo. */
.calendario-convocar { font-size: 13.5px; }

/* El atributo «hidden» lo pierde cualquier «display» del autor: hay que decirlo.
   Es lo que apaga la rejilla o la lista según la vista elegida, y las flechas
   del mes cuando la lista está puesta. */
.calendario-semana[hidden],
.calendario-rejilla[hidden],
.calendario-agenda[hidden],
.calendario-boton[hidden] { display: none; }

/* ---- Conmutador de vista (Calendario | Lista) ----
   Una pastilla con las dos opciones dentro, de la misma altura que los botones
   vecinos. La opción activa va en azul sólido: es un estado, no un botón más. */
.vista-conmutador {
  display: flex;
  align-items: center;
  gap: 2px;
  height: var(--alto-control);
  padding: 3px;
  border: 1px solid var(--borde-suave);
  border-radius: var(--radio-control);
  background: var(--superficie);
}
.vista-boton {
  height: 100%;
  padding: 0 14px;
  border: 0;
  border-radius: var(--radio-control);
  background: transparent;
  font-size: 13px;
  font-weight: 600;
  color: var(--texto-suave);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}
.vista-boton:hover { background: var(--azul-claro); color: var(--azul); }
.vista-boton.es-activa,
.vista-boton.es-activa:hover {
  background: var(--azul);
  color: #FFFFFF;
}

/* El paso de una vista a otra. Lo que se va se desvanece hacia arriba y lo que
   llega entra desde abajo, en ese orden: el gesto cuenta que es la misma
   información colocada de otra manera, y no una pantalla nueva.

   Los tiempos son cortos a propósito —130 ms de salida y 220 de entrada— porque
   esto se pulsa para ver algo, no para ver la animación. El de salida está
   también en panel.js, que es quien espera a que termine para hacer el cambio. */
@keyframes vista-sale {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(-8px); }
}
@keyframes vista-entra {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
.vista-sale { animation: vista-sale 130ms ease-in both; }
.vista-entra { animation: vista-entra 220ms cubic-bezier(0.2, 0.7, 0.3, 1) both; }

/* Quien pide menos movimiento en su sistema no ve ninguno: panel.js ni siquiera
   entra en la animación, y esto cubre lo que hubiera quedado a medias. */
@media (prefers-reduced-motion: reduce) {
  .vista-sale,
  .vista-entra { animation: none; }
}

/* ---- Vista de lista ----
   Ocupa el hueco de la rejilla y hace scroll ella sola: primero las próximas
   convocatorias y debajo las pasadas, cada grupo con su titulillo. */
.calendario-agenda {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 26px;
  padding: 22px 28px 28px;
}

.agenda-titulo {
  margin: 0 0 10px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--texto-tenue);
}

.agenda-lista {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Cada convocatoria, una tarjeta con el mismo filo dorado que la pastilla del
   calendario: es la misma cosa vista de otra manera, y debe parecerlo. */
.agenda-fila {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 18px 12px 15px;
  border: 1px solid var(--borde-suave);
  border-left: 3px solid var(--acento);
  border-radius: var(--radio-caja);
  background: var(--fondo);
  color: inherit;
  transition: background 140ms ease;
}
a.agenda-fila:hover { background: var(--azul-claro); text-decoration: none; color: inherit; }

/* Las pasadas, con el filo apagado y algo más tenues: siguen ahí, pero ya no
   piden atención. */
.agenda-fila.es-pasada { border-left-color: var(--borde); }
.agenda-fila.es-pasada .agenda-dia,
.agenda-fila.es-pasada .agenda-nombre { color: var(--texto-medio); }

/* El día grande y el mes debajo, como una hoja de calendario de mesa. */
.agenda-fecha {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex-shrink: 0;
  min-width: 44px;
}
.agenda-dia {
  font-family: var(--serif);
  font-size: 24px;
  line-height: 1.1;
  color: var(--azul-oscuro);
}
.agenda-mes {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--texto-tenue);
}

.agenda-detalle {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.agenda-nombre {
  font-size: 15px;
  font-weight: 600;
  color: var(--azul-oscuro);
}
.agenda-cuando { font-size: 13px; color: var(--texto-suave); }

/* La marca de «Hoy», junto al título de la convocatoria del día. */
.agenda-es-hoy {
  /* inline-block y no inline: cuando la fila va tachada (una suspendida),
     el tachado del nombre atraviesa lo que lleve dentro, y una pastilla azul
     con una raya encima se lee como un error de pintado. Una caja inline
     atómica no hereda ese tachado —es lo mismo que salva a las marcas de
     «Movida» y «Suspendida» de aquí abajo—, y «HOY» sigue siendo lo que es:
     el día en el que estamos, no algo que se haya anulado. */
  display: inline-block;
  margin-left: 10px;
  padding: 2px 9px;
  border-radius: var(--radio-control);
  background: var(--azul);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #FFFFFF;
  vertical-align: 2px;
}

/* Suspendida: el nombre tachado y una marca al lado que lo dice con palabras,
   porque el tachado no se oye. La fila entera se apaga, como las pasadas: no se
   celebra, pero sigue en la lista para quien estaba citado. */
.agenda-fila.es-suspendida { border-left-color: var(--borde); }
.agenda-fila.es-suspendida .agenda-nombre {
  color: var(--texto-suave);
  text-decoration: line-through;
}
.agenda-suspendida {
  display: inline-block;
  margin-left: 10px;
  padding: 2px 9px;
  border: 1px solid #EED9DA;
  border-radius: var(--radio-control);
  background: #FDF1F1;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #8C2E33;
  /* La marca no se tacha: es justo lo que hay que leer. */
  text-decoration: none;
  vertical-align: 2px;
}

/* Movida: sin tachar —tachado está lo que no se celebra— y con la marca de al
   lado diciendo a qué día se fue. En azul y no en el granate de las
   suspendidas: no es «no se celebra», es «se celebra ese otro día». */
.agenda-fila.es-movida .agenda-nombre { color: var(--texto-medio); }
.agenda-movida {
  display: inline-block;
  margin-left: 10px;
  padding: 2px 9px;
  border: 1px solid #D5DFF0;
  border-radius: var(--radio-control);
  background: #EEF3FB;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--azul);
  /* Como la marca de suspendida: es justo lo que hay que leer. */
  text-decoration: none;
  vertical-align: 2px;
}

.agenda-flecha {
  flex-shrink: 0;
  font-size: 16px;
  color: var(--texto-tenue);
  transition: color 140ms ease, transform 140ms ease;
}
a.agenda-fila:hover .agenda-flecha {
  color: var(--azul);
  transform: translateX(3px);
}
@media (prefers-reduced-motion: reduce) {
  .agenda-flecha { transition: none; }
  a.agenda-fila:hover .agenda-flecha { transform: none; }
}

.agenda-vacia {
  margin: 0;
  font-size: 14.5px;
  color: var(--texto-suave);
}

/* ---- Convocatorias pasadas: un mes plegado por línea ----
   Lo que ya se celebró se acumula sin parar, y desplegado convierte la lista en
   un rollo por el que hay que bajar para llegar a nada. Plegado, un curso entero
   son nueve líneas. Es un <details>: se abre y se cierra sin JavaScript. */
.agenda-plegable {
  border-bottom: 1px solid var(--borde-suave);
}
.agenda-plegable:last-child { border-bottom: none; }

.agenda-plegable-resumen {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 4px;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--azul-oscuro);
  cursor: pointer;
  /* Fuera el triángulo del navegador: la flecha la pone «::after», que se puede
     colocar a la derecha y girar al abrir. */
  list-style: none;
}
.agenda-plegable-resumen::-webkit-details-marker { display: none; }
.agenda-plegable-resumen:hover { color: var(--azul); }

.agenda-plegable-cuenta {
  font-size: 13px;
  font-weight: 400;
  color: var(--texto-tenue);
}

/* La flecha, a la derecha del todo: apunta abajo cerrada y arriba abierta. */
.agenda-plegable-resumen::after {
  content: "";
  margin-left: auto;
  width: 7px;
  height: 7px;
  border-right: 1.8px solid var(--texto-tenue);
  border-bottom: 1.8px solid var(--texto-tenue);
  border-radius: 1px;
  transform: translateY(-2px) rotate(45deg);
  transition: transform 160ms ease;
}
.agenda-plegable[open] > .agenda-plegable-resumen::after {
  transform: translateY(1px) rotate(225deg);
}
.agenda-plegable[open] > .agenda-plegable-resumen { color: var(--azul); }

/* Las filas del mes abierto, separadas de su título y del mes siguiente. */
.agenda-plegable > .agenda-lista { padding: 2px 0 14px; }

@media (prefers-reduced-motion: reduce) {
  .agenda-plegable-resumen::after { transition: none; }
}

/* Ventanas bajas: el mes con las celdas a su altura mínima ya no cabe entre la
   cabecera y el pie. Entonces el panel suelta el reparto de altura y la página
   vuelve a hacer scroll, igual que en móvil: un scroll normal de página se ve
   venir, mientras que uno metido dentro de la rejilla pasa desapercibido y deja
   media hoja del mes escondida sin avisar. */
@media (max-height: 880px) {
  .pagina-panel { height: auto; min-height: 100vh; }
  .calendario,
  .calendario-rejilla,
  .calendario-agenda { flex: none; }
}

.calendario-sin-js {
  margin: 0;
  padding: 28px;
  font-size: 15px;
  color: var(--texto-suave);
}

/* --------------------------------------------------------------------------
   Panel de administración: reparto de roles

   Una columna de tarjetas con la misma piel que la tarjeta de acceso (superficie
   blanca, borde tenue y la misma sombra) para que la pantalla de gestión no
   parezca de otra aplicación. A diferencia del panel, esta página crece con el
   contenido y hace scroll con normalidad.
   -------------------------------------------------------------------------- */
.admin {
  flex: 1;
  /* El body es flex, y un flex item arranca con «min-width: auto»: sin esto, el
     ancho mínimo de la tabla estiraría toda la columna y la página se desbordaría
     a lo ancho en cuanto la pantalla fuera estrecha. */
  min-width: 0;
  /* Sin «z-index» a propósito, al contrario que .acceso y .panel: un z-index aquí
     crea un contexto de apilamiento y encierra dentro a todo lo de esta pantalla,
     de modo que la lista de un desplegable no podría taparle nada al pie fijo
     (z-index 1000) por mucho que se le suba el suyo. Sobre el sello del fondo
     (z-index 0) sigue quedando por el orden del documento: va después en el HTML. */
  position: relative;
  width: 100%;
  max-width: 1000px;
  margin: 0 auto;
  padding: 34px var(--margen-lateral) 40px;
}

.admin-cabecera { margin-bottom: 26px; }
.admin-cabecera .antetitulo { margin-bottom: 12px; }

.admin-titulo-fila { position: relative; display: flex; align-items: center; gap: 12px; }

.admin-titular {
  margin: 0;
  font-family: var(--serif);
  font-size: 38px;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--azul-oscuro);
}

/* Vuelta al panel: fuera del flujo, en el margen que queda a la izquierda del
   contenido («right: 100%» la saca de la columna). Es una salida, no un contenido
   de esta pantalla: no ocupa sitio en el layout ni empuja el titular. El texto va
   oculto para lectores de pantalla, con «title» para el ratón. */
.admin-volver {
  position: absolute;
  right: 100%;
  top: 50%;
  transform: translateY(-50%);
  margin-right: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  border: 1px solid var(--borde);
  border-radius: 50%;
  background: var(--superficie);
  color: var(--azul-oscuro);
  transition: background 140ms ease, border-color 140ms ease;
}
.admin-volver:hover {
  background: var(--azul-claro);
  border-color: var(--azul-claro);
  color: var(--azul);
  text-decoration: none;
}
.admin-entrada {
  margin: 12px 0 0;
  max-width: 68ch;
  font-size: 15.5px;
  line-height: 1.6;
  color: var(--texto-suave);
  text-wrap: pretty;
}
.admin-entrada strong { font-weight: 600; color: var(--texto-medio); }

/* Sin «overflow: hidden» (que sí lleva la tarjeta de acceso): la lista de un
   desplegable tiene que poder salirse de la tarjeta. El filo superior redondea sus
   propias esquinas para que la tarjeta no pierda la forma. */
.admin-tarjeta {
  margin-bottom: 22px;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: 6px;
  box-shadow: 0 18px 44px -28px rgba(26, 47, 87, 0.28);
}
.admin-tarjeta .tarjeta-filo { border-radius: 5px 5px 0 0; }
.admin-tarjeta .tarjeta-filo::before { border-top-left-radius: 5px; }

.admin-cuerpo { padding: 26px 28px; }

.admin-titulo {
  margin: 0;
  font-size: 19px;
  font-weight: 600;
  color: var(--azul-oscuro);
}
.admin-nota {
  margin: 8px 0 0;
  max-width: 72ch;
  font-size: 14px;
  line-height: 1.55;
  color: var(--texto-suave);
  text-wrap: pretty;
}

/* Título de sección y buscador en la misma línea mientras haya sitio. El aire con
   la tabla que viene debajo lo pone ella (ver «.tabla»), para que sea el mismo
   venga de un título, de una descripción o de un buscador. */
.admin-fila-titulo {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
}

/* Formularios de una línea: los campos crecen y el botón se alinea con ellos
   («flex-end», porque los campos llevan etiqueta encima y el botón no). */
.admin-formulario,
.admin-busqueda {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 14px;
  margin-top: 18px;
}
.admin-busqueda { margin-top: 0; align-items: center; }
.admin-formulario .campo { margin-bottom: 0; }
/* El botón cierra la fila por la derecha. Con el desplegable de cargo a su ancho
   fijo, los campos ya no dejan sitio para él en la misma línea: así el salto se
   lee como el final del formulario y no como un control descolgado. */
.admin-formulario .boton-accion { margin-left: auto; }
.admin-formulario .campo-nip { width: 180px; }
/* Los campos de este formulario son más bajos que los de la pantalla de acceso, y
   con la misma medida que el desplegable de al lado: en una fila de controles, uno
   más alto que otro se ve como un error de montaje. */
.admin-formulario .campo input {
  height: var(--alto-control);
  padding: 0 14px;
  font-size: 14.5px;
}

.admin-busqueda input {
  width: 260px;
  height: var(--alto-control);
  padding: 0 14px;
  border: 1px solid var(--borde);
  border-radius: var(--radio-control);
  background: var(--superficie);
  font-size: 14.5px;
  color: var(--texto);
}
.admin-busqueda input:focus {
  outline: none;
  border-color: var(--azul);
  box-shadow: 0 0 0 3px rgba(34, 61, 113, 0.14);
}

/* ---- Desplegable propio (los roles) ---- */
/* El <select> nativo no se puede vestir: la lista de opciones la pinta el sistema
   operativo, con su tipografía y su azul, no los de la Universidad. Así que el
   <select> se queda como el que guarda el valor y viaja en el formulario, y lo que
   se ve es el botón y la lista que monta static/js/selector.js. Sin JavaScript no
   hay «esta-vestido» y se ve el control del navegador, que funciona igual. */
.selector { position: relative; display: inline-block; }

.selector-nativo {
  height: var(--alto-control);
  padding: 0 12px;
  border: 1px solid var(--borde);
  border-radius: var(--radio-control);
  background: var(--superficie);
  font-size: 14.5px;
  color: var(--texto);
  cursor: pointer;
}
.selector-nativo:focus {
  outline: none;
  border-color: var(--azul);
  box-shadow: 0 0 0 3px rgba(34, 61, 113, 0.14);
}
/* Vestido: el <select> sigue en el formulario, pero fuera de la vista, del
   tabulador (tabIndex=-1) y del ratón. No se usa «display: none» porque conviene
   que siga siendo un control real del formulario. */
.esta-vestido .selector-nativo {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.selector-boton {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  min-width: 172px;
  height: var(--alto-control);
  padding: 0 12px;
  border: 1px solid var(--borde);
  border-radius: var(--radio-control);
  background: var(--superficie);
  font-size: 14.5px;
  font-weight: 600;
  color: var(--texto);
  text-align: left;
  cursor: pointer;
  transition: border-color 140ms ease, box-shadow 140ms ease;
}
.selector-boton:hover { border-color: var(--texto-tenue); }
.selector-boton svg { flex-shrink: 0; color: var(--texto-tenue); transition: transform 140ms ease; }

/* Abierto: el mismo cerco azul que un campo con el foco, y la flecha girada. */
.esta-abierto .selector-boton {
  border-color: var(--azul);
  box-shadow: 0 0 0 3px rgba(34, 61, 113, 0.14);
}
.esta-abierto .selector-boton svg { transform: rotate(180deg); color: var(--azul); }

.selector-lista {
  position: absolute;
  /* Por encima del pie fijo (z-index 1000): en la última fila de una tabla, la
     lista cae justo ahí y quedaría cortada por él. Para que este número sirva de
     algo, ningún antepasado puede crear un contexto de apilamiento (ver «.admin»). */
  z-index: 1100;
  top: calc(100% + 6px);
  left: 0;
  min-width: 100%;
  margin: 0;
  padding: 5px;
  list-style: none;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio-caja);
  box-shadow: 0 16px 36px -18px rgba(26, 47, 87, 0.45);
}
/* El atributo «hidden» lo pierde cualquier «display» del autor: hay que decirlo. */
.selector-lista[hidden] { display: none; }
/* Hacia arriba cuando abajo no cabe (lo decide static/js/selector.js al abrir):
   dentro de un diálogo bajo, la lista caía sobre los botones de guardar y se
   quedaba pegada al filo de la tarjeta, que parecía cortada. */
.esta-arriba .selector-lista,
.esta-arriba .selector-panel {
  top: auto;
  bottom: calc(100% + 6px);
}
/* La lista es la que tiene el foco mientras está abierta, pero el cerco ya lo
   dibuja el botón y la posición se ve en la opción activa: dos marcas de foco a la
   vez confunden más que una. */
.selector-lista:focus { outline: none; }

/* ---- Desplegable con buscador (los cargos) ---- */
/* Con muchas opciones, selector.js mete la lista en un panel con un buscador
   arriba: el panel toma la posición y la piel que tenía la lista suelta, y la
   lista queda dentro, recortada en alto y con scroll. */
.selector-panel {
  position: absolute;
  z-index: 1100; /* como .selector-lista: por encima del pie fijo */
  top: calc(100% + 6px);
  left: 0;
  min-width: 100%;
  width: max-content;
  max-width: min(360px, 84vw);
  padding: 5px;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio-caja);
  box-shadow: 0 16px 36px -18px rgba(26, 47, 87, 0.45);
}
.selector-panel[hidden] { display: none; }
.selector-panel .selector-lista {
  position: static;
  min-width: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
.selector-lista-recortada { max-height: 250px; overflow-y: auto; }
/* Recortada en ancho por el panel, la opción larga parte de línea en lugar de
   desbordar por la derecha. */
.selector-lista-recortada .selector-opcion { white-space: normal; }
.selector-opcion[hidden] { display: none; }

.selector-buscador {
  width: 100%;
  height: 34px;
  margin-bottom: 5px;
  padding: 0 10px;
  border: 1px solid var(--borde);
  border-radius: var(--radio-control);
  background: var(--superficie);
  font-size: 14px;
  color: var(--texto);
}
.selector-buscador:focus {
  outline: none;
  border-color: var(--azul);
  box-shadow: 0 0 0 3px rgba(34, 61, 113, 0.14);
}

/* Un desplegable buscable mide siempre lo mismo, elija lo que elija: los cargos
   van de «Economía» a «Innovación, Transferencia y Formación Permanente», y un
   control que cambiara de ancho con cada elección movería de sitio todo lo que
   tiene al lado (en la tabla, una columna entera por fila). Lo que no cabe se
   corta con puntos suspensivos aquí, no en la lista: ahí la opción se lee
   entera, partida en dos líneas si hace falta. */
/* Ancho fijo, sin porcentajes: dentro de una celda de tabla un «100%» no se
   puede resolver mientras se está calculando la propia columna, y el control
   volvía a medir lo que midiera el nombre más largo. En pantallas estrechas se
   suelta al ancho de la columna (ver el bloque de 620px). */
.selector.es-buscable { width: 260px; }
.selector-valor {
  flex: 1;
  /* Sin esto, un hijo de un flex no baja de su ancho natural y el texto largo
     desborda el botón en lugar de cortarse. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.selector-opcion {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px 8px 10px;
  border-radius: var(--radio-control);
  font-size: 14.5px;
  color: var(--texto-medio);
  cursor: pointer;
  white-space: nowrap;
}
/* Activa: la que se elegiría al pulsar Intro (con el teclado) o la que está bajo
   el puntero. Elegida: la que vale ahora mismo. */
.selector-opcion.es-activa { background: var(--azul-claro); color: var(--azul-oscuro); }
.selector-opcion.es-elegida { font-weight: 600; color: var(--azul-oscuro); }

/* La marca ocupa su hueco siempre: si apareciera y desapareciera, el texto de las
   opciones bailaría al cambiar de una a otra. */
.selector-marca { display: flex; flex-shrink: 0; color: var(--azul); opacity: 0; }
.es-elegida .selector-marca { opacity: 1; }

.boton-accion {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--alto-control);
  padding: 0 18px;
  border: 1px solid var(--azul);
  border-radius: var(--radio-control);
  background: var(--azul);
  font-size: 14.5px;
  font-weight: 600;
  color: #FFFFFF;
  white-space: nowrap;
  cursor: pointer;
  transition: background 140ms ease;
}
.boton-accion:hover { background: var(--azul-oscuro); border-color: var(--azul-oscuro); }

/* Cuando la clase viste un enlace y no un <button> (el «Ir a la convocatoria» de
   una movida, el «Descargar» de un documento), el «a:hover» general de la hoja
   le gana en especificidad al color del botón: el texto se volvía del mismo azul
   oscuro que el fondo al que acaba de cambiar, y encima subrayado. Aquí manda el
   botón, que es lo que se está viendo. */
a.boton-accion:hover { color: #FFFFFF; text-decoration: none; }

/* Variante tenue para las acciones que se repiten en cada fila: en una tabla,
   veinte botones azules gritan todos a la vez y ninguno destaca. Cambia el
   color y nada más: la medida es la misma, para que dos botones seguidos no se
   vean como dos tamaños distintos. */
.boton-accion-tenue {
  border-color: var(--borde);
  background: var(--superficie);
  color: var(--azul-oscuro);
}
.boton-accion-tenue:hover { background: var(--azul-claro); border-color: var(--borde); }
/* La variante tenue ya lleva el azul oscuro del texto, así que del «a:hover» solo
   sobra el subrayado (la paginación de cuentas, la descarga de un documento). */
a.boton-accion-tenue:hover { color: var(--azul-oscuro); }

/* ---- Menús desplegables ---- */
/* Un botón que abre dos o tres opciones (el «Descargar» del orden del día). Es
   un <details>: el navegador trae hecho el abrir y cerrar, y menu.js añade el
   cierre al pulsar fuera (ver static/js/menu.js). El <summary> se viste con las
   clases de botón de siempre; aquí solo se le quita lo que le pone el navegador
   por ser un <summary> (la flechita de desplegar). */
.menu { position: relative; }
.menu > summary { list-style: none; user-select: none; }
.menu > summary::-webkit-details-marker { display: none; }
/* La lista, colgada bajo el botón y alineada a su derecha, con la pinta de las
   tarjetas flotantes de la interfaz. El z-index la pone por delante de las
   filas de puntos, que llevan posicionamiento propio. */
.menu-lista {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 30;
  min-width: 250px;
  padding: 6px;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio-caja);
  box-shadow: 0 16px 36px -20px rgba(26, 47, 87, 0.5);
}
.menu-opcion {
  display: block;
  padding: 9px 12px;
  border-radius: 7px;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--texto);
  text-decoration: none;
  white-space: nowrap;
}
.menu-opcion:hover { background: var(--azul-claro); color: var(--azul-oscuro); }
/* La línea pequeña que dice qué se lleva cada opción (el ZIP, el PDF). */
.menu-pista {
  display: block;
  margin-top: 1px;
  font-size: 12.5px;
  font-weight: 400;
  color: var(--texto-nota);
}

/* ---- Tablas ---- */
/* Sin marco con scroll propio: cualquier «overflow» aquí recortaría la lista de los
   desplegables de rol, que se abren dentro de las celdas. Las columnas son pocas y
   de contenido corto, así que en pantallas estrechas basta con que el texto parta
   de línea y los controles de la última columna se apilen (ver el bloque de 620px). */
.tabla {
  width: 100%;
  /* Separación con lo que haya encima (el título de la sección o su descripción):
     pegada al texto, la fila de encabezados se lee como una línea más del párrafo. */
  margin-top: 22px;
  border-collapse: collapse;
  font-size: 14.5px;
}
.tabla th,
.tabla td {
  padding: 12px 14px 12px 0;
  text-align: left;
  vertical-align: middle;
  border-bottom: 1px solid var(--borde-suave);
}
.tabla th {
  padding-top: 0;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--texto-tenue);
  white-space: nowrap;
}
.tabla tbody tr:last-child td { border-bottom: none; }

.tabla-nombre { display: block; font-weight: 600; color: var(--texto); }
.tabla-secundario { display: block; margin-top: 2px; font-size: 13px; color: var(--texto-tenue); }
/* Cifras en tabular para que los NIP queden alineados en columna. */
.tabla-nip { font-variant-numeric: tabular-nums; color: var(--texto-medio); }
.tabla-vacia { padding: 22px 0; color: var(--texto-suave); }

/* ---- Paginación (la tabla de cuentas del panel de administración) ---- */
/* Tres piezas con los extremos fijos: en la primera y en la última página falta
   uno de los dos enlaces, y el hueco vacío mantiene «Página X de Y» centrado en
   lugar de dejar que el enlace que queda se lo lleve todo a un lado. */
.paginacion {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--borde);
}
.paginacion-hueco { min-width: 96px; }
.paginacion-donde { font-size: 14px; font-weight: 600; color: var(--texto-medio); }
.paginacion-total { font-weight: 400; color: var(--texto-tenue); }

.tabla-formulario { display: flex; align-items: center; gap: 10px; }

/* El cargo y el derecho a voto, que solo acompañan al rol Usuario (cargo.js los
   esconde con los demás roles). En el formulario de alta la casilla lleva su
   etiqueta al lado; en la tabla va sola y quien la nombra es la columna. */
.casilla { width: 15px; height: 15px; accent-color: var(--azul); cursor: pointer; }
.campo-voto {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 600;
  color: var(--texto-medio);
  cursor: pointer;
  white-space: nowrap;
}
/* En el formulario de una línea, la casilla se alinea con los controles de al
   lado, que llevan etiqueta encima y ella no. */
.admin-formulario .campo-voto { height: var(--alto-control); }
/* Los que esconde cargo.js: sus contenedores traen «display» propio y el
   atributo «hidden» no puede con él, así que se le da la razón aquí. */
[data-solo-usuario][hidden] { display: none; }

/* Botón de solo icono para las acciones que se repiten en cada fila de una
   tabla: con la palabra escrita, «Guardar» ocupaba en la fila más que el cargo.
   El nombre accesible lo lleva dentro (texto oculto) y el «title» lo cuenta a
   quien ve el dibujo y no sabe qué hace. */
.boton-icono {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--alto-control);
  height: var(--alto-control);
  padding: 0;
  border: 1px solid var(--borde);
  border-radius: 50%;
  background: var(--superficie);
  color: var(--texto-tenue);
  cursor: pointer;
  transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
}
.boton-icono:hover {
  background: var(--azul-claro);
  border-color: var(--azul);
  color: var(--azul);
}
/* Con cambios sin guardar, el botón deja de ser un icono discreto y pasa a ser
   la acción principal de su fila: es lo que hay que pulsar antes de irse. */
.boton-icono.es-pendiente {
  background: var(--azul);
  border-color: var(--azul);
  color: #FFFFFF;
}
.boton-icono.es-pendiente:hover {
  background: var(--azul-oscuro);
  border-color: var(--azul-oscuro);
  color: #FFFFFF;
}
/* Y la fila entera se tiñe: con la tabla larga, el botón solo se ve al llegar a
   él, y lo que hay que notar es que esa cuenta está a medias. */
.tabla tbody tr.hay-cambios td { background: var(--azul-claro); }

/* La columna del voto: la casilla, centrada bajo su cabecera. Con el nombre de
   la tabla delante para poder con el «text-align: left» de «.tabla th, td». */
.tabla .tabla-columna-centrada { text-align: center; }
/* La persona se queda con todo lo que sobre: los controles de las demás columnas
   miden lo que miden, y sin esto el reparto automático le daba lo justo para
   partir cada nombre en tres líneas y hacer la fila más alta que ninguna. */
.tabla-cuentas th:first-child { width: 100%; }
/* El botón de guardar cierra la fila por la derecha. */
.tabla-guardar { text-align: right; }

/* --------------------------------------------------------------------------
   Diálogos (<dialog> nativo): nueva convocatoria y proponer un punto

   El navegador se encarga del fondo, del foco y de Escape. Aquí solo se le da la
   piel de las tarjetas de Ágora y se centra en la ventana: el <dialog> de serie
   sale pegado arriba y con el borde del sistema.
   -------------------------------------------------------------------------- */
.dialogo {
  width: min(440px, calc(100vw - 32px));
  padding: 0;
  /* La hoja del navegador le pone «overflow: auto» a todo <dialog>, y con eso el
     calendario del selector de día se recortaba contra el borde del diálogo en
     lugar de sobresalir. Aquí no hace falta ningún scroll: el diálogo tiene dos
     campos y mide lo que mide. */
  overflow: visible;
  border: 1px solid var(--borde);
  border-radius: 6px;
  background: var(--superficie);
  color: var(--texto);
  box-shadow: 0 30px 70px -30px rgba(26, 47, 87, 0.55);
}
.dialogo::backdrop { background: rgba(26, 47, 87, 0.45); }

.dialogo-cuerpo { padding: 26px 28px 24px; }

.dialogo-titulo {
  margin: 0;
  font-family: var(--serif);
  font-size: 24px;
  font-weight: 500;
  color: var(--azul-oscuro);
}
/* Un título que recibe el foco al abrirse su diálogo (el de los resultados, que
   es largo: ver _resultados.html) no lleva el anillo del resto de la interfaz.
   Ahí el anillo no orienta a nadie —no es un control, y no se llega a él con el
   tabulador—: lo único que hacía era que el titular pareciera un campo de texto.
   El foco sigue puesto, que es lo que lee en voz alta un lector de pantalla. */
.dialogo-titulo[tabindex]:focus-visible { outline: none; }

.dialogo-nota {
  margin: 8px 0 20px;
  font-size: 14px;
  line-height: 1.55;
  color: var(--texto-suave);
  text-wrap: pretty;
}

.dialogo-campos { display: flex; flex-wrap: wrap; gap: 14px; }
.dialogo-campos .campo { margin-bottom: 0; }
/* El formulario de la convocatoria son dos filas, y cada una es su propio
   «.dialogo-campos»: así el reparto no depende de dónde caiga el salto. */
.dialogo-campos + .dialogo-campos { margin-top: 14px; }
/* Sin la nota de debajo del título, los campos empiezan aquí. */
.dialogo-titulo + .dialogo-campos { margin-top: 20px; }

/* Primera fila: las horas se conforman con lo justo y el día se queda con el
   resto. El día no puede bajar de ahí: dentro va la fecha escrita entera («14 de
   octubre de 2026»), y más estrecho parte en dos líneas y descuadra la fila. */
.dialogo-campos .campo-fecha { flex: 1 1 230px; }
.dialogo-campos .campo-hora { flex: 0 1 118px; }
/* Segunda fila: la ubicación se queda con lo que no ocupa el botón del
   comentario, que mide lo que mide su texto (y crece cuando dice «escrito»). */
.dialogo-campos .campo-ubicacion { flex: 1 1 160px; }
/* El comentario general se escribe en su propio diálogo: aquí solo está el botón
   que lo abre. Al pie de su fila, para que quede alineado con la ubicación de al
   lado y no con su etiqueta. */
.dialogo-campos .campo-comentario { flex: 0 0 auto; align-self: flex-end; }
/* En el de proponer un punto, un campo por línea: el título necesita todo el
   ancho, y el desplegable de tipo al lado lo dejaría en una caja donde no cabe
   ni un enunciado corto. */
.dialogo-campos .campo-titulo,
.dialogo-campos .campo-tipo { flex: 1 1 100%; }
.dialogo-campos .selector { display: block; width: 100%; }

/* El anuncio de la documentación pendiente, dentro del diálogo: la etiqueta
   encima y el texto debajo. En fila, en un diálogo estrecho, al texto le quedan
   cuatro palabras por línea. */
.dialogo .convocatoria-pendiente {
  flex-direction: column;
  gap: 6px;
  margin-top: 22px;
  padding: 14px 16px;
}

/* Adjuntar es otro asunto que el título y el tipo: se separa del bloque de
   campos, en lugar de quedar pegado al desplegable como si fuera su pista. Y no
   arrastra el margen inferior de «.campo», que aquí lo pone «.dialogo-acciones». */
.dialogo .campo-documentos { margin-top: 22px; margin-bottom: 0; }
/* Los campos de un diálogo, todos de la misma altura y con la misma caja que los
   del resto de los formularios. */
.dialogo-campos input {
  width: 100%;
  height: var(--alto-control);
  padding: 0 14px;
  border-radius: var(--radio-control);
  font-size: 14.5px;
}

/* La hora se escribe cifra a cifra y los dos puntos se ponen solos al llegar al
   minutero (ver static/js/convocatorias.js): con cifras de ancho distinto, cada
   tecla movería un poco lo que ya está escrito. */
.campo-hora input { font-variant-numeric: tabular-nums; }

/* ---- Selector de día propio ----
   El «input type=date» lo pinta el sistema operativo: su tipografía, su idioma y
   un formato de fecha que cambia de máquina a máquina. Esto es el calendario del
   panel en pequeño, con la piel de Ágora. El valor viaja en un campo oculto en
   formato ISO (ver static/js/convocatorias.js). */
.fecha { position: relative; }

.fecha-boton {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  height: var(--alto-control);
  padding: 0 14px;
  border: 1px solid var(--borde);
  border-radius: var(--radio-control);
  background: var(--superficie);
  font-size: 14.5px;
  font-weight: 600;
  color: var(--texto);
  text-align: left;
  cursor: pointer;
  transition: border-color 140ms ease, box-shadow 140ms ease;
}
.fecha-boton:hover { border-color: var(--texto-tenue); }
.fecha-boton svg { flex-shrink: 0; color: var(--texto-tenue); }
.fecha-boton[aria-expanded="true"] {
  border-color: var(--azul);
  box-shadow: 0 0 0 3px rgba(34, 61, 113, 0.14);
}
.fecha-boton[aria-expanded="true"] svg { color: var(--azul); }

.fecha-panel {
  position: absolute;
  z-index: 20;
  top: calc(100% + 8px);
  left: 0;
  width: 268px;
  max-width: calc(100vw - 48px);
  padding: 12px;
  border: 1px solid var(--borde);
  border-radius: 10px;
  background: var(--superficie);
  box-shadow: 0 20px 44px -22px rgba(26, 47, 87, 0.5);
}
/* El atributo «hidden» lo pierde cualquier «display» del autor: hay que decirlo. */
.fecha-panel[hidden] { display: none; }

.fecha-cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}
.fecha-cabecera .calendario-boton { min-width: 30px; height: 30px; padding: 0; }
.fecha-mes { font-size: 14px; font-weight: 600; color: var(--azul-oscuro); }

.fecha-semana,
.fecha-rejilla { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }

.fecha-dia-semana {
  padding: 4px 0 6px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-align: center;
  color: var(--texto-tenue);
}

.fecha-dia {
  height: 32px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: transparent;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--texto-medio);
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.fecha-dia:hover { background: var(--azul-claro); color: var(--azul-oscuro); }
.fecha-dia.es-otro-mes { color: var(--texto-tenue); opacity: 0.55; }
/* Hoy se insinúa con un borde; el día elegido va en azul lleno. Dos marcas
   distintas para dos cosas distintas, que además pueden coincidir. */
.fecha-dia.es-hoy { border-color: var(--azul-claro); color: var(--azul); }
.fecha-dia.es-elegida {
  background: var(--azul);
  border-color: var(--azul);
  color: #FFFFFF;
  opacity: 1;
}
.fecha-dia.es-elegida:hover { background: var(--azul-oscuro); color: #FFFFFF; }

.dialogo-acciones {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 24px;
}

/* Lo que contesta el servidor cuando el formulario del diálogo no vale (ver
   static/js/convocatorias.js). Solo ocupa sitio cuando lo hay: nace con
   «hidden», así que el diálogo mide lo de siempre hasta que algo falla, y
   entonces el aviso aparece justo encima de los botones, que es donde se está
   mirando al intentar guardar. Los campos que fallan van además con el borde
   rojo de «aria-invalid», como en la validación del navegador. */
.dialogo-fallo {
  margin: 18px 0 0;
  padding: 10px 12px;
  border: 1px solid #E4C3C5;
  border-radius: var(--radio-caja);
  background: #FBF3F3;
  font-size: 13.5px;
  line-height: 1.5;
  color: #8C2E33;
}
.dialogo-fallo[hidden] { display: none; }

/* Cuando en la línea de acciones hay algo que deshace lo que se está mirando
   («Quitar del orden del día»), se va al extremo contrario: separado de
   «Guardar», que es lo que se pulsa de corrido, para que no se confundan. */
.dialogo-acciones-repartidas { justify-content: space-between; }
.dialogo-acciones-grupo { display: flex; gap: 10px; }

/* Quitar no borra el punto —queda entre los rechazados y se puede recuperar—,
   así que no lleva el rojo lleno de una acción sin vuelta: solo el color. */
.boton-accion-quitar {
  border-color: #EED9DA;
  background: #FDF1F1;
  color: #8C2E33;
}
.boton-accion-quitar:hover { background: #F8E3E4; border-color: #E4C5C7; }

/* Borrar sí es una acción sin vuelta (la convocatoria entera y todo lo que
   colgaba de ella), así que va con el rojo lleno: el mismo peso visual que el
   botón de guardar, pero sin que se pueda confundir con él. Solo en el botón que
   confirma, dentro del diálogo; el que abre el diálogo lleva la versión suave. */
.boton-accion-borrar {
  border-color: #9B3239;
  background: #9B3239;
  color: #FFFFFF;
}
.boton-accion-borrar:hover { background: #7F272D; border-color: #7F272D; }

/* --------------------------------------------------------------------------
   Pantalla de una convocatoria: orden del día y puntos

   Misma piel que el panel de administración (tarjetas blancas sobre el fondo,
   borde tenue y la misma sombra). Las clases son propias y no compartidas con
   «.admin» porque esta pantalla la abre cualquiera, no solo quien administra: lo
   que cambia con el rol son los botones de dentro.
   -------------------------------------------------------------------------- */
.convocatoria {
  flex: 1;
  min-width: 0;
  /* Sin «z-index», por lo mismo que «.admin»: las listas de los desplegables
     tienen que poder taparle el pie fijo. */
  position: relative;
  width: 100%;
  /* El mismo ancho que el panel (ver «.panel»). Era de 1000px, y con él la fila
     de un punto no daba de sí: el título se partía en dos para dejarle sitio a
     los botones, y aun así los del último punto se caían a una segunda línea.
     Lo que se lee dentro no se estira por esto —el comentario general y el
     resumen siguen cortados a 72 caracteres—, solo la lista de puntos. */
  max-width: 1200px;
  margin: 0 auto;
  padding: 34px var(--margen-lateral) 40px;
}

.convocatoria-cabecera { margin-bottom: 26px; }
.convocatoria-cabecera .antetitulo { margin-bottom: 12px; }

.convocatoria-titulo-fila { position: relative; display: flex; align-items: center; gap: 12px; }

.convocatoria-titular {
  margin: 0;
  font-family: var(--serif);
  font-size: 38px;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--azul-oscuro);
}

/* La vuelta al calendario, en el margen que queda a la izquierda de la columna
   (como la del panel de administración): es una salida, no contenido. */
.convocatoria-volver {
  position: absolute;
  right: 100%;
  top: 50%;
  transform: translateY(-50%);
  margin-right: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  border: 1px solid var(--borde);
  border-radius: 50%;
  background: var(--superficie);
  color: var(--azul-oscuro);
  transition: background 140ms ease, border-color 140ms ease;
}
.convocatoria-volver:hover {
  background: var(--azul-claro);
  border-color: var(--azul-claro);
  color: var(--azul);
  text-decoration: none;
}

/* Una convocatoria suspendida se lee tachada desde el titular, igual que en el
   calendario, y la etiqueta de al lado lo dice con palabras. */
.convocatoria-titular.es-suspendida {
  color: var(--texto-suave);
  text-decoration: line-through;
}
.etiqueta-suspendida {
  flex-shrink: 0;
  padding: 4px 12px;
  border: 1px solid #EED9DA;
  border-radius: var(--radio-control);
  background: #FDF1F1;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #8C2E33;
}

/* Movida: sin tachar, como en el calendario. El tachado es de lo que no se
   celebra, y esta se celebra en otro día: lo dice la etiqueta de al lado, en
   azul, y la pantalla enlaza a donde está ahora todo. */
.convocatoria-titular.es-movida { color: var(--texto-medio); }
.etiqueta-movida {
  flex-shrink: 0;
  padding: 4px 12px;
  border: 1px solid #D5DFF0;
  border-radius: var(--radio-control);
  background: #EEF3FB;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--azul);
}

/* Cuándo y dónde, en una línea. Es un flex y no texto corrido porque el icono
   del sitio le movía la línea base a su trozo: centrados en la fila, los dos van
   a la misma altura pase lo que pase dentro de cada uno. */
.convocatoria-hora {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  margin: 10px 0 0;
  font-size: 16px;
  color: var(--texto-suave);
}
.convocatoria-cuando { font-variant-numeric: tabular-nums; }

/* Dónde se celebra, detrás de un separador: es el otro dato de «cuándo y
   dónde», no un apartado aparte. */
.convocatoria-lugar {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding-left: 14px;
  border-left: 1px solid var(--borde);
}
.convocatoria-lugar svg { flex-shrink: 0; color: var(--texto-tenue); }

/* Lo que ACRI puede hacer con la convocatoria entera. Debajo del «cuándo y
   dónde» y en su propia línea: no son acciones de ningún punto. */
.convocatoria-gestion {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  /* Con menos, los botones se leían como parte de la línea del «cuándo y dónde»
     en lugar de como lo que se puede hacer con la convocatoria. */
  margin-top: 26px;
}

/* El comentario general es HTML del editor (saneado al guardarlo), así que aquí
   se le da forma a lo que puede traer: párrafos, listas, enlaces y poco más. */
.convocatoria-comentario {
  margin-top: 14px;
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--texto);
  text-wrap: pretty;
}
.convocatoria-comentario > :first-child { margin-top: 0; }
.convocatoria-comentario > :last-child { margin-bottom: 0; }
.convocatoria-comentario p { margin: 0 0 10px; }
.convocatoria-comentario ul,
.convocatoria-comentario ol { margin: 0 0 10px; padding-left: 22px; }
.convocatoria-comentario li { margin-bottom: 4px; }
.convocatoria-comentario a { color: var(--azul); }

.convocatoria-tarjeta {
  margin-bottom: 22px;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: 6px;
  box-shadow: 0 18px 44px -28px rgba(26, 47, 87, 0.28);
}
.convocatoria-tarjeta .tarjeta-filo { border-radius: 5px 5px 0 0; }
.convocatoria-tarjeta .tarjeta-filo::before { border-top-left-radius: 5px; }

.convocatoria-cuerpo { padding: 26px 28px; }

.convocatoria-titulo {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  font-size: 19px;
  font-weight: 600;
  color: var(--azul-oscuro);
}
/* El título de una sección con su acción a la derecha (el + de proponer). */
.convocatoria-fila-titulo {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

/* Proponer un punto: un botón redondo con el signo y nada más. Lo que se viene a
   leer aquí son los puntos, y un formulario de tres campos siempre desplegado
   pesaba más en la pantalla que la acción que representa. El nombre lo lleva
   dentro, oculto, para quien no ve el signo. */
.boton-mas {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: var(--alto-control);
  height: var(--alto-control);
  padding: 0;
  border: 1px solid var(--azul);
  border-radius: 50%;
  background: var(--azul);
  color: #FFFFFF;
  cursor: pointer;
  transition: background 140ms ease, border-color 140ms ease, transform 140ms ease;
}
.boton-mas:hover { background: var(--azul-oscuro); border-color: var(--azul-oscuro); }
.boton-mas:active { transform: scale(0.94); }
@media (prefers-reduced-motion: reduce) {
  .boton-mas:active { transform: none; }
}

/* Cuántos puntos esperan revisión, al lado del título de la sección. */
.convocatoria-cuenta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  height: 24px;
  padding: 0 7px;
  border-radius: 12px;
  background: var(--azul-claro);
  font-size: 13px;
  font-weight: 700;
  color: var(--azul);
}

.convocatoria-nota {
  margin: 8px 0 0;
  max-width: 72ch;
  font-size: 14px;
  line-height: 1.55;
  color: var(--texto-suave);
  text-wrap: pretty;
}
.convocatoria-nota strong { font-weight: 600; color: var(--texto-medio); }

.convocatoria-vacio {
  margin: 20px 0 0;
  font-size: 15px;
  color: var(--texto-suave);
}

/* Lo que está anunciado pero todavía no funciona (la documentación de un punto
   espera al almacén de ficheros). Se ve que está ahí y se ve que no está listo. */
.convocatoria-pendiente {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin-top: 24px;
  padding: 16px 18px;
  border: 1px dashed var(--borde);
  border-radius: 5px;
  background: var(--fondo);
}
.convocatoria-pendiente .convocatoria-nota { margin: 0; }

.etiqueta-pendiente {
  flex-shrink: 0;
  padding: 4px 10px;
  border-radius: 12px;
  background: #FBF3E4;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #7A5716;
  white-space: nowrap;
}

/* ---- Lista de puntos ---- */
/* Vale para el <ol> del orden del día y para los <ul> de propuestos y
   rechazados: el número de cada punto lo pone el servidor («orden»), no el
   navegador, así que la lista nunca lleva su propia numeración. */
.puntos { margin: 22px 0 0; padding: 0; list-style: none; }

/* El número, el punto y sus botones en una fila. Se dobla si no caben: los
   botones bajan enteros a una segunda línea en vez de estrujar el título y la
   firma hasta partirlos palabra a palabra, que es lo que pasaba con un voto de
   nombre largo o una pantalla estrecha. */
.punto {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 14px;
  padding: 16px 0;
  border-bottom: 1px solid var(--borde-suave);
}
.punto:last-child { border-bottom: none; }
.punto:first-child { padding-top: 0; }

/* El número del orden del día. Es el «orden» que guarda el servidor, no la
   posición visual: si alguna vez no coinciden, es que hay algo que arreglar. */
.punto-posicion {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  /* Centrado sobre la primera línea del título, como los botones del final:
     (38 − 28) / 2, la mitad de lo que le sobra a un control de alto. */
  margin-top: 5px;
  border-radius: 50%;
  background: var(--azul);
  font-size: 13.5px;
  font-weight: 700;
  color: #FFFFFF;
}

/* Crece con la fila, pero no por debajo de 260px: ese es el ancho que dobla la
   fila en lugar de seguir estrechando el título.

   El relleno de arriba es lo que pone en el mismo eje las tres partes de la
   fila. Los botones miden un control de alto (38px) y van alineados arriba, así
   que su centro cae a 19px del borde del punto; con estos 8px, el centro de la
   primera línea del título cae ahí mismo (8 + 21/2). Y sigue cayendo ahí cuando
   el título se parte en dos, que es lo que no conseguía ninguna alineación de
   flex: «center» los descolocaba en cuanto una fila era más alta que otra. */
.punto-cuerpo { flex: 1 1 260px; min-width: 0; padding-top: 8px; }

/* El título manda en su línea y la ocupa entera. La altura de línea va escrita y
   no heredada: es de lo que sale el relleno de «.punto-cuerpo», y con la del
   navegador cambiaría con la tipografía. */
.punto-titulo {
  display: block;
  margin: 0;
  font-size: 15.5px;
  line-height: 1.35;
  font-weight: 600;
  color: var(--texto);
}

.punto-etiqueta {
  display: inline-block;
  margin-left: 10px;
  white-space: nowrap;
  padding: 3px 10px;
  border-radius: var(--radio-control);
  font-size: 12px;
  font-weight: 600;
  color: var(--texto-medio);
  background: var(--fondo);
  border: 1px solid var(--borde-suave);
}
/* Un punto de resolución se vota: se distingue de un vistazo de uno informativo.
   Lo dice el dorado de la casa teñiendo la pastilla entera, y no un filo a la
   izquierda: con los extremos redondos, ese filo quedaba como una uña. */
.punto-etiqueta-resolucion {
  border-color: #E9D8B4;
  background: #FBF3E4;
  color: #7A5716;
}

/* Debajo del título, de qué tipo es el punto y de quién viene. La etiqueta va
   aquí y no pegada al título porque al lado del título dependía de lo que
   midiera este: en un título corto se quedaba en su línea y en uno largo caía
   sola a la siguiente, y los puntos de una misma lista no medían lo mismo. En
   esta línea empieza siempre en el mismo sitio —bajo la primera letra del
   título—, que es lo que deja mirar la lista en vertical y ver de un vistazo
   cuáles se votan. */
.punto-firma {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  margin: 8px 0 0;
  font-size: 13px;
  color: var(--texto-tenue);
}
/* La separación ya la pone el «gap» de la línea. */
.punto-firma .punto-etiqueta { margin-left: 0; }

/* Aceptar y rechazar, en las listas de propuestos: como las herramientas del
   orden del día, al final de la fila y sin estrujar lo que hay a su izquierda. */
.punto-acciones {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
  margin-left: auto;
}
.punto-acciones form { display: flex; }

/* Los extremos de la lista de ordenar: la flecha se queda, pero apagada.
   Quitarla movería los botones de sitio en la primera y la última fila. */
.calendario-boton:disabled { opacity: 0.35; cursor: default; }
.calendario-boton:disabled:hover {
  background: var(--superficie);
  border-color: var(--borde-suave);
  color: var(--azul-oscuro);
}

/* ---- Lo que cada punto lleva a la derecha: votar, anotar y editar ----
   Un botón por cosa, cada uno abre su diálogo, todos al final de la fila del
   punto: debajo del título ocupaban una línea entera por punto, y en una
   convocatoria de diez puntos la lista se estiraba media pantalla solo de
   botones. El botón ya dice lo que hay (el voto puesto, si las notas están
   guardadas), que es lo único que se necesita saber de un vistazo. */
.punto-herramientas {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  flex-shrink: 0;
  /* Al final de la fila, y también cuando bajan a la suya: así quedan donde
     estaban, alineados con el borde derecho de la tarjeta. */
  margin-left: auto;
}
/* Los de la fila del punto son más pequeños que un botón de la página: son tres
   seguidos y van dentro de una lista. «.boton-accion-icono» es el mismo dibujo a
   tamaño normal, para los botones sueltos (el «Ordenar» de la cabecera). */
/* Algo más estrechos de lados que un botón suelto (18px): son cinco en fila y
   cada píxel que no se llevan es sitio para el título del punto. */
.punto-herramienta { font-size: 13.5px; padding: 0 14px; }
.punto-herramienta,
.boton-accion-icono { gap: 8px; }
/* Los que se quedan en el icono a secas (documentación y editar): un cuadrado
   del alto de cualquier control, sin el relleno lateral que le pone su texto a
   un botón normal. El nombre lo llevan en texto oculto y en el «title». */
.punto-herramienta-solo-icono {
  width: var(--alto-control);
  padding: 0;
  flex-shrink: 0;
}
.punto-herramienta svg,
.boton-accion-icono svg { color: var(--texto-tenue); }
.punto-herramienta:hover svg,
.boton-accion-icono:hover svg { color: var(--azul); }
/* Cerrada la votación, el botón del voto ya no abre nada: se queda apagado y
   solo dice cómo quedó. Sin el azul del :hover ni el cambio de color del icono,
   que prometerían que se puede pulsar. */
.punto-herramienta:disabled {
  background: var(--fondo);
  color: var(--texto-tenue);
  cursor: default;
}
.punto-herramienta:disabled:hover { background: var(--fondo); }
.punto-herramienta:disabled:hover svg { color: var(--texto-tenue); }

/* ---- Las herramientas del orden del día, en columnas ----
   Con los botones sueltos en una fila pegada a la derecha, cada punto empezaba
   sus botones en un sitio distinto —uno no se vota, otro no tiene resumen, otro
   no lleva documentación—, y la lista se leía como un montón de pastillas
   descolocadas. Aquí cada herramienta tiene su columna, siempre la misma y en el
   mismo orden, así que los botones de todos los puntos caen unos debajo de
   otros; donde un punto no tiene herramienta queda el hueco, que es lo que
   sostiene la columna.

   Las columnas se anclan a la derecha (la rejilla entera lleva «margin-left:
   auto» de «.punto-herramientas»), de modo que un texto más largo de lo previsto
   —«Tu voto · requiere información»— ensancha su columna hacia la izquierda sin
   descolocar las que tiene al lado. Por eso los anchos son mínimos y no fijos.

   Solo en pantalla ancha: por debajo, los botones bajan a su propia línea (ver
   más abajo) y ahí no hay nada que alinear. Y solo en el orden del día
   («.puntos-orden»): en las listas de propuestos, la fila termina en «Aceptar» y
   «Rechazar», que no son de esta rejilla. */
@media (min-width: 1040px) {
  .puntos-orden .punto-herramientas {
    /* El ancho de cada columna. Van en variables porque las que no usa nadie en
       toda la lista se apagan más abajo. */
    --columna-voto: minmax(188px, auto);      /* cabe «Tu voto · no favorable» */
    --columna-resumen: minmax(154px, auto);   /* cabe «Generar resumen» */
    --columna-nota: minmax(124px, auto);      /* cabe «Mis notas» y su punto */
    --columna-documentos: var(--alto-control);
    --columna-editar: var(--alto-control);

    display: grid;
    grid-template-columns:
      var(--columna-voto) var(--columna-resumen) var(--columna-nota)
      var(--columna-documentos) var(--columna-editar);
  }

  /* Una columna solo guarda sitio si alguien de la lista la usa. Quien no
     gestiona no edita ningún punto, y un orden del día sin nada que votar no
     tiene votos: reservarles la columna dejaba la fila cortada antes del borde
     de la tarjeta. Donde «:has()» no llegue, la columna se queda vacía como
     estaba, que es de lo que no se muere nadie. */
  .puntos-orden:not(:has(.punto-herramienta-voto)) .punto-herramientas { --columna-voto: 0px; }
  .puntos-orden:not(:has(.punto-herramienta-resumen)) .punto-herramientas { --columna-resumen: 0px; }
  .puntos-orden:not(:has(.punto-herramienta-nota)) .punto-herramientas { --columna-nota: 0px; }
  .puntos-orden:not(:has(.punto-herramienta-documentos)) .punto-herramientas { --columna-documentos: 0px; }
  .puntos-orden:not(:has(.punto-herramienta-editar)) .punto-herramientas { --columna-editar: 0px; }
  .puntos-orden .punto-herramienta-voto { grid-column: 1; }
  .puntos-orden .punto-herramienta-resumen { grid-column: 2; }
  .puntos-orden .punto-herramienta-nota { grid-column: 3; }
  .puntos-orden .punto-herramienta-documentos { grid-column: 4; }
  .puntos-orden .punto-herramienta-editar { grid-column: 5; }
  /* El aviso de que el resumen no salió no es una herramienta: va debajo de
     todas, no en el hueco de ninguna. */
  .puntos-orden .resumen-fallo { grid-column: 1 / -1; }
}

/* Por debajo de ese ancho no caben los botones al lado del título: bajan a su
   propia línea, pegados a la derecha, y lo hacen en todos los puntos a la vez.
   Que bajaran solo en el punto al que no le cabían era justo lo que rompía la
   lista. */
@media (max-width: 1039px) {
  /* Todo el ancho menos el número y su hueco (28 + 14): así el número se queda
     al lado del título —que es de lo que es el número— y lo único que baja son
     los botones. Con el 100% de siempre, el número se quedaba solo en una
     línea y el punto empezaba en la siguiente. */
  .puntos-orden .punto-cuerpo { flex-basis: calc(100% - 42px); }
}

/* El sí o el no de un botón, cuando no hace falta ninguna palabra para decirlo:
   el punto azul de «Mis notas». Del color de la marca de la casa y del tamaño
   de la tilde de una i: se ve al pasar la vista por la columna y no pesa como
   otra pastilla más. */
.punto-herramienta-senal {
  width: 7px;
  height: 7px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--azul);
}

/* El estado, más tenue que la acción: «Tu voto · favorable». */
.punto-herramienta-marca {
  padding-left: 8px;
  border-left: 1px solid var(--borde);
  font-weight: 400;
  color: var(--texto-tenue);
}

/* ---- Resumen de la documentación de un punto propuesto ----
   Mientras el servidor llama al modelo (ver static/js/resumen.js), el texto
   del botón dice «Generando…» con un degradado rosa-morado-azul en movimiento.
   El truco es el de siempre para colorear letras: el degradado es el fondo del
   <span>, recortado a la forma del texto, y lo que se anima es su posición. */
.es-generando [data-resumen-texto] {
  background: linear-gradient(90deg, #E453A7, #7A5CFF, #3F8CFF, #B45CFF, #E453A7);
  background-size: 200% auto;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: resumen-generando 1.8s linear infinite;
}
@keyframes resumen-generando {
  from { background-position: 0% center; }
  to { background-position: -200% center; }
}
/* Sin movimiento para quien lo tiene pedido así: se queda el degradado quieto,
   que ya dice «esto está en marcha» sin marear. */
@media (prefers-reduced-motion: reduce) {
  .es-generando [data-resumen-texto] { animation: none; }
}

/* El fallo al generar (sin documentación, el modelo que no responde): lo
   escribe resumen.js junto al botón, como el de la votación en su diálogo. En
   la fila de herramientas ocupa su propia línea, debajo de los botones. */
.resumen-fallo {
  display: block;
  flex-basis: 100%;
  margin: 4px 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: #8C2E33;
}
.resumen-fallo[hidden] { display: none; }

/* La vista del resumen dentro de su diálogo: HTML que sale del editor, con el
   mismo aire que el comentario general de la convocatoria.

   El scroll lo lleva el texto y no el diálogo entero: un resumen de varios
   párrafos crecía hasta salirse de la ventana por abajo, y con el diálogo
   desplazándose se iban con él el titular y los botones de «Editar» y
   «Cerrar». Así el marco se queda quieto y solo rueda lo que hay que leer.
   «overscroll-behavior» corta el rebote que, al llegar al final, seguía
   moviendo la página de detrás. */
.resumen-contenido {
  margin-top: 18px;
  max-height: min(56vh, 460px);
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Sitio para la barra, que si no se come el final de las líneas. */
  padding-right: 14px;
  font-size: 15px;
  line-height: 1.65;
  color: var(--texto);
}
.resumen-contenido p,
.resumen-contenido ul,
.resumen-contenido ol { margin: 0 0 10px; }
.resumen-contenido > :last-child { margin-bottom: 0; }

/* ---- Documentación de un punto ----
   La lista vive en su diálogo (una tarjeta por documento) y cada documento
   tiene su visor: un diálogo ancho con el PDF servido por la aplicación en un
   iframe. La src del iframe la pone static/js/documentos.js al abrir. */
.documentos {
  margin: 18px 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.documento {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border: 1px solid var(--borde-suave);
  border-radius: var(--radio-caja);
  background: var(--fondo);
}
.documento-icono { flex-shrink: 0; color: var(--texto-tenue); }
.documento-datos {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.documento-nombre {
  font-size: 14.5px;
  font-weight: 600;
  color: var(--azul-oscuro);
  overflow-wrap: anywhere;
}
.documento-ficha { font-size: 12.5px; color: var(--texto-tenue); }
.documento-acciones { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.documento-acciones .boton-accion { font-size: 13px; padding: 0 14px; }
/* El de quitar se queda en el icono, y sin el relleno lateral de los otros dos:
   con la palabra escrita, la fila no cabe en una línea junto a un nombre de
   fichero de verdad. */
.documento-acciones .punto-herramienta-solo-icono { padding: 0; }

/* El diálogo de la documentación es más ancho que uno normal: un nombre de
   fichero real («certificado_cierre_tt_20260730_201011.pdf») con su ficha y sus
   dos botones al lado no cabe en 440 px sin partirse por la mitad. */
.dialogo-documentos {
  width: min(600px, calc(100vw - 32px));
  /* Un punto puede acumular documentos, y debajo va el formulario de añadir: a
     partir de unos cuantos, el diálogo no cabe en la ventana. Aquí sí se deja
     rodar (dentro no hay nada que tenga que sobresalir, como el calendario del
     selector de día). */
  max-height: calc(100vh - 48px);
  overflow: auto;
}

/* Añadir documentación a un punto ya propuesto: es otro formulario dentro del
   mismo diálogo, así que se separa con una raya de lo que ya está subido. */
.documentos-anadir {
  margin-top: 22px;
  padding-top: 20px;
  border-top: 1px solid var(--borde-suave);
}
.documentos-anadir .campo-documentos { margin: 0; }
.documentos-anadir .dialogo-acciones { margin-top: 16px; }

.dialogo-visor { width: min(860px, calc(100vw - 32px)); }
/* La altura del PDF: lo que deje la pantalla, sin comerse el título ni los
   botones del diálogo. */
.visor-marco {
  display: block;
  width: 100%;
  height: min(68vh, 720px);
  margin-top: 18px;
  border: 1px solid var(--borde);
  border-radius: var(--radio-caja);
  background: var(--fondo);
}

/* El campo de adjuntar. El borde discontinuo dice «aquí se dejan ficheros». El
   botón y el texto de al lado son de la página y no del navegador: el control
   de serie se pinta en el idioma del navegador («Choose files») y no hay manera
   de traducirlo. El <input> de verdad sigue ahí, transparente y encima de la
   caja entera, así que un clic en cualquier punto abre el selector del sistema.
   Ver templates/convocatorias/_campo_documentos.html. */
.adjuntar {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border: 1px dashed var(--borde);
  border-radius: var(--radio-caja);
  background: var(--fondo);
  transition: border-color 140ms ease, box-shadow 140ms ease;
}
.adjuntar:hover { border-color: var(--azul); }
/* El foco se pinta aquí y no en el <input>: con «opacity: 0» el contorno del
   navegador tampoco se vería. */
.adjuntar:focus-within {
  border-color: var(--azul);
  box-shadow: 0 0 0 3px rgba(34, 61, 113, 0.14);
}
.adjuntar-fichero {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}
.adjuntar-boton {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  height: 30px;
  padding: 0 14px;
  border: 1px solid var(--borde);
  border-radius: var(--radio-control);
  background: var(--superficie);
  font-size: 13px;
  font-weight: 600;
  color: var(--azul-oscuro);
}
.adjuntar-estado { min-width: 0; font-size: 13.5px; color: var(--texto-suave); }

/* Lo elegido, un renglón por fichero (lo escribe static/js/adjuntar.js). El
   control de serie solo dice «2 files»: cuáles son, no. */
.adjuntar-elegidos {
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.adjuntar-elegido {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 8px 7px 12px;
  border: 1px solid var(--borde-suave);
  border-radius: var(--radio-caja);
  background: var(--superficie);
}
.adjuntar-elegido-nombre {
  flex: 1;
  min-width: 0;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--azul-oscuro);
  overflow-wrap: anywhere;
}
.adjuntar-elegido-peso { flex-shrink: 0; font-size: 12.5px; color: var(--texto-tenue); }
.adjuntar-quitar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--texto-tenue);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}
.adjuntar-quitar:hover { background: var(--azul-claro); color: var(--azul-oscuro); }

.campo-pista { margin: 8px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--texto-tenue); }

/* La raya de «sin documentación» en la tabla de mis propuestas. */
.tabla-vacio { color: var(--texto-tenue); }

/* ---- Votación de un punto de resolución (dentro de su diálogo) ---- */
.votacion-opciones { display: flex; flex-wrap: wrap; gap: 8px; }

.voto-boton {
  display: inline-flex;
  align-items: center;
  height: var(--alto-control);
  padding: 0 16px;
  border: 1px solid var(--borde);
  border-radius: var(--radio-control);
  background: var(--superficie);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--texto-medio);
  cursor: pointer;
  transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
}
.voto-boton:hover { border-color: var(--azul); color: var(--azul-oscuro); }

/* El voto propio, marcado en azul. Es la única señal de voto que da la pantalla:
   quién ha votado qué no se enseña en ninguna parte. */
.voto-boton.es-mi-voto {
  border-color: var(--azul);
  background: var(--azul);
  color: #FFFFFF;
}
.voto-boton.es-mi-voto:hover { background: var(--azul-oscuro); color: #FFFFFF; }

/* Lo que acompaña a la abstención: el botón que abre el editor de «qué
   información falta». Separado de las tres opciones por una raya, porque no es
   una cuarta manera de votar, y fuera de la vista mientras el voto no sea la
   abstención: aparece al votarla y se recoge al cambiar a otra opción (ver
   static/js/votacion.js).

   La entrada va en rejilla de una fila: animar su alto de «0fr» a «1fr» deja
   crecer al diálogo sin el salto de enseñar el bloque de golpe. El cuerpo lleva
   el recorte porque el botón de dentro tiene alto propio y no encoge con la
   fila. Donde el alto no se sepa animar, el bloque entra apareciendo, que es lo
   que hay que anunciar. */
.votacion-informacion {
  display: grid;
  grid-template-rows: 1fr;
  margin: 16px 0 0;
  padding: 14px 0 0;
  border-top: 1px solid var(--borde-suave);
}
.votacion-informacion[hidden] { display: none; }
.votacion-informacion-cuerpo { min-height: 0; overflow: hidden; }
.votacion-informacion.es-entrando { animation: votacion-informacion-entra 260ms ease-out; }

@keyframes votacion-informacion-entra {
  from { grid-template-rows: 0fr; margin-top: 0; padding-top: 0; opacity: 0; }
  to { grid-template-rows: 1fr; opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .votacion-informacion.es-entrando { animation-duration: 1ms; }
}

/* El fallo al guardar un voto sin recargar la página: solo lo escribe
   votacion.js cuando lo hay (la red, la sesión, un punto que ya no está). */
.votacion-fallo {
  margin: 14px 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: #8C2E33;
}

/* Recuento: solo para ACRI (la plantilla ni lo recibe si no lo es). */
.votacion-recuento {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin: 18px 0 0;
  padding: 12px 14px;
  border: 1px solid var(--borde-suave);
  border-radius: var(--radio-caja);
  background: var(--fondo);
  font-size: 13.5px;
  color: var(--texto-medio);
}
.votacion-recuento-titulo {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--texto-tenue);
}
.votacion-cifra strong { font-variant-numeric: tabular-nums; color: var(--azul-oscuro); }
.votacion-total { color: var(--texto-tenue); }

/* Lo que se pide en las abstenciones, debajo del recuento y solo para ACRI.
   Una tarjeta por petición, sin nombre: es texto con formato del editor, y lo
   que importa es qué falta, no de quién es. */
.votacion-pedido { margin: 14px 0 0; }
.votacion-pedido-lista {
  display: grid;
  gap: 8px;
  margin: 10px 0 0;
  padding: 0;
  list-style: none;
}
.votacion-pedido-texto {
  padding: 12px 14px;
  border: 1px solid var(--borde-suave);
  border-radius: var(--radio-caja);
  background: var(--fondo);
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--texto-medio);
}
/* El HTML del editor trae sus propios párrafos: sin esto, el primero y el
   último separarían el texto de los bordes de su tarjeta. */
.votacion-pedido-texto > :first-child { margin-top: 0; }
.votacion-pedido-texto > :last-child { margin-bottom: 0; }

/* ---- Resultado de una votación cerrada (dentro de su diálogo) ----
   Solo lo ven ACRI y Estrategia Digital, y solo desde que llega el día del acto
   (ver templates/convocatorias/_resultados.html). Es la única pantalla
   donde el sentido de un voto va junto a quien lo emitió, así que se lee como lo
   que es —un acta— y no como un tablero.

   El diálogo lleva scroll propio, al revés que los demás: crece con los puntos
   de resolución y con los vicerrectorados, y ninguno de los dos tiene tope.
   Dentro no hay nada que deba salirse de sus bordes (ni calendario ni
   desplegable), así que aquí «overflow: auto» no recorta nada. */
.dialogo-resultados {
  width: min(620px, calc(100vw - 32px));
  max-height: min(80vh, 780px);
  overflow: auto;
}

/* Los botones de la cabecera de una sección, juntos: la fila los reparte con
   «space-between» y sueltos se irían cada uno a una punta. */
.convocatoria-fila-acciones { display: flex; align-items: center; gap: 8px; }

.resultados { display: grid; gap: 24px; }
.resultados-punto + .resultados-punto {
  padding-top: 24px;
  border-top: 1px solid var(--borde-suave);
}

.resultados-titulo {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  font-size: 15.5px;
  font-weight: 600;
  color: var(--texto);
}
/* El número del orden del día, como en la fila del punto pero más pequeño: aquí
   acompaña a un titular, no encabeza la fila entera. */
.resultados-posicion {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--azul);
  font-size: 12.5px;
  font-weight: 700;
  color: #FFFFFF;
}

/* La barra del resumen: una celda por papeleta, agrupadas por sentido y con las
   de quien no votó al final. Todas ocupan lo mismo («flex: 1 1 0»), así que el
   ancho de cada sentido sale del número de votos sin calcular ninguna anchura:
   la Content-Security-Policy no admite estilos en línea, y una barra con
   porcentajes escritos en el HTML los necesitaría.

   Va con «aria-hidden» en la plantilla: lo que dice la barra lo dicen debajo las
   cifras y el listado, con palabras. */
.resultados-barra {
  display: flex;
  gap: 2px;
  margin: 14px 0 0;
  padding: 0;
}
.resultados-celda {
  flex: 1 1 0;
  height: 8px;
  border-radius: 2px;
  background: var(--borde);
}
/* Los mismos tres colores que las pastillas del listado, y de la misma familia
   que los estados de un punto: verde lo que sale adelante, rojo lo que no, azul
   la abstención. */
.resultados-celda-favorable { background: #4E9E80; }
.resultados-celda-contrario { background: #C2686C; }
.resultados-celda-abstencion { background: #7E9CD0; }
.resultados-celda-sin-voto { background: var(--borde); }

.resultados-cifras {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  margin: 10px 0 0;
  font-size: 13px;
  color: var(--texto-medio);
}
/* El punto de color delante de cada cifra: es lo que ata el número con su tramo
   de la barra sin repetir ninguna leyenda. */
.resultados-cifra::before {
  content: "";
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-right: 7px;
  border-radius: 50%;
  vertical-align: 1px;
}
.resultados-cifra strong { font-variant-numeric: tabular-nums; color: var(--azul-oscuro); }
.resultados-cifra-favorable::before { background: #4E9E80; }
.resultados-cifra-contrario::before { background: #C2686C; }
.resultados-cifra-abstencion::before { background: #7E9CD0; }

/* El listado, un cargo por línea: el nombre a la izquierda y su voto a la
   derecha, para poder recorrerlo por el borde en lugar de leerlo entero. */
.resultados-lista {
  display: grid;
  gap: 2px;
  margin: 14px 0 0;
  padding: 0;
  list-style: none;
}
.resultados-fila {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 12px;
  border-radius: var(--radio-caja);
  background: var(--fondo);
}
.resultados-firma { font-size: 13.5px; line-height: 1.4; color: var(--texto-medio); }

.resultados-sentido {
  flex-shrink: 0;
  padding: 3px 10px;
  border-radius: var(--radio-control);
  font-size: 12.5px;
  font-weight: 600;
  white-space: nowrap;
}
.resultados-sentido-favorable { background: #EDF6F2; color: #1F5C4C; }
.resultados-sentido-contrario { background: #FDF1F1; color: #8C2E33; }
.resultados-sentido-abstencion { background: #EEF2F9; color: #223d71; }
/* Quien no votó: en gris y solo con el filo. Es la ausencia de un voto, no un
   cuarto sentido, y no debe pesar en la lista como los otros tres. */
.resultados-sentido-sin-voto {
  background: transparent;
  border: 1px solid var(--borde);
  color: var(--texto-tenue);
}

/* ---- Colocar el orden del día (dentro de su diálogo) ----
   La lista entera, movible: se arrastra con el ratón y se mueve con las flechas
   de cada fila (ver static/js/ordenar.js). Nada de esto viaja al servidor hasta
   que se guarda. */
.ordenar-lista {
  margin: 0;
  padding: 0;
  list-style: none;
  border: 1px solid var(--borde-suave);
  border-radius: var(--radio-caja);
  /* Con muchos puntos, la lista lleva su propio scroll: el diálogo no puede
     crecer más alto que la ventana. */
  max-height: min(52vh, 420px);
  overflow-y: auto;
}

.ordenar-punto {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--borde-suave);
  background: var(--superficie);
  /* La fila entera se arrastra, no solo el agarre: es más fácil de coger, y el
     agarre queda como la señal de que se puede. */
  cursor: grab;
}
.ordenar-punto:last-child { border-bottom: none; }
.ordenar-punto:first-child { border-radius: var(--radio-caja) var(--radio-caja) 0 0; }
.ordenar-punto:last-child { border-radius: 0 0 var(--radio-caja) var(--radio-caja); }
.ordenar-punto:hover { background: var(--fondo); }

/* Mientras se arrastra: la fila que viaja se apaga y se queda como el hueco que
   va dejando, para que lo que se lea sea el sitio al que va a caer. */
.ordenar-punto.esta-arrastrando {
  cursor: grabbing;
  opacity: 0.45;
  background: var(--azul-claro);
}
.esta-ordenando .ordenar-punto:hover { background: var(--superficie); }

.ordenar-agarre { display: flex; flex-shrink: 0; color: var(--texto-tenue); }

.ordenar-posicion {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--azul-claro);
  font-size: 12.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--azul);
}

.ordenar-titulo {
  flex: 1;
  min-width: 0;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--texto);
}
.ordenar-titulo .punto-etiqueta { font-weight: 600; }

.ordenar-flechas { display: flex; flex-shrink: 0; gap: 6px; }
/* Más pequeñas que un control de la página: son dos por fila y van dentro de
   una lista que ya es densa. */
.ordenar-flechas .calendario-boton {
  min-width: 30px;
  height: 30px;
  padding: 0;
}

/* ---- Editor de las notas (Summernote) ----
   Va tal cual viene, con su barra y sus diálogos: es el mismo editor y la misma
   versión que en UEZ. Aquí solo se retoca lo que lo delataría como venido de
   otra aplicación —la tipografía del sistema y las esquinas vivas— y se le sube
   el diálogo de enlace por encima de su propia barra, como también hace UEZ. */
.dialogo-ancho { width: min(680px, calc(100vw - 32px)); }
/* Entre el estrecho y el ancho: para el de proponer, donde debajo de los dos
   campos va el bloque de adjuntar con la lista de lo elegido. */
.dialogo-medio { width: min(520px, calc(100vw - 32px)); }
/* El de la convocatoria, un poco más: en su primera fila van tres campos —el
   día, la hora y la hora de fin— y con el ancho del de proponer, el día se
   quedaba sin sitio para su fecha escrita y la hora de fin caía a otra línea. */
.dialogo-convocatoria { width: min(580px, calc(100vw - 32px)); }

/* Sin «overflow: hidden» para redondear: recortaría la paleta de colores y los
   desplegables de la barra, que salen fuera de la caja del editor. Cada pieza
   redondea su esquina. */
.note-editor.note-frame {
  margin: 0;
  border: 1px solid var(--borde);
  border-radius: var(--radio-caja);
}
.note-editor .note-toolbar {
  border-radius: calc(var(--radio-caja) - 1px) calc(var(--radio-caja) - 1px) 0 0;
  background: var(--fondo);
  border-bottom: 1px solid var(--borde-suave);
}
.note-editor .note-statusbar {
  border-radius: 0 0 calc(var(--radio-caja) - 1px) calc(var(--radio-caja) - 1px);
}
.note-editor .note-editable {
  font-family: var(--sans);
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--texto);
}
.note-editor .note-btn,
.note-editor .note-form-control,
.note-editor .note-placeholder { font-family: var(--sans); }
.note-editor .note-btn { border-radius: 4px; }
.note-modal-backdrop { z-index: 3040; }
.note-modal { z-index: 3050; }

/* ---- Notas privadas: el recuadro de dentro del diálogo ----
   Es lo que se ve sin JavaScript (con él, encima se monta el editor). */
.dialogo textarea {
  width: 100%;
  padding: 10px 14px;
  border: 1px solid var(--borde);
  border-radius: var(--radio-caja);
  background: var(--superficie);
  font-family: inherit;
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--texto);
  resize: vertical;
}
.dialogo textarea:focus {
  outline: none;
  border-color: var(--azul);
  box-shadow: 0 0 0 3px rgba(34, 61, 113, 0.14);
}

.punto-estado {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 12px;
  font-size: 12.5px;
  font-weight: 600;
}
.punto-estado-propuesto { background: #EEF2F9; color: #223d71; }
.punto-estado-aceptado  { background: #EDF6F2; color: #1F5C4C; }
.punto-estado-rechazado { background: #FDF1F1; color: #8C2E33; }

/* --------------------------------------------------------------------------
   Pie
   -------------------------------------------------------------------------- */
.pie {
  position: fixed;
  left: 0;
  bottom: 0;
  width: 100%;
  z-index: 1000;
  padding: 12px 0;
  background: var(--azul);
  color: #FFFFFF;
  text-align: center;
}
.pie-contenido { max-width: 1200px; margin: 0 auto; padding: 0 20px; }
.pie p { margin: 0 0 3px; }
.pie p:last-child { margin-bottom: 0; }
.pie-principal { font-size: 0.8em; }
.pie-linea { font-size: 0.7em; }
/* La universidad y el contacto van en la misma línea; el guion es adorno, así
   que se dibuja aquí y no en el texto, con su aire a los dos lados. */
.pie-separador { padding: 0 0.5em; }
.pie a { color: #FFFFFF; text-decoration: underline; }
.pie a:hover { color: #FFFFFF; text-decoration: none; }

/* --------------------------------------------------------------------------
   Adaptación a pantallas menores.

   El prototipo está compuesto para escritorio; estas reglas son la parte que le
   falta para servirse en un navegador real.
   -------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  .acceso-rejilla { gap: 56px; }
  .presentacion-titulo { font-size: 78px; }
}

@media (max-width: 900px) {
  :root { --margen-lateral: 28px; }

  /* Ya no queda margen a la izquierda del contenido donde ponerla: la vuelta al
     panel entra en la fila del titular, delante de él. */
  .admin-volver,
  .convocatoria-volver { position: static; transform: none; margin-right: 0; }

  .acceso { align-items: flex-start; }
  .acceso-rejilla {
    grid-template-columns: 1fr;
    gap: 44px;
    padding-top: 56px;
    padding-bottom: 56px;
    max-width: 620px;
  }
  .presentacion-titulo { font-size: 66px; }
  /* Más pequeño y más tenue: en una pantalla estrecha el sello queda debajo del
     contenido, no a un lado, y a tamaño completo compite con el texto. */
  .sello { width: 520px; right: -130px; bottom: -150px; opacity: 0.4; }
}

/* La cabecera con sesión abierta lleva cuatro cosas a la derecha (nombre, rol,
   administración y salida) y por debajo de unos 820 px ya no caben en la misma
   línea que la marca: se reparte en dos filas y la cabecera crece en lugar de
   desbordarse a lo ancho. */
@media (max-width: 820px) {
  :root { --alto-cabecera: auto; }

  .cabecera {
    padding-top: 14px;
    padding-bottom: 14px;
    flex-wrap: wrap;
    gap: 14px;
  }
  .sesion { flex-wrap: wrap; gap: 10px; }
}

@media (max-width: 620px) {
  :root {
    /* Por debajo de unos 480 px la primera línea del pie («Vicerrectorado de
       Estrategia Digital e Inteligencia Artificial») pasa a dos líneas y el pie
       crece. Se reserva hueco de sobra; el pie se ve igual. */
    --alto-pie: 80px;
  }

  /* Los avisos, de lado a lado: una columna de 400 px no cabe. */
  .avisos {
    top: calc(var(--alto-cabecera) + 12px);
    left: 16px;
    right: 16px;
    width: auto;
  }

  .marca { gap: 14px; }
  .marca-logo { width: 132px; }
  .marca-nombre { font-size: 21px; }

  .presentacion-titulo { font-size: 54px; }
  .sello { width: 400px; right: -120px; bottom: -130px; }
  .tarjeta-cuerpo { padding: 30px 24px 24px; }
  .tarjeta-titulo { font-size: 23px; }
  .boton-sso { font-size: 15.5px; gap: 10px; padding: 0 16px; }

  .tarjeta-pie { flex-direction: column; align-items: flex-start; gap: 10px; }

  .panel { padding-top: 32px; }
  .panel-saludo { font-size: 32px; }

  .admin-titular,
  .convocatoria-titular { font-size: 30px; }
  .admin-cuerpo,
  .convocatoria-cuerpo { padding: 22px 18px; }

  /* Un punto por columna: el título arriba y sus botones debajo. En fila, los
     botones dejarían al título en una caja de cuatro caracteres. */
  .punto { flex-wrap: wrap; }
  /* En estrecho, los botones del documento caen bajo su nombre. */
  .documento { flex-wrap: wrap; }
  .punto-cuerpo { flex-basis: 100%; }

  /* En el diálogo de ordenar no se puede arrastrar (los navegadores táctiles no
     lo traen), así que las flechas son la única forma de mover: se les deja el
     alto de siempre para que se puedan pulsar con el dedo. */
  .ordenar-flechas .calendario-boton { min-width: var(--alto-control); height: var(--alto-control); }
  .ordenar-agarre { display: none; }
  .convocatoria-pendiente { flex-direction: column; gap: 10px; }

  /* En el diálogo, el día y la hora en dos líneas: uno al lado del otro se
     quedan en dos cajas donde no cabe ni la fecha. */
  .dialogo-campos { flex-direction: column; }
  .dialogo-acciones { flex-direction: column-reverse; }
  .dialogo-acciones .boton-accion { width: 100%; }
  /* Apiladas, «Guardar» arriba y «Quitar» al final: en columna, lo que deshace
     no puede quedar donde el dedo baja buscando el botón de confirmar. */
  .dialogo-acciones-repartidas { flex-direction: column-reverse; }
  .dialogo-acciones-grupo { flex-direction: column-reverse; }
  .dialogo-acciones-repartidas form { width: 100%; }
  /* Un campo por línea: en móvil, tres controles en fila quedan en una tira de
     cajas de 60 px que no se pueden ni pulsar. */
  .admin-formulario .campo,
  .admin-formulario .selector,
  .admin-formulario .boton-accion,
  .admin-busqueda input { width: 100%; }
  .admin-busqueda { flex-direction: column; align-items: stretch; }

  /* La última columna de las tablas lleva desplegable y botón: apilados caben, en
     fila no. Y las columnas se reparten el ancho a partes iguales en lugar de
     ajustarse al contenido, que es lo que hacía crecer la tabla más que la
     pantalla; el texto largo (un correo) parte de línea donde toque. */
  .tabla { table-layout: fixed; }
  .tabla th,
  .tabla td { padding: 10px 10px 10px 0; }
  .tabla th { white-space: normal; }
  /* Solo la línea secundaria (el correo) puede partirse por donde sea: en el resto
     rompería palabras normales, y en el desplegable dejaría un «Administra dor». */
  .tabla-secundario { overflow-wrap: anywhere; }
  /* El reparto a partes iguales de «table-layout: fixed» deja las columnas de
     los desplegables demasiado estrechas: el NIP, el voto y el botón de guardar
     necesitan muy poco y les ceden su parte. */
  .tabla-cuentas th:nth-child(1) { width: 26%; }
  .tabla-cuentas th:nth-child(2) { width: 9%; }
  .tabla-cuentas th:nth-child(3) { width: 24%; }
  .tabla-cuentas th:nth-child(4) { width: 26%; }
  .tabla-cuentas th:nth-child(5) { width: 7%; }
  .tabla-cuentas th:nth-child(6) { width: 8%; }
  .tabla-formulario { flex-direction: column; align-items: stretch; }
  /* El título de la sección, encima del buscador y no a su lado. */
  .admin-fila-titulo { flex-direction: column; align-items: stretch; }
  .selector { display: block; }
  /* Aquí manda la columna, no el ancho fijo del desplegable buscable. */
  .selector.es-buscable { width: 100%; }
  .selector-boton { min-width: 0; }
  /* La lista crece hacia la izquierda: anclada a la derecha no se sale de la vista. */
  .selector-lista { left: auto; right: 0; }

  /* En pantallas pequeñas el mes no cabe en la vista: el calendario recupera su
     altura natural y la página vuelve a hacer scroll con normalidad. */
  .pagina-panel { height: auto; min-height: 100vh; }
  .calendario { flex: none; }
  .calendario-rejilla { flex: none; }

  .calendario-cabecera { padding: 16px 16px; }
  .calendario-mes { font-size: 20px; }
  .calendario-selector { left: 10px; right: 10px; justify-content: space-between; }
  .calendario-rejilla { grid-auto-rows: minmax(52px, auto); gap: 3px; padding: 8px; }
  .calendario-celda { padding: 4px; align-items: center; }
  .calendario-numero { min-width: 26px; height: 26px; font-size: 13px; }
  /* En móvil no cabe el texto: cada evento se reduce a un punto dorado. */
  .calendario-evento {
    align-self: center;
    width: 6px;
    height: 6px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: var(--acento);
    overflow: hidden;
    color: transparent;
  }
  /* En 6px no cabe ni una letra, y la de dentro (la hora, la flecha) trae color
     propio: sin apagarla también, asoma un trozo por el recorte del punto. */
  .calendario-evento * { color: transparent; }
  /* Y el punto sigue diciendo de qué es: apagado el de una suspendida, hueco el
     de una movida —queda el sitio, la convocatoria está en otro día—. Quien
     necesite más lo tiene escrito en la lista de debajo del calendario. */
  .calendario-evento.es-suspendida {
    background: var(--borde);
    color: transparent;
  }
  .calendario-evento.es-movida {
    /* El contorno a rayas y su hueco son de la cápsula; aquí no hay cápsula. */
    border: none;
    padding: 0;
    background: transparent;
    box-shadow: inset 0 0 0 1.5px var(--acento);
    color: transparent;
  }
}
