/* Lo minimo: MudBlazor pone el resto. Mismo fichero que
   frontend-blazor/Tentalo.POS.ServerStatus/wwwroot/css/app.css mas la espera del guardia.

   LAS CLASES `.arranque` Y `.loading-*` LAS COMPARTEN DOS PANTALLAS
   =================================================================
   wwwroot/index.html (la pantalla de arranque del WASM, que pinta el navegador antes de que haya
   aplicacion) y Components/Guardia.razor (la espera de GET /api/auth/me, que ya pinta Blazor).
   Estan aqui y no en el <head> de index.html precisamente para eso: la parte 1 de
   docs/central/esperas.md pedia que el salto de una a otra no se notara, y la unica forma de que
   no se note es que sean el MISMO lienzo. Tocar un color o un tamano de aqui cambia las dos. */
html, body {
    margin: 0;
    padding: 0;
    font-family: "Segoe UI", "Segoe UI Variable", Tahoma, Verdana, sans-serif;
}

/* Pantalla de arranque, mientras baja el runtime de WebAssembly. */
.arranque {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100vh;
    color: #5a5a5a;
    text-align: center;
}

.arranque img {
    width: 240px;
    max-width: 60vw;
    margin-bottom: 1.5rem;
}

.loading-progress {
    position: relative;
    display: block;
    width: 5rem;
    height: 5rem;
    margin: 1rem auto;
}

.loading-progress circle {
    fill: none;
    stroke: #e6dced;
    stroke-width: 0.5rem;
    transform-origin: 50% 50%;
    transform: rotate(-90deg);
}

.loading-progress circle:last-child {
    stroke: #672990;
    stroke-dasharray: calc(3.141592653589793 * var(--blazor-load-percentage, 0%) * 0.8) 500%;
    transition: stroke-dasharray 0.05s ease-in-out;
}

.loading-progress-text {
    font-weight: 600;
    color: #672990;
}

.loading-progress-text:after {
    content: var(--blazor-load-percentage-text, "Cargando");
}

/* El anillo del guardia: el mismo dibujo, pero girando en vez de contando. Con la aplicacion ya
   arrancada no hay porcentaje que ensenar -el WASM esta bajado- y lo que hay que decir es «sigue
   pasando algo». */
.loading-progress-indeterminado circle:last-child {
    stroke-dasharray: 70% 500%;
    transition: none;
    animation: arranque-giro 1.1s linear infinite;
}

@keyframes arranque-giro {
    from { transform: rotate(-90deg); }
    to { transform: rotate(270deg); }
}

.arranque-texto {
    margin: 0;
}

/* Regla 7 de docs/central/esperas.md: nada late para siempre, y quien haya pedido menos
   movimiento no ve ninguno (WCAG 2.3.3). El anillo se queda quieto y sigue diciendo lo mismo. */
@media (prefers-reduced-motion: reduce) {
    .loading-progress-indeterminado circle:last-child {
        animation: none;
    }
}

/* --- La barra de actividad global -------------------------------------- */
/* Regla 1 de docs/central/esperas.md: toda peticion en vuelo se ve. Tres pixeles bajo la barra
   superior, encendida mientras haya cualquier llamada al bus o al API en curso.

   Los 48 px son la altura de `MudAppBar Dense="true"`, que es la que usan los DOS marcos
   (AdminLayout y MainLayout). No se usa `var(--mud-appbar-height)` porque MudBlazor deja esa
   variable en los 64 px del tema aunque la barra sea densa, y la raya quedaria flotando.

   `pointer-events: none` porque la raya cruza la pantalla entera y no debe robar ni un clic. */
.admin-actividad {
    position: fixed;
    top: 48px;
    left: 0;
    right: 0;
    z-index: 1305;
    pointer-events: none;
}

.admin-actividad-barra {
    height: 3px;
}

/* La linea de la regla 4: pasados 4 s, la espera se explica. Va pegada a la raya, centrada y
   discreta: es informacion, no una alarma. */
.admin-actividad-aviso {
    margin: 0 auto;
    padding: 0.15rem 0.75rem 0.2rem;
    width: fit-content;
    max-width: 92vw;
    background: #ffffff;
    border: 1px solid #e6dced;
    border-top: none;
    border-radius: 0 0 6px 6px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    color: #5f5f5f;
    font-size: 0.78rem;
    text-align: center;
}

/* La misma linea, pero debajo del esqueleto que esta esperando (PanelWidget). */
.admin-espera-larga {
    color: #5f5f5f;
    font-size: 0.78rem;
    margin-top: 0.5rem;
}

/* Mientras se descarga el ensamblado de un modulo diferido (App.razor, plantilla Navigating).
   Aparece a los 400 ms: en red local la descarga dura decimas y un cartel que aparece y
   desaparece molesta mas que la propia espera. */
.admin-descarga {
    color: #5f5f5f;
    font-size: 0.85rem;
    margin-top: 0.75rem;
}

#blazor-error-ui {
    color: #2b2b2b;
    background: #ffe9c4;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1500;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
}

/* ==========================================================================
   PANTALLAS PUBLICAS DE PANTALLA PARTIDA  (docs\central\diseno-web.md)
   --------------------------------------------------------------------------
   El patron de la pagina de entrada: panel de marca oscuro a la izquierda y
   formulario en blanco a la derecha. Lo montan Layout\AccesoLayout.razor,
   Components\PanelMarca.razor, Components\Constelacion.razor y
   Pages\Acceso.razor.

   Va aqui, en el CSS global, y no en un fichero .razor.css con aislamiento:
   el aislamiento obliga a enlazar Tentalo.Central.Web.styles.css desde
   index.html, y ese enlace lo escribe el mecanismo de marcadores del SDK que
   en el modelo hosted esta apagado a proposito (ver el .csproj). Los nombres
   llevan prefijo (acceso-, panel-marca-, constelacion-) para que hagan de
   ambito por si solos.

   Cortes responsivos, y son los tres que se prueban con el navegador:
     >= 1024 px   dos columnas 50/50
     768-1023 px  dos columnas 40/60
     <  768 px    una columna: el panel pasa a cabecera compacta arriba
   ========================================================================== */

:root {
    /* Tinta de marca (docs\branding.md), la misma que el tema de App.razor. */
    --tentalo-marca: #672990;
    /* Los dos extremos del degradado del panel: morado casi negro -> marca. */
    --tentalo-oscuro: #0e0613;
    --tentalo-oscuro-medio: #230c33;
    /* Paso intermedio: la marca rebajada, para que el degradado no salte. */
    --tentalo-marca-hondo: #45195f;
    /* Acento claro: la palabra resaltada del titular y los checks. */
    --tentalo-acento: #d3a5f0;
    --tentalo-tinta: #2b2b2b;
    --tentalo-tinta-suave: #5f5f5f;
    --tentalo-borde: #ded7e4;
    /* Alto minimo de campos y botones: el pulgar en un movil (WCAG 2.5.5). */
    --tentalo-toque: 48px;
}

/* --- El marco: AccesoLayout, sin barra superior ------------------------- */

.marco-publico {
    min-height: 100vh;
    /* dvh donde exista: en un movil la barra del navegador se come los vh. */
    min-height: 100dvh;
}

/* --- La rejilla de la pantalla partida ---------------------------------- */

.acceso {
    display: grid;
    grid-template-columns: 1fr 1fr;
    min-height: 100vh;
    min-height: 100dvh;
    background: #fff;
    /* Ni una barra horizontal en ningun ancho: la constelacion se recorta. */
    overflow-x: hidden;
}

/* --- Panel de marca ----------------------------------------------------- */

.panel-marca {
    position: relative;
    display: flex;
    overflow: hidden;
    color: #fff;
    /* El brillo radial va PRIMERO: en background las capas se pintan de la
       primera a la ultima de arriba a abajo. Discreto (alfa 0.2) y pegado a
       la esquina: con mas alfa el panel deja de leerse oscuro. */
    background:
        radial-gradient(circle at 14% 8%, rgba(196, 128, 246, 0.2), transparent 52%),
        linear-gradient(155deg,
            var(--tentalo-oscuro) 0%,
            var(--tentalo-oscuro-medio) 38%,
            var(--tentalo-marca-hondo) 74%,
            var(--tentalo-marca) 100%);
}

.panel-marca-contenido {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    width: 100%;
    padding: clamp(2rem, 3.4vw, 3.75rem);
    gap: 2rem;
}

.panel-marca-cabecera {
    display: flex;
    align-items: center;
    gap: 0.9rem;
}

.panel-marca-logo {
    /* El logo original es 360x112; aqui va pequeno y con la altura mandando. */
    height: 34px;
    width: auto;
    /* El logo es trazo MORADO sobre transparente (docs\branding.md): sobre el
       panel oscuro no se ve. brightness(0) lo pasa a negro conservando el
       alfa y invert(1) lo deja en blanco, asi que sale la misma silueta en
       blanco sin necesidad de un segundo fichero de marca. */
    filter: brightness(0) invert(1);
}

.panel-marca-identidad {
    display: flex;
    flex-direction: column;
    line-height: 1.25;
}

.panel-marca-nombre {
    font-size: 1.05rem;
    font-weight: 600;
    letter-spacing: 0.01em;
}

.panel-marca-subtitulo {
    font-size: 0.8rem;
    color: rgba(255, 255, 255, 0.68);
}

/* El cuerpo se centra verticalmente y empuja el pie abajo. */
.panel-marca-cuerpo {
    display: flex;
    flex-direction: column;
    gap: 1.75rem;
    margin-block: auto;
    max-width: 30rem;
}

.panel-marca-titular {
    margin: 0;
    font-size: clamp(1.85rem, 2.4vw + 0.55rem, 2.85rem);
    font-weight: 700;
    line-height: 1.14;
    letter-spacing: -0.015em;
}

.panel-marca-titular-linea,
.panel-marca-titular-resalte {
    display: block;
}

.panel-marca-titular-resalte {
    color: var(--tentalo-acento);
}

.panel-marca-parrafo {
    margin: 0;
    max-width: 26rem;
    font-size: 0.98rem;
    line-height: 1.6;
    color: rgba(255, 255, 255, 0.78);
}

.panel-marca-puntos {
    display: flex;
    flex-direction: column;
    gap: 1.15rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.panel-marca-punto {
    display: flex;
    align-items: flex-start;
    gap: 0.8rem;
}

.panel-marca-check {
    flex: 0 0 auto;
    width: 22px;
    height: 22px;
    margin-top: 0.15rem;
}

.panel-marca-check circle {
    fill: rgba(255, 255, 255, 0.12);
    stroke: var(--tentalo-acento);
    stroke-width: 1.4;
}

.panel-marca-check path {
    fill: none;
    stroke: var(--tentalo-acento);
    stroke-width: 2.2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.panel-marca-punto-texto {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.panel-marca-punto-titulo {
    font-size: 0.95rem;
    font-weight: 600;
}

.panel-marca-punto-detalle {
    font-size: 0.87rem;
    line-height: 1.45;
    color: rgba(255, 255, 255, 0.7);
}

.panel-marca-pie {
    margin: 0;
    font-size: 0.75rem;
    color: rgba(255, 255, 255, 0.5);
}

/* --- La constelacion ---------------------------------------------------- */

.constelacion {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    /* Decorativa: nunca captura un clic ni tapa el texto. */
    pointer-events: none;
}

.constelacion-lineas line {
    stroke: #fff;
    stroke-width: 0.7;
}

.constelacion-puntos circle {
    fill: #fff;
}

/* --- Columna del formulario -------------------------------------------- */

.acceso-panel {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: clamp(1.75rem, 3.4vw, 4rem);
    background: #fff;
}

.acceso-formulario {
    width: 100%;
    max-width: 25rem;
}

.acceso-bienvenido {
    margin: 0 0 0.4rem;
    font-size: clamp(1.6rem, 0.9vw + 1.35rem, 2.05rem);
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--tentalo-tinta);
}

.acceso-subtitulo {
    margin: 0 0 2.25rem;
    font-size: 0.95rem;
    line-height: 1.5;
    color: var(--tentalo-tinta-suave);
}

.acceso-campo {
    margin-bottom: 1.15rem;
}

/* Los campos de MudBlazor, con el borde mas redondo y el alto de toque. Se
   llega a sus clases desde fuera porque el CSS es global (ver la nota de
   arriba): con aislamiento haria falta ::deep. */
.acceso-campo .mud-input.mud-input-outlined {
    min-height: var(--tentalo-toque);
    border-radius: 12px;
    background: #fdfcfe;
}

.acceso-campo .mud-input.mud-input-outlined .mud-input-outlined-border {
    border-radius: 12px;
    border-color: var(--tentalo-borde);
}

.acceso-campo .mud-input.mud-input-outlined:hover .mud-input-outlined-border {
    border-color: #b79bc8;
}

.acceso-boton {
    min-height: var(--tentalo-toque);
    border-radius: 12px;
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    text-transform: none;
}

.acceso-error {
    margin-top: 1rem;
}

.acceso-nota {
    margin: 1.5rem 0 0;
    font-size: 0.76rem;
    line-height: 1.45;
    color: #8a8a8a;
}

/* Solo se ve cuando el panel de marca es la cabecera compacta del movil. */
.acceso-pie-movil {
    display: none;
    margin: 2rem 0 0;
    font-size: 0.72rem;
    color: #9a9a9a;
    text-align: center;
}

/* --- Corte 768-1023 px: dos columnas 40/60 ------------------------------ */

@media (min-width: 768px) and (max-width: 1023.98px) {
    .acceso {
        grid-template-columns: 40% 60%;
    }

    .panel-marca-contenido {
        gap: 1.5rem;
    }

    .panel-marca-cuerpo {
        gap: 1.35rem;
    }

    .panel-marca-parrafo {
        font-size: 0.92rem;
    }
}

/* --- Corte < 768 px: una columna y cabecera compacta -------------------- */

@media (max-width: 767.98px) {
    .acceso {
        grid-template-columns: 1fr;
        /* La cabecera se ajusta a su contenido; el formulario se queda el resto. */
        grid-template-rows: auto 1fr;
    }

    /* La cabecera compacta: logo, nombre y titular. Ni constelacion, ni
       subtitulo, ni parrafo, ni puntos, ni pie (el pie lo pone el formulario). */
    .panel-marca .constelacion,
    .panel-marca-subtitulo,
    .panel-marca-parrafo,
    .panel-marca-puntos,
    .panel-marca-pie {
        display: none;
    }

    .panel-marca-contenido {
        flex-direction: row;
        flex-wrap: wrap;
        align-items: center;
        gap: 0.5rem 1rem;
        padding: 1rem 1rem 1.15rem;
    }

    .panel-marca-cabecera {
        flex: 0 0 auto;
        gap: 0.7rem;
    }

    .panel-marca-logo {
        height: 28px;
    }

    .panel-marca-nombre {
        font-size: 0.98rem;
    }

    /* El titular cabe al lado del logo cuando hay sitio y baja a su propia
       linea cuando no (en 375 px no cabe, y eso es lo correcto). */
    .panel-marca-cuerpo {
        flex: 1 1 14rem;
        margin-block: 0;
        max-width: none;
    }

    .panel-marca-titular {
        font-size: 1.02rem;
        font-weight: 600;
        line-height: 1.35;
        letter-spacing: 0;
    }

    .panel-marca-titular-linea,
    .panel-marca-titular-resalte {
        display: inline;
    }

    /* Los dos trozos del titular pasan a ir en linea, y entre ellos hace falta
       el espacio que el compilador de Razor se come del salto de linea del
       marcado. white-space: pre para que no lo colapse el navegador; sigue
       habiendo sitio para partir la linea ahi si no cabe. */
    .panel-marca-titular-resalte::before {
        content: " ";
        white-space: pre;
    }

    .acceso-panel {
        /* Margenes de 16 px a los lados, que es lo que pide el diseno. */
        padding: 2.25rem 16px 2rem;
        justify-content: flex-start;
    }

    .acceso-subtitulo {
        margin-bottom: 1.75rem;
    }

    .acceso-pie-movil {
        display: block;
    }
}

/* ==========================================================================
   BARRA DEL ADMINISTRADOR EN PANTALLAS ESTRECHAS
   --------------------------------------------------------------------------
   No es un rediseno de Layout\AdminLayout.razor: es la correccion del unico
   desborde que tenia en 375 px. El nombre de la instalacion se partia en dos
   lineas y empujaba el boton de cambiar de instalacion y el menu de usuario
   -con "Salir" dentro- fuera de la pantalla, donde no habia forma de
   alcanzarlos porque la barra recorta lo que le sobra.

   La regla del sistema de diseno esta en docs\central\diseno-web.md: toda
   pantalla se prueba en 375, 768 y 1280 px antes de darse por hecha.
   ========================================================================== */

.admin-barra-nombre {
    /* min-width: 0 es la pieza que falta en un hijo de flex: sin ella el
       texto no se deja recortar y el contenedor crece. */
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.admin-barra-cambiar {
    flex: 0 0 auto;
    /* 48 px tambien por encima de 600 px: la regla 3 del sistema de diseno no
       depende del ancho, y este boton se quedaba en 37 px en escritorio
       (medido en 768 px al cerrar el modulo 1 de la fase C3). */
    min-height: var(--tentalo-toque);
}

/* Con el texto oculto queda un boton con solo el icono: sin el relleno de un
   boton con etiqueta y con el alto minimo de toque. */
@media (max-width: 599.98px) {
    .admin-barra-cambiar {
        min-width: var(--tentalo-toque);
        min-height: var(--tentalo-toque);
        padding-left: 0.5rem;
        padding-right: 0.5rem;
    }

    .admin-barra-cambiar .mud-button-icon-start {
        margin-right: 0;
        margin-left: 0;
    }
}

/* ==========================================================================
   EL DASHBOARD Y LAS PIEZAS COMUNES DE LOS MODULOS (fase C3, modulo 1)
   --------------------------------------------------------------------------
   Aqui esta lo que el apartado 7 de docs\central\diseno-web.md dejaba por
   decidir: tarjetas de cifra, cabecera de pagina, paneles con sus cuatro
   estados, tablas densas, insignias y el grafico.

   Sin CSS aislado (.razor.css) a proposito -el aislamiento necesita el
   paquete de estilos del SDK, que en el modelo hosted esta apagado-, asi que
   todo lleva PREFIJO admin-. Es lo que hace de ambito.
   ========================================================================== */

/* --- Cabecera de pagina ------------------------------------------------- */

.admin-cabecera {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 0.75rem 1.5rem;
    margin-bottom: 1.5rem;
}

.admin-cabecera-texto {
    /* min-width: 0 para que un titulo largo se recorte dentro de su columna
       en vez de empujar las acciones fuera de la pantalla. */
    min-width: 0;
    flex: 1 1 16rem;
}

.admin-cabecera-volver {
    margin-left: -0.5rem;
    margin-bottom: 0.25rem;
    min-height: 2rem;
}

.admin-cabecera-titulo {
    margin: 0;
    font-size: clamp(1.4rem, 0.8vw + 1.2rem, 1.8rem);
    font-weight: 600;
    line-height: 1.2;
    color: var(--tentalo-tinta);
}

.admin-cabecera-subtitulo {
    margin: 0.35rem 0 0;
    font-size: 0.9rem;
    color: var(--tentalo-tinta-suave);
}

.admin-cabecera-acciones {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    /* flex-wrap y max-width: con DOS acciones -un boton y el indicador de
       refresco, que es lo que trae el modulo 6- en 375 px no caben al lado y
       la caja se salia 39 px de la pantalla, dejando el boton de recargar
       inalcanzable (regla 2 del apartado 6 del sistema de diseno). Con el
       modulo 1 no se veia porque sus ocho pantallas solo llevaban el
       indicador. */
    flex: 0 1 auto;
    flex-wrap: wrap;
    max-width: 100%;
}

/* --- Indicador de tiempo real ------------------------------------------- */

.admin-refresco {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.8rem;
    color: var(--tentalo-tinta-suave);
}

.admin-refresco-punto {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background: #2e9e5b;
}

/* El latido dice "se esta actualizando" sin gastar una palabra, y SOLO mientras se actualiza:
   es la regla 7 de docs/central/esperas.md (nada late para siempre) y la fea 11 del informe de
   C:\tmp\spinners, donde el punto de «Actualizado a las 18:07» seguia latiendo indefinidamente
   diciendo que pasaba algo cuando no pasaba nada. Quieto, dice lo que de verdad hay: los datos
   son de esa hora. Y se respeta a quien haya pedido menos movimiento (WCAG 2.3.3). */
.admin-refresco-punto-vivo {
    animation: admin-latido 2s ease-in-out infinite;
}

@keyframes admin-latido {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.35; }
}

@media (prefers-reduced-motion: reduce) {
    .admin-refresco-punto-vivo {
        animation: none;
    }
}

/* --- Tarjetas de cifra --------------------------------------------------- */

.admin-cifras {
    display: grid;
    gap: 1rem;
    margin-bottom: 1.25rem;
    /* auto-fit + minmax es lo que evita una media query por numero de
       tarjetas: cuando no caben cuatro de 11 rem, caben dos, y cuando no
       caben dos, una. Los modificadores de abajo solo ajustan el minimo
       segun cuantas tarjetas trae la pantalla. */
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
}

.admin-cifras-3 {
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}

.admin-cifras-5 {
    grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
}

.admin-cifra {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 1rem 1.1rem 1.1rem;
    border-radius: 10px;
}

.admin-cifra-etiqueta {
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--tentalo-tinta-suave);
}

.admin-cifra-valor {
    font-size: clamp(1.35rem, 1vw + 1.1rem, 1.75rem);
    font-weight: 600;
    line-height: 1.25;
    color: var(--tentalo-tinta);
    /* Tabular para que una cifra que cambia cada 60 s no mueva la tarjeta. */
    font-variant-numeric: tabular-nums;
}

.admin-cifra-destacada .admin-cifra-valor {
    color: var(--tentalo-marca);
    font-weight: 700;
}

.admin-cifra-detalle {
    font-size: 0.78rem;
    color: var(--tentalo-tinta-suave);
}

.admin-cifra-extra {
    margin-top: 0.6rem;
}

/* --- Paneles ------------------------------------------------------------- */

.admin-panel {
    border-radius: 10px;
    margin-bottom: 1.25rem;
    overflow: hidden;
}

.admin-panel-cabecera {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.9rem 1.1rem;
    border-bottom: 1px solid var(--tentalo-borde);
}

.admin-panel-titulo {
    margin: 0;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--tentalo-tinta);
}

.admin-panel-cuerpo {
    padding: 1.1rem;
}

.admin-panel-error {
    margin: 0;
}

.admin-panel-vacio {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.6rem;
    padding: 2.25rem 1rem;
    text-align: center;
    color: var(--tentalo-tinta-suave);
}

/* Una tabla ancha hace scroll DENTRO de su panel, nunca en la pagina: es la
   regla de docs\central\diseno-web.md. Por debajo de Breakpoint.Sm, MudTable
   apila cada fila en una tarjeta con sus DataLabel y ya no hace falta. */
.admin-tabla {
    overflow-x: auto;
}

.admin-tabla .mud-table-cell {
    font-variant-numeric: tabular-nums;
}

/* --- Rejilla de tarjetas (locales, centros de venta, almacenes) ---------- */

.admin-rejilla-tarjetas {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

.admin-tarjeta-enlace {
    text-decoration: none;
    color: inherit;
    display: block;
    border-radius: 10px;
}

.admin-tarjeta-enlace:focus-visible {
    outline: 2px solid var(--tentalo-marca);
    outline-offset: 2px;
}

.admin-tarjeta {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    height: 100%;
    padding: 1rem 1.1rem 1.1rem;
    border-radius: 10px;
    transition: box-shadow 0.15s ease-in-out;
}

.admin-tarjeta-enlace:hover .admin-tarjeta {
    box-shadow: 0 2px 10px rgba(103, 41, 144, 0.18);
}

.admin-tarjeta-titulo {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--tentalo-tinta);
}

.admin-tarjeta-cifra {
    font-size: 1.7rem;
    font-weight: 700;
    line-height: 1.15;
    color: var(--tentalo-marca);
    font-variant-numeric: tabular-nums;
}

.admin-tarjeta-etiqueta {
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--tentalo-tinta-suave);
}

.admin-tarjeta-datos {
    margin-top: 0.4rem;
}

.admin-tarjeta-mas {
    margin-top: auto;
    padding-top: 0.6rem;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--tentalo-marca);
}

/* --- Dos columnas que se apilan ----------------------------------------- */

.admin-dos-columnas {
    display: grid;
    gap: 1.25rem;
    grid-template-columns: 1fr;
}

@media (min-width: 1024px) {
    .admin-dos-columnas {
        grid-template-columns: 1fr 1fr;
    }
}

/* --- Barra de ocupacion -------------------------------------------------- */

.admin-ocupacion {
    height: 6px;
    border-radius: 3px;
    background: #ece7f1;
    overflow: hidden;
}

.admin-ocupacion-relleno {
    height: 100%;
    border-radius: 3px;
    transition: width 0.3s ease-in-out;
}

/* Los tres tramos del administrador React: lleno es BUENO en ocupacion. */
.admin-ocupacion-alta { background: #2e9e5b; }
.admin-ocupacion-media { background: #d99423; }
.admin-ocupacion-baja { background: #c1443c; }

@media (prefers-reduced-motion: reduce) {
    .admin-ocupacion-relleno {
        transition: none;
    }
}

/* --- Insignias de las tablas de almacen --------------------------------- */

.admin-insignia {
    display: inline-block;
    min-width: 3rem;
    padding: 0.2rem 0.45rem;
    border-radius: 6px;
    font-size: 0.8rem;
    font-weight: 700;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.admin-insignia-roja { background: #fbe6e4; color: #8e2b25; }
.admin-insignia-ambar { background: #fdf1dc; color: #7a5410; }
.admin-insignia-verde { background: #e4f4ea; color: #1d6b3c; }
.admin-insignia-neutra { background: #f0ecf3; color: var(--tentalo-tinta-suave); }

.admin-lista-principal {
    font-weight: 600;
    font-size: 0.88rem;
}

.admin-lista-secundario {
    font-size: 0.78rem;
    color: var(--tentalo-tinta-suave);
}

/* --- Fichas de usuario activo ------------------------------------------- */

.admin-usuarios {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

.admin-usuario {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.35rem;
    padding: 1rem;
    border: 1px solid var(--tentalo-borde);
    border-radius: 10px;
    text-align: center;
}

.admin-usuario-avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 3.25rem;
    height: 3.25rem;
    border-radius: 50%;
    color: #fff;
    font-size: 1.35rem;
    font-weight: 700;
}

.admin-usuario-datos {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}

.admin-usuario-nombre {
    font-weight: 600;
    font-size: 0.95rem;
}

.admin-usuario-perfil {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--tentalo-tinta-suave);
}

.admin-usuario-actividad {
    font-size: 0.75rem;
    color: var(--tentalo-tinta-suave);
}

.admin-usuario-cifras {
    width: 100%;
    margin-top: 0.5rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--tentalo-borde);
}

/* --- El grafico de ventas por horas ------------------------------------- */

.admin-grafico-svg {
    display: block;
    width: 100%;
    height: auto;
}

.admin-grafico-rejilla {
    stroke: var(--tentalo-borde);
    stroke-width: 1;
}

.admin-grafico-eje {
    stroke: #c6bbcf;
    stroke-width: 1;
}

.admin-grafico-escala,
.admin-grafico-hora {
    font-size: 11px;
    fill: var(--tentalo-tinta-suave);
}

.admin-grafico-barra {
    fill: var(--tentalo-marca);
}

.admin-grafico-linea {
    fill: none;
    stroke: #b48ccb;
    stroke-width: 2;
    stroke-dasharray: 4 3;
    stroke-linejoin: round;
}

.admin-grafico-leyenda {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem 1rem;
    margin-top: 0.75rem;
    font-size: 0.8rem;
    color: var(--tentalo-tinta-suave);
}

.admin-grafico-clave {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
}

.admin-grafico-muestra-barra,
.admin-grafico-muestra-linea {
    display: inline-block;
    width: 0.85rem;
}

.admin-grafico-muestra-barra {
    height: 0.7rem;
    border-radius: 2px;
    background: var(--tentalo-marca);
}

.admin-grafico-muestra-linea {
    height: 0;
    border-top: 2px dashed #b48ccb;
}

.admin-grafico-total {
    margin-left: auto;
    font-weight: 600;
}

/* --- El menu lateral: secciones y estrellas ----------------------------- */

.admin-menu-seccion {
    display: block;
    margin-top: 0.25rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-size: 0.7rem;
}

/* La estrella va AL LADO del enlace, no dentro: dentro seria un boton dentro
   de un enlace (marcado invalido) y pulsarla navegaria. */
.admin-menu-fila {
    display: flex;
    align-items: center;
}

.admin-menu-fila > .mud-nav-item {
    flex: 1 1 auto;
    min-width: 0;
}

.admin-menu-estrella {
    flex: 0 0 auto;
    margin-right: 0.35rem;
}

/* Regla 2 de docs/central/esperas.md: la respuesta a una pulsacion es inmediata. El destino de
   una navegacion tarda entre 0,26 s y 3 s en pintarse (fea 6 del informe) y lo unico que el
   navegador da gratis es el `:hover`; sin esto, pulsar un enlace del menu no se ve, y lo que hace
   la gente entonces es volver a pulsar. Hundir el enlace cuesta cero milisegundos porque lo pinta
   el compositor, no Blazor.

   Quien marca el destino DESPUES es el propio `NavLink`, que se suscribe a
   `NavigationManager.LocationChanged` y se repinta con `active` en cuanto la URL cambia -antes de
   que la pantalla llegue, que es justo lo que hace falta-. Lo comprueba
   `EsperasTests.El_enlace_del_menu_se_marca_al_cambiar_la_ruta`. */
.mud-navmenu a.mud-nav-link:active {
    transform: scale(0.98);
    background-color: rgba(103, 41, 144, 0.12);
}

@media (prefers-reduced-motion: reduce) {
    .mud-navmenu a.mud-nav-link:active {
        transform: none;
    }
}

/* El esqueleto de la seccion FAVORITOS. Reserva el alto de las N filas que se esperan (N = el
   ultimo recuento, guardado en localStorage) para que el menu no salte cuando lleguen del bus:
   era la fea 7 del informe, 330 px de empujon a un menu que ya se estaba leyendo. */
.admin-menu-esqueleto {
    padding: 0 1rem;
}

.admin-menu-esqueleto .mud-skeleton {
    height: var(--tentalo-toque);
    margin: 0;
}

/* --- Altura minima de toque en el marco del administrador --------------- */
/* La regla 3 de docs\central\diseno-web.md pide 48 px de alto minimo en
   campos y botones, y se escribio para la pantalla de acceso. En el
   administrador hay tres controles que por omision de MudBlazor se quedan
   cortos y que son de los que MAS se pulsan: los enlaces del menu lateral
   (41 px), los botones de icono pequenos -actualizar y la estrella de
   favoritos- (26 px) y el boton pequeno de «Volver» (37 px). Medido con el
   navegador en 768 px antes de esta regla.

   No se sube el tamano del icono, solo la caja: el area de toque crece y el
   dibujo se queda igual, que es lo que pide WCAG 2.5.5. */
.mud-navmenu .mud-nav-link,
.mud-navmenu .mud-nav-link-disabled {
    min-height: var(--tentalo-toque);
}

.admin-menu-estrella,
.admin-refresco .mud-icon-button {
    min-width: var(--tentalo-toque);
    min-height: var(--tentalo-toque);
}

.admin-cabecera-volver {
    min-height: var(--tentalo-toque);
}

/* El chip «próximamente» de un modulo sin pantalla se salia del cajon de 240 px
   con las etiquetas largas («Comunicaciones»): se recortaba a media palabra.
   Con envoltura, la etiqueta y el chip caen en dos lineas, que es lo correcto
   en un cajon estrecho. Medido en 1280 px al cerrar el modulo 1 de la C3. */
.mud-navmenu .mud-nav-link .d-inline-flex {
    flex-wrap: wrap;
    row-gap: 0.15rem;
}

/* --- Comunicaciones (modulo 6 de la fase C3) ---------------------------- */
/* El semaforo de un local: punto de color MAS su palabra. El color solo no
   vale -WCAG 1.4.1, y esta pantalla se recorre buscando el rojo-, asi que
   nunca se pinta el punto sin el texto al lado. */

.admin-comms-semaforo {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-weight: 600;
    font-size: 0.85rem;
    white-space: nowrap;
}

.admin-comms-punto {
    display: inline-block;
    width: 0.6rem;
    height: 0.6rem;
    border-radius: 50%;
    background: currentColor;
    flex: 0 0 auto;
}

/* Los mismos tres colores que la barra de ocupacion del modulo 1, para que
   verde signifique lo mismo en todo el administrador. */
.admin-comms-ok { color: #1d6b3c; }
.admin-comms-aviso { color: #7a5410; }
.admin-comms-error { color: #8e2b25; }

/* El motivo va debajo del estado y en dos lineas como maximo de ancho: los
   mensajes del nucleo son frases enteras («El local lleva mas de una hora sin
   conectarse a la central.») y en una celda de tabla sin acotar empujan las
   demas columnas fuera. */
.admin-comms-motivo {
    display: block;
    font-size: 0.76rem;
    line-height: 1.35;
    color: var(--tentalo-tinta-suave);
    /* min() y no 22rem a secas: en 375 px la celda mide menos que eso y un
       max-width mayor que el hueco empuja la tabla apilada fuera de su
       contenedor. */
    max-width: min(22rem, 100%);
    margin-top: 0.15rem;
    overflow-wrap: anywhere;
}

.admin-comms-nombre {
    display: block;
    font-weight: 600;
    font-size: 0.9rem;
    color: var(--tentalo-marca);
    text-decoration: none;
}

.admin-comms-nombre:hover,
.admin-comms-nombre:focus-visible {
    text-decoration: underline;
}

/* Los dos botones de la ultima columna caen en dos lineas cuando no caben, en
   vez de recortarse: son los unicos controles de accion de la pantalla. */
.admin-comms-acciones {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    justify-content: flex-end;
}

/* «Publicar datos» partido en dos lineas dentro de la celda hacia que la fila
   creciera al doble de alto y que la columna del nombre se partiera en tres.
   Con nowrap la tabla mide 20 px mas y las siete columnas caben en 1280 px;
   por debajo del corte, MudTable apila la fila y esto deja de aplicar. */
.admin-comms-acciones .mud-button-root {
    white-space: nowrap;
}

/* El envoltorio de una tarjeta de cifra que necesita un gancho de prueba.
   Cifra.razor no captura atributos sueltos (no tiene CaptureUnmatchedValues) y
   no se le anade por esto: la rejilla .admin-cifras usa `display: grid`, asi
   que un div intermedio tiene que estirarse para que la tarjeta siga midiendo
   lo mismo que sus hermanas. */
.admin-comms-cifra {
    display: flex;
}

.admin-comms-cifra > .admin-cifra {
    flex: 1 1 auto;
}

/* «Informacion adicional»: el motivo del estado, debajo de las cifras de su
   seccion en el detalle. */
.admin-comms-adicional {
    margin-top: 1rem;
}

.admin-comms-adicional p {
    margin: 0.2rem 0 0;
    font-size: 0.88rem;
    font-weight: 600;
}

/* Altura de toque de los botones de accion (regla 3 del sistema de diseno, 48
   px). MudBlazor da 36 px a un boton normal, 31 a uno pequeno y 24 a un chip
   pulsable, y los tres aparecen en las pantallas de comunicaciones: los dos
   botones de la cabecera, los dos de cada fila de la tabla y los cuatro chips
   del filtro por estado. Medido con el navegador en 1280 px.

   Crece la caja y no el texto, igual que hizo el modulo 1 con los enlaces del
   menu y los botones de icono. La cabecera se toca por su clase compartida a
   proposito: es el sitio de las acciones de TODAS las pantallas del
   administrador, y el «Cambiar limites» de la ficha de la instalacion tenia el
   mismo problema desde la fase C1. */
.admin-cabecera-acciones .mud-button-root,
.admin-comms-acciones .mud-button-root,
.admin-comms-filtro .mud-chip {
    min-height: var(--tentalo-toque);
}

/* La tabla no puede pasarse de su contenedor. Con Breakpoint.Sm, MudBlazor
   apila cada fila en una tarjeta y la tabla deja de tener columnas que la
   acoten: en 375 px la de «Órdenes de configuración» salia 47 px por la
   derecha de su panel -sin hacer scrollear la pagina, porque .admin-tabla la
   recorta, pero recortando texto-. Medido en el detalle del local 1. */
/* Con Breakpoint.Sm, MudBlazor apila cada fila en una tarjeta poniendo la
   celda en `display: flex` con la etiqueta en un ::before. Un elemento flex no
   baja de su min-content, asi que el ancho de la tabla lo fija la celda con el
   texto mas largo: en 375 px la de «Órdenes de configuración» pedia 345 px
   dentro de un contenedor de 298 y se salia 47 px por la derecha (la pagina no
   scrollea -.mud-table-container recorta- pero recorta texto). Con
   table-layout: fixed el min-content deja de mandar, y con min-width: 0 en los
   hijos de la celda el texto envuelve en vez de empujar.
   Solo en modo apilado: en escritorio las columnas siguen midiendose por su
   contenido. Medido en el detalle del local 1 a 375 px. */
.admin-tabla .mud-sm-table .mud-table-root {
    width: 100%;
    max-width: 100%;
    table-layout: fixed;
}

.admin-tabla .mud-sm-table .mud-table-cell > * {
    min-width: 0;
}

/* ======================================================================== */
/*  MODULO 2 DE LA FASE C3: EL MARCO CRUD DEL CATALOGO                      */
/*  docs\central\fase-c3.md, «Módulo 2, parte 1».                           */
/*                                                                          */
/*  Dos prefijos: «crud-» para el marco (lista, ficha, secciones, edicion   */
/*  multiple) y «campo-» para los controles compartidos de                  */
/*  Components\Campos\. Van aqui y no en un .razor.css por el apartado 1    */
/*  del sistema de diseno (el aislamiento de CSS esta apagado en el modelo  */
/*  hosted).                                                                */
/* ======================================================================== */

/* --- La barra de la lista: buscador, contador y acciones de grupo -------- */
.crud-barra {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 0.75rem;
}

.crud-buscador {
    flex: 1 1 16rem;
    max-width: 26rem;
}

.crud-contador {
    color: var(--tentalo-tinta-suave);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* La barra de acciones de grupo solo existe con algo seleccionado, asi que se
   distingue con el fondo de marca al 6 %: aparece y desaparece, y sin un
   cambio de fondo el usuario no ve que la tabla le esta ofreciendo algo. */
.crud-seleccion {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
    padding: 0.5rem 0.75rem;
    border-radius: 8px;
    background: rgba(103, 41, 144, 0.06);
}

.crud-principal {
    font-weight: 600;
}

/* Las celdas numericas a la derecha y con cifras de ancho fijo, como pide el
   apartado 7.4 del sistema de diseno. */
.crud-celda-numero {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.crud-celda-centro {
    text-align: center;
}

/* Los botones de accion de una fila, juntos y sin envolver. */
.crud-celda-acciones {
    text-align: center;
    white-space: nowrap;
}

/* --- La ficha ------------------------------------------------------------ */
/* Las secciones de un formulario largo, separadas siempre por lo mismo. */
.crud-secciones > * {
    margin-bottom: 1rem;
}

/* Dos columnas cuando caben, una cuando no. Sin media query: el minmax decide.
   22 rem es el ancho por debajo del cual un campo con etiqueta larga
   («Pregunta antes de avisar de un cambio de cantidad») deja de leerse. */
.crud-secciones-dos {
    display: grid;
    /* min(22rem, 100%) y no 22rem a secas: con 22rem el minimo de la pista (352 px)
       es mayor que el ancho disponible en 375 px (343 px con los margenes del
       contenedor), y la columna se sale del contenedor. La pagina no scrollea
       -algo mas arriba tiene overflow hidden- pero los campos de la derecha
       quedan RECORTADOS, que por la regla 2 del sistema de diseno es peor: un
       campo recortado es un campo inalcanzable. Medido en la ficha de producto,
       donde salian 69 elementos fuera de pantalla. */
    grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
    gap: 1rem;
    align-items: start;
}

.crud-secciones-dos > * {
    min-width: 0;
}

/* Guardar y Cancelar: al final del formulario en escritorio y PEGADOS al borde
   inferior por debajo de 900 px. El formulario de un producto mide cuatro
   pantallas de movil y con los botones al final hay que recorrerlo entero para
   guardar un campo. */
.crud-ficha-acciones {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-top: 1rem;
    padding: 0.75rem 0;
}

.crud-ficha-estado {
    white-space: nowrap;
}

@media (max-width: 900px) {
    .crud-ficha-acciones {
        position: sticky;
        bottom: 0;
        z-index: 3;
        margin-top: 0.5rem;
        padding: 0.75rem;
        border-top: 1px solid var(--tentalo-borde);
        border-radius: 8px 8px 0 0;
        background: #fff;
        box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.06);
    }
}

/* El esqueleto de la ficha (Components\Crud\EsqueletoDeFicha.razor): un campo
   fantasma mide como uno de verdad, o sea, etiqueta encima del control y el
   mismo hueco debajo que .campo. La caja y las dos columnas no se definen aqui:
   son .admin-panel y .crud-secciones-dos, las mismas del formulario, que es
   justo lo que hace que no haya salto de altura cuando llegan los datos. */
.crud-campo-esqueleto {
    margin-bottom: 0.75rem;
}

.crud-errores {
    margin: 0.4rem 0 0;
    padding-left: 1.2rem;
}

.crud-errores li {
    font-size: 0.9rem;
}

/* --- Secciones plegables de una ficha ----------------------------------- */
.crud-seccion {
    margin-bottom: 1rem;
}

.crud-seccion-titulo {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* --- Precios por tarifa ------------------------------------------------- */
/* Una fila por tarifa: nombre a la izquierda y los tres precios y el IVA
   propio al lado. Por debajo de 700 px se apila, que es lo que hace que la
   fila se lea en un movil sin scroll horizontal. */
.crud-precios-fila {
    display: grid;
    grid-template-columns: minmax(8rem, 1fr) repeat(3, minmax(7rem, 8rem)) minmax(9rem, 12rem);
    gap: 0.5rem;
    align-items: start;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--tentalo-borde);
}

.crud-precios-tarifa {
    display: flex;
    flex-direction: column;
    padding-top: 0.6rem;
}

.crud-precios-nombre {
    font-weight: 600;
}

.crud-precios-iva {
    font-size: 0.76rem;
    color: var(--tentalo-tinta-suave);
}

@media (max-width: 700px) {
    .crud-precios-fila {
        grid-template-columns: 1fr;
    }

    .crud-precios-tarifa {
        padding-top: 0;
    }
}

/* --- Horarios de una categoria ----------------------------------------- */
.crud-horario {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--tentalo-borde);
}

.crud-horario-horas {
    display: flex;
    gap: 0.5rem;
    flex: 1 1 16rem;
}

.crud-horario-dias {
    display: flex;
    flex-wrap: wrap;
    gap: 0.1rem;
}

/* --- Edicion multiple --------------------------------------------------- */
.crud-multiple {
    display: flex;
    flex-direction: column;
}

/* Una fila por modificacion: la casilla «aplicar» arriba y su campo debajo,
   sangrado. El campo se queda VISIBLE y apagado cuando la casilla no esta
   marcada (opacidad y sin puntero): en una lista de veinticuatro
   modificaciones hay que poder ver de que va cada una antes de marcarla. */
.crud-modificacion {
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--tentalo-borde);
}

.crud-modificacion-activa {
    background: rgba(103, 41, 144, 0.04);
}

.crud-modificacion-texto {
    display: flex;
    flex-direction: column;
}

.crud-modificacion-categoria {
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--tentalo-tinta-suave);
}

.crud-modificacion-descripcion {
    font-size: 0.92rem;
}

.crud-modificacion-campo {
    margin-left: 2.5rem;
}

.crud-modificacion-campo-apagado {
    opacity: 0.45;
    pointer-events: none;
}

/* El filtro por etiqueta de la lista de categorias, encima de la tabla. */
.crud-filtro-etiquetas {
    margin-bottom: 1rem;
}

/* --- Los campos compartidos -------------------------------------------- */
.campo {
    margin-bottom: 0.75rem;
}

.campo-precio {
    max-width: 12rem;
}

.campo-nota {
    margin: 0 0 0.75rem;
    font-size: 0.87rem;
}

.campo-error {
    display: block;
    margin: -0.4rem 0 0.6rem;
}

.campo-casilla {
    margin-bottom: 0.2rem;
}

/* Las casillas anidadas del nucleo (control de lotes, lote en venta, lote
   automatico, dias de caducidad) se sangran de verdad: sin sangria, cuatro
   casillas seguidas no dicen que una depende de la anterior. */
.campo-casilla-hijos {
    margin-left: 1.75rem;
    padding-left: 0.5rem;
    border-left: 2px solid var(--tentalo-borde);
}

.campo-multiple {
    margin-bottom: 1rem;
}

.campo-multiple-etiqueta {
    display: block;
    margin-bottom: 0.3rem;
    font-weight: 600;
}

.campo-multiple-vacio {
    margin-bottom: 0.4rem;
    color: var(--tentalo-tinta-suave);
}

.campo-multiple-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    margin-bottom: 0.4rem;
}

.campo-multiple-anadir {
    max-width: 26rem;
}

/* --- Color -------------------------------------------------------------- */
.campo-color-pie {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: -0.4rem 0 0.75rem;
}

.campo-color-muestra {
    width: 20px;
    height: 20px;
    border: 1px solid var(--tentalo-borde);
    border-radius: 4px;
}

/* --- Imagen ------------------------------------------------------------- */
.campo-imagen {
    margin-bottom: 1rem;
}

.campo-imagen-cuerpo {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 1rem;
}

.campo-imagen-vista {
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border: 1px dashed var(--tentalo-borde);
    border-radius: 8px;
    background: #fafafa;
}

.campo-imagen-vista img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

.campo-imagen-acciones {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

/* El InputFile de Blazor no se puede estilar: se esconde y lo activa la
   etiqueta que lo envuelve, que ES el boton. Con 48 px de alto, como pide la
   regla 3 del sistema de diseno. */
.campo-imagen-boton {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    min-height: var(--tentalo-toque);
    padding: 0 0.9rem;
    border: 1px solid var(--tentalo-marca);
    border-radius: 4px;
    color: var(--tentalo-marca);
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
}

.campo-imagen-boton:hover {
    background: rgba(103, 41, 144, 0.06);
}

.campo-imagen-boton:focus-within {
    outline: 2px solid var(--tentalo-marca);
    outline-offset: 2px;
}

.campo-imagen-input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
}

/* --- Texto traducible y listas de texto -------------------------------- */
.campo-traducible-idiomas {
    margin: -0.4rem 0 0.75rem;
}

.campo-lista {
    margin-bottom: 1rem;
}

.campo-lista-fila {
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

.campo-lista-fila .campo {
    flex: 1 1 auto;
    margin-bottom: 0.35rem;
}

.campo-grupos {
    margin-bottom: 1rem;
}

/* --- Altura de toque en el marco CRUD (regla 3, 48 px) ----------------- */
/* MudBlazor da 36 px a un boton normal, 31 a uno pequeno y 30 a un boton de
   icono pequeno; en estas pantallas aparecen los tres: «Nuevo», los de la
   barra de seleccion, los de cada fila de la tabla y los de anadir y quitar de
   las listas. Crece la caja, no el icono. */
.crud-boton,
.crud-accion,
.crud-seleccion .mud-button-root,
.crud-horario .mud-icon-button,
.campo-lista-fila .mud-icon-button {
    min-height: var(--tentalo-toque);
    min-width: var(--tentalo-toque);
}

/* El submenu del catalogo: sus entradas tienen la misma altura de toque que
   las de primer nivel. */
.admin-menu-grupo .mud-nav-link {
    min-height: var(--tentalo-toque);
}

/* --- Los controles que MudBlazor deja por debajo de 48 px -------------- */
/* Medido con el navegador en 375 px sobre la lista de productos: la regla 3
   del sistema de diseno pide 48 px de alto minimo en CAMPOS y BOTONES, y
   MudBlazor deja cuatro cosas por debajo en estas pantallas.

   Se corrige aqui y no quitando Margin.Dense de los campos: sin denso, un
   MudTextField mide 56 px y la ficha de un producto (sesenta campos) crecería
   un 40 % de alto por un margen que nadie necesita. Con esto crece la CAJA
   hasta 48 y el texto se queda como esta, que es lo que pide WCAG 2.5.5 y lo
   mismo que hizo el modulo 1 con los enlaces del menu.

   Los cuatro casos, con su medida original:
     - los campos densos del marco (40 px);
     - la casilla de seleccion de cada fila de MudTable (24 la celda, 32 el
       input): es el control que mas se pulsa de la lista;
     - el desplegable de «ordenar por» que MudTable ensena en modo tarjeta
       por debajo de 600 px (32 px);
     - el desplegable de «filas por pagina» del paginador (20 px). */
.campo .mud-input-control,
.campo .mud-input-root,
.campo input.mud-input-slot,
.crud-buscador,
.crud-buscador .mud-input-root,
.crud-buscador input.mud-input-slot {
    min-height: var(--tentalo-toque);
}

.admin-tabla .mud-table-cell-checkbox,
.admin-tabla .mud-table-cell-checkbox .mud-checkbox-input,
.admin-tabla .mud-table-cell-checkbox .mud-button-root {
    min-height: var(--tentalo-toque);
    min-width: var(--tentalo-toque);
}

.admin-tabla .mud-table-smalldevices-sortselect,
.admin-tabla .mud-table-smalldevices-sortselect .mud-input-control,
.admin-tabla .mud-table-smalldevices-sortselect input,
.admin-tabla .mud-table-pagination-select,
.admin-tabla .mud-table-pagination-select input {
    min-height: var(--tentalo-toque);
}

/* Las casillas de verificacion y los botones de radio: la casilla de «aplicar»
   de la edicion multiple, las de CampoCasilla (nueve en la ficha de producto),
   las de una fila de tabla (formatos adicionales, tallas y colores) y los dos
   radios de «vale en todos / solo en algunos» de CampoGruposDeTpv y de los
   centros de venta. Lo que se pulsa es el boton con el icono, que MudBlazor
   deja en 32 px con Dense. */
.crud-modificacion-casilla .mud-button-root,
.mud-radio .mud-button-root,
.mud-radio input,
.campo-casilla-control,
.campo-casilla-control .mud-button-root,
.campo-casilla-control input,
.admin-tabla .mud-table-cell .mud-input-control-boolean-input .mud-button-root {
    min-height: var(--tentalo-toque);
    min-width: var(--tentalo-toque);
}

/* Los botones de adorno de un campo (la flecha y la X de un autocompletado, el
   selector de color) y los botones de texto pequenos de los campos. */
.campo .mud-input-adornment .mud-icon-button,
.mud-input-clear-button,
.campo-color-pie .mud-button-root,
.campo-imagen-acciones .mud-button-root,
.campo-traducible-idiomas .mud-expand-panel-header {
    min-height: var(--tentalo-toque);
}

/* Los campos que van DENTRO de una celda de tabla (stock minimo y maximo por
   almacen, proporcion de un formato, cantidad de un ingrediente): no llevan la
   clase «campo» porque en una celda no quieren margen inferior. */
.admin-tabla .mud-input-control,
.admin-tabla .mud-input-root,
.admin-tabla input.mud-input-slot {
    min-height: var(--tentalo-toque);
}

/* --- Las tablas del marco CRUD: 37 columnas caben scrollando ----------- */
/* La lista de productos tiene ONCE columnas fijas mas TRES por tarifa, y la
   instalacion de prueba tiene ocho tarifas: 37 columnas. Con la tabla al 100 %
   del panel, MudTable reparte 947 px entre 37 columnas (25 px cada una) y las
   cabeceras se solapan: no se lee nada. Medido en 1280 px.

   La solucion es la del sistema de diseno -una tabla ancha hace scroll DENTRO
   de su contenedor- y son TRES cosas, ninguna opcional:

     1. HorizontalScrollbar en el MudTable (CrudLista.razor), que es lo que pone
        el contenedor con overflow-x: auto.
     2. min-width: max-content en la tabla. Sin esto la tabla sigue midiendo el
        100 % del contenedor y no hay nada que scrollear: es el paso que falta en
        casi todas las recetas que se encuentran de esto.
     3. white-space: nowrap en las celdas, para que cada columna pida el ancho de
        su contenido en vez de partir «Conversion Bebida N/Incl» en cinco lineas.

   Solo en las tablas del marco (.crud-tabla): las del dashboard tienen cinco
   columnas, caben de sobra y ahi envolver esta bien. */
/* El selector va con la MISMA forma (y una clase mas) que la regla del modulo 6
   de unas lineas arriba -«.admin-tabla .mud-sm-table .mud-table-root»-, porque
   si no gana ella: esa regla pone width: 100 % y table-layout: fixed para que
   en modo tarjeta el texto envuelva, y con fixed + min-width: max-content el
   navegador reparte los 7.752 px A PARTES IGUALES (210 px por columna), con lo
   que un nombre largo se pinta ENCIMA de la familia. Con auto, «Id» mide lo que
   mide un numero y «Nombre» lo que mida el nombre mas largo.

   La regla del modulo 6 sigue aplicandose a sus tablas y, por debajo de 600 px,
   tambien a estas: ver la media query del final del bloque. */
.admin-tabla.crud-tabla .mud-sm-table .mud-table-root {
    width: auto;
    max-width: none;
    table-layout: auto;
    min-width: max-content;
}

.crud-tabla .mud-table-cell {
    white-space: nowrap;
}

/* NO se recorta el nombre con puntos suspensivos. Se probo con
   max-width + text-overflow: ellipsis y en una tabla dimensionada por
   max-content el recorte se pinta ENCIMA de la columna siguiente («Louis
   Latour Romanee St Vivant GRAND CRU» se metia dentro de la familia): el ancho
   de la columna lo calcula el navegador con el contenido completo y el max-width
   del enlace no entra en esa cuenta. La columna mide lo que mida el nombre mas
   largo y la tabla scrollea, que es justo para lo que esta el scroll. */

/* En modo tarjeta (por debajo de 600 px) manda lo contrario: la celda ocupa una
   fila entera, no hay a donde scrollear y el texto SI tiene que envolver. */
@media (max-width: 599px) {
    .admin-tabla.crud-tabla .mud-sm-table .mud-table-root {
        width: 100%;
        max-width: 100%;
        table-layout: fixed;
        min-width: 0;
    }

    .crud-tabla .mud-table-cell {
        white-space: normal;
    }

    .crud-tabla .mud-table-cell .crud-principal {
        max-width: none;
        white-space: normal;
    }
}

/* ============================================================================
   MODULO 3 DE LA FASE C3: LOCALES Y TPVs (parte 1)
   ============================================================================
   Cinco piezas, todas con prefijo `locales-`, `tpv-`, `config-` o
   `impuestos-`. El resto de las quince pantallas se pinta con lo que ya hay:
   cabecera, paneles con sus cuatro estados, el marco CRUD del modulo 2 y los
   campos compartidos. */

/* Los atajos de la lista de locales: una tarjeta por local con los enlaces a
   sus dos pantallas propias (configuracion y comunicaciones). Rejilla y no
   tabla porque cada tarjeta es un DESTINO y no algo que se compare columna a
   columna, que es el criterio del §7.4 del sistema de diseno.

   minmax(min(16rem, 100%), 1fr) y no minmax(16rem, 1fr): con la pista fija, en
   375 px la columna pide 256 px dentro de un contenedor de 343 y se sale. Es el
   mismo fallo que el modulo 2 midio en .crud-secciones-dos. */
.locales-atajos {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(16rem, 100%), 1fr));
    gap: .75rem;
}

.locales-atajo {
    min-width: 0;
}

/* Los datos de la central en la ficha de un local: dos columnas de
   etiqueta/valor en escritorio, una en movil. */
.locales-central {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
    gap: 0 1.5rem;
}

.locales-central-acciones {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-top: .5rem;
}

/* Los tres pasos del alta de un local. Numerada porque el orden importa: sin el
   segundo paso el local existe y no sincroniza. */
.locales-alta-pasos {
    margin: 0 0 1rem;
    padding-left: 1.25rem;
    line-height: 1.6;
    font-size: .875rem;
}

.locales-alta-pasos > li {
    margin-bottom: .35rem;
}

/* Los cuatro campos de impresion de un documento en la ficha de un TPV, en una
   caja con su titulo: son cuatro bloques iguales y sin separarlos no se sabe
   donde acaba uno. */
.tpv-impresion {
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 6px;
    padding: .75rem;
    margin-bottom: .75rem;
    min-width: 0;
}

/* --- Configuracion del servidor de un local ------------------------------- */

/* Las claves de una seccion. Dos columnas en escritorio y una en movil: cada
   clave es un bloque de tres lineas (nombre, control y pie con tipo, fecha y
   origen), asi que con 114 claves en una sola columna la pantalla mide metros. */
.config-claves {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(20rem, 100%), 1fr));
    gap: .75rem 1.25rem;
}

.config-clave {
    min-width: 0;
    padding: .5rem .75rem;
    border-left: 3px solid transparent;
    border-radius: 4px;
}

/* Una clave con el valor cambiado se marca con la tinta de marca en el borde
   izquierdo: es lo que permite encontrar lo que se ha tocado sin leer las 114,
   y va ACOMPANADO del chip «cambiada» porque el color solo no vale (WCAG 1.4.1). */
.config-clave-cambiada {
    border-left-color: var(--mud-palette-primary);
    background: var(--mud-palette-action-default-hover);
}

.config-clave-cabecera {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem;
    margin-bottom: .25rem;
}

/* La clave en monoespaciada y partible: hay nombres de 40 caracteres
   (EnableIntegrationServicesDocumentExternalInfo) y en una columna de 20rem hay
   que poder partirlos o se salen. */
.config-clave-nombre {
    font-family: Consolas, "Courier New", monospace;
    font-size: .8125rem;
    font-weight: 600;
    overflow-wrap: anywhere;
    min-width: 0;
}

.config-clave-pie {
    display: block;
    margin-top: .15rem;
    overflow-wrap: anywhere;
}

/* --- Impuestos ------------------------------------------------------------- */

/* La tabla de los diez IVA lleva campos DENTRO de las celdas, y un campo denso
   de MudBlazor mide 40 px de alto: por debajo de los 48 px de la regla 3 del
   sistema de diseno. Se corrige creciendo la caja y no el dibujo, igual que hizo
   el modulo 2 con sus nueve controles. */
.impuestos-tabla .mud-input-control {
    min-height: 48px;
}

.impuestos-tabla .mud-table-cell {
    padding-top: .25rem;
    padding-bottom: .25rem;
}

/* La columna del nombre no puede quedarse en su min-content: con
   table-layout: auto y cinco campos, el navegador le daba 90 px y el nombre
   «Reducido 10%» no cabia. */
.impuestos-tabla .mud-table-cell:nth-child(2) {
    min-width: 12rem;
}

/* --- Modulo 2, parte 2: ofertas y menus ------------------------------------ */

/* Dos campos que van JUNTOS en una linea y se apilan en un movil: el «desde /
   hasta» de una oferta y sus dos cantidades («se lleva 3, paga 2»). No se usa
   .crud-precios-fila porque esa lleva el nombre de la tarifa delante y reparte
   el ancho de otra manera. */
.crud-fechas {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
}

.crud-fechas > * {
    flex: 1 1 12rem;
    min-width: 0;
}

.crud-modificacion-fechas {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-bottom: 0.5rem;
}

.crud-modificacion-fechas > * {
    flex: 1 1 10rem;
    min-width: 0;
}

/* La programacion de una oferta reusa .crud-horario (los horarios de una
   categoria), pero es UNA sola y no una lista: sin la raya de separacion de
   abajo, que ahi separaba una fila de la siguiente y aqui no separa nada. */
.crud-horario-oferta {
    border-bottom: none;
    padding-bottom: 0;
}

/* Los grupos de platos de un menu son una tabla con cuatro campos por fila. Un
   campo denso de MudBlazor mide 40 px, por debajo de los 48 de la regla 3 del
   sistema de diseno: se corrige creciendo la caja, igual que la tabla de
   impuestos del modulo 3 y la de tallas y colores del producto. */
.menu-grupos .mud-input-control {
    min-height: 48px;
}

.menu-grupos .mud-table-cell {
    padding-top: 0.25rem;
    padding-bottom: 0.25rem;
    vertical-align: top;
}

/* Sin esto, con cuatro columnas de campos el navegador le da al nombre su
   min-content (unos 90 px) y no cabe «Postres». */
.menu-grupos .mud-table-cell:nth-child(1),
.menu-grupos .mud-table-cell:nth-child(2),
.menu-grupos .mud-table-cell:nth-child(4) {
    min-width: 11rem;
}

/* Una seccion que ocupa las DOS columnas de .crud-secciones-dos. Es un div que
   envuelve al panel y no una clase del propio panel porque PanelWidget no admite
   una clase: envolverlo hace del div el elemento de la rejilla, que es donde
   tiene que ir el grid-column.

   Lo necesita la tabla de grupos de platos de un menu (cinco columnas, cuatro de
   ellas con un campo dentro: en media pagina el navegador le da 60 px a cada una
   y no cabe ni «Postres») y la de precios por tarifa. */
.crud-seccion-ancha {
    grid-column: 1 / -1;
    min-width: 0;
}

/* ============================================================================
   MODULO 7 DE LA FASE C3: ALMACEN Y COMPRAS (parte 1)
   ============================================================================
   Cuatro piezas que el marco no tenia: el PIE DE TOTALES de un documento de
   compra, la fila de descuentos que va encima, la cabecera de la consulta de
   stock y el resumen de existencias por almacen de la ficha de un lote.

   Todo con las variables del sistema de diseno (docs\central\diseno-web.md): ni
   un color ni un espaciado escrito a mano. */

/* El pie de totales. Es una lista de pares etiqueta/cifra alineada a la derecha
   y no una tabla: son de dos a seis filas sin cabecera, y una tabla ahi
   ensenaria bordes que separan lo que se lee junto. */
.almacen-totales {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    margin-left: auto;
    max-width: 26rem;
}

/* Mientras el nucleo recalcula se ensena el ultimo total en gris en vez de
   vaciar la caja: al cambiar la cantidad de una linea el total se pide otra vez
   y una caja que parpadea a vacio en cada pulsacion es peor que un numero medio
   segundo desactualizado. */
.almacen-totales-esperando {
    opacity: 0.55;
    transition: opacity 120ms ease-in-out;
}

.almacen-totales-linea {
    display: flex;
    justify-content: space-between;
    gap: 1.5rem;
    align-items: baseline;
}

/* El desglose por tipo de IVA cuelga de la base imponible: mas pequeno, gris y
   sangrado, para que se lea como detalle y no como otro total. */
.almacen-totales-detalle {
    font-size: 0.8125rem;
    color: var(--mud-palette-text-secondary);
    padding-left: 1rem;
}

/* Las cifras en tabular para que la coma decimal caiga en la misma columna:
   sin esto, 1.234,50 y 98,00 no se pueden comparar de un vistazo. */
.almacen-totales-cifra {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.almacen-totales-total {
    font-weight: 600;
    font-size: 1.0625rem;
}

/* Los dos descuentos del documento, uno al lado del otro cuando caben. El
   minimo de 14rem es el ancho al que la etiqueta «Descuento por pronto pago
   (€)» deja de partirse en tres lineas. */
.almacen-descuentos {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: 0.75rem;
    margin-bottom: 1rem;
}

/* La cabecera de la consulta de stock: almacen, producto y las dos fechas. El
   producto necesita mas sitio que las fechas porque su buscador ensena el
   nombre entero, asi que no es un auto-fit de columnas iguales. */
.almacen-consulta {
    display: grid;
    grid-template-columns: minmax(12rem, 1fr) minmax(16rem, 2fr) minmax(9rem, 0.8fr) minmax(9rem, 0.8fr);
    gap: 0.75rem;
    align-items: start;
}

/* Por debajo de 900 px las cuatro columnas no caben sin que el buscador de
   producto quede en 120 px: se apilan en dos y luego en una. Es el mismo corte
   que usa .crud-secciones-dos. */
@media (max-width: 900px) {
    .almacen-consulta {
        grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    }
}

/* Las cifras grandes del resumen (stock actual de un producto, total de un
   lote): la misma forma que los widgets del panel, en pequeno. */
.almacen-resumen {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    align-items: baseline;
}

.almacen-resumen-cifra {
    font-size: 1.5rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    line-height: 1.2;
}

.almacen-resumen-etiqueta {
    font-size: 0.8125rem;
    color: var(--mud-palette-text-secondary);
}

/* Un movimiento que RESTA existencias se pinta en rojo y uno que las suma en
   verde. Es color ADEMAS del signo, no en lugar de el: el numero ya lleva su
   menos delante, asi que quien no distingue los dos colores lee lo mismo
   (regla 6 del sistema de diseno). */
.almacen-salida {
    color: var(--mud-palette-error);
}

.almacen-entrada {
    color: var(--mud-palette-success);
}

/* ======================================================================== */
/* MODULO 5 — INFORMES                                                      */
/* ======================================================================== */

/* El estado «no hay nada» de un panel que NO es un PanelWidget: la rejilla de
   resultados y la pantalla de un informe que no existe. Es el mismo aspecto
   que .admin-panel-vacio, y esta aparte porque aquel vive DENTRO del panel del
   dashboard, con su padding ya puesto. */
.admin-vacio {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.6rem;
    padding: 2.25rem 1rem;
    text-align: center;
    color: var(--tentalo-tinta-suave);
}

/* --- La vista generica de parametros ------------------------------------ */

/* Los parametros van en una rejilla que se adapta, no en columnas fijas: hay
   informes con tres parametros y otros con ocho, y una rejilla de dos columnas
   deja el de tres con un hueco enorme. El minimo de 16rem es lo que necesita un
   desplegable con «Grupos de cajas: todos (12)» dentro sin partir el texto. */
.informe-parametros {
    display: grid;
    gap: 0 1rem;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}

.informe-parametro {
    min-width: 0;
}

/* Un parametro de lista (las familias, las categorias) ocupa la fila entera:
   su casilla «todas» y su selector de varios elementos no caben en 16rem. */
.informe-parametro-ancho {
    grid-column: 1 / -1;
}

.informe-parametros-acciones {
    display: flex;
    align-items: center;
    margin-top: 0.5rem;
}

/* --- La rejilla de resultados ------------------------------------------- */

.informe-resultado-cabecera {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
}

/* Los botones de exportacion se envuelven en vez de recortarse: en 375 px, un
   «CSV» y un «PDF» al lado de «1.284 filas» no caben en una linea, y un boton
   recortado es un boton inalcanzable (es la regla de CabeceraPagina). */
.informe-resultado-acciones {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}

/* La tabla de un informe hace scroll DENTRO de su panel y nunca en la pagina:
   es la regla de docs\central\diseno-web.md, y aqui es obligatoria de verdad
   porque un informe de productos tiene nueve columnas y en 375 px ninguna
   combinacion cabe. Va en el contenedor y no en la tabla para que el
   FixedHeader de MudTable siga funcionando. */
.informe-rejilla {
    overflow-x: auto;
}

/* Breakpoint.None en la tabla: NO se apila en tarjetas por fila en el movil,
   al reves que las listas del catalogo. Una fila de informe son nueve numeros
   que se comparan con los de la fila de al lado; apilada en una tarjeta con
   nueve etiquetas deja de poderse comparar y ocupa una pantalla entera. Lo que
   se hace es desplazar la tabla, que es lo que hace una hoja de calculo. */
.informe-tabla .mud-table-cell {
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.informe-celda-numero {
    text-align: right;
}

.informe-celda-texto {
    text-align: left;
}

/* La cabecera es un boton para poder ordenar con el teclado y para que el
   lector de pantalla diga «botón, Ordenar por Total». Se le quita el aspecto de
   boton -mayusculas, relleno- para que siga leyendose como una cabecera. */
.informe-orden {
    padding: 0 0.25rem;
    min-width: 0;
    font-weight: 600;
    text-transform: none;
    letter-spacing: normal;
}

.informe-celda-numero .informe-orden {
    margin-left: auto;
}

.informe-total {
    font-weight: 700;
    border-top: 2px solid var(--tentalo-borde);
}

/* --- El indice de informes ---------------------------------------------- */

.informe-indice-grupo {
    display: grid;
    gap: 0.4rem;
    grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
}

.informe-indice-fila {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    min-width: 0;
}

.informe-indice-tarjeta {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    flex: 1 1 auto;
    min-width: 0;
    /* 48 px de alto minimo: la regla de la zona de toque del sistema de diseno. */
    min-height: var(--tentalo-toque);
    padding: 0.5rem 0.6rem;
    border-radius: 8px;
    text-decoration: none;
    color: inherit;
}

.informe-indice-tarjeta:hover {
    background-color: rgba(103, 41, 144, 0.06);
}

.informe-indice-tarjeta:focus-visible {
    outline: 2px solid var(--tentalo-marca);
    outline-offset: 2px;
}

.informe-indice-titulo {
    display: block;
    font-weight: 600;
    line-height: 1.3;
}

.informe-indice-nota {
    display: block;
    font-size: 0.8rem;
    color: var(--tentalo-tinta-suave);
    line-height: 1.35;
}

/* --- Los tres paneles del negocio --------------------------------------- */

.informe-paneles {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
}

.informe-panel-barras {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.informe-panel-barra-texto {
    display: flex;
    justify-content: space-between;
    gap: 0.75rem;
    font-size: 0.85rem;
    margin-bottom: 0.2rem;
}

/* La pista es relativa y las dos barras van encima una de otra: la de base
   marca el total y la de margen, la parte que queda. Con dos barras apiladas en
   vertical ocuparia el doble y habria que leer dos numeros para compararlas. */
.informe-panel-barra-pista {
    position: relative;
    height: 10px;
    border-radius: 5px;
    background-color: rgba(103, 41, 144, 0.08);
    overflow: hidden;
}

.informe-panel-barra-relleno,
.informe-panel-barra-margen {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    border-radius: 5px;
}

.informe-panel-barra-relleno {
    background-color: rgba(103, 41, 144, 0.35);
}

.informe-panel-barra-margen {
    background-color: var(--tentalo-marca);
}

.informe-panel-leyenda {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.9rem;
    font-size: 0.78rem;
    color: var(--tentalo-tinta-suave);
}

.informe-punto {
    display: inline-block;
    width: 0.7rem;
    height: 0.7rem;
    border-radius: 2px;
    border: 1px solid var(--tentalo-borde);
    vertical-align: -1px;
}

.informe-punto-base {
    background-color: rgba(103, 41, 144, 0.35);
}

.informe-punto-margen {
    background-color: var(--tentalo-marca);
}

.informe-panel-total {
    display: flex;
    justify-content: space-between;
    gap: 0.75rem;
    margin-top: 0.9rem;
    padding-top: 0.6rem;
    border-top: 1px solid var(--tentalo-borde);
}

/* --- El mapa de calor ---------------------------------------------------- */

.informe-mapa {
    overflow-x: auto;
}

.informe-mapa-tabla {
    border-collapse: separate;
    border-spacing: 2px;
    font-size: 0.72rem;
}

.informe-mapa-tabla th {
    font-weight: 600;
    color: var(--tentalo-tinta-suave);
    padding: 0 0.2rem;
    text-align: center;
}

.informe-mapa-tabla th[scope="row"] {
    text-align: right;
    padding-right: 0.4rem;
    position: sticky;
    left: 0;
    /* Sin fondo, las celdas de colores se ven pasar por debajo del dia al
       desplazar la tabla en el movil. */
    background-color: #fff;
}

/* Cuadradas y pequenas: siete por veinticuatro son 168 celdas y lo que se lee
   es la FORMA de la mancha, no cada celda. Se les da un borde muy claro para
   que una fila con dos horas vacias en medio siga leyendose como una rejilla. */
.informe-mapa-celda {
    width: 18px;
    min-width: 18px;
    height: 18px;
    border-radius: 3px;
    border: 1px solid rgba(0, 0, 0, 0.04);
    padding: 0;
}

/* El valor de la celda esta en el DOM para el lector de pantalla, pero no se
   pinta: no cabe. No se usa display:none porque eso lo esconde tambien del
   lector, que es justo lo contrario de lo que se quiere. */
.informe-mapa-oculto {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

.informe-mapa-medida {
    min-width: 12rem;
}

/* ==========================================================================
   FORMATOS DE IMPRESION (modulo 3, parte 2)
   Anadido por la parte 2 del modulo 3, al final del fichero: es el UNICO
   fichero compartido que toca ese trabajo y asi no se cruza con nada de
   arriba. Prefijos: «impresion-», «escpos-», «plantilla-» y «cupon-».
   ========================================================================== */

/* --- La vista previa de un ticket ESC/POS -------------------------------- */
/* Un ticket ESC/POS llega del nucleo YA MONTADO: los espacios de dentro son
   los que alinean la columna de precios. Con una tipografia proporcional o con
   un ancho que no sea el del papel esa alineacion se rompe, y la vista previa
   deja de servir para lo unico para lo que existe. De ahi: monoespaciada,
   white-space: pre y un ancho por numero de columnas.

   Los anchos y los cuerpos de letra son los MISMOS del administrador React
   (frontend-react/src/styles/admin/original-sources/38399/
   065-escpos-print-preview.less), medidos alli para que una linea de N columnas
   quepa justa. Lo que cambia es que aqui van con min(100 %, …): en 375 px una
   caja de 360 px se saldria del panel, y un contenido recortado es contenido
   inalcanzable (regla 2 del sistema de diseno). */
.escpos-vista-previa {
    box-sizing: border-box;
    font-family: "Roboto Mono", ui-monospace, "Cascadia Mono", Consolas, "Courier New", monospace;
    font-size: 12px;
    line-height: 1.35;
    white-space: pre;
    /* Un ticket con una imagen de cabecera puede traer lineas mas anchas que el
       papel: scrollea DENTRO de su caja y no en la pagina, que es la regla del
       sistema de diseno para cualquier contenido ancho. */
    overflow-x: auto;
    margin: 0;
    padding: 0.75rem;
    border: 1px solid var(--tentalo-borde);
    border-radius: 6px;
    background-color: #fff;
    color: var(--tentalo-tinta);
    min-height: 8rem;
}

.escpos-vista-previa-32 {
    width: min(100%, 250px);
}

.escpos-vista-previa-42 {
    width: min(100%, 327px);
}

.escpos-vista-previa-48 {
    width: min(100%, 355px);
    font-size: 11px;
}

.escpos-vista-previa-56 {
    width: min(100%, 360px);
    font-size: 9.5px;
}

.escpos-vista-previa-64 {
    width: min(100%, 350px);
    font-size: 8.75px;
}

/* Las marcas de la impresora que VistaPreviaEscPos deja pasar. «dw» es doble
   ancho, y en pantalla se imita separando las letras, igual que el React; «dh»
   es doble alto y no se pinta distinto -tampoco lo hace el React- porque un
   cuerpo mayor descolocaria la rejilla de caracteres. */
.escpos-vista-previa .escpos-dw {
    letter-spacing: 8px;
}

.escpos-vista-previa .escpos-prop {
    font-size: 9pt;
}

/* Las imagenes que el nucleo incrusta en la cabecera del ticket: centradas y
   sin pasar del ancho del papel. */
.escpos-vista-previa img {
    display: block;
    width: auto;
    max-width: 100%;
    margin: 0 auto;
}

/* El hueco de un codigo de barras o de un QR. El nucleo manda la MARCA con su
   valor, no el dibujo; el React pone en su sitio dos PNG genericos que trae
   dentro (ocho kilobytes de base64 entre los dos) y aqui se dibujan con CSS,
   que dice lo mismo y no pesa nada. */
.escpos-simbolo {
    display: block;
    margin: 0.4rem auto;
    border: 1px solid var(--tentalo-borde);
}

.escpos-simbolo-barras {
    width: 60%;
    height: 2.6rem;
    background-image: repeating-linear-gradient(
        90deg,
        var(--tentalo-tinta) 0 2px,
        #fff 2px 4px,
        var(--tentalo-tinta) 4px 5px,
        #fff 5px 9px
    );
}

.escpos-simbolo-qr {
    width: 5.5rem;
    height: 5.5rem;
    background-image:
        repeating-linear-gradient(90deg, var(--tentalo-tinta) 0 6px, #fff 6px 12px),
        repeating-linear-gradient(0deg, var(--tentalo-tinta) 0 6px, #fff 6px 12px);
    background-blend-mode: difference;
}

/* --- La vista previa de una pagina RDL ----------------------------------- */
/* Son PNG ya renderizados por el nucleo, una imagen por pagina. A ancho
   completo del panel y con su borde, para que se vea donde acaba la hoja. */
.impresion-vista-previa-paginas img {
    display: block;
    width: 100%;
    height: auto;
    margin-bottom: 0.5rem;
    border: 1px solid var(--tentalo-borde);
    border-radius: 4px;
    background-color: #fff;
}

.impresion-vista-previa-acciones {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.75rem;
}

/* --- El editor de elementos de cabecera, pie y cupon ---------------------- */
/* Una fila es: los dos botones de orden a la izquierda, el cuerpo (un campo de
   texto con sus cuatro conmutadores, o una imagen) en medio y el de quitar a la
   derecha. Con grid y no con flex porque las tres piezas tienen que quedar
   alineadas entre filas aunque una lleve imagen y la de al lado texto. */
.plantilla-elemento {
    display: grid;
    grid-template-columns: auto 1fr auto;
    gap: 0.5rem;
    align-items: start;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--tentalo-borde);
}

.plantilla-elemento:last-of-type {
    border-bottom: none;
}

/* min-width: 0 en el hijo de una rejilla: sin el, un campo de texto no baja de
   su min-content y la fila se sale del panel en 375 px. Es lo mismo que hace
   .crud-secciones-dos con sus columnas. */
.plantilla-elemento-cuerpo {
    min-width: 0;
}

.plantilla-elemento-orden,
.plantilla-elemento-quitar {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    /* Los botones a la altura del campo y no del borde de la fila: el campo
       lleva su etiqueta flotante encima. */
    padding-top: 0.25rem;
}

.plantilla-elemento-atributos {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    margin-top: -0.25rem;
    margin-bottom: 0.5rem;
}

.plantilla-elementos-acciones {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.75rem;
}

/* Altura de toque (regla 3 del sistema de diseno): los botones de orden, el de
   quitar y los cuatro conmutadores de estilo son botones de icono pequenos, que
   MudBlazor deja en 30 px. Crece la caja, no el dibujo. */
.plantilla-elemento-boton {
    min-height: var(--tentalo-toque);
    min-width: var(--tentalo-toque);
}

/* --- La ayuda de marcadores de un cupon ---------------------------------- */
.cupon-marcadores code {
    white-space: nowrap;
}

.cupon-marcadores td:first-child {
    width: 1%;
    white-space: nowrap;
    vertical-align: top;
}

.cupon-marcadores-ejemplo {
    text-align: center;
    margin: 0.5rem 0;
}
