﻿/* =====================================================================================
   design-patterns.css — Kit de lenguaje visual (prefijo tw-)
   -------------------------------------------------------------------------------------
   Hoja reutilizable, sin dependencias de ninguna página concreta. Acompaña a
   DESIGN-GENERIC.md, que explica cada patrón y cómo usarlo.

   Requisitos mínimos:
   - Bootstrap 3 (opcional): se usa para .form-control, .btn* y .modal*. Si no está,
     los bloques siguen funcionando salvo los modales.
   - Una fuente de iconos (Nerd Font, Glyphicons, SVG…): el kit solo dimensiona los
     iconos, no los provee.

   Cómo se usa:
   1. Envolver el contenido de la pantalla en <div class="tw-page">.
   2. Componer con los bloques: tw-filter (criterios), tw-form (alta/edición),
      tw-results-bar + tw-grid-wrap + tw-grid (listado), tw-footer (pie fijo),
      tw-state-* (estados), tw-tag (badges), modales Bootstrap dentro de tw-page.
   3. Ajustar los tokens de :root si el proyecto tiene otro acento.

   Breakpoints: 767 px (móvil), 991 px (tablet), y modo compacto para pantallas
   anchas pero bajas (iPad apaisado): (min-width: 768px) and (max-height: 820px).
   ===================================================================================== */

/* -------------------------------------------------------------------------------------
   0. Tokens
   ------------------------------------------------------------------------------------- */
:root {
    --tw-accent: #337ab7;             /* cabecera de grid, línea de acento, acción principal */
    --tw-accent-dark: #2e6da4;        /* borde del botón principal */
    --tw-accent-hover: #286090;
    --tw-focus: #007bff;
    --tw-focus-halo: rgba(0, 123, 255, 0.28);
    --tw-text: #1f2933;
    --tw-text-2: #3e4c59;             /* labels, títulos de bloque */
    --tw-text-3: #5f6b7a;             /* ayudas, contexto */
    --tw-text-muted: #66717f;
    --tw-text-disabled: #8a94a0;
    --tw-bg: #ffffff;
    --tw-bg-panel: #f6f8fa;           /* bloque de criterios */
    --tw-bg-soft: #fafbfc;            /* estados, paginador */
    --tw-bg-zebra: #f8fafc;
    --tw-bg-hover: #eaf2fb;
    --tw-bg-pager: #eef2f6;           /* paginador fijo */
    --tw-bg-disabled: aliceblue;
    --tw-border: #c9d1d9;
    --tw-border-strong: #000000;      /* borde de inputs */
    --tw-border-tag: #b9c4d0;
    --tw-border-row: #e3e8ee;
    --tw-border-soft: #dfe4ea;
    --tw-danger: #b3261e;
    --tw-danger-halo: rgba(179, 38, 30, 0.18);
    --tw-warn: #b26a00;               /* línea de acento de avisos */
    --tw-warn-bg: #fff6e0;
    --tw-warn-border: #f0d9a6;
    --tw-success: #28a745;
    --tw-pending-halo: rgba(240, 173, 78, 0.7); /* criterio modificado y no aplicado */
    --tw-font: Roboto, "Segoe UI", system-ui, -apple-system, sans-serif;
    --tw-control-h: 40px;
    --tw-control-h-mobile: 44px;
    --tw-footer-h: 92px;              /* reserva inferior para el pie fijo (§11).
                                         Si el layout ya pinta un pie propio bajo el
                                         contenido, bajarlo por pagina (ver §8.1) */
    --tw-head-bg: #f2f2f2;            /* fondo de la banda de cabecera (§8.1) */
    --tw-head-border: #cfcfcf;
    --tw-head-pull: 50px;             /* margen lateral del layout: lo que hay que sacar
                                         la banda para llegar al borde de la ventana */
    --tw-head-pad: 50px;              /* padding interior de la banda */
    --tw-z-footer: 900;
    --tw-z-modal-backdrop: 2090;
    --tw-z-modal: 2100;               /* por encima de una navbar fija (2000) */
}

/* -------------------------------------------------------------------------------------
   1. Página
   ------------------------------------------------------------------------------------- */
.tw-page {
    font-family: var(--tw-font);
    font-size: 14px;
    color: var(--tw-text);
    padding-bottom: var(--tw-footer-h); /* hueco para el pie fijo */
}

    .tw-page *:focus-visible {
        outline: 3px solid var(--tw-text);
        outline-offset: 2px;
    }

/* Ocultar visualmente conservando la semántica (caption, títulos para lectores) */
.tw-sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

/* Rótulos comunes */
.tw-block-title,
.tw-filter-title {
    display: block;
    margin: 0 0 10px 0;
    padding: 0;
    border: 0;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--tw-text-2);
}

    /* El título de una sección va pegado a SU listado, no a mitad de camino entre el listado
       anterior y el suyo. Se baja con padding-top y NO con margin-top: los márgenes verticales
       de hermanos se fusionan (queda el mayor de los dos), así que un margin-top de 4px lo
       absorbería el margen inferior del bloque de arriba y el título no se movería. Los 4px se
       descuentan por abajo, de modo que la separación entre dos listados no cambia.
       No se aplica a tw-filter-title: ese va dentro del panel de criterios, con su propio
       padding, y ahí no hace falta. */
    .tw-block-title {
        padding-top: 4px;
        margin-bottom: 6px;
    }

.tw-label,
.tw-field > label,
.tw-group-title {
    display: block;
    margin: 0 0 4px 0;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .03em;
    text-transform: uppercase;
    color: var(--tw-text-2);
}

    /* Label que lleva un valor de referencia ("Temperature 20 ºC"): sin mayúsculas */
    .tw-label-lc {
        text-transform: none;
        letter-spacing: 0;
        font-size: 13px;
    }

.tw-help {
    margin: 8px 0 0 0;
    font-size: 12px;
    color: var(--tw-text-3);
}

.tw-error {
    display: block;
    margin: 6px 0 0 0;
    font-size: 12px;
    font-weight: 600;
    color: var(--tw-danger);
}

/* Banda de cabecera de pantalla (§8.1): titulo + linea de contexto sobre fondo gris,
   de borde a borde y pegada a la barra de navegacion (sin hueco). Los margenes
   negativos compensan el margen lateral del layout (--tw-head-pull). */
.tw-page-head {
    margin: 0 calc(-1 * var(--tw-head-pull)) 14px calc(-1 * var(--tw-head-pull));
    padding: 10px var(--tw-head-pad) 10px var(--tw-head-pad);
    background-color: var(--tw-head-bg);
    border: 1px solid var(--tw-head-border);
}

/* Titulo de la pantalla */
.tw-page-title {
    margin: 0 0 4px 0;
    font-size: 22px;
    font-weight: 700;
    color: var(--tw-text);
}

    /* dentro de la banda la linea de contexto va un punto mayor */
    .tw-page-head .tw-context {
        margin-bottom: 0;
        font-size: 18px;
    }

/* Línea de contexto ("Task description: …", "Mixture: …") */
.tw-context {
    margin: 0 0 14px 0;
    font-size: 14px;
    line-height: 1.5;
    color: var(--tw-text-3);
}

    .tw-context strong,
    .tw-context .tw-context-value {
        font-weight: 600;
        color: var(--tw-text);
    }

/* -------------------------------------------------------------------------------------
   2. Controles
   ------------------------------------------------------------------------------------- */
.tw-field {
    min-width: 0;
}

    .tw-field .form-control,
    .tw-field input[type="text"],
    .tw-field input[type="password"],
    .tw-field input[type="email"],
    .tw-field input[type="tel"],
    .tw-field input[type="number"],
    .tw-field input[type="date"],
    .tw-field input[type="time"],
    .tw-field input[type="datetime-local"],
    .tw-field select,
    .tw-field textarea {
        display: block;
        width: 100%;
        max-width: none; /* anula límites globales tipo max-width: 280px */
        height: var(--tw-control-h);
        padding: 4px 12px;
        font-family: inherit;
        font-size: 15px;
        letter-spacing: .02em;
        border: 1px solid var(--tw-border-strong);
        border-radius: 4px;
        background-color: var(--tw-bg);
        color: var(--tw-text);
        box-shadow: none;
    }

    .tw-field select {
        padding-top: 0;
        padding-bottom: 0;
    }

    .tw-field textarea {
        height: auto;
        min-height: 104px;
        padding: 8px 12px;
        line-height: 1.45;
        resize: vertical;
    }

    .tw-field .form-control:focus,
    .tw-field input:focus,
    .tw-field select:focus,
    .tw-field textarea:focus {
        border-color: var(--tw-focus);
        outline: none;
        box-shadow: 0 0 0 3px var(--tw-focus-halo);
    }

    .tw-field .form-control[aria-invalid="true"],
    .tw-field input[aria-invalid="true"],
    .tw-field textarea[aria-invalid="true"] {
        border-color: var(--tw-danger);
        box-shadow: 0 0 0 3px var(--tw-danger-halo);
    }

    .tw-field input:disabled,
    .tw-field select:disabled,
    .tw-field textarea:disabled {
        background-color: var(--tw-bg-disabled);
        opacity: .85;
    }

/* Anchos de campo según el dato (dentro de una fila flex) */
.tw-field-short  { flex: 0 1 250px; min-width: 180px; }   /* combo, código, matrícula */
.tw-field-date   { flex: 0 1 250px; min-width: 220px; }   /* fecha / fecha-hora */
.tw-field-long   { flex: 1 1 240px; }                     /* texto libre: absorbe el sobrante */
.tw-field-num    { flex: 0 1 200px; min-width: 160px; }   /* importe, cantidad, horas */
.tw-field-full   { flex: 1 1 100%; }
.tw-field-concept { max-width: 720px; }                   /* dato corto fuera de una fila */

    .tw-field-num input,
    .tw-num {
        text-align: right;
        font-variant-numeric: tabular-nums;
    }

    .tw-field-item input,
    .tw-center {
        text-align: center;
        font-variant-numeric: tabular-nums;
    }

.tw-field-item {
    max-width: 160px;
}

/* Fila de campos */
.tw-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 12px 20px;
}

/* Control + acción auxiliar en la misma línea (input + escáner, input + botón de partidas) */
.tw-input-aux {
    display: flex;
    align-items: center;
    gap: 8px;
}

    .tw-input-aux .form-control,
    .tw-input-aux input {
        flex: 1 1 auto;
        min-width: 0;
    }

/* Label + acción auxiliar (micrófono, calendario, check) en la misma línea */
.tw-label-row {
    display: flex;
    flex-wrap: wrap; /* en campos estrechos el auxiliar baja en vez de aplastar el rotulo */
    align-items: center;
    gap: 10px;
    margin: 0 0 4px 0;
}

    .tw-label-row label,
    .tw-label-row .tw-label {
        margin: 0;
    }

/* Check auxiliar del rotulo, pegado a la derecha (p.ej. "Agrupar por" en un criterio).
   Se usa junto a tw-option: <span class="tw-option tw-label-check">. */
.tw-label-check {
    margin-left: auto;
    font-size: 12px;
    position: relative;
    top: -2px; /* lo despega del control que va justo debajo */
}

/* Botón de icono auxiliar: 40 px, borde, icono en acento */
.tw-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 40px;
    width: 40px;
    height: 40px;
    margin: 0;
    padding: 0;
    border: 1px solid var(--tw-border);
    border-radius: 4px;
    background-color: var(--tw-bg);
    color: var(--tw-accent);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
    transition: background-color .15s ease-in-out;
}

    .tw-icon-btn:hover {
        background-color: var(--tw-bg-hover);
    }

    .tw-icon-btn img {
        width: 22px;
        height: 22px;
    }

/* Botón secundario con icono y texto (Select batch, Search batch…) */
.tw-btn-aux {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 40px;
    margin: 0;
    padding: 0 12px;
    border: 1px solid var(--tw-border);
    border-radius: 4px;
    background: var(--tw-bg);
    color: var(--tw-accent);
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    text-decoration: none !important;
}

    .tw-btn-aux:hover {
        background: var(--tw-bg-hover);
    }

    .tw-btn-aux img {
        width: 22px;
        height: 22px;
    }

/* Opciones (radios / checks) en línea */
.tw-options {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 18px;
    min-height: 24px;
}

.tw-option {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 14px;
    color: var(--tw-text);
}

    .tw-option > span {
        display: inline-flex !important; /* span que envuelve al input en algunos frameworks */
        align-items: center;
    }

    .tw-option input[type="radio"],
    .tw-option input[type="checkbox"] {
        margin: 0;
        width: 16px;
        height: 16px;
        accent-color: var(--tw-accent);
        cursor: pointer;
    }

    .tw-option label {
        margin: 0;
        font-weight: 400;
        cursor: pointer;
    }

/* Check con texto en negrita (ajuste de un formulario) */
.tw-check {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    min-height: 32px;
}

    .tw-check input[type="checkbox"] {
        width: 18px;
        height: 18px;
        margin: 0;
        cursor: pointer;
    }

    .tw-check label {
        margin: 0;
        font-size: 14px;
        font-weight: 600;
        color: var(--tw-text);
        cursor: pointer;
    }

    .tw-check:not(:has(input)) {
        display: none; /* el servidor no ha pintado el control: no dejar hueco */
    }

/* Grupo de opciones con título (STATUS, ENGINE…) dentro de un bloque de criterios */
.tw-group {
    flex: 0 1 auto;
    min-width: 0;
}

/* -------------------------------------------------------------------------------------
   3. Botones
   ------------------------------------------------------------------------------------- */
.tw-btn {
    display: inline-flex !important;
    align-items: center;
    justify-content: center;
    gap: 8px;
    height: var(--tw-control-h);
    min-width: 130px;
    margin: 0 !important;
    padding: 0 16px;
    border: 1px solid transparent;
    border-radius: 4px;
    font-family: inherit;
    font-size: 13px;
    font-weight: 600;
    line-height: 1 !important;
    text-decoration: none !important;
    cursor: pointer;
    box-shadow: 3px 3px 5px -3px rgba(0, 0, 0, 0.4);
    transition: background-color .15s ease-in-out;
}

.tw-btn-primary   { background: var(--tw-accent); border-color: var(--tw-accent-dark); color: #fff; }
.tw-btn-primary:hover { background: var(--tw-accent-hover); color: #fff; }
.tw-btn-secondary { background: #31b0d5; border-color: #269abc; color: #fff; }  /* navegación (Return, Cancel) */
.tw-btn-secondary:hover { background: #269abc; color: #fff; }
.tw-btn-outline   { background: var(--tw-bg); border-color: var(--tw-border); color: var(--tw-text-2); box-shadow: none; }
.tw-btn-outline:hover { background: var(--tw-bg-hover); }
.tw-btn-danger    { background: #d9534f; border-color: #d43f3a; color: #fff; }
.tw-btn-danger:hover { background: #c9302c; color: #fff; }
.tw-btn-success   { background: var(--tw-success); border-color: #1e7e34; color: #fff; }
.tw-btn-success:hover { background: #218838; color: #fff; }

/* El display:inline-flex !important de .tw-btn gana a las clases de ocultación del
   proyecto: hay que devolverles la prioridad o un botón que el código oculta se seguiría
   viendo (y no da ningún error, solo aparece algo que no debía). */
.tw-btn.oculto,
.tw-btn[hidden] {
    display: none !important;
}

.tw-btn.disabled,
.tw-btn:disabled,
.tw-btn[aria-disabled="true"] {
    opacity: .5;
    cursor: not-allowed;
    pointer-events: none;
}

.tw-busy {
    opacity: .65;
    cursor: progress;
}

/* Icono dentro de un botón (glifo o img) */
.tw-btn-icon {
    font-size: 20px;
    line-height: 1;
    color: inherit !important;
}

.tw-btn img.tw-btn-icon {
    width: 22px;
    height: 22px;
}

/* Centrado vertical del glifo en un botón con icono al lado del texto.

   El flex centra la CAJA del icono, pero el dibujo no está centrado dentro de ella: a 20px, la
   tinta del glifo va de 13 a 31 (centro 22) en un botón de 40, o sea 2px por debajo de la mitad.
   Son dos cosas sumadas: Bootstrap le pone position:relative; top:1px a todo .glyphicon, y la
   propia fuente deja 1px más de hueco arriba que abajo. Al lado del texto, que sí queda centrado
   por su altura de mayúscula, esos 2px se ven.

   Se sube 2px (de top:1px a top:-1px) y la tinta pasa a ir de 11 a 29: centro 20, el del botón.

   No se toca el pie: ahí el icono va ENCIMA del texto y su posición ya está ajustada a mano
   (§11), así que se deja como está. */
.tw-btn:not(.tw-btn-foot) > .tw-btn-icon {
    top: -1px;
}

/* El clip de Glyphicons viene dibujado en diagonal. En vertical ocupa menos a lo ancho y se
   reconoce igual, que es lo que interesa en un botón estrecho.

   La escala no es decoración: al girar 45°, lo que estaba en la diagonal de la caja pasa a
   estar en el lado, y el dibujo crece 1,41 veces (se salía por arriba del botón). El 0,707 es
   justo la inversa, así que el clip vertical acaba midiendo lo mismo que medía en diagonal y
   que el resto de iconos. */
.tw-btn-icon.glyphicon-paperclip {
    transform: rotate(-35deg) scale(0.707);
}

/* El ojo es de los glifos que más llenan su caja, y al lado de los demás iconos del pie pesa
   más de la cuenta. Se reduce un 20% para que se lea al mismo tamaño óptico. Va con transform y
   no con font-size para no tocar la caja: así el botón no se recoloca y el contador que lleva al
   lado se queda donde está. */
.tw-btn-icon.glyphicon-eye-open {
    transform: scale(0.8);
}

/* Selector de fichero. Un <input type="file"> no se puede maquetar, así que lo que se ve es un
   <label for="..."> con aspecto de botón y el campo va escondido detrás. Pulsar el rótulo abre el
   diálogo: es comportamiento nativo del navegador, sin JavaScript, y funciona sea cual sea el
   tamaño del botón.

   El campo se deja de 1px en vez de display:none para que siga siendo enfocable con el tabulador.

   Lo que NO hay que hacer es el truco clásico de taparlo con un font-size gigante: ata el ancho
   del botón al del campo (que además cambia con el idioma del navegador) y en cuanto el botón
   crece, el borde deja de estar cubierto y el clic se cuela a lo que haya debajo. */
.tw-file {
    position: relative;
    display: inline-block;
}

    .tw-file input[type=file] {
        position: absolute;
        left: 0;
        top: 0;
        width: 1px;
        height: 1px;
        opacity: 0;
    }

    .tw-file > .tw-btn {
        margin: 0;
        cursor: pointer;
    }

/* Nombre del fichero elegido, bajo el selector */
.tw-file-name {
    margin: 10px 0 0 0;
    text-align: center;
    font-weight: 600;
    color: var(--tw-accent);
}

/* Acción de bloque (Accept conditions, Make mixture…): centrada, 44 px */
.tw-block-actions {
    display: flex;
    justify-content: center;
    margin: 14px 0 0 0;
}

.tw-btn-action {
    height: 44px;
    min-width: 200px;
    padding: 0 18px;
    gap: 10px;
}

/* Botón de pie: 150 × 54 en escritorio; en móvil 46 × 44 y solo icono */
.tw-btn-foot {
    width: 150px;
    height: 54px;
    /* El hueco se separa en los dos ejes: 10px entre icono y rótulo, pero CERO entre líneas.
       Con el gap a secas, una segunda línea se lleva otros 10px y ya no cabe en los 44px de
       alto del botón en tablet: el contador se salía por debajo. */
    column-gap: 10px;
    row-gap: 0px;
    font-size: 12px;
    /* El botón NO se parte en dos líneas: si lo hiciera, el icono se quedaría en la primera y
       dejaría de estar centrado en el alto. Quien se parte es la pila del rótulo (tw-btn-stack),
       que va aparte justamente para eso. */
    flex-wrap: nowrap;
    text-align: center;
}

    .tw-btn-foot .tw-btn-icon {
        font-size: 34px;
    }

    /* Contador dentro del botón ("Adjuntos (3)"). Va en su propio elemento y NO dentro de
       tw-btn-text a propósito: en móvil el kit oculta el rótulo y dejaría el botón sin decir
       cuántos hay, que es la mitad de la información que lleva. Aquí fluye como uno más: si cabe
       va detrás del rótulo y si no, baja a la segunda línea, centrado. */
    .tw-btn-foot .tw-btn-count {
        font-size: 12px;
        line-height: 1;
    }

    /* Rótulo y contador van juntos en una pila: si caben, en la misma línea; si no, el contador
       baja debajo del rótulo. Lo importante es que el ICONO se queda FUERA de ese reparto, así
       que el align-items del botón lo centra en los 44 o 54px de alto en vez de dejarlo pegado
       a la primera línea, que es donde se quedaba cuando los tres eran hermanos. */
    .tw-btn-foot .tw-btn-stack {
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
        align-items: baseline;
        column-gap: 4px;
        row-gap: 0px;
        /* Se queda con el ancho que sobre tras el icono y parte por dentro. El min-width a 0 es
           imprescindible: por defecto un elemento flexible no encoge por debajo de su contenido
           y empujaría al icono fuera del botón. */
        flex: 1 1 auto;
        min-width: 0;
        /* Un rótulo de una sola palabra más ancha que el hueco hay que partirlo o se sale del
           botón. Probado con hyphens: auto para que parta con guion, pero en 52px de hueco se va
           a TRES líneas y con el contador ya no cabe en los 44 de alto; sin guion son dos y
           entra. Para un rótulo así de largo la solución no es de CSS, es acortar el texto. */
        overflow-wrap: anywhere;
    }

/* -------------------------------------------------------------------------------------
   4. Bloque de criterios / filtros (§18.3)
   ------------------------------------------------------------------------------------- */
.tw-filter {
    border: 1px solid var(--tw-border);
    border-left: 4px solid var(--tw-accent);
    background: var(--tw-bg-panel);
    padding: 12px 14px 10px 14px;
    margin: 0 0 14px 0;
    font-size: 14px;
    color: var(--tw-text);
}

/* Cabecera siempre visible: título, filtro activo y mostrar/ocultar con texto */
.tw-filter-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 10px;
}

    .tw-filter-head .tw-filter-title {
        margin: 0;
    }

.tw-filter-body {
    margin-top: 10px;
}

.tw-filter-toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
}

.tw-filter-toggle-text {
    margin: 0;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .03em;
    text-transform: uppercase;
    color: var(--tw-accent);
    cursor: pointer;
    user-select: none;
}

    /* LOCALIZACIÓN DGT: los textos de esta aplicación van en español.
       Son los dos únicos textos visibles del kit (aquí y en .tw-collapsed más abajo).
       Si se actualiza el kit desde otro proyecto, volver a traducirlos. */
    .tw-filter-toggle-text::before {
        content: "Ocultar criterios";
    }

/* Botón de plegar: 32 px, icono en acento */
.tw-filter-toggle-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    margin: 0 !important;
    padding: 0;
    border: 1px solid var(--tw-border);
    border-radius: 4px;
    background-color: var(--tw-bg);
    color: var(--tw-accent);
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
}

    .tw-filter-toggle-btn:hover {
        background-color: var(--tw-bg-hover);
    }

/* Plegado: la sección queda como barra; solo se oculta el cuerpo.
   Se acepta la clase neutra .tw-collapsed y también .oculto (clase global de algunos proyectos). */
.tw-page .tw-filter.tw-collapsed,
.tw-page .tw-filter.oculto {
    display: block !important;
    padding-top: 8px;
    padding-bottom: 8px;
}

    .tw-page .tw-filter.tw-collapsed .tw-filter-body,
    .tw-page .tw-filter.oculto .tw-filter-body {
        display: none;
    }

    /* LOCALIZACIÓN DGT: textos en español (ver el comentario de .tw-filter-toggle-text) */
    .tw-page .tw-filter.tw-collapsed .tw-filter-toggle-text::before,
    .tw-page .tw-filter.oculto .tw-filter-toggle-text::before {
        content: "Mostrar criterios";
    }

    /* plegado: la flecha del boton apunta hacia abajo */
    .tw-page .tw-filter.tw-collapsed .tw-filter-toggle-btn > *,
    .tw-page .tw-filter.oculto .tw-filter-toggle-btn > * {
        transform: rotate(180deg);
    }

/* Indicador "hay un filtro aplicado" (icono, en rojo semántico) */
.tw-filter-active {
    color: var(--tw-danger);
    font-size: 18px;
    line-height: 1;
    vertical-align: middle;
    background: transparent;
}

/* Fila de criterios: campos + acción principal alineada con la base de los inputs */
.tw-filter-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 12px 20px;
}

    .tw-filter-row + .tw-filter-row {
        margin-top: 12px;
    }

.tw-filter-row-groups {
    align-items: flex-start;
    gap: 10px 28px;
}

/* Acciones del bloque: aplicar (acento) y quitar (discreta), a la derecha */
.tw-field-action {
    flex: 0 0 auto;
    display: flex;
    align-items: flex-end;
    gap: 8px;
    margin-left: auto;
}

.tw-btn-filter,
.tw-btn-clear {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    margin: 0;
    padding: 0;
    border-radius: 4px;
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
    box-shadow: 3px 3px 5px -3px rgba(0, 0, 0, 0.4);
    transition: background-color .15s ease-in-out;
}

.tw-btn-filter {
    border: 1px solid var(--tw-accent-dark);
    background-color: var(--tw-accent);
    color: #fff;
}

    .tw-btn-filter:hover {
        background-color: var(--tw-accent-hover);
    }

    /* Criterio modificado y pendiente de aplicar: halo ámbar sobre el botón */
    .tw-btn-filter.tw-pending {
        box-shadow: 0 0 0 3px var(--tw-pending-halo);
    }

.tw-btn-clear {
    border: 1px solid var(--tw-border);
    background-color: var(--tw-bg);
    color: var(--tw-text-2);
}

    .tw-btn-clear:hover {
        background-color: var(--tw-bg-hover);
    }

/* -------------------------------------------------------------------------------------
   5. Formulario de alta / edición (§18.6)
   ------------------------------------------------------------------------------------- */
.tw-form {
    display: flex;
    flex-direction: column;
    gap: 14px;
    margin: 0;
}

/* Grupo de campos relacionados: fieldset + legend, separador superior, sin tarjeta */
.tw-block {
    margin: 4px 0 0 0;
    padding: 12px 0 0 0;
    border: 0;
    border-top: 1px solid var(--tw-border-row);
    min-width: 0;
}

    .tw-block > .tw-row {
        margin: 0 0 12px 0;
    }

    .tw-block > .tw-row:last-child {
        margin-bottom: 0;
    }

    .tw-block.tw-disabled .tw-block-title {
        color: var(--tw-text-disabled);
    }

/* Bloque deshabilitado en conjunto (asistentes por pasos): controles atenuados, textos legibles */
.tw-disabled {
    pointer-events: none;
}

    .tw-disabled input:not([type="radio"]):not([type="checkbox"]),
    .tw-disabled select,
    .tw-disabled textarea,
    .tw-disabled button,
    .tw-disabled a {
        opacity: .75 !important;
        filter: grayscale(100%);
    }

    .tw-disabled input[type="radio"],
    .tw-disabled input[type="checkbox"] {
        opacity: .7 !important;
        accent-color: #9a9a9a;
    }

/* Resumen de un registro en una línea (valores de referencia en acento) */
.tw-summary {
    margin: 0 0 12px 0;
    line-height: 1.5;
}

    .tw-summary strong {
        font-weight: 700;
    }

    .tw-summary .tw-summary-value {
        color: var(--tw-accent);
        font-weight: 600;
    }

/* -------------------------------------------------------------------------------------
   6. Resumen de resultados (§18.2) y tamaño de página
   ------------------------------------------------------------------------------------- */
.tw-results-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 14px;
    margin: 0 0 8px 2px;
}

.tw-results-left,
.tw-results-right {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 14px;
}

.tw-results-right {
    margin-left: auto;
}

.tw-results-count {
    font-size: 15px;
    font-weight: 700;
}

.tw-results-context {
    font-size: 13px;
    color: var(--tw-text-3);
}

.tw-results-total {
    font-size: 14px;
    color: var(--tw-text-2);
}

    .tw-results-total strong {
        color: var(--tw-text);
    }

.tw-pagesize {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

    .tw-pagesize label {
        margin: 0;
        font-size: 12px;
        font-weight: 600;
        letter-spacing: .03em;
        text-transform: uppercase;
        color: var(--tw-text-2);
    }

    .tw-pagesize select,
    .tw-pagesize .form-control {
        display: inline-block;
        width: 76px;
        height: 32px;
        padding: 0 4px;
        font-size: 14px;
        text-align: center;
        text-align-last: center;
        border: 1px solid var(--tw-border-strong);
        border-radius: 4px;
        background-color: var(--tw-bg);
    }

/* Indicador lineal (KPI / progreso) */
.tw-kpi {
    display: flex;
    align-items: center;
    gap: 10px;
}

    .tw-kpi-label {
        font-size: 12px;
        font-weight: 600;
        letter-spacing: .03em;
        text-transform: uppercase;
        color: var(--tw-text-2);
        white-space: nowrap;
    }

    .tw-kpi-bar {
        flex: 1 1 auto;
        position: relative;
        height: 14px;
        border-radius: 7px;
        background: var(--tw-border-row);
    }

    .tw-kpi-fill {
        position: absolute;
        left: 0;
        top: 0;
        bottom: 0;
        border-radius: 7px;
        background: var(--tw-success);
    }

    .tw-kpi-value {
        font-size: 12px;
        font-weight: 700;
        font-variant-numeric: tabular-nums;
    }

/* -------------------------------------------------------------------------------------
   7. Grid (§18.4)
   ------------------------------------------------------------------------------------- */
.tw-grid-wrap {
    border: 1px solid var(--tw-border);
    background: var(--tw-bg);
    margin: 0 0 14px 0;
}

    .tw-grid-wrap:empty,
    .tw-grid-wrap:not(:has(table)) {
        display: none; /* el servidor no ha pintado la tabla: no dejar un marco vacío */
    }

/* Variante: columnas desconocidas o muchas -> desplazamiento horizontal dentro del marco */
.tw-grid-hscroll {
    overflow-x: auto;
}

    .tw-grid-hscroll .tw-grid th,
    .tw-grid-hscroll .tw-grid td {
        white-space: nowrap;
    }

        /* ...pero no al mensaje de "no hay datos": ese es una frase, no un dato de una columna,
           y con nowrap estira la tabla hasta que asoma una barra de scroll en una rejilla que
           está vacía. */
        .tw-grid-hscroll .tw-grid td .tw-state {
            white-space: normal;
        }

/* Variante: scroll vertical interno con cabecera fija (el alto lo pone la página o un JS) */
.tw-grid-scroll {
    overflow-y: auto;
    overflow-x: hidden;
    max-height: 60vh;
}

    .tw-grid-scroll .tw-grid thead th {
        position: sticky;
        top: 0;
        z-index: 3;
    }

.tw-grid {
    width: 100%;
    margin: 0;
    border-collapse: collapse;
    table-layout: auto;
}

    .tw-grid caption {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        white-space: nowrap;
    }

    .tw-grid th {
        background: var(--tw-accent) !important;
        color: #fff !important;
        font-size: 12px;
        font-weight: 600;
        letter-spacing: .04em;
        text-transform: uppercase;
        text-align: left;
        vertical-align: bottom;
        padding: 8px 10px;
        white-space: nowrap;
    }

    .tw-grid td {
        text-align: left;
        vertical-align: middle;
        padding: 7px 10px;
        border-top: 1px solid var(--tw-border-row);
        line-height: 1.45;
        background-clip: padding-box;
    }

    /* La primera fila NO lleva separador: ese borde cae justo debajo de la cabecera, que ya
       delimita de sobra, y sobre una fila con color propio (un vencimiento en rojo, una fila
       seleccionada) se lee como una línea blanca metida entre la cabecera y el primer dato. */
    .tw-grid tbody tr:first-child td {
        border-top: 0;
    }

    /* Zebra y hover sobre la fila (no sobre la celda): así un color de fila puesto por el
       servidor con estilo inline (selección, estado) prevalece sin !important */
    .tw-grid tbody tr:nth-child(even) {
        background-color: var(--tw-bg-zebra);
    }

    .tw-grid tbody tr:hover {
        background-color: var(--tw-bg-hover);
    }

    /* Fila de totales al pie de un listado: fondo suave y remate en acento */
    .tw-grid tbody tr.tw-row-total td {
        background-color: var(--tw-bg-panel);
        border-top: 2px solid var(--tw-accent);
        font-weight: 700;
    }

    .tw-grid tbody tr.tw-selected {
        background-color: #a1dcf2 !important;
    }

/* Ayudas de columna: la cabecera se alinea igual que sus datos */
.tw-grid th.tw-col-num,
.tw-grid td.tw-col-num {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.tw-grid th.tw-col-center,
.tw-grid td.tw-col-center,
.tw-grid th.tw-col-flag,
.tw-grid td.tw-col-flag {
    text-align: center;
}

.tw-grid th.tw-col-flag,
.tw-grid td.tw-col-flag {
    width: 40px;
}

.tw-grid th.tw-col-date,
.tw-grid td.tw-col-date {
    width: 110px;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.tw-grid th.tw-col-datetime,
.tw-grid td.tw-col-datetime {
    width: 215px;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.tw-grid th.tw-col-key,
.tw-grid td.tw-col-key {
    white-space: nowrap;
    font-weight: 600;
}

.tw-grid th.tw-col-nowrap,
.tw-grid td.tw-col-nowrap {
    white-space: nowrap;
}

.tw-grid td.tw-col-long {
    width: 38%;
    min-width: 220px;
    overflow-wrap: anywhere;
}

.tw-grid th.tw-col-actions,
.tw-grid td.tw-col-actions {
    width: 56px;
    min-width: 56px;
    text-align: center;
    padding-left: 6px;
    padding-right: 6px;
}

/* Sufijo de baja importancia dentro de una celda (milisegundos, unidad) */
.tw-grid .tw-muted,
.tw-muted {
    color: var(--tw-text-muted);
    font-size: .85em;
}

/* Enlace de dato (nombre de fichero, referencia) */
.tw-grid td a.tw-link,
.tw-link {
    color: var(--tw-accent);
    font-weight: 600;
    text-decoration: none;
}

    .tw-grid td a.tw-link:hover,
    .tw-link:hover {
        text-decoration: underline;
    }

/* Botón de fila: 32 px cuadrado */
.tw-row-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    margin: 0 2px;
    padding: 0;
    border-radius: 4px;
    font-size: 15px;
    line-height: 1;
}

    .tw-row-btn img {
        width: 22px;
        height: 22px;
    }

/* Contenido largo dentro de una celda con scroll propio (lo puede envolver el servidor) */
.tw-grid .tw-truncated {
    max-height: 100px;
    overflow-y: auto;
    padding-right: 4px;
}

/* Paginador: acepta la fila .GridPager de ASP.NET GridView y una lista .tw-pager */
.tw-grid tr.GridPager td,
.tw-grid tr.tw-pager-row td {
    padding: 6px 10px;
    background: var(--tw-bg-soft);
    border-top: 1px solid var(--tw-border) !important;
}

.tw-grid .GridPager table,
.tw-grid .tw-pager-row table {
    display: block;
    width: auto !important;
    min-width: 0 !important;
    margin: 0 auto;
}

    .tw-grid .GridPager table tbody,
    .tw-grid .tw-pager-row table tbody {
        display: block;
    }

    .tw-grid .GridPager table tr,
    .tw-grid .tw-pager-row table tr {
        display: flex !important;
        flex-wrap: wrap;
        justify-content: center !important;
        gap: 6px !important;
        background: transparent;
    }

    .tw-grid .GridPager table td,
    .tw-grid .tw-pager-row table td {
        display: block;
        margin: 0 !important;
        padding: 0 !important;
        border: 0;
        background: transparent;
    }

.tw-grid .GridPager a,
.tw-grid .GridPager span,
.tw-pager a,
.tw-pager span {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 32px;
    height: 32px;
    margin: 0 !important;
    padding: 0 8px !important;
    border: 1px solid var(--tw-border);
    border-radius: 4px;
    background: var(--tw-bg);
    box-shadow: none;
    font-size: 13px;
    font-weight: 600;
    color: var(--tw-text);
    text-shadow: none;
    text-decoration: none;
}

    .tw-grid .GridPager a:hover,
    .tw-pager a:hover {
        background: var(--tw-bg-hover);
    }

.tw-grid .GridPager span,
.tw-pager span {
    background: var(--tw-accent);
    border-color: var(--tw-accent-dark);
    color: #fff;
}

.tw-pager {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 6px;
    padding: 6px 10px;
    background: var(--tw-bg-soft);
    border-top: 1px solid var(--tw-border);
}

/* Paginador siempre visible en un grid con scroll interno (PC y tablet):
   las celdas se pegan al borde inferior del contenedor; la línea se pinta con box-shadow
   porque en una celda sticky de una tabla con border-collapse el border-top no se pinta.
   En tablet/móvil, cuando la tabla es más ancha que la pantalla, la tabla interna del
   paginador se pega al borde izquierdo visible: ancho = 100vw menos los márgenes de la página
   (ajustar --tw-pager-visible-w si el contenedor tiene otros márgenes). */
.tw-grid-scroll,
.tw-grid-sticky-pager {
    --tw-pager-visible-w: calc(100vw - 118px);
}

@media (min-width: 768px) {
    .tw-grid-scroll .tw-grid tr.GridPager > td,
    .tw-grid-sticky-pager .tw-grid tr.GridPager > td,
    .tw-grid-scroll .tw-grid tr.tw-pager-row > td,
    .tw-grid-sticky-pager .tw-grid tr.tw-pager-row > td {
        position: sticky;
        bottom: 0;
        z-index: 5;
        background: var(--tw-bg-pager);
        box-shadow: 0 -2px 0 var(--tw-accent), 0 -8px 10px -6px rgba(31, 41, 51, 0.35);
    }
}

.tw-grid-scroll .tw-grid .GridPager table,
.tw-grid-hscroll .tw-grid .GridPager table,
.tw-grid-scroll .tw-grid .tw-pager-row table,
.tw-grid-hscroll .tw-grid .tw-pager-row table {
    position: sticky;
    left: 0;
    width: var(--tw-pager-visible-w) !important;
    max-width: 100%;
    margin: 0;
}

/* Fila vacía del GridView (EmptyDataTemplate) sin padding para que el estado ocupe el ancho */
.tw-grid .tw-empty-row td {
    padding: 0;
    border: 0;
}

/* -------------------------------------------------------------------------------------
   8. Estados (§18.1 y §10)
   ------------------------------------------------------------------------------------- */
.tw-state {
    display: block;
    padding: 16px 14px;
    color: var(--tw-text-2);
    font-size: 14px;
    line-height: 1.5;
}

    .tw-state strong {
        display: block;
        margin-bottom: 2px;
        color: var(--tw-text);
    }

/* Sin consulta todavía: instrucción con borde discontinuo */
.tw-state-initial {
    border: 1px dashed var(--tw-border);
    background: var(--tw-bg-soft);
    margin: 0 0 14px 0;
}

    .tw-state-initial strong {
        display: inline;
    }

/* Sin resultados (no es un error) */
.tw-state-empty {
    background: var(--tw-bg-soft);
}

/* Estado en línea bajo una barra de resultados (una sola frase) */
.tw-state-inline {
    padding: 10px 2px;
    font-size: 13px;
}

/* -------------------------------------------------------------------------------------
   9. Badges (§18.5)
   ------------------------------------------------------------------------------------- */
.tw-tag {
    display: inline-block;
    max-width: 100%;
    padding: 1px 7px;
    border: 1px solid var(--tw-border-tag);
    border-radius: 3px;
    background: var(--tw-bg);
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: .03em;
    line-height: 1.5;
    color: var(--tw-text);
    white-space: normal;
    vertical-align: middle;
}

.tw-tag-ok      { border-color: var(--tw-success); color: #1e7e34; }
.tw-tag-warn    { border-color: var(--tw-warn);    color: var(--tw-warn); }
.tw-tag-danger  { border-color: var(--tw-danger);  color: var(--tw-danger); }
.tw-tag-accent  { border-color: var(--tw-accent);  color: var(--tw-accent); }

/* Marca circular (número de ítem hecho, contador) */
.tw-dot {
    display: inline-block;
    min-width: 26px;
    height: 26px;
    padding: 0 6px;
    border-radius: 50%;
    background: var(--tw-success);
    color: #fff;
    font-size: 14px;
    font-weight: 700;
    line-height: 26px;
    text-align: center;
    white-space: nowrap;
}

/* -------------------------------------------------------------------------------------
   10. Pie fijo (§11)
   ------------------------------------------------------------------------------------- */
.tw-footer {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: var(--tw-z-footer);
    margin: 0;
    /* El relleno inferior suma el area segura del dispositivo: en un iPad o iPhone sin
       boton de inicio, el indicador de gestos se dibuja ENCIMA del contenido y taparia
       los botones. Donde no hay indicador env() vale 0 y no cambia nada.
       Requiere viewport-fit=cover en el meta viewport del layout. */
    padding: 8px 0 10px 0;
    /* declaracion aparte (no dentro del atajo): asi los navegadores que no entiendan
       env() se quedan con los 10px de arriba en vez de descartar todo el padding.
       El minimo de 20px separa los botones del borde inferior aunque el navegador no
       informe del area segura (los emuladores dibujan el indicador pero devuelven 0). */
    padding-bottom: max(20px, calc(10px + env(safe-area-inset-bottom, 0px)));
    background: rgba(255, 255, 255, 0.96);
    border-top: 1px solid var(--tw-border-soft);
}

    .tw-footer-inner {
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
        align-items: center;
        gap: 24px;
    }

    .tw-footer .tw-btn {
        margin: 0 !important;
    }

/* -------------------------------------------------------------------------------------
   11. Modales (Bootstrap 3) — §13
   ------------------------------------------------------------------------------------- */
/* Todos los modales (también los del layout, fuera de tw-page) por encima de una navbar fija */
.modal {
    z-index: var(--tw-z-modal);
}

.modal-backdrop {
    z-index: var(--tw-z-modal-backdrop);
}

/* Envoltorio para modales sueltos, sobre todo los que vienen de un include compartido:
   les da el ámbito .tw-page que necesitan TODAS las reglas de modal de aquí abajo, sin
   heredar la reserva inferior del pie fijo, que dejaría un hueco en blanco porque dentro
   solo hay diálogos posicionados fijos. */
.tw-page.tw-modal-host {
    padding-bottom: 0;
}

.tw-page .modal.in {
    display: flex !important;
    align-items: center;
    justify-content: center;
    top: 0;
    margin: 0;
    padding: 0 !important;
    overflow-y: auto;
}

    .tw-page .modal.in .modal-dialog {
        margin: 0;
        max-height: 100vh;
    }

.tw-page .modal-content {
    border-radius: 4px;
    border: 1px solid var(--tw-border);
    border-left: 4px solid var(--tw-accent); /* editar / crear / consumir */
    box-shadow: 0 8px 24px rgba(31, 41, 51, 0.18);
}

.tw-page .tw-modal-danger .modal-content  { border-left-color: var(--tw-danger); }
.tw-page .tw-modal-success .modal-content { border-left-color: var(--tw-success); }
.tw-page .tw-modal-warn .modal-content    { border-left-color: var(--tw-warn); }

/* Bootstrap añade un clearfix (::before/::after display:table) a cabecera y pie: en un contenedor
   flex serían ítems vacíos que suman gap y apilan los botones en un modal estrecho */
.tw-page .modal-header::before,
.tw-page .modal-header::after,
.tw-page .modal-footer::before,
.tw-page .modal-footer::after {
    content: none;
    display: none;
}

.tw-page .modal-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 15px;
    background: var(--tw-bg) !important;
    color: var(--tw-text) !important;
    border-bottom: 1px solid var(--tw-border-row);
    text-align: left;
}

    .tw-page .tw-modal-warn .modal-header {
        background: var(--tw-warn-bg) !important;
        border-bottom-color: var(--tw-warn-border);
    }

    .tw-page .modal-header .modal-title {
        display: inline;
        margin: 0;
        font-size: 16px;
        font-weight: 700;
        line-height: 1.3;
        color: var(--tw-text);
    }

    /* Icono de cabecera: glifo en el color del acento del diálogo */
    .tw-page .modal-header .tw-modal-icon {
        color: var(--tw-accent);
        font-size: 22px;
        line-height: 1;
    }

    .tw-page .tw-modal-danger .modal-header .tw-modal-icon { color: var(--tw-danger); }
    .tw-page .tw-modal-warn .modal-header .tw-modal-icon   { color: #7a4b00; }

    /* Icono PNG blanco del proyecto: sobre un recuadro de acento para que se vea */
    .tw-page .modal-header img {
        width: 28px;
        height: 28px;
        padding: 4px;
        border-radius: 4px;
        background: var(--tw-accent);
    }

    .tw-page .tw-modal-danger .modal-header img {
        background: var(--tw-danger);
    }

    .tw-page .modal-header .close {
        order: 2;
        margin: 0 0 0 auto;
        float: none;
        font-size: 24px;
        line-height: 1;
        color: var(--tw-text);
        opacity: .5;
    }

        .tw-page .modal-header .close:hover,
        .tw-page .modal-header .close:focus {
            opacity: .9;
        }

.tw-page .modal-body {
    margin: 0 !important;
    padding: 14px 15px !important;
}

    .tw-page .modal-body h4 {
        margin: 0 0 12px 0;
        font-size: 15px;
        font-weight: 700;
    }

    /* Dentro del modal el formulario usa tw-form / tw-field con 12 px entre campos */
    .tw-page .modal-body .tw-form {
        gap: 12px;
    }

    .tw-page .modal-body .tw-field textarea {
        min-height: 96px;
    }

/* Modal alto: el cuerpo tiene scroll interno, cabecera y pie siempre visibles */
.tw-page .tw-modal-tall .modal-content {
    max-height: calc(100vh - 20px);
    overflow: hidden;
}

.tw-page .tw-modal-tall .modal-body {
    max-height: calc(100vh - 200px);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

/* Modal ancho (grid dentro del diálogo) */
.tw-page .tw-modal-wide .modal-dialog {
    width: calc(100vw - 40px);
    max-width: calc(100vw - 40px);
}

.tw-page .modal-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 10px 16px;
    margin: 0 !important;
    padding: 10px 15px !important;
    border-top: 1px solid var(--tw-border-row) !important;
    text-align: center;
}

    /* Filas de botones (p. ej. acciones secundarias encima de Cancel/Save) */
    .tw-page .modal-footer > .tw-modal-actions,
    .tw-page .modal-footer > .text-center {
        flex: 1 1 100%;
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
        gap: 10px 16px;
        margin: 0 !important;
    }

    .tw-page .modal-footer .btn,
    .tw-page .modal-footer .tw-btn {
        display: inline-flex !important;
        flex-direction: row !important;
        align-items: center;
        justify-content: center;
        gap: 8px;
        width: auto;
        height: 44px !important;
        min-width: 130px;
        margin: 0 !important;
        padding: 0 14px;
        font-size: 13px;
        font-weight: 600;
        line-height: 1 !important;
    }

/* Tarjetas planas para listas de elección dentro de un modal (lotes, opciones) */
.tw-card-list {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px;
}

.tw-card {
    flex: 1 1 240px;
    max-width: 360px;
    padding: 10px 12px;
    border: 1px solid var(--tw-border);
    border-radius: 4px;
    background: var(--tw-bg);
}

    .tw-card-title {
        margin: 0 0 6px 0;
        font-size: 14px;
        font-weight: 700;
        color: var(--tw-text);
    }

    .tw-card p {
        margin: 0 0 2px 0;
        font-size: 13px;
        color: var(--tw-text-2);
    }

/* -------------------------------------------------------------------------------------
   12. Modo compacto: pantallas anchas pero bajas (iPad apaisado 1024 × 768, portátiles 1366 × 768)
   El formulario entero debe caber sin scroll. No afecta al escritorio normal (alto ≥ 821 px).
   ------------------------------------------------------------------------------------- */
@media (min-width: 768px) and (max-height: 820px) {
    .tw-page {
        /* usa la variable para que una pagina pueda reducir la reserva (§8.1) */
        padding-bottom: min(80px, var(--tw-footer-h));
    }

    .tw-form {
        gap: 8px;
    }

    .tw-row {
        gap: 8px 20px;
    }

    .tw-field .form-control,
    .tw-field input[type="text"],
    .tw-field input[type="password"],
    .tw-field input[type="email"],
    .tw-field input[type="tel"],
    .tw-field input[type="number"],
    .tw-field input[type="date"],
    .tw-field input[type="time"],
    .tw-field input[type="datetime-local"],
    .tw-field select {
        height: 36px;
        font-size: 14px;
    }

    .tw-field textarea {
        min-height: 66px;
        height: 66px;
    }

    .tw-icon-btn {
        flex-basis: 32px;
        width: 32px;
        height: 32px;
        font-size: 17px;
    }

    .tw-btn-aux {
        height: 36px;
    }

    .tw-label-row {
        margin-bottom: 2px;
    }

    .tw-help,
    .tw-error {
        margin-top: 2px;
    }

    .tw-block {
        padding-top: 6px;
    }

    .tw-block-title {
        margin-bottom: 4px;
    }

    .tw-block-actions {
        margin-top: 8px;
    }

    .tw-btn-action {
        height: 40px;
    }

    .tw-check {
        min-height: 24px;
    }

    .tw-summary,
    .tw-context {
        margin-bottom: 6px;
    }

    .tw-footer {
        padding: 6px 0 8px 0;
        padding-bottom: max(18px, calc(8px + env(safe-area-inset-bottom, 0px))); /* area segura (§11) */
    }

    .tw-btn-foot {
        height: 44px;
    }

        /* Aquí el icono NO se toca: con el rótulo y el contador en su pila, las dos líneas
           miden 24px y el icono de 30 sigue siendo el elemento más alto del botón, así que
           se centra solo y conserva el mismo tamaño que en los demás botones del pie. */
}

/* -------------------------------------------------------------------------------------
   13. Móvil (< 768 px)
   ------------------------------------------------------------------------------------- */
@media (max-width: 767px) {
    /* la banda de cabecera tiene que sacar tambien el padding lateral de .tw-page */
    :root {
        --tw-head-pull: 16px;
        --tw-head-pad: 8px;
    }

    .tw-page {
        padding-left: 8px;
        padding-right: 8px;
        padding-bottom: min(80px, var(--tw-footer-h));
    }

    .tw-page-title {
        font-size: 16px;
    }

    .tw-page-head .tw-context {
        font-size: 14px;
    }

    .tw-filter {
        padding: 10px 10px 8px 10px;
    }

    /* Todo apilado al 100 %, en el mismo orden */
    .tw-field,
    .tw-field-short,
    .tw-field-date,
    .tw-field-long,
    .tw-field-full,
    .tw-group,
    .tw-field-action {
        flex: 1 1 100%;
        min-width: 0;
        max-width: none;
    }

    .tw-field-num {
        flex: 1 1 150px; /* dos importes caben en 390 px; si no, se apilan solos */
        min-width: 0;
    }

    .tw-field .form-control,
    .tw-field input[type="text"],
    .tw-field input[type="password"],
    .tw-field input[type="email"],
    .tw-field input[type="tel"],
    .tw-field input[type="number"],
    .tw-field input[type="date"],
    .tw-field input[type="time"],
    .tw-field input[type="datetime-local"],
    .tw-field select {
        height: var(--tw-control-h-mobile);
        font-size: 16px; /* evita el zoom automático de iOS al enfocar */
    }

    .tw-field textarea {
        min-height: 120px;
    }

    .tw-icon-btn {
        flex-basis: 44px;
        width: 44px;
        height: 44px;
    }

    .tw-btn-aux {
        height: 44px;
    }

    .tw-option input[type="radio"],
    .tw-option input[type="checkbox"] {
        width: 20px;
        height: 20px;
    }

    .tw-field-action {
        margin-top: 2px;
    }

    .tw-btn-filter {
        flex: 1 1 auto;
        height: 44px;
    }

    .tw-btn-clear {
        flex: 0 0 54px;
        width: 54px;
        height: 44px;
    }

    .tw-filter-toggle-btn {
        width: 40px;
        height: 40px;
    }

    .tw-btn {
        height: var(--tw-control-h-mobile);
    }

    .tw-btn-action {
        width: 100%;
        min-width: 0;
    }

    /* Pie: 46 × 44, solo icono. Los 46 salen de la pantalla más exigente: 7 botones a 46 con
       6px de hueco son 358, que entran en los 375 de un móvil normal. Se pone aquí y no en cada
       página para que un pie no se vea más grande que otro al cambiar de pantalla. */
    .tw-footer-inner {
        gap: 16px;
    }

    .tw-btn-foot {
        width: 46px;
        min-width: 0; /* si no, gana el min-width:130px de .tw-btn y el pie se parte en varias filas */
        height: 44px;
        padding: 0;
    }

        .tw-btn-foot .tw-btn-text {
            display: none;
        }

        /* En móvil el icono se queda en 30: el botón mide 46x44 y no admite los 34 de tablet
           y escritorio. */
        .tw-btn-foot .tw-btn-icon {
            font-size: 30px;
        }

        /* Sin rótulo, el contador se queda al lado del icono. El icono NO se toca: mantiene los
           30px del resto de botones del pie, que es lo que hace que el pie se lea como una fila
           y no como botones sueltos. Lo que cede es el hueco entre los dos, de 10 a 2px, que es
           lo justo para que quepan en los 46px de ancho. El :has() acota el ajuste a los botones
           con contador; sin él habría que marcarlos con una clase en el marcado, y la clase de
           estos botones la reescribe el servidor. */
        .tw-btn-foot .tw-btn-count {
            font-size: 11px;
        }

        /* Sin rótulo que repartir, la pila NO debe encogerse. Con el flex: 1 1 auto y el
           min-width: 0 que lleva para escritorio, aquí se quedaba en 3px de ancho y el contador
           se partía en dos líneas ("(3" arriba y ")" abajo), que era lo que obligaba a bajar el
           icono a 24px para hacerle sitio. Fijándola a su contenido cabe con el icono a 30, el
           mismo que el resto de botones del pie. */
        .tw-btn-foot .tw-btn-stack {
            flex: 0 0 auto;
            overflow-wrap: normal;
        }

        /* Y el contador se mete 8px dentro de la caja del icono para recuperar el hueco muerto:
           ningún glifo llega a sus bordes y el paréntesis trae su propio margen de letra, así
           que entre tinta y tinta siguen quedando unos 2px. Sin esto el número asoma por el
           borde derecho del botón. */
        .tw-btn-foot:has(.tw-btn-count) .tw-btn-count {
            margin-left: -8px;
        }

        /* Y el clip, 5px más. Al girarlo, su caja pasa a ser el CONTORNO del dibujo girado, que
           es más ancho que el dibujo: mide 2px de hueco pero se ven 6. Este ajuste recupera esa
           diferencia. No vale para los demás iconos, que sí llenan su caja. */
        .tw-btn-foot .tw-btn-icon.glyphicon-paperclip + .tw-btn-stack {
            margin-left: -5px;
        }

        /* El ojo, 2. Aquí el margen es menor porque este glifo sí llena su caja: lo que se
           recupera es sólo el margen de letra del paréntesis, no un contorno girado. */
        .tw-btn-foot .tw-btn-icon.glyphicon-eye-open + .tw-btn-stack {
            margin-left: -2px;
        }

    .tw-results-right {
        flex: 1 1 100%;
        margin-left: 0;
    }

    .tw-pagesize select,
    .tw-pagesize .form-control {
        height: 36px;
    }

    /* Grid con scroll horizontal (variantes hscroll/scroll): letra 12 px */
    .tw-grid-hscroll .tw-grid td,
    .tw-grid-scroll .tw-grid td {
        padding: 6px 8px;
        font-size: 12px;
    }

    .tw-grid-scroll,
    .tw-grid-hscroll {
        --tw-pager-visible-w: calc(100vw - 18px);
    }

    .tw-grid-scroll {
        max-height: none;
        overflow-x: auto;
    }

        .tw-grid-scroll .tw-grid,
        .tw-grid-hscroll .tw-grid {
            width: max-content;
            min-width: 100%;
        }

    /* Grid como bloques (tw-grid-blocks): cabecera oculta visualmente, cada fila un bloque.
       Etiquetar con data-label solo los datos que no se reconocen por su formato.
       Poner roles ARIA (table/row/columnheader/cell) en el marcado. */
    .tw-grid-blocks thead,
    .tw-grid-blocks th,
    .tw-grid-blocks tbody tr:has(th) {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        padding: 0;
        margin: 0;
        border: 0;
    }

    .tw-grid-blocks,
    .tw-grid-blocks tbody {
        display: block;
        width: 100%;
    }

        .tw-grid-blocks tbody tr {
            display: flex;
            flex-wrap: wrap;
            align-items: baseline;
            gap: 2px 12px;
            padding: 9px 10px 10px 10px;
            border-top: 1px solid var(--tw-border);
        }

            .tw-grid-blocks tbody tr:first-child {
                border-top: 0;
            }

            .tw-grid-blocks tbody tr:nth-child(even) {
                background: var(--tw-bg-zebra);
            }

    .tw-grid-blocks td {
        display: block;
        width: auto !important;
        padding: 0;
        border: 0;
        order: 2; /* por debajo de tw-col-primary (1) y por encima de tw-col-actions (3) */
    }

        .tw-grid-blocks td.tw-col-primary {
            order: 1;
            flex: 1 1 100%;
            font-size: 15px;
            font-weight: 600;
        }

        .tw-grid-blocks td.tw-col-long {
            flex: 1 1 100%;
            min-width: 0;
        }

        .tw-grid-blocks td[data-label]::before {
            content: attr(data-label);
            display: block;
            font-size: 11px;
            font-weight: 600;
            letter-spacing: .04em;
            text-transform: uppercase;
            color: var(--tw-text-3);
        }

        .tw-grid-blocks td.tw-col-actions {
            order: 3;
            flex: 0 0 auto;
            margin-left: auto;
        }

        .tw-grid-blocks td a.tw-link {
            display: inline-block;
            min-height: 44px;
            line-height: 44px;
        }

    .tw-grid-blocks tr.GridPager,
    .tw-grid-blocks tr.tw-pager-row {
        display: block;
        padding: 6px 10px;
    }

        .tw-grid-blocks tr.GridPager > td,
        .tw-grid-blocks tr.tw-pager-row > td {
            display: block;
            width: 100% !important;
        }

    .tw-grid-blocks .tw-empty-row {
        display: block;
        padding: 0;
    }

    .tw-row-btn {
        width: 44px;
        height: 44px;
        font-size: 18px;
    }

    .tw-grid .GridPager a,
    .tw-grid .GridPager span,
    .tw-pager a,
    .tw-pager span {
        min-width: 44px;
        height: 44px;
    }

    /* Modales al ancho de la pantalla, botones repartidos */
    .tw-page .modal.in .modal-dialog {
        width: calc(100% - 20px);
        max-width: calc(100% - 20px);
    }

    .tw-page .tw-modal-wide .modal-dialog {
        width: calc(100vw - 20px);
        max-width: calc(100vw - 20px);
    }

    .tw-page .tw-modal-tall .modal-body {
        max-height: calc(100vh - 190px);
    }

    .tw-page .modal-footer .btn,
    .tw-page .modal-footer .tw-btn {
        min-width: 0;
        flex: 1 1 0;
    }
}

/* Campo deshabilitado: el mismo color con el ratón encima. Estilos.css trae
   input.form-control:disabled:hover { background-color: #e9e9e9 }, que en hover gana al
   aliceblue del kit (más especificidad) y el campo cambiaba a gris al pasar por encima,
   como si reaccionara. Con la misma especificidad y cargado después, gana esto. */
.tw-field input:disabled:hover,
.tw-field select:disabled:hover,
.tw-field textarea:disabled:hover {
    background-color: var(--tw-bg-disabled);
}
