/* Ajustes propios sobre la plantilla InApp. Se cargan después de inapp.css.
   Nunca se edita inapp.css: así una actualización de la plantilla no borra
   nuestro trabajo. */

/* -------------------------------------------------------------------------- */
/*  Poppins, alojada por nosotros                                              */
/* -------------------------------------------------------------------------- */
/*  ⚠️  ÚNICA EDICIÓN QUE HEMOS HECHO SOBRE inapp.css: se le borró la primera
    línea, que era

        @import "https://fonts.googleapis.com/css2?family=Poppins:ital,wght@…"

    Si una actualización de la plantilla la repone, hay que volver a quitarla o
    la fuente se pedirá dos veces.

    Por qué:

    · Un `@import` dentro del CSS es el peor sitio para una fuente. El navegador
      no sabe que existe hasta haber descargado y parseado los 612 KB de
      inapp.css; recién entonces pide la hoja a Google y recién entonces el
      archivo a gstatic. Cuatro saltos en serie antes de pintar una letra.
    · Pedía 18 variantes en tres alfabetos —incluido devanagari— para usar
      cuatro pesos en latín. Ni una sola itálica aparece en la aplicación.
    · Cada carga enviaba la IP del usuario a Google. Manejamos datos de personas
      reales; no hace falta entregárselos a un tercero para pintar texto.
    · Si gstatic responde lento o está bloqueado, el texto salta de fuente. En
      una vereda con mala conexión eso se ve.

    Ahora: cero peticiones a terceros, dos saltos en vez de cuatro, y la CSP
    puede cerrarse sin abrirle hueco a dos dominios ajenos.

    Solo los cuatro pesos que se usan (400 cuerpo · 500 etiquetas · 600 títulos
    · 700 énfasis). El 300 lo usan `.lead` y `.display-*` de la plantilla, que
    no aparecen en ninguna vista.

    `unicode-range` hace que latin-ext solo se descargue si algún carácter cae
    en ese rango. El español entero vive en el subconjunto latin, así que en la
    práctica bajan 32 KB, no 64 KB — pero un nombre con un carácter raro sigue
    saliendo en Poppins y no en la fuente de reserva.

    Poppins es SIL Open Font License 1.1: alojarla es justo lo que la licencia
    permite. El texto completo está en app/assets/fonts/OFL.txt.

    `font-display: swap` pinta el texto de inmediato con la fuente del sistema y
    lo cambia al llegar Poppins. Preferimos ese cambio a un texto invisible.   */

@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/poppins-latin-400-f3f0f5ee.woff2") format("woff2");
  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: "Poppins";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/poppins-latin-ext-400-ec743ea9.woff2") format("woff2");
  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;
}

@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/poppins-latin-500-ce788af2.woff2") format("woff2");
  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: "Poppins";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/poppins-latin-ext-500-299751ea.woff2") format("woff2");
  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;
}

@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/poppins-latin-600-1f3eca1a.woff2") format("woff2");
  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: "Poppins";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/poppins-latin-ext-600-230a7a24.woff2") format("woff2");
  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;
}

@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/poppins-latin-700-54e23180.woff2") format("woff2");
  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: "Poppins";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/poppins-latin-ext-700-9c681fff.woff2") format("woff2");
  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;
}

/* -------------------------------------------------------------------------- */
/*  Barra lateral colapsada                                                    */
/* -------------------------------------------------------------------------- */
/*  La plantilla ya oculta el rótulo del logo al colapsar:

        .sidebar.collapsed .logo-text { display: none }

    pero ese rótulo lleva la utilidad `.d-flex` de Bootstrap, que es
    `display: flex !important`. La utilidad gana, el texto se queda a su ancho
    completo y se desborda sobre el botón de colapso. Le devolvemos la
    prioridad a la regla de la plantilla.

    `overflow: hidden` en el área del logo es el cinturón además de los
    tirantes: mientras la barra mida 60 px, nada puede asomarse fuera.        */
.sidebar.collapsed .logo-text {
  display: none !important;
}

.sidebar.collapsed .logo-area {
  overflow: hidden;
}

/* -------------------------------------------------------------------------- */
/*  Tabla apilable en móvil                                                    */
/* -------------------------------------------------------------------------- */
/*  Bootstrap resuelve las tablas anchas con `.table-responsive`, que es un
    desplazamiento horizontal. En un teléfono de 390 px eso obliga a arrastrar
    una tabla de seis columnas para leer una fila.

    `.table-stack` hace lo que corresponde: bajo `md` cada fila se convierte en
    una ficha y cada celda muestra su propio encabezado, tomado de `data-label`.
    Un solo marcado, dos presentaciones — no hay filas duplicadas que mantener
    sincronizadas.                                                             */

@media (max-width: 767.98px) {
  .table-stack thead {
    display: none;
  }

  .table-stack tbody tr {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.35rem 1rem;
    padding: 0.75rem;
    border: 1px solid var(--bs-border-color, #e5e5e5);
    border-radius: 0.375rem;
    margin-bottom: 0.75rem;
  }

  /* `th` además de `td`: la celda que identifica la fila es un `th scope="row"`
     desde que se corrigió la semántica de la tabla, y estas reglas se quedaron
     apuntando solo a `td`. `td:first-child` dejó de casar con nada — la celda
     del nombre no ocupaba el ancho completo y arrastraba su fondo. */
  .table-stack tbody td,
  .table-stack tbody th {
    display: block;
    min-width: 0;
    overflow-wrap: anywhere;
    padding: 0;
    border: none;
    background: none;
  }

  /* La primera celda identifica la fila: ocupa el ancho completo. Se apunta al
     primer hijo sea cual sea su etiqueta, para que no vuelva a pasar. */
  .table-stack tbody tr > *:first-child {
    grid-column: 1 / -1;
  }

  .table-stack tbody td::before {
    content: attr(data-label);
    display: block;
    font-size: 0.6875rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    opacity: 0.68;
    margin-bottom: 0.05rem;
  }

  /* La celda de identidad no repite su encabezado. */
  .table-stack tbody tr > *:first-child::before {
    display: none;
  }

  /* Tampoco las que no tienen encabezado que mostrar —la de acciones—, o
     dejarían una línea en blanco sobre el botón. */
  .table-stack tbody td:not([data-label])::before {
    display: none;
  }

  /* Las alineaciones a la derecha no tienen sentido apiladas. */
  .table-stack tbody td.text-end,
  .table-stack tbody td.text-center {
    text-align: left !important;
  }

  /* `.table-responsive` alrededor de una tabla apilada reintroduciría el
     desplazamiento; se anula. */
  .table-responsive:has(.table-stack) {
    overflow-x: visible;
  }
}

/* -------------------------------------------------------------------------- */
/*  Foco de los grupos de campos                                               */
/* -------------------------------------------------------------------------- */
/*  Bootstrap pinta el anillo de foco sobre el `.form-control`, pero dentro de
    un `.input-group` el campo es solo una parte del control: el icono vive en
    un `.input-group-text` aparte. El resultado es un recuadro naranja flotando
    alrededor del texto mientras la lupa se queda fuera, como si fueran dos
    cosas distintas.

    El anillo pertenece al grupo entero. Se dibuja con `:focus-within` y se
    apaga el del campo — apagarlo sin sustituto sería quitarle a un usuario de
    teclado la única señal de dónde está.                                      */

.input-group {
  border-radius: 0.5rem;
  transition: box-shadow 0.15s ease;
}

.input-group:focus-within {
  box-shadow: 0 0 0 3px rgba(230, 98, 57, 0.16);
}

.input-group:focus-within .form-control,
.input-group:focus-within .input-group-text {
  border-color: #e66239;
}

/* El campo ya no dibuja su propio anillo: lo hace el grupo. */
.input-group .form-control:focus,
.input-group .form-select:focus {
  box-shadow: none;
}

/* -------------------------------------------------------------------------- */
/*  Búsqueda mientras se escribe                                               */
/* -------------------------------------------------------------------------- */
/*  Turbo marca el marco con `aria-busy` mientras trae los resultados. Bajarle
    la opacidad es todo el indicador de carga que hace falta: dice que algo
    está pasando sin mover nada de sitio ni provocar un salto.

    `transition-delay` evita el parpadeo cuando la respuesta llega en 40 ms —
    solo se atenúa si de verdad se está tardando.                              */
turbo-frame[aria-busy="true"] {
  opacity: 0.45;
  transition: opacity 0.15s ease 0.2s;
}

turbo-frame {
  display: block;
  transition: opacity 0.15s ease;
}

/* -------------------------------------------------------------------------- */
/*  Objetivos táctiles                                                         */
/* -------------------------------------------------------------------------- */
/*  El fontanero trabaja de pie, en la calle y con una mano. 44 px es el
    mínimo que un pulgar acierta de forma fiable.                              */
@media (max-width: 767.98px) {
  .btn,
  .form-control,
  .form-select {
    min-height: 2.75rem;
  }

  /*  16 px es el umbral por debajo del cual iOS amplía la página al enfocar un
      campo. La plantilla los deja en 14 px, así que el primer toque en el
      formulario de ingreso desplazaba y agrandaba toda la pantalla.
      No es preferencia estética: es lo que evita ese salto.                  */
  .form-control,
  .form-select,
  textarea,
  body {
    font-size: 1rem;
  }

  /*  Enlaces del menú, insignias y botones de cerrar quedaban entre 19 y 38 px
      de alto. El área táctil se amplía sin mover nada de sitio: se estira el
      relleno, no la caja visible.                                            */
  .sidebar .nav-link,
  .dropdown-item {
    min-height: 2.75rem;
    display: flex;
    align-items: center;
  }

  /*  Los números de página son objetivos pequeños y contiguos: 34 px de ancho
      y pegados unos a otros. Se separan y se ensanchan, que es lo que evita
      pulsar la página 3 queriendo la 2.                                      */
  .page-link {
    min-width: 2.75rem;
    min-height: 2.75rem;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .pagination {
    gap: 0.25rem;
  }

  .btn-close {
    width: 2.75rem;
    height: 2.75rem;
    background-position: center;
  }

  /*  Los botones de icono son cuadrados de 32-36 px: el ancho también cuenta,
      no solo el alto. Y el logotipo y el avatar son objetivos reales —llevan
      al panel y abren el menú de la cuenta— aunque no lo parezcan.           */
  .btn-icon,
  .topbar .dropdown > button,
  .topbar .nav-link,
  .topbar form button,
  .logo-area > a {
    min-width: 2.75rem;
    min-height: 2.75rem;
  }

  /*  Las insignias del aviso de duplicados son enlaces que filtran la lista.
      Se les da altura sin cambiar el tamaño del texto: crece el área que el
      pulgar acierta, no la tipografía.                                       */
  a.badge {
    display: inline-flex;
    align-items: center;
    min-height: 2.75rem;
    padding-left: 0.75rem;
    padding-right: 0.75rem;
  }
}

/* -------------------------------------------------------------------------- */
/*  Tooltips y diálogo de confirmación                                         */
/* -------------------------------------------------------------------------- */
/*  El tooltip de Bootstrap es negro puro. Junto a una paleta de azul pizarra y
    naranja se ve como una pieza pegada de otro sitio. Va en el azul oscuro de
    la marca —el mismo del logotipo— que además da más contraste que el negro
    sobre el fondo claro.                                                      */

.tooltip .tooltip-inner {
  background-color: #302c4d;
  color: #fff;
  font-size: 0.75rem;
  line-height: 1.35;
  padding: 0.4rem 0.65rem;
  border-radius: 0.5rem;
  max-width: 15rem;
  box-shadow: 0 6px 16px rgba(48, 44, 77, 0.22);
}

.tooltip .tooltip-arrow::before {
  border-top-color: #302c4d;
  border-bottom-color: #302c4d;
  border-left-color: #302c4d;
  border-right-color: #302c4d;
}

.tooltip.show {
  opacity: 1;
}

/*  El icono del diálogo toma el tono de la acción: informativo por defecto,
    peligroso cuando lo que se confirma destruye algo.                         */
.confirm-dialog-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  flex-shrink: 0;
  border-radius: 50%;
  background: rgba(230, 98, 57, 0.12);
  color: #e66239;
}

.confirm-dialog--danger .confirm-dialog-icon {
  background: rgba(220, 53, 69, 0.12);
  color: #dc3545;
}

/* -------------------------------------------------------------------------- */
/*  Modo oscuro                                                                */
/* -------------------------------------------------------------------------- */
/*  La plantilla redefine 52 variables bajo `[data-bs-theme=dark]`, así que todo
    lo que se apoya en tokens semánticos —`bg-body`, `text-body-emphasis`,
    `--bs-border-color`— sigue al tema solo. Aquí van únicamente las piezas
    propias que fijamos a mano.                                                */

/*  El tooltip conserva el azul de la marca en los dos temas: es un color de
    identidad, no una superficie. Sobre fondo oscuro necesita un borde para
    despegarse, o se funde con lo que hay detrás.                              */
[data-bs-theme="dark"] .tooltip .tooltip-inner {
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.45);
}

/*  El anillo de foco naranja pierde fuerza sobre fondo oscuro: se sube la
    opacidad para que siga siendo la señal evidente de dónde está el cursor.  */
[data-bs-theme="dark"] .input-group:focus-within {
  box-shadow: 0 0 0 3px rgba(230, 98, 57, 0.32);
}

/*  Las fichas en que se apila la tabla en el teléfono toman el borde del tema;
    el color de reserva era el claro y en oscuro dejaba una caja invisible.   */
@media (max-width: 767.98px) {
  [data-bs-theme="dark"] .table-stack tbody tr {
    border-color: var(--bs-border-color);
  }
}

/*  Elevación en oscuro.

    En claro la jerarquía sale sola: página gris, tarjeta blanca — la tarjeta
    se levanta. En oscuro la plantilla asigna `--bs-body-bg: #171717` a la
    tarjeta y `--bs-tertiary-bg: #1f1f1f` a la página, así que la tarjeta queda
    MÁS oscura que el fondo y se hunde en lugar de elevarse.

    En una interfaz oscura la elevación se expresa con luz: cuanto más cerca
    del usuario, más clara la superficie. Se invierten los dos papeles.        */

[data-bs-theme="dark"] body {
  background-color: var(--bs-body-bg) !important;
}

/*  El panel de ayuda entra en la misma cuenta: flota por encima del contenido,
    así que le toca la superficie de arriba. Bootstrap lo deja en `body-bg`, que
    aquí es exactamente el color de lo que hay detrás — el panel se abriría sin
    que se notara dónde empieza.                                              */
[data-bs-theme="dark"] .card,
[data-bs-theme="dark"] .modal-content,
[data-bs-theme="dark"] .offcanvas {
  background-color: var(--bs-secondary-bg);
}

/*  La barra superior y la lateral son el marco, no contenido: se quedan al
    nivel del fondo y se separan por el borde, no por el color.               */
[data-bs-theme="dark"] .topbar,
[data-bs-theme="dark"] .sidebar {
  background-color: var(--bs-body-bg) !important;
}

/*  La cabecera de una tarjeta no es otra superficie: es la misma tarjeta.
    Mandarla a `--bs-body-bg` la hundía por debajo de lo que encabeza, que es
    justo al revés de la regla de arriba —cuanto más cerca del usuario, más
    clara la superficie—. Y en claro esa diferencia no existe: blanco sobre
    blanco. El oscuro estaba inventando una jerarquía que el claro no tiene.

    El `!important` es para ganarle a `.bg-body`, que el componente pone en su
    plantilla; así da igual que una vista use `.bg-body` y otra
    `.bg-transparent` —conviven las dos en el panel— porque las dos acaban
    igual. Se separan por el borde.                                            */
[data-bs-theme="dark"] .card > .card-header,
[data-bs-theme="dark"] .card > .card-footer {
  background-color: transparent !important;
  border-color: var(--bs-border-color);
}

/*  Los enlaces del menú lateral traen un gris oscuro fijo (#262626) de la
    plantilla. Sobre la barra clara se leen; sobre la oscura quedan en 1,18:1
    —prácticamente invisibles—. El color de un texto es un papel, no un valor:
    aquí es "texto sobre la superficie", y eso lo sabe el tema.               */
[data-bs-theme="dark"] .sidebar .nav-link {
  color: var(--bs-body-color);
}

[data-bs-theme="dark"] .sidebar .nav-link:hover,
[data-bs-theme="dark"] .sidebar .nav-link.active {
  color: #f08a63;
  background-color: rgba(230, 98, 57, 0.16);
}

/* -------------------------------------------------------------------------- */
/*  Movimiento reducido                                                        */
/* -------------------------------------------------------------------------- */
/*  Quien lo pide en el sistema operativo suele hacerlo por vértigo o migraña,
    no por gusto. Se apaga el movimiento y se conserva el resultado: los
    estados siguen cambiando, simplemente llegan de golpe.

    `0.01ms` en lugar de `0` porque un `transitionend` que nunca se dispara
    deja colgado a cualquier código que lo espere.                            */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .modal.fade .modal-dialog {
    transform: none !important;
  }
}

/* -------------------------------------------------------------------------- */
/*  Contraste del botón primario                                               */
/* -------------------------------------------------------------------------- */
/*  El naranja de la marca con texto blanco da 3,40:1, por debajo del 4,5:1 que
    exige AA para texto normal — y el botón primario es el control más pulsado
    de la aplicación. Oscurecido un 15% queda en 4,56:1 y a simple vista sigue
    siendo el mismo naranja. El de la marca se conserva para bordes, iconos y
    acentos, donde el umbral es 3:1.                                          */
.btn-primary {
  --bs-btn-bg: #c35330;
  --bs-btn-border-color: #c35330;
  --bs-btn-hover-bg: #a94628;
  --bs-btn-hover-border-color: #a94628;
  --bs-btn-active-bg: #a94628;
  --bs-btn-active-border-color: #a94628;
  --bs-btn-disabled-bg: #c35330;
  --bs-btn-disabled-border-color: #c35330;
}

/*  Enlace de salto: fuera de la pantalla hasta que recibe el foco. Ocultarlo
    con `display:none` lo sacaría también del orden de tabulación, que es
    justo lo contrario de lo que se busca.                                    */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 2000;
  padding: 0.75rem 1.25rem;
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  border: 2px solid #c35330;
  border-radius: 0 0 0.5rem 0;
  text-decoration: none;
  font-weight: 600;
}

.skip-link:focus {
  left: 0;
}

/* -------------------------------------------------------------------------- */
/*  Panel de ayuda                                                             */
/* -------------------------------------------------------------------------- */
/*  El ancho por defecto del offcanvas de Bootstrap es 400 px fijos, que en un
    teléfono de 390 px deja el contenido cortado. `min()` lo resuelve sin una
    media query: en escritorio 420 px, en móvil el ancho de la pantalla.      */
#help-panel {
  width: min(440px, 100%);
}

/*  Y el panel de edición igual, que se quedó fuera. Es donde se cobra: con
    los 400 px fijos de Bootstrap, en un teléfono de 375 px el formulario del
    mostrador salía cortado por la izquierda —la etiqueta y el prefijo «$» del
    importe— y no hay forma de desplazarlo hasta ahí.                        */
#panel-shell {
  width: min(440px, 100%);
}

/*  Prosa del manual. La plantilla no estiliza contenido largo porque no lo
    tenía: sus pantallas son formularios y tablas. Aquí sí hay párrafos, y sin
    esto salen pegados unos a otros.

    El límite de línea es deliberado: 65 caracteres es donde el ojo deja de
    perder el renglón al saltar. En un panel angosto casi nunca se alcanza,
    pero el día que alguien ensanche el panel seguirá leyéndose.              */
.help-prose {
  font-size: 0.9375rem;
  line-height: 1.65;
  max-width: 65ch;
  color: var(--bs-body-color);
}

.help-prose > *:first-child {
  margin-top: 0;
}

.help-prose > *:last-child {
  margin-bottom: 0;
}

.help-prose p,
.help-prose ul,
.help-prose ol {
  margin-bottom: 1rem;
}

.help-prose h2,
.help-prose h3 {
  font-size: 1rem;
  font-weight: 600;
  margin-top: 1.75rem;
  margin-bottom: 0.5rem;
}

.help-prose ul,
.help-prose ol {
  padding-left: 1.25rem;
}

.help-prose li {
  margin-bottom: 0.35rem;
}

.help-prose code {
  font-size: 0.875em;
  padding: 0.1rem 0.35rem;
  border-radius: 0.25rem;
  background: var(--bs-secondary-bg);
  color: var(--bs-emphasis-color);
}

/*  Una nota al margen: lo que hay que saber antes de pulsar, no después.     */
.help-prose blockquote {
  margin: 1.25rem 0;
  padding: 0.75rem 1rem;
  border-left: 3px solid #c35330;
  background: var(--bs-secondary-bg);
  border-radius: 0 0.375rem 0.375rem 0;
  font-size: 0.9em;
}

.help-prose blockquote > *:last-child {
  margin-bottom: 0;
}

.help-prose table {
  width: 100%;
  margin-bottom: 1rem;
  font-size: 0.9em;
  border-collapse: collapse;
}

.help-prose th,
.help-prose td {
  padding: 0.5rem 0.5rem 0.5rem 0;
  border-bottom: 1px solid var(--bs-border-color);
  text-align: left;
  vertical-align: top;
}

.help-prose kbd {
  font-size: 0.8em;
}

.help-section-title {
  letter-spacing: 0.06em;
}

/* -------------------------------------------------------------------------- */
/*  El logotipo                                                                */
/* -------------------------------------------------------------------------- */
/*  Los colores estaban en `style="…!important"` dentro del partial, y una
    declaración `!important` en el atributo `style` le gana a cualquier hoja.
    Mientras estuvieran ahí, el logo no podía responder al tema — y no
    respondía: sobre la tarjeta oscura del ingreso, el azul quedaba en 1,15:1.

    En oscuro el azul profundo se aclara lo justo para separarse del fondo
    (3,59:1) y la palabra sube al color de énfasis. El naranja de marca no se
    toca en ningún tema: es la identidad.                                     */
.logo-deep { fill: #302c4d; }
.logo-mid { fill: #657e92; }
.logo-pale { fill: #a3b2be; }
.logo-word { color: #302c4d; }

.logo-tagline {
  color: #a3b2be;
  font-size: 10px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-top: 5px;
}

[data-bs-theme="dark"] .logo-deep { fill: #6f68a6; }
[data-bs-theme="dark"] .logo-word { color: var(--bs-emphasis-color); }
[data-bs-theme="dark"] .logo-tagline { color: var(--bs-secondary-color); }

/* -------------------------------------------------------------------------- */
/*  Modo oscuro: lo que la plantilla dejó clavado en claro                     */
/* -------------------------------------------------------------------------- */
/*  Todo lo que sigue tiene una sola causa: la plantilla escribe colores
    literales donde Bootstrap usa tokens. Un literal no gira con el tema, y en
    oscuro se queda exactamente donde estaba.

    Las razones de contraste citadas están calculadas sobre el color compuesto
    real —resolviendo el alfa contra la superficie de abajo—, no estimadas.
    Referencia en oscuro: página #171717 · tarjeta #262626 · texto #d4d4d4.   */

/*  1 · `--bs-secondary-rgb` es de las poquísimas que la plantilla NO redefine
    en oscuro: se queda en 82,82,82. Como todas las insignias neutras son
    `text-secondary` sobre `bg-secondary bg-opacity-10`, quedaban en 1,83:1 —
    ni chip ni texto. Cambiar el token lo arregla en todas de una vez.        */
[data-bs-theme="dark"] {
  --bs-secondary-rgb: 163, 163, 163;

  /*  2 · Bootstrap 5.3 trae esta variable de fábrica y la plantilla la perdió
      al recortar su bloque oscuro. Sin ella, el aspa de `.btn-close` es un SVG
      negro sobre fondo negro: 1,23:1. El cerrar del panel de ayuda no se veía
      y solo quedaba la tecla Esc.                                            */
  --bs-btn-close-filter: invert(1) grayscale(100%) brightness(200%);
}

/*  3 · `.btn-outline-secondary` fija `--bs-btn-color: #525252` literal: 1,94:1
    sobre la tarjeta. Es el botón secundario de toda la aplicación —incluido el
    «Cancelar» del diálogo que confirma un borrado, que es el peor sitio
    posible para un texto que no se lee.

    El hover también se invierte: en claro oscurece, y en oscuro tiene que
    aclarar o la respuesta al puntero desaparece.                             */
[data-bs-theme="dark"] .btn-outline-secondary {
  --bs-btn-color: #d4d4d4;
  --bs-btn-border-color: #737373;
  --bs-btn-hover-color: #171717;
  --bs-btn-hover-bg: #d4d4d4;
  --bs-btn-hover-border-color: #d4d4d4;
  --bs-btn-active-color: #171717;
  --bs-btn-active-bg: #d4d4d4;
  --bs-btn-active-border-color: #d4d4d4;
  --bs-btn-disabled-color: #737373;
  --bs-btn-disabled-border-color: #525252;
}

/*  4 · Las tres piezas blancas de la barra superior: menú, menú móvil y ayuda.
    `.btn-light` es #f5f5f5 con texto negro, sin pasar por ningún token.      */
[data-bs-theme="dark"] .btn-light {
  --bs-btn-color: var(--bs-body-color);
  --bs-btn-bg: var(--bs-secondary-bg);
  --bs-btn-border-color: var(--bs-border-color);
  --bs-btn-hover-color: var(--bs-emphasis-color);
  --bs-btn-hover-bg: #333333;
  --bs-btn-hover-border-color: #737373;
  --bs-btn-active-color: var(--bs-emphasis-color);
  --bs-btn-active-bg: #3d3d3d;
  --bs-btn-active-border-color: #737373;
}

/*  5 · Las cuatro alertas.

    Bootstrap las define con tokens semánticos y giran solas; la plantilla las
    vuelve a declarar al final de su hoja con colores claros literales y
    `!important` en cada línea. En oscuro son bloques casi blancos a 17:1
    contra la página: cada mensaje de «guardado» es un fogonazo.

    `[data-bs-theme=dark] .alert-x` tiene especificidad (0,2,0) contra la
    (0,1,0) de la plantilla, así que gana aun estando las dos en `!important`.
    Se devuelven a los tokens que ya existen, conservando la barra de 4 px que
    la plantilla añadió — que aquí no es adorno: `--bs-danger-bg-subtle` queda
    a 1,01:1 de la página, y la barra y el borde son lo único que sostiene la
    alerta.                                                                    */
[data-bs-theme="dark"] .alert-danger {
  background-color: var(--bs-danger-bg-subtle) !important;
  border: 1px solid var(--bs-danger-border-subtle) !important;
  border-left: 4px solid #fb2c36 !important;
  color: var(--bs-danger-text-emphasis) !important;
}

[data-bs-theme="dark"] .alert-success {
  background-color: var(--bs-success-bg-subtle) !important;
  border: 1px solid var(--bs-success-border-subtle) !important;
  border-left: 4px solid #00c951 !important;
  color: var(--bs-success-text-emphasis) !important;
}

[data-bs-theme="dark"] .alert-info {
  background-color: var(--bs-info-bg-subtle) !important;
  border: 1px solid var(--bs-info-border-subtle) !important;
  border-left: 4px solid #00b8db !important;
  color: var(--bs-info-text-emphasis) !important;
}

[data-bs-theme="dark"] .alert-warning {
  background-color: var(--bs-warning-bg-subtle) !important;
  border: 1px solid var(--bs-warning-border-subtle) !important;
  border-left: 4px solid #f0b100 !important;
  color: var(--bs-warning-text-emphasis) !important;
}

/*  Efecto colateral del arreglo anterior: el botón «Salir» de la franja de
    modo consulta es `btn-outline-dark`, negro sobre #171717, y hasta ahora se
    leía únicamente porque la alerta seguía siendo blanca.                    */
[data-bs-theme="dark"] .btn-outline-dark {
  --bs-btn-color: var(--bs-body-color);
  --bs-btn-border-color: #737373;
  --bs-btn-hover-color: #171717;
  --bs-btn-hover-bg: var(--bs-body-color);
  --bs-btn-hover-border-color: var(--bs-body-color);
}

/*  6 · El código y las citas del manual quedaron del mismo color que el panel
    —1,00:1— cuando subimos el `.offcanvas` a `--bs-secondary-bg`. Viven dentro
    de una superficie ya elevada, así que suben otro escalón en lugar de
    repetir el de abajo.                                                       */
[data-bs-theme="dark"] .help-prose code,
[data-bs-theme="dark"] .help-prose blockquote {
  background: rgba(var(--bs-emphasis-color-rgb), 0.07);
}

/*  7 · La franja del logo es un hijo posicionado con su propio `background:
    #fff`, así que el arreglo de elevación de `.sidebar` no la alcanzaba: una
    banda blanca de 60 px a 17,93:1. El borde derecho de la barra era otra
    línea brillante de arriba abajo de la pantalla.                           */
[data-bs-theme="dark"] .sidebar .logo-area {
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  border-bottom-color: var(--bs-border-color);
}

[data-bs-theme="dark"] .sidebar {
  border-right-color: var(--bs-border-color);
}

/* -------------------------------------------------------------------------- */
/*  Desplegable con buscador                                                   */
/* -------------------------------------------------------------------------- */
/*  Ver searchable_select_controller.js. El `select` nativo sigue ahí y sigue
    siendo el que envía el formulario; solo deja de verse.

    No se oculta con `display:none` ni con `.visually-hidden`: un select
    desplazado fuera de la pantalla se sigue pudiendo enviar y sigue pudiendo
    mostrar el mensaje de validación nativa del navegador anclado donde toca.
    Con `display:none` el navegador no tiene dónde anclarlo y descarta el envío
    en silencio.                                                              */
.ss {
  position: relative;
}

.ss-native {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  border: 0;
}

/*  El disparador reutiliza `.form-select`, así que hereda el borde, el alto y
    la flecha del tema sin copiar un solo valor.                              */
.ss-trigger {
  text-align: left;
  cursor: pointer;
}

.ss-value-empty {
  color: var(--bs-secondary-color);
}

.ss-panel {
  position: absolute;
  top: calc(100% + 0.25rem);
  left: 0;
  right: 0;
  z-index: 1050;
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: 0.5rem;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  overflow: hidden;
}

.ss-search {
  padding: 0.5rem;
  border-bottom: 1px solid var(--bs-border-color);
}

.ss-list {
  list-style: none;
  margin: 0;
  padding: 0.25rem;
  max-height: 16rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/*  44 px de alto mínimo. El fontanero elige el sector desde el teléfono, de
    pie y en la calle; una fila de 28 px ahí es una lotería.                  */
.ss-option {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 0.5rem 0.75rem;
  border-radius: 0.375rem;
  cursor: pointer;
  font-size: 0.9375rem;
}

/*  Una sola clase para el ratón y para el teclado: si el resaltado del teclado
    se viera distinto del del ratón, quien navega con teclado estaría usando
    otra interfaz.                                                            */
.ss-option:hover,
.ss-option.ss-active {
  background: var(--bs-secondary-bg);
}

.ss-option[aria-selected="true"] {
  font-weight: 600;
  color: #c35330;
}

.ss-empty {
  margin: 0;
  padding: 1rem 0.75rem;
  text-align: center;
  font-size: 0.875rem;
  color: var(--bs-secondary-color);
}

/*  En el teléfono el panel no cuelga del campo: sube desde abajo y ocupa el
    ancho de la pantalla. Un desplegable anclado a un campo que está a media
    altura deja la lista debajo del teclado virtual.                          */
@media (max-width: 575.98px) {
  .ss-panel {
    position: fixed;
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    border-radius: 0.75rem 0.75rem 0 0;
    box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.2);
  }

  .ss-list {
    max-height: 50vh;
    padding-bottom: env(safe-area-inset-bottom);
  }
}

/*  El panel flota por encima de la tarjeta, así que le toca la superficie de
    arriba — la misma cuenta que las tarjetas y el panel de ayuda.            */
[data-bs-theme="dark"] .ss-panel {
  background: #303030;
  border-color: #737373;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
}

[data-bs-theme="dark"] .ss-option:hover,
[data-bs-theme="dark"] .ss-option.ss-active {
  background: rgba(var(--bs-emphasis-color-rgb), 0.1);
}

[data-bs-theme="dark"] .ss-option[aria-selected="true"] {
  color: #f08a63;
}

/*  Sin buscador el foco vive en la lista, y el anillo de foco del navegador
    alrededor de toda ella no dice nada: lo que indica dónde está el usuario es
    la opción resaltada, que es justo lo que `aria-activedescendant` anuncia.  */
.ss-list:focus {
  outline: none;
}

/* -------------------------------------------------------------------------- */
/*  Formularios dentro del panel                                               */
/* -------------------------------------------------------------------------- */
/*  La rejilla de Bootstrap responde al ancho de la VENTANA, no al del
    contenedor. Un `col-md-8` dentro de un panel de 440 px sigue partiendo en
    dos columnas como si tuviera la pantalla entera, y el resultado es lo que
    se ve: una etiqueta de dos palabras rota en dos renglones al lado de un
    campo estrecho.

    Dentro del panel se apila. No es solo una salida al problema del ancho: un
    formulario de una columna se lee en un solo recorrido de la vista, y eso
    vale también en pantallas anchas cuando los campos son pocos.             */
.offcanvas .row > [class*="col-"] {
  flex: 0 0 100%;
  max-width: 100%;
}

/*  El ancho de un campo dice qué se espera dentro.

    Un número de tres cifras en una caja de 440 px pide un párrafo; una fecha
    en una caja de 440 px, lo mismo. Estrecharlos no es estética: es la única
    pista que tiene alguien sobre la forma de la respuesta antes de escribirla.

    `max-width` y no `width`: en un teléfono estrecho el campo se encoge con
    el panel en vez de desbordarlo.                                          */
.field-narrow {
  max-width: 8rem;
}

.field-medium {
  max-width: 14rem;
}

/*  Dos campos cortos en una línea, dentro del panel.
 
    La regla de una columna de arriba es la correcta por defecto, pero deja
    mucho aire cuando lo que se apila son dos números de dos cifras: la vista
    recorre dos renglones completos para leer «1 a 6» y «0».

    Se hace con flex sobre el CONTENEDOR y no con la rejilla de Bootstrap, que
    es la lección de por qué esto estaba mal antes: `flex-basis` en rem hace
    que los dos campos se pongan uno debajo del otro cuando el panel no da para
    los dos, sin preguntarle nada al ancho de la ventana.

    Solo para campos que además se leen juntos. Emparejar dos cosas que no
    tienen relación ahorra un renglón y cobra una duda.                       */
.field-pair {
  display: flex;
  flex-wrap: wrap;
  gap: 0 1rem;
}

.field-pair > * {
  flex: 1 1 9rem;
  min-width: 0;
}

/*  Dentro del par manda la columna: el `max-width` de un campo estrecho lo
    dejaría a la mitad de su mitad.                                          */
.field-pair .form-control,
.field-pair .form-select {
  max-width: 100%;
}

/*  ── El nombre de una fila es una puerta ──────────────────────────────────

    Los nombres de las tablas llevan a la ficha del registro, pero iban en el
    color del texto y sin subrayado: se leían como texto y nadie los pulsaba.
    Pasó dos veces —los municipios de un departamento y las casas de un
    sector— y las dos veces la pregunta fue la misma, «¿cómo entro aquí?».

    Color siempre, subrayado al pasar por encima: en una tabla de veinticinco
    filas subrayarlo todo es ruido, pero sin color no hay nada que delate que
    se puede pulsar.

    No se usa `--bs-link-color`: la plantilla lo redefine a #404040 en el tema
    claro, que es casi el color del texto —de ahí venía el problema—.

    Tampoco es el naranja de marca tal cual. #E66239 sobre blanco da 3,40:1 y
    el mínimo para texto normal son 4,5:1; esto es texto de tabla, no un
    titular, así que no se acoge a la excepción del texto grande. #BD502F es
    ese mismo naranja al 82 %: 4,83:1 sobre blanco y 4,58:1 sobre el gris de
    las filas alternas, que es el peor caso de los dos.

    En oscuro sí vale el token de la plantilla —ahí lo deja en un salmón que
    da 8,42:1—, así que se toma de él en vez de inventar una tonalidad.

    Naranja y no el ámbar de anoche: el tono nunca fue el problema —lo pisaba
    `.card a`, ver más abajo—. La negrita la pone la vista con `fw-semibold`,
    no esta regla: `link-record` también se usa dentro de frases del manual.  */
.link-record {
  color: #bd502f;
  text-decoration: none;
}

[data-bs-theme="dark"] .link-record {
  color: var(--bs-link-color);
}

.link-record:hover,
.link-record:focus-visible {
  text-decoration: underline;
}

/* -------------------------------------------------------------------------- */
/*  Secciones plegables de la barra lateral                                    */
/* -------------------------------------------------------------------------- */
/*  El rótulo de sección era un `div` de solo texto. Ahora es el botón que
    pliega la sección, así que tiene que parecerse a lo que era —mismo tamaño,
    mismo gris, mismas mayúsculas— y comportarse como un botón: foco visible,
    cursor de pulsar y un objetivo táctil que se pueda acertar de pie en la
    calle, que es donde el fontanero usa esto.                                */
.sidebar .nav-section-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  width: 100%;
  min-height: 44px;
  padding: 0.75rem 1rem 0.25rem;
  border: 0;
  background: none;
  color: var(--bs-secondary-color);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-align: start;
  cursor: pointer;
}

.sidebar .nav-section-toggle:hover {
  color: var(--bs-body-color);
}

/*  La flecha gira en vez de cambiar de icono: el movimiento dice hacia dónde
    va el contenido, y un icono que se sustituye parpadea.                    */
.sidebar .nav-section-chevron {
  display: inline-flex;
  transition: transform 0.15s ease-out;
}

.sidebar .nav-section-toggle[aria-expanded="true"] .nav-section-chevron {
  transform: rotate(90deg);
}

@media (prefers-reduced-motion: reduce) {
  .sidebar .nav-section-chevron {
    transition: none;
  }
}

.sidebar .nav-group--closed {
  display: none;
}

/*  Con la barra reducida a iconos no hay rótulos que pulsar, así que tampoco
    puede haber secciones plegadas: quedarían iconos escondidos sin forma de
    sacarlos. Reducida, se ven todos.                                         */
.sidebar.collapsed .nav-group--closed {
  display: block;
}

/* -------------------------------------------------------------------------- */
/*  Panel                                                                      */
/* -------------------------------------------------------------------------- */
/*  El cuadro del icono de cada cifra. Gris, no de un color por tarjeta: cuatro
    colores puestos para distinguir tarjetas hacen que el color deje de
    significar nada, y entonces el ámbar de la mora —el único que dice algo— se
    pierde entre los demás.                                                   */
.dashboard-chip {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border-radius: 0.5rem;
  background: var(--bs-secondary-bg);
  color: var(--bs-secondary-color);
}

.dashboard-chip--alert {
  background: var(--bs-warning-bg-subtle);
  color: var(--bs-warning-text-emphasis);
}

/*  El anillo de cobertura, dibujado con un degradado cónico y recortado por
    dentro con una máscara. Sin imágenes y sin librería de gráficas: es un
    porcentaje, no un gráfico.

    `--ring` lo pone la vista con el número entero de por ciento.            */
.dashboard-ring {
  position: relative;
  display: grid;
  place-items: center;
  width: 132px;
  height: 132px;
  flex-shrink: 0;
  border-radius: 50%;
  background: conic-gradient(var(--bs-primary) calc(var(--ring) * 1%), var(--bs-secondary-bg) 0);
  text-align: center;
}

/*  El agujero del centro. Va con `::before` y el texto encima, porque una
    máscara sobre el elemento recortaría también el número.                  */
.dashboard-ring::before {
  content: "";
  position: absolute;
  inset: 12px;
  border-radius: 50%;
  background: var(--bs-body-bg);
}

.dashboard-ring > * {
  position: relative;
}

/*  Las tarjetas del panel son enlaces enteros. Sin esto heredan el subrayado y
    el color de enlace, y una tarjeta entera subrayada se lee mal.

    Es `a > .card` y nunca `.card a`: eso alcanzaba a todos los enlaces de
    dentro de cualquier tarjeta —casi todos, porque cada tabla vive en una— y
    con 0-1-1 le ganaba a `.link-record`, que es 0-1-0.                       */
a > .card {
  color: inherit;
}

a:hover > .card,
a:focus-visible > .card {
  border-color: var(--bs-primary-border-subtle);
}

/*  Las tarjetas de una fila del panel, a la misma altura. Sin esto la más alta
    manda y las de al lado quedan flotando arriba, con media tarjeta en blanco
    debajo — que es lo que hace que un panel parezca desarmado.              */
.dashboard-row > [class*="col-"] > .card {
  height: 100%;
}

/* -------------------------------------------------------------------------- */
/*  La barra lateral se desplaza                                               */
/* -------------------------------------------------------------------------- */
/*  La plantilla le da `height: 100vh` y nada más: con nueve enlaces sobraba
    sitio, con veintiuno los últimos quedaban debajo del borde de la pantalla y
    no había forma de llegar a ellos —«El acueducto» era literalmente
    inalcanzable en un portátil de trece pulgadas—.

    Columna flexible y el menú con su propio desplazamiento. `min-height: 0` es
    lo que lo hace funcionar: sin él, un hijo flexible no encoge por debajo de
    su contenido y el `overflow` nunca llega a activarse.                     */
.sidebar {
  display: flex;
  flex-direction: column;
}

.sidebar .nav {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;

  /*  `.nav` de Bootstrap trae `flex-wrap: wrap`. Con el alto sin limitar eso
      no se notaba; al limitarlo, los enlaces que no cabían se pasaban a una
      segunda columna a la derecha en vez de quedarse abajo esperando el
      desplazamiento — «Parametrización» aparecía partida al lado.            */
  flex-wrap: nowrap;
}

/*  La barra del desplazamiento, fina y discreta: a lo ancho normal se come el
    rótulo de los enlaces en 240 px.                                          */
.sidebar .nav::-webkit-scrollbar {
  width: 6px;
}

.sidebar .nav::-webkit-scrollbar-thumb {
  background: var(--bs-border-color);
  border-radius: 3px;
}

.sidebar .nav {
  scrollbar-width: thin;
}

/* -------------------------------------------------------------------------- */
/*  Informes                                                                   */
/* -------------------------------------------------------------------------- */
/*  Las tres cifras del periodo, en una sola tarjeta con divisiones y no en
    tres cajas sueltas: son la misma frase —entró, salió, queda— y en tres
    cajas se leían como tres cosas distintas.

    El fondo es el gris de la aplicación con un punto más de peso, y la última
    columna lleva el naranja de marca. Ni blanco sobre blanco, que no se
    distinguía del resto de la pantalla, ni una tarjeta de color por dato, que
    era lo que se veía infantil.                                              */
.reports-summary {
  display: block;
  overflow: hidden;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius-lg);
  background: var(--bs-body-bg);
}

.reports-tally {
  height: 100%;
  padding: 1.25rem 1.5rem;
  color: var(--bs-secondary-color);
  border-inline-end: 1px solid var(--bs-border-color);
}

.reports-summary .col:last-child .reports-tally {
  border-inline-end: 0;
}

.reports-tally .fs-3 {
  color: var(--bs-body-color);
}

/*  La diferencia es la conclusión de las otras dos, así que se ve como tal.  */
.reports-tally--strong {
  background: var(--bs-primary-bg-subtle);
  color: var(--bs-primary-text-emphasis);
}

.reports-tally--strong .fs-3 {
  color: var(--bs-primary-text-emphasis);
}

@media (max-width: 767.98px) {
  .reports-tally {
    border-inline-end: 0;
    border-block-end: 1px solid var(--bs-border-color);
  }
}

/*  Las tarjetas del catálogo. El icono en un cuadro ámbar suave: es lo primero
    que distingue una tarjeta de otra en una rejilla de siete iguales, antes de
    leer el título.                                                           */
.report-card {
  transition: border-color 0.15s, box-shadow 0.15s;
}

/*  Y lo mismo con el teclado.
    
    La tarjeta entera se resalta al pasar el ratón, pero el enlace de dentro es
    `stretched-link`: al tabular, el anillo del navegador rodea solo el título y
    la tarjeta no dice nada. Quien navega con teclado recibía media señal.
    
    `:focus-within` y no `:focus`: lo que recibe el foco es el enlace, y lo que
    tiene que responder es la tarjeta que lo contiene. Vale también para los dos
    botones de abajo, y está bien — la tarjeta activa es esa.                  */
.report-card:hover,
.report-card:focus-within {
  border-color: var(--bs-primary-border-subtle);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}

.report-card__icon {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 0.5rem;
  background: var(--bs-warning-bg-subtle);
  color: var(--bs-warning-text-emphasis);
}

.report-card:hover .report-card__icon,
.report-card:focus-within .report-card__icon {
  background: var(--bs-primary-bg-subtle);
  color: var(--bs-primary-text-emphasis);
}

/*  Los botones van encima del enlace que cubre la tarjeta; sin esto el clic en
    «Imprimir» abriría la pantalla del informe.                               */
.report-card__actions {
  position: relative;
  z-index: 2;
  border-block-start: 1px solid var(--bs-border-color);
}

.report-card__actions form {
  margin: 0;
}

@media (prefers-reduced-motion: reduce) {
  .report-card {
    transition: none;
  }
}

/* -------------------------------------------------------------------------- */
/*  Cabeceras que ordenan                                                      */
/* -------------------------------------------------------------------------- */
/*  La cabecera es un enlace de verdad —el orden va en la URL y lo aplica el
    servidor— pero no debe parecer uno: subrayada y azul, una fila de seis
    columnas se lee como una barra de navegación y no como el encabezado de una
    tabla. Hereda el color del texto y solo se subraya al pasar por encima.    */
.th-sort {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
}

.th-sort:hover {
  color: inherit;
  text-decoration: underline;
}

/*  El foco tiene que verse: por teclado se recorren las cabeceras tabulando y
    sin esto no hay forma de saber en cuál se está.                            */
.th-sort:focus-visible {
  outline: 2px solid var(--bs-primary);
  outline-offset: 2px;
  border-radius: .125rem;
}

/*  Siempre visible, no solo al pasar por encima: escondida, en una pantalla
    táctil nadie se enteraba de que la tabla se ordena. Dos puntas significa
    «se puede ordenar»; una sola, «está ordenada así». 0,65 y no menos: el
    trazo es de 1,75 px y el suavizado lo aclara, así que a 0,55 los bordes se
    quedaban en 3,5:1. Así da 5:1 largos en los dos temas.                    */
.th-sort-hint {
  display: inline-flex;
  opacity: .65;
  transition: opacity .12s ease-in-out;
}

.th-sort:hover .th-sort-hint,
.th-sort:focus-visible .th-sort-hint {
  opacity: 1;
}

/*  En el móvil la cabecera no existe —cada fila es una ficha— así que la tabla
    se ordena desde donde se pueda, y aquí no hay nada que reservar.           */
@media (max-width: 767.98px) {
  .th-sort-hint {
    display: none;
  }
}

/* -------------------------------------------------------------------------- */
/*  Botones que avisan mientras trabajan                                       */
/* -------------------------------------------------------------------------- */
/*  El girito, en los dos sitios donde un botón se queda trabajando: la descarga
    de la hoja —que se enciende desde JavaScript, porque una descarga no dispara
    ningún evento que se pueda escuchar— y la subida, donde lo pone Turbo al
    cambiarle el contenido al botón mientras la petición está en el aire.

    Solo el tamaño: a 2 rem, que es el de fábrica, no cabe dentro de un botón
    pequeño.                                                                   */
.btn .spinner-border-sm {
  width: 0.875rem;
  height: 0.875rem;
  border-width: 0.15em;
}

/* -------------------------------------------------------------------------- */
/*  En un dedo no existe «pasar por encima»                                    */
/* -------------------------------------------------------------------------- */
/*  Al tocar, el navegador aplica `:hover` y ahí lo deja hasta que se toque otra
    cosa. En un escritorio da igual; en la calle miente.

    El caso que lo obliga es el recorrido del fontanero: cada casa pendiente es
    un `btn-outline-primary` que ocupa la fila entera, y Bootstrap lo rellena de
    color al pasar por encima. La casa que acaba de marcar se le quedaba pintada
    como si estuviera seleccionada — en la única pantalla cuyo trabajo es decir
    dónde va, y con sesenta filas iguales debajo.

    No se puede envolver la regla de Bootstrap, así que se devuelve al estado de
    reposo usando sus propias variables. `:active` y `:focus-visible` siguen
    intactos: el toque sí tiene que verse mientras dura.                        */
@media (hover: none) {
  .btn:hover {
    background-color: var(--bs-btn-bg);
    border-color: var(--bs-btn-border-color);
    color: var(--bs-btn-color);
  }

  /*  Y las decoraciones propias, por lo mismo: son pistas de ratón. Solo la del
      ratón — el gemelo del foco se queda, que en una tableta con teclado sigue
      haciendo falta.                                                          */
  a:hover:not(:focus-visible) > .card,
  .report-card:hover:not(:focus-within) {
    border-color: var(--bs-border-color);
    box-shadow: none;
  }

  .report-card:hover:not(:focus-within) .report-card__icon {
    background: var(--bs-warning-bg-subtle);
    color: var(--bs-warning-text-emphasis);
  }

  .th-sort:hover {
    text-decoration: none;
  }
}

/* -------------------------------------------------------------------------- */
/*  `hidden` tiene que ocultar                                                 */
/* -------------------------------------------------------------------------- */
/*  Las utilidades de display de Bootstrap —`.d-flex`, `.d-block`, `.d-grid`—
    llevan `!important`, y le ganan al `display: none` que el navegador aplica
    por el atributo `hidden`. El resultado es un elemento marcado como oculto
    que se sigue viendo, y ningún error en ninguna parte.

    Se descubrió midiendo: el renglón del vuelto del mostrador es un `.d-flex`,
    y ocupaba sus cuarenta píxeles en todos los cobros exactos aunque el
    controlador lo diera por escondido.

    Va aquí y no en cada sitio porque el que escribe `hidden:` da por hecho que
    oculta, y nadie va a acordarse de esta excepción dentro de seis meses.    */
[hidden] {
  display: none !important;
}

/* -------------------------------------------------------------------------- */
/*  «Cuánto paga», la casilla del mostrador                                    */
/* -------------------------------------------------------------------------- */
/*  Es el único número que se teclea en esta pantalla, se dice en voz alta y se
    comprueba contra el billete que hay encima del mostrador. En el tamaño de
    los demás campos se perdía entre ellos, y con alguien esperando delante lo
    que hace falta es leerlo de un vistazo sin acercarse.

    El grande va en el campo Y en el prefijo: Bootstrap crece el input-group
    con `.input-group-lg`, que el componente no pone, y sin tocar el prefijo el
    «$» queda pequeño al lado de una cifra el doble de alta.                  */
.campo-importe .form-control,
.campo-importe .input-group-text {
  font-size: 1.375rem;
  padding-top: 0.375rem;
  padding-bottom: 0.375rem;
}

.campo-importe .form-control {
  font-weight: 700;
  color: var(--bs-emphasis-color);
}

/*  Y la etiqueta, que si no queda de un tamaño que no le corresponde a lo que
    hay debajo.                                                               */
.campo-importe .form-label {
  font-weight: 600;
  color: var(--bs-emphasis-color);
}

/* -------------------------------------------------------------------------- */
/*  La tabla del reparto, en el panel de cobro                                 */
/* -------------------------------------------------------------------------- */
/*  Seis renglones —la deuda anterior, las facturas y el total— ocupaban más de
    doscientos píxeles, y cada uno empuja el botón de cobrar hacia abajo. Lo que
    marca el alto no es el texto sino la casilla «Abona», así que se aprieta la
    casilla y de paso la celda.

    No se baja más: por debajo de esto la casilla deja de parecer una casilla, y
    es donde se corrige el reparto cuando alguien señala una factura.         */
.tabla-reparto :is(td, th) {
  padding-top: 0.2rem;
  padding-bottom: 0.2rem;
  vertical-align: middle;
}

.tabla-reparto .form-control-sm {
  padding-top: 0.1rem;
  padding-bottom: 0.1rem;
}

/* -------------------------------------------------------------------------- */
/*  Las acciones sobre un miembro, en la ficha del acueducto                   */
/* -------------------------------------------------------------------------- */
/*  Cinco acciones en la celda de un renglón —quitar el acceso, quitar el
    segundo factor, cambiar el correo, cambiar los roles— y cada una con su
    propio ancho dejaban los bordes en escalera y el renglón descuadrado.

    Un ancho fijo para la columna entera lo resuelve de una vez: todo lo que
    hay dentro comparte los dos bordes, y la tabla deja de estirarse cuando uno
    de los desplegables se abre.                                              */
.acciones-miembro {
  width: 13rem;
  max-width: 100%;
}
