:root {
  --ip-primario: #1d3557;
  --ip-primario-oscuro: #14253d;
  --ip-acento: #2a9d8f;
  --ip-fondo: #f4f6f9;
  /* Sistema de microinteracciones (sección "Microinteracciones" al final): una duración corta para estados
     (color, fondo, sombra), una larga para apariciones, una sola curva de salida suave, y el anillo de foco
     (color, grosor y separación del borde). Toda transición o animación del archivo usa estas variables. */
  --ip-duracion: 0.15s;
  --ip-duracion-larga: 0.2s;
  --ip-curva: cubic-bezier(0.2, 0, 0, 1);
  --ip-foco-color: #258cfb;
  --ip-foco-grosor: 3px;
  --ip-foco-separacion: 2px;
}

/* Tema oscuro (requisito "permitir también que el tema sea oscuro"): "static/js/tema.js" fija
   data-bs-theme="dark" en <html> antes del primer pintado. Bootstrap 5.3 ya adapta con ese mismo
   atributo casi todo (tarjetas, tablas, formularios, alertas, paginación, botones); aquí solo se
   redefinen las variables --ip-* propias. El resto de los colores fijos de este archivo se revisó uno
   por uno: los que son texto o borde sobre un fondo que cambia con el tema pasaron a un token de
   Bootstrap (--bs-border-color, --bs-secondary-color, --bs-tertiary-bg, --bs-emphasis-color...); los que
   deben verse siempre igual se dejaron igual (el escáner de cámara, y el fondo blanco de los logos y del
   código QR, que deben seguir siendo blancos para que se lean). La impresión de etiquetas (@media print,
   más abajo) fuerza colores claros aparte, sin depender de estas variables. */
[data-bs-theme="dark"] {
  --ip-primario: var(--ip-primario-oscuro);
  --ip-fondo: #121417;
}

html {
  position: relative;
  min-height: 100%;
}

/* Utilidad que Bootstrap no incluye: permite que un hijo flex se encoja por debajo de su contenido. */
.min-w-0 {
  min-width: 0;
}

body {
  background-color: var(--ip-fondo);
}

/* Foco visible (microinteracciones): un solo anillo, el mismo en claro y en oscuro, para todo lo interactivo,
   con :focus-visible en vez de :focus: aparece con el teclado (y en los campos de texto también al hacer clic,
   porque el navegador lo pide), pero no se queda pegado a un botón después de pulsarlo con el ratón, igual
   que en Bootstrap 5.3. Es un "outline" separado del borde por --ip-foco-separacion, no una sombra: el hueco
   deja ver el fondo real que haya detrás (tarjeta, tabla, menú azul) y --ip-foco-color (#258cfb) contrasta
   3:1 o más sobre los fondos de los dos temas (#f4f6f9, #fff, #212529, #121417 y el azul #1d3557 del menú).
   Sustituye al anillo antiguo (".btn:focus { box-shadow }", que se quedaba tras el clic) y a los de
   Bootstrap; nunca se quita un outline sin dejar este sustituto (guardiana:
   nucleo/tests/test_microinteracciones.py). */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
.btn:focus-visible,
.btn-close:focus-visible,
.nav-link:focus-visible,
.page-link:focus-visible,
.form-control:focus-visible,
.form-select:focus-visible,
.form-check-input:focus-visible {
  outline: var(--ip-foco-grosor) solid var(--ip-foco-color);
  outline-offset: var(--ip-foco-separacion);
  box-shadow: none;
}

/* Estructura general de la aplicación */

.ip-shell {
  display: flex;
  min-height: 100vh;
}

.ip-area-principal {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-width: 0;
}

.ip-contenido {
  flex: 1 1 auto;
  padding: 1rem;
}

@media (min-width: 992px) {
  .ip-contenido {
    padding: 2rem;
  }
}

/* Menú lateral */

.ip-menu {
  width: 260px;
}

@media (min-width: 992px) {
  .ip-menu.offcanvas-lg {
    position: sticky;
    top: 0;
    height: 100vh;
    overflow-x: hidden;
    overflow-y: auto;
    flex-shrink: 0;
    border: none;
  }
}

.ip-menu-interno {
  background-color: var(--ip-primario);
  color: #fff;
  min-height: 100%;
}

.ip-menu-titulo {
  color: #fff;
  font-size: 0.95rem;
  font-weight: 600;
}

.ip-menu-nav {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  overflow-y: auto;
}

.ip-enlace-menu {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  min-height: 44px;
  padding: 0.5rem 0.75rem;
  border-left: 3px solid transparent;
  border-radius: 0.25rem;
  color: rgba(255, 255, 255, 0.85);
  text-decoration: none;
}

.ip-enlace-menu:hover {
  background-color: rgba(255, 255, 255, 0.08);
  color: #fff;
}

.ip-enlace-menu.active {
  border-left-color: var(--ip-acento);
  background-color: rgba(255, 255, 255, 0.12);
  color: #fff;
  font-weight: 600;
}

/* Al pulsar (ratón o dedo) el fondo sube un punto más que al pasar el ratón: el estado :active se percibe
   aunque el usuario tenga el movimiento reducido (la transición vive en el bloque del final). */
.ip-enlace-menu:active {
  background-color: rgba(255, 255, 255, 0.16);
}

/* En el menú lateral el anillo de foco va por dentro del enlace (separación negativa): la lista es desplazable
   (overflow) y un anillo exterior quedaría cortado en el primer y en el último enlace. */
.ip-enlace-menu:focus-visible {
  outline: var(--ip-foco-grosor) solid var(--ip-foco-color);
  outline-offset: calc(-1 * var(--ip-foco-grosor));
}

.ip-enlace-menu .bi {
  font-size: 1.1rem;
}

.ip-menu-pie {
  border-top-color: rgba(255, 255, 255, 0.15) !important;
}

.ip-texto-atenuado {
  color: rgba(255, 255, 255, 0.7);
}

.ip-boton-cerrar-sesion {
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}

/* Selector de tema claro/oscuro/automático (static/js/tema.js + static/js/site.js): en el pie del menú
   lateral, y en compacto (sin la etiqueta "Tema" ni el estado en texto) bajo la tarjeta de acceso. */

.ip-selector-tema {
  margin-bottom: 0.75rem;
}

.ip-selector-tema-acceso {
  display: flex;
  justify-content: center;
  margin-top: 1rem;
}

/* Barra superior (menor a lg) */

.ip-barra-superior {
  background-color: var(--ip-primario);
  color: #fff;
}

.ip-barra-superior .ip-marca-nombre {
  color: #fff;
}

.ip-boton-menu {
  color: #fff;
}

.ip-boton-menu:hover {
  color: #fff;
}

/* Marca de la institución (ViewComponent) */

.ip-marca {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  color: #fff;
  min-width: 0;
}

/* Fondo blanco fijo (no --ip-fondo): la imagen del escudo suele traer transparencia y en tema oscuro se
   perdería contra un fondo oscuro; se mantiene igual en los dos temas a propósito, igual que el fondo del
   código QR más abajo. */
.ip-marca-logo {
  height: 32px;
  width: 32px;
  object-fit: contain;
  background-color: #fff;
  border-radius: 0.25rem;
  padding: 2px;
  flex-shrink: 0;
}

.ip-marca-icono {
  font-size: 1.75rem;
  flex-shrink: 0;
}

.ip-marca-nombre {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* En el menú lateral el nombre usa hasta dos líneas: los nombres de instituciones suelen ser largos. */
.ip-menu .ip-marca-nombre {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  white-space: normal;
  line-height: 1.25;
}

.ip-marca-acceso {
  flex-direction: column;
  color: var(--ip-primario);
}

/* En tema oscuro estos tres ya no van sobre una tarjeta clara (Bootstrap oscurece ".card" con el mismo
   data-bs-theme): --ip-primario pasa a ser un azul muy oscuro (arriba) y dejaría el texto casi invisible,
   así que se usa el color de énfasis de Bootstrap, pensado justo para eso. */
[data-bs-theme="dark"] .ip-marca-acceso,
[data-bs-theme="dark"] .ip-marca-icono-acceso,
[data-bs-theme="dark"] .ip-marca-nombre-acceso {
  color: var(--bs-emphasis-color);
}

.ip-marca-logo-acceso {
  height: 64px;
  width: 64px;
  object-fit: contain;
}

.ip-marca-icono-acceso {
  font-size: 3rem;
  color: var(--ip-primario);
}

.ip-marca-nombre-acceso {
  font-weight: 700;
  font-size: 1.1rem;
  color: var(--ip-primario);
}

/* Tarjetas */

.ip-tarjeta {
  box-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.08);
  border: none;
}

/* Elevación leve al pasar el ratón o al enfocar con el teclado el enlace que la envuelve (inicio, Reportes):
   la sombra cambia siempre; el desplazamiento de 2 px y la transición viven en el bloque de movimiento del
   final (con "prefers-reduced-motion: reduce" solo cambia la sombra, al instante). */
.ip-tarjeta:hover,
a:focus-visible > .ip-tarjeta {
  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.12);
}

/* El enlace que envuelve una tarjeta pasa a bloque del alto de su columna, para que su anillo de foco rodee la
   tarjeta entera (un <a> en línea con un bloque dentro se parte en fragmentos y el anillo sale roto). "height:
   100%" conserva la maquetación de hoy: el "h-100" de la tarjeta se resolvía contra la columna (flex, estirada)
   y ahora contra el enlace, que mide lo mismo. Un navegador sin :has() ignora la regla y queda como hoy. */
a:has(> .ip-tarjeta) {
  display: block;
  height: 100%;
}

.ip-tarjeta-deshabilitada {
  opacity: 0.7;
}

/* Página de acceso (login) */

/* "flex-direction: column" para que ".ip-selector-tema-acceso" (segundo hijo, agregado con el tema
   oscuro) quede DEBAJO de la tarjeta y no al lado: con la dirección por defecto ("row") ambos quedarían
   centrados uno junto al otro. Con un solo hijo (antes de este cambio) el resultado visual era igual. */
.ip-body-acceso {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background-color: var(--ip-fondo);
}

.ip-tarjeta-acceso {
  width: 100%;
  max-width: 420px;
}

/* Botón de escanear en Inicio */

.ip-boton-escanear {
  width: 100%;
}

@media (min-width: 576px) {
  .ip-boton-escanear {
    width: auto;
    min-width: 280px;
  }
}

/* Logo de la institución en Configuración: fondo blanco fijo (no --ip-fondo), igual que
   ".ip-marca-logo", porque el logo puede traer transparencia. */
.ip-logo-vista-previa {
  max-width: 160px;
  max-height: 160px;
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  border: 1px solid var(--bs-border-color);
  border-radius: 0.375rem;
  background-color: #fff;
}

.ip-logo-vacio {
  background-color: #f8f9fa;
}

/* Código QR en la ficha del bien: fondo blanco fijo (sin relleno: encogería el código), igual que
   ".ip-marca-logo" y ".ip-etiqueta-qr" más abajo. El PNG que genera "patrimonio/servicios/qr.py" ya trae
   su propio fondo blanco con margen (zona de silencio, border=4) y cubre todo el recuadro, así que este
   respaldo solo importa mientras la imagen carga o si llegara a fallar. */
.ip-qr-ficha {
  width: 180px;
  height: 180px;
  max-width: 100%;
  background-color: #fff;
  image-rendering: pixelated;
}

/* Botones de acción en tablas: tamaño cómodo para pantalla táctil */

.ip-acciones-tabla .btn {
  min-width: 38px;
  min-height: 38px;
}

/* Formulario plegable para anular una baja o un alta (ficha del bien y sus listados): el <summary> se ve
   como un botón, sin la flecha de despliegue nativa del navegador. */

.ip-anular-plegable > summary {
  list-style: none;
  cursor: pointer;
}

.ip-anular-plegable > summary::-webkit-details-marker {
  display: none;
}

.ip-anular-plegable[open] > summary {
  margin-bottom: 0.5rem;
}

.ip-anular-plegable form {
  min-width: 220px;
  max-width: 100%;
}

/* Traslado y verificación desde la ficha del bien: trabajo de campo, tamaño cómodo para el dedo */

.ip-boton-campo {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}

/* Miniaturas de fotografías de bienes (ficha y formulario) */

/* El fondo es un literal a propósito, no un token de Bootstrap: en tema claro "--bs-tertiary-bg" vale
   #f8f9fa, no #fff, así que cambiaría (aunque sea sutilmente) el fondo detrás de fotos no cuadradas en
   el propio tema claro. En tema oscuro sí se adapta (regla siguiente): #fff fijo quedaría como un
   recuadro blanco pegado sobre el fondo oscuro. */
.ip-foto-miniatura {
  width: 96px;
  height: 96px;
  object-fit: cover;
  border: 1px solid var(--bs-border-color);
  border-radius: 0.375rem;
  background-color: #fff;
}

[data-bs-theme="dark"] .ip-foto-miniatura {
  background-color: var(--bs-tertiary-bg);
}

/* Selección de filas para imprimir etiquetas (listado de bienes) */

.ip-columna-checkbox {
  width: 2.5rem;
}

/* Hoja de etiquetas para imprimir (Bienes/Etiquetas) */

.ip-etiquetas-cuadricula {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.5rem;
}

@media (max-width: 575.98px) {
  .ip-etiquetas-cuadricula {
    grid-template-columns: 1fr;
  }
}

.ip-etiqueta {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem;
  border: 1px solid var(--bs-border-color);
  border-radius: 0.25rem;
  break-inside: avoid;
}

/* Fondo blanco fijo, igual que ".ip-qr-ficha": esta es la vista previa en pantalla; al imprimir (@media
   print, más abajo) el color siempre es claro de todos modos. */
.ip-etiqueta-qr {
  width: 72px;
  height: 72px;
  flex-shrink: 0;
  background-color: #fff;
  image-rendering: pixelated;
}

.ip-etiqueta-texto {
  min-width: 0;
}

.ip-etiqueta-codigo {
  font-size: 1rem;
  font-weight: 700;
}

.ip-etiqueta-denominacion {
  font-size: 0.8rem;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

/* El color es un literal a propósito, no un token de Bootstrap: en tema claro "--bs-secondary-color"
   vale rgba(33, 37, 41, 0.75) (~#585b5e), no #6c757d, así que cambiaría el color del propio tema claro.
   El bloque @media print de más abajo repite este mismo literal por la misma razón. En tema oscuro sí
   se adapta (regla siguiente), porque #6c757d fijo quedaría con poco contraste sobre el fondo oscuro. */
.ip-etiqueta-ubicacion,
.ip-etiqueta-institucion {
  font-size: 0.7rem;
  color: #6c757d;
}

[data-bs-theme="dark"] .ip-etiqueta-ubicacion,
[data-bs-theme="dark"] .ip-etiqueta-institucion {
  color: var(--bs-secondary-color);
}

/* Escáner de código QR con la cámara */

.ip-escaner-video-contenedor {
  position: relative;
  width: 100%;
  max-width: 480px;
  /* Antes de iniciar la cámara el video no tiene tamaño: la proporción fija evita que la guía se deforme. */
  aspect-ratio: 4 / 3;
  margin: 0 auto;
  background-color: #000;
  border-radius: 0.375rem;
  overflow: hidden;
}

.ip-escaner-video {
  display: block;
  width: 100%;
  height: auto;
}

.ip-escaner-guia {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 60%;
  aspect-ratio: 1 / 1;
  transform: translate(-50%, -50%);
  border: 3px solid rgba(255, 255, 255, 0.85);
  border-radius: 0.5rem;
  box-shadow: 0 0 0 999px rgba(0, 0, 0, 0.25);
  pointer-events: none;
}

/* Impresión: solo se imprimen las etiquetas (Bienes/Etiquetas); el resto de las páginas tampoco
   imprime el menú lateral ni la barra superior. Los elementos con "ip-no-imprimir" son avisos o
   botones que solo tienen sentido en pantalla. */

@media print {
  /* Siempre en claro, aunque la pantalla esté en tema oscuro: "color-scheme" evita que el navegador
     aplique su propio ajuste oscuro a lo que no tenga un color explícito, y "body" fuerza fondo blanco y
     texto negro por si el usuario tiene activado "imprimir gráficos de fondo" (si no lo tiene, el
     navegador ya omite el fondo oscuro por su cuenta, pero el texto sin este ajuste seguiría siendo el
     gris claro de --bs-body-color en oscuro, casi ilegible sobre el papel). */
  html {
    color-scheme: light;
  }

  body {
    background-color: #fff;
    color: #000;
  }

  .ip-menu,
  .ip-barra-superior,
  .ip-no-imprimir {
    display: none !important;
  }

  .ip-shell {
    display: block;
  }

  .ip-contenido {
    padding: 0;
  }

  .ip-etiqueta {
    border-color: #adb5bd;
  }

  /* El QR (".ip-etiqueta-qr") ya es una imagen opaca con su propio fondo blanco: no depende de esto. */
  .ip-etiqueta-ubicacion,
  .ip-etiqueta-institucion {
    color: #6c757d;
  }

  @page {
    margin: 8mm;
  }
}

/* Campos compactos de las acciones del panel del Maestro (motivo y rol en línea con el botón) */

.ip-campo-motivo {
  width: 12rem;
}

.ip-campo-rol {
  width: 10rem;
}

/* Barra de avance de la toma de inventario (Inventario/Index): el atributo style está prohibido, así que el
   ancho de la barra se fija con una clase por decena en vez de un porcentaje exacto en línea. */

.ip-ancho-0 { width: 0%; }
.ip-ancho-10 { width: 10%; }
.ip-ancho-20 { width: 20%; }
.ip-ancho-30 { width: 30%; }
.ip-ancho-40 { width: 40%; }
.ip-ancho-50 { width: 50%; }
.ip-ancho-60 { width: 60%; }
.ip-ancho-70 { width: 70%; }
.ip-ancho-80 { width: 80%; }
.ip-ancho-90 { width: 90%; }
.ip-ancho-100 { width: 100%; }

/* Selector de nivel (RadioSelect, sin JavaScript en línea) en el editor de roles (Maestro/Roles/Crear y
   Editar): la celda "Nivel" de la tabla "table-sm" es angosta en un celular y el <label> por defecto de
   Django partía "Administrar" en dos líneas, dejando el círculo del radio solo en la primera y la palabra
   sola en la segunda, como si fuera una quinta opción vacía. Se apilan las opciones y se evita el salto de
   línea dentro de cada una; la tabla ya es "table-responsive" (crear.html/editar.html) y se desplaza
   horizontalmente si hiciera falta más ancho. */
.ip-niveles-rol div + div {
  margin-top: 0.35rem;
}

.ip-niveles-rol label {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  white-space: nowrap;
}

/* Organigrama (Configuracion/Unidades y detalle de entidad): sangría por nivel del árbol (0 a 3: Alta Gerencia,
   Gerencia, Oficina, Sub oficina) */

.ip-arbol-nivel-0 {
  display: inline-block;
}

.ip-arbol-nivel-1 {
  display: inline-block;
  padding-left: 1.25rem;
}

.ip-arbol-nivel-2 {
  display: inline-block;
  padding-left: 2.5rem;
}

.ip-arbol-nivel-3 {
  display: inline-block;
  padding-left: 3.75rem;
}

/* Documentos (tanda 2): anchos de las columnas de la tabla de ítems y texto con saltos de línea en el
   detalle (los párrafos del esquema se guardan con "\n"; el atributo style está prohibido por la CSP). */

.ip-col-item-cantidad {
  width: 7rem;
}

.ip-col-item-unidad {
  width: 11rem;
}

.ip-col-item-total {
  width: 8rem;
}

.ip-texto-multilinea {
  white-space: pre-line;
}

.ip-indicador {
  font-variant-numeric: tabular-nums;
}

/* Gráficos de los reportes (tanda 4, "tramite/servicios/graficos.py"): SVG generado en el servidor, sin estilos
   en línea (CSP). Las marcas llevan el color de su serie y los textos los colores de texto de Bootstrap (nunca el
   de la serie). Orden fijo de ocho colores validado para daltonismo (separación entre vecinos) en claro sobre
   #fff y en oscuro sobre #212529; en claro, tres quedan por debajo de 3:1 frente al fondo y por eso cada barra
   lleva su valor escrito y la pantalla muestra la tabla de datos debajo. En oscuro son los mismos tonos con otro
   paso, no una inversión automática. «Otros tipos» (más de ocho) va en gris. */

.ip-grafico {
  display: block;
  width: 100%;
  max-width: 27rem; /* viewBox de 360 de ancho: escala 1,2 en escritorio, ~0,8 a 375 px */
  height: auto;
  font-family: inherit;
}

.ip-grafico-etiqueta {
  fill: var(--bs-body-color);
  font-size: 14px;
}

.ip-grafico-valor {
  fill: var(--bs-body-color);
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.ip-grafico-leyenda,
.ip-grafico-vacio {
  fill: var(--bs-secondary-color);
  font-size: 13px;
}

.ip-grafico-serie-1 { fill: #2a78d6; }
.ip-grafico-serie-2 { fill: #eb6834; }
.ip-grafico-serie-3 { fill: #1baf7a; }
.ip-grafico-serie-4 { fill: #eda100; }
.ip-grafico-serie-5 { fill: #e87ba4; }
.ip-grafico-serie-6 { fill: #008300; }
.ip-grafico-serie-7 { fill: #4a3aa7; }
.ip-grafico-serie-8 { fill: #e34948; }
.ip-grafico-serie-otros { fill: #898781; }

[data-bs-theme="dark"] .ip-grafico-serie-1 { fill: #3987e5; }
[data-bs-theme="dark"] .ip-grafico-serie-2 { fill: #d95926; }
[data-bs-theme="dark"] .ip-grafico-serie-3 { fill: #199e70; }
[data-bs-theme="dark"] .ip-grafico-serie-4 { fill: #c98500; }
[data-bs-theme="dark"] .ip-grafico-serie-5 { fill: #d55181; }
[data-bs-theme="dark"] .ip-grafico-serie-6 { fill: #008300; }
[data-bs-theme="dark"] .ip-grafico-serie-7 { fill: #9085e9; }
[data-bs-theme="dark"] .ip-grafico-serie-8 { fill: #e66767; }

.ip-grafico path:hover {
  opacity: 0.8;
}

/* Muestra de color de cada tipo en la cabecera de la tabla de datos del gráfico apilado (Reportes): el mismo
   color de la serie del gráfico, para relacionar cada columna con su tramo. La columna tiene un ancho mínimo
   para que el nombre del tipo ocupe dos o tres líneas y la muestra quede junto a la primera palabra (a 375 px
   la tabla se desplaza dentro de "table-responsive"). */
.ip-grafico-muestra {
  width: 0.75rem;
  height: 0.75rem;
  margin-right: 0.25rem;
  vertical-align: -0.05rem;
}

.ip-col-tipo-grafico {
  min-width: 9rem;
}

/* La tabla de datos del gráfico de una sola serie no pasa del ancho del propio gráfico. */
.ip-tabla-grafico {
  max-width: 36rem;
}

/* Microinteracciones (guía «Claude animador web», aplicada con criterio institucional a un sistema municipal):
   retroalimentación sutil, sin JavaScript nuevo y sin nada en línea (CSP). Los estados en reposo, los colores
   base y la maquetación no cambian: solo se agregan transiciones cortas, una elevación leve de las tarjetas,
   la aparición suave de avisos y realces fijos. Es un sistema, no parches: duraciones y curvas son las
   variables --ip-duracion* y --ip-curva de :root (nunca números sueltos) y TODO movimiento vive dentro de
   @media (prefers-reduced-motion: no-preference): quien pide menos movimiento en su sistema operativo no
   recibe ninguna transición ni animación (los cambios de color y de sombra siguen, pero al instante). Nada
   parpadea sin parar. El foco visible está arriba, junto a :root. Guardiana:
   nucleo/tests/test_microinteracciones.py. */

/* Buzón: la fila de un documento devuelto (etapa DEVUELTO, volvió con observación; "Documento.esta_devuelto")
   se destaca de forma fija, sin parpadeo: fondo con el tono sutil de aviso de Bootstrap, que se adapta a los
   dos temas (--bs-warning-bg-subtle), y una barra a la izquierda del color de la etapa. La barra va como
   box-shadow, no como borde, para no mover el texto de la columna; hay que repetir la sombra interna con la
   que Bootstrap 5.3 pinta el fondo de las celdas (--bs-table-bg-state...), porque una declaración box-shadow
   reemplaza a la otra y se perdería el resaltado al pasar el ratón (.table-hover).
   Contraste (revisión app profesional, ronda 1, hallazgo 10): sobre ese fondo, el azul de los enlaces y de
   .text-primary (#0d6efd) y el gris de .text-secondary (#6c757d) quedaban por debajo de 4,5:1 en tema claro
   (4,06 y 4,23; con el ratón encima, 3,45 y 3,59). Dentro de la fila se usan los tonos "emphasis" de Bootstrap,
   que también cambian con el tema: enlaces con el color de enlace activo (--bs-link-hover-color, 5,81:1 en claro
   y 7,31:1 en oscuro), .text-primary y .text-secondary con --bs-primary/secondary-text-emphasis (12:1 en claro,
   6:1 en oscuro). Guardiana: nucleo/tests/test_microinteracciones.py:BuzonRealceContrasteTests. */
.ip-fila-devuelta > td {
  --bs-table-bg: var(--bs-warning-bg-subtle);
  --bs-link-color-rgb: var(--bs-link-hover-color-rgb);
}

.ip-fila-devuelta .text-primary {
  color: var(--bs-primary-text-emphasis) !important;
}

.ip-fila-devuelta .text-secondary {
  color: var(--bs-secondary-text-emphasis) !important;
}

.ip-fila-devuelta > td:first-child {
  box-shadow: inset 0.25rem 0 0 var(--bs-warning),
    inset 0 0 0 9999px var(--bs-table-bg-state, var(--bs-table-bg-type, var(--bs-table-accent-bg)));
}

@media (prefers-reduced-motion: no-preference) {
  /* Botones, enlaces (los del menú lateral incluidos), paginación, campos y las marcas de los gráficos: color,
     fondo, borde, sombra, opacidad y desplazamiento en 0,15 s. Bootstrap ya transiciona sus botones y campos
     con ese mismo tiempo; aquí se unifican todos con las variables del sistema. */
  a,
  summary,
  .btn,
  .btn-close,
  .page-link,
  .nav-link,
  .form-control,
  .form-select,
  .form-check-input,
  .ip-enlace-menu,
  .ip-grafico path {
    transition: color var(--ip-duracion) var(--ip-curva), background-color var(--ip-duracion) var(--ip-curva),
      border-color var(--ip-duracion) var(--ip-curva), box-shadow var(--ip-duracion) var(--ip-curva),
      opacity var(--ip-duracion) var(--ip-curva), transform var(--ip-duracion) var(--ip-curva);
  }

  /* Pulsar un botón se nota: baja 1 px mientras está presionado (además del color activo de Bootstrap). */
  .btn:active {
    transform: translateY(1px);
  }

  /* Tarjetas del inicio, de Reportes y de Configuración: elevación leve (la sombra cambia siempre, arriba; el
     desplazamiento de 2 px solo aquí). */
  .ip-tarjeta {
    transition: transform var(--ip-duracion) var(--ip-curva), box-shadow var(--ip-duracion) var(--ip-curva);
  }

  .ip-tarjeta:hover,
  a:focus-visible > .ip-tarjeta {
    transform: translateY(-2px);
  }

  /* Alertas, mensajes de Django y errores de formulario: aparecen con un fundido corto al cargar la página. */
  .alert,
  .invalid-feedback {
    animation: ip-aparecer var(--ip-duracion-larga) var(--ip-curva);
  }

  /* Contador del Buzón en el menú: un solo realce al cargar (crece y vuelve), nunca continuo. */
  .ip-enlace-menu .badge {
    animation: ip-realce var(--ip-duracion-larga) var(--ip-curva);
  }

  @keyframes ip-aparecer {
    from {
      opacity: 0;
      transform: translateY(-0.25rem);
    }

    to {
      opacity: 1;
      transform: none;
    }
  }

  @keyframes ip-realce {
    50% {
      transform: scale(1.15);
    }
  }
}
