:root {
    --synbex-primary: #2c5cfc;
    --synbex-primary-hover: #254cd1;
    --synbex-primary-soft: #eef2ff;
    --synbex-danger: #dc2626;
    --synbex-danger-soft: #fef2f2;
    --synbex-success: #16a34a;
    --synbex-success-hover: #15803d;
    /* Quórum: presentes y ausentes. Van aparte de primary/danger porque son
       dos partes de un mismo gráfico y tienen que leerse como pareja —
       pedido explícito: azul de verdad (no el violeta de la marca) y un
       amarillo anaranjado. Se aclaran en modo oscuro, igual que el resto. */
    --synbex-quorum-present: #2563eb;
    --synbex-quorum-absent: #f59e0b;
    --synbex-text: #111827;
    --synbex-text-muted: #6b7280;
    /* Segunda vuelta del mismo pedido: "que baje un tono, más gris, que no sea
       tan blanco" — surface pasó de #ffffff a #f8f9fb y de ahí a #eef0f3;
       bg de #f6f7fb a #edeff3 y de ahí a #e3e5eb. Border bajado junto con
       surface/bg para seguir notándose contra el gris más oscuro. */
    --synbex-border: #d1d5dc;
    --synbex-surface: #eef0f3;
    --synbex-bg: #e3e5eb;
    --synbex-radius-lg: 16px;
    --synbex-radius-md: 10px;
    --synbex-shadow: 0 1px 2px rgba(17, 24, 39, 0.04), 0 12px 32px -8px rgba(17, 24, 39, 0.12);
    --synbex-font: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --synbex-phone-link: #0d9488;
    /* Resplandor de fondo de la pantalla de login (Login.razor.css) — antes
       usaba --synbex-primary-soft (índigo) ahí, así que el login se veía azul
       aunque el resto de la app ya no lo fuera. Ahora es neutro, a partir del
       propio texto, no del color de acento. */
    --synbex-ambient-glow: rgba(17, 24, 39, 0.035);
}

@media (prefers-color-scheme: dark) {
    :root {
        --synbex-primary: #819df8;
        --synbex-primary-hover: #bfccf8;
        --synbex-primary-soft: rgba(44, 92, 252, 0.16);
        --synbex-danger: #f87171;
        --synbex-danger-soft: rgba(248, 113, 113, 0.12);
        --synbex-success: #4ade80;
        --synbex-success-hover: #86efac;
        --synbex-quorum-present: #60a5fa;
        --synbex-quorum-absent: #fbbf24;
        --synbex-text: #f3f4f6;
        --synbex-text-muted: #9ca3af;
        /* Antes #2b2f3a/#1a1d27/#111319 — un gris con tinta azulada que se
           sentía como el azul genérico de apps de IA. Grafito neutro: mismo
           nivel de oscuridad, cero tinta de color, elegido a propósito para
           NO tocar los botones/enlaces (siguen en --synbex-primary de siempre). */
        --synbex-border: #2e2e32;
        --synbex-surface: #1c1c1f;
        --synbex-bg: #121214;
        --synbex-shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 12px 32px -8px rgba(0, 0, 0, 0.5);
        --synbex-phone-link: #2dd4bf;
        --synbex-ambient-glow: rgba(255, 255, 255, 0.035);
    }
}


/* ===========================================================================
   Escala de interfaz — tamaño continuo, no por saltos
   ===========================================================================

   El problema: un corte único (antes: `@media (max-width: 1400px)`) hace que
   TODO cambie de golpe en un punto. A 1401px se ve grande, a 1399px se ve
   chico, y entre medio no pasa nada. Además solo servía para quien estuviera
   justo en ese rango.

   Lo que hace esto: cada medida se estira entre dos anclas y el navegador
   interpola solo todo lo que hay en medio.

       1280px de ancho visible ....... el tamaño más compacto
       1920px de ancho visible ....... el tamaño de siempre

   Por qué esas dos: 1920 es el monitor sin zoom (ahí nada cambia respecto de
   como se veía antes), y 1280 es ese mismo monitor al 150 %, que es el caso
   que se estuvo revisando. Debajo de 1280 se queda en el valor compacto;
   arriba de 1920 se queda en el de siempre. Nunca crece más allá.

   Y sirve igual para las dos cosas que preguntaba el pedido, porque para el
   navegador son la misma: subir el zoom al 150 % en un monitor de 1920 deja
   1280px de ancho visible, exactamente lo mismo que un portátil de 1280 sin
   zoom. Los dos reciben el mismo tamaño, y un portátil de 1366 recibe el suyo,
   un pasito más grande. Con el 101 % también funciona: quedan 1901px, así que
   todo baja alrededor de un 3 % — poquito, pero baja, que es justo lo pedido.

   La fórmula de cada línea, por si hay que agregar una medida nueva:

       valor de 1280 â valor de 1920,  con  Î = MAX â MIN
       clamp(MIN, calc(C + D vw), MAX)   con   C = MIN â 2 Ã Î
                                                D = Î Ã· 6.4

   Los valores de acá afuera son los de SIEMPRE (los máximos). El celular y las
   ventanas angostas se quedan con esos y no se tocan: la escala fluida se
   enciende solo de 901px para arriba, que es donde empieza el diseño de
   computador. Así el celular sigue teniendo su propio tamaño, pensado para el
   dedo, y no hereda el de una pantalla chica de escritorio. */
:root {
    /* Menú de la izquierda */
    --ui-marca-nombre: 22px;
    --ui-marca-pad-y: 13px;
    --ui-marca-caja: 34px;
    --ui-marca-caja-fz: 17px;
    --ui-marca-logo-alto: 90px;
    --ui-sidebar-ancho: 250px;
    --ui-sidebar-rail: 74px;
    --ui-sidebar-pad-y: 20px;
    --ui-sidebar-pad-x: 16px;
    --ui-nav-fz: 14px;
    --ui-nav-pad-y: 9px;
    --ui-nav-pad-x: 12px;
    --ui-nav-icono: 18px;
    --ui-fz-micro: 11px;

    /* Barra de arriba y contenido */
    --ui-header-pad-y: 14px;
    --ui-pad-lateral: 28px;
    --ui-usuario-fz: 13.5px;
    --ui-etiqueta-fz: 11.5px;
    --ui-etiqueta-pad-x: 8px;
    --ui-boton-fz: 13px;
    --ui-volver-icono: 15px;
    --ui-boton-pad-y: 7px;
    --ui-boton-pad-x: 14px;
    --ui-btn-icono: 36px;
    --ui-btn-icono-fz: 16px;
    --ui-a11y-btn: 48px;
    --ui-a11y-fz: 22px;

    /* Pantallas de botones grandes (hubs) */
    --ui-hub-icono: 54px;
    --ui-hub-titulo: 24px;
    --ui-hub-sub: 14px;
    --ui-hub-encabezado-mb: 30px;
    --ui-hub-3-ancho: 1180px;
    --ui-hub-gap: 18px;
    --ui-grid-gap: 14px;
    --ui-tile-pad-y: 18px;
    --ui-tile-pad-x: 22px;
    --ui-tile-pad-y-m: 22px;
    --ui-tile-pad-x-m: 24px;
    --ui-tile-gap: 16px;
    --ui-tile-icono: 48px;
    --ui-tile-icono-m: 54px;
    --ui-tile-icono-svg: 26px;
    --ui-tile-titulo: 15.5px;
    --ui-tile-titulo-m: 16px;
    --ui-tile-desc: 13px;
    --ui-tile-desc-m: 13.5px;
    --ui-tile-chevron: 30px;
    --ui-paq-valor: 17px;
    --ui-paq-valor-fecha: 14px;
    --ui-paq-etiqueta: 11px;
    --ui-paq-sub: 12.5px;
    --ui-paq-nota: 12px;
    --ui-paq-detalles: 12.5px;
}

@media (min-width: 901px) {
    :root {
        --ui-marca-nombre: clamp(17px, calc(7px + 0.78125vw), 22px);
        --ui-marca-pad-y: clamp(10px, calc(4px + 0.46875vw), 13px);
        --ui-marca-caja: clamp(28px, calc(16px + 0.9375vw), 34px);
        --ui-marca-caja-fz: clamp(14px, calc(8px + 0.46875vw), 17px);
        --ui-marca-logo-alto: clamp(66px, calc(18px + 3.75vw), 90px);
        --ui-sidebar-ancho: clamp(220px, calc(160px + 4.6875vw), 250px);
        --ui-sidebar-rail: clamp(64px, calc(44px + 1.5625vw), 74px);
        --ui-sidebar-pad-y: clamp(14px, calc(2px + 0.9375vw), 20px);
        --ui-sidebar-pad-x: clamp(12px, calc(4px + 0.625vw), 16px);
        --ui-nav-fz: clamp(12.5px, calc(9.5px + 0.234375vw), 14px);
        --ui-nav-pad-y: clamp(7px, calc(3px + 0.3125vw), 9px);
        --ui-nav-pad-x: clamp(10px, calc(6px + 0.3125vw), 12px);
        --ui-nav-icono: clamp(16px, calc(12px + 0.3125vw), 18px);
        --ui-fz-micro: clamp(10px, calc(8px + 0.15625vw), 11px);

        --ui-header-pad-y: clamp(10px, calc(2px + 0.625vw), 14px);
        --ui-pad-lateral: clamp(18px, calc(1.5625vw - 2px), 28px);
        --ui-usuario-fz: clamp(12px, calc(9px + 0.234375vw), 13.5px);
        --ui-etiqueta-fz: clamp(10.5px, calc(8.5px + 0.15625vw), 11.5px);
        --ui-etiqueta-pad-x: clamp(6px, calc(2px + 0.3125vw), 8px);
        --ui-boton-fz: clamp(11.5px, calc(8.5px + 0.234375vw), 13px);
        --ui-volver-icono: clamp(13px, calc(9px + 0.3125vw), 15px);
        --ui-boton-pad-y: clamp(6px, calc(4px + 0.15625vw), 7px);
        --ui-boton-pad-x: clamp(11px, calc(5px + 0.46875vw), 14px);
        --ui-btn-icono: clamp(30px, calc(18px + 0.9375vw), 36px);
        --ui-btn-icono-fz: clamp(14px, calc(10px + 0.3125vw), 16px);
        --ui-a11y-btn: clamp(40px, calc(24px + 1.25vw), 48px);
        --ui-a11y-fz: clamp(18px, calc(10px + 0.625vw), 22px);

        --ui-hub-icono: clamp(40px, calc(12px + 2.1875vw), 54px);
        --ui-hub-titulo: clamp(19px, calc(9px + 0.78125vw), 24px);
        --ui-hub-sub: clamp(12.5px, calc(9.5px + 0.234375vw), 14px);
        --ui-hub-encabezado-mb: clamp(18px, calc(1.875vw - 6px), 30px);
        --ui-hub-3-ancho: clamp(824px, calc(112px + 55.625vw), 1180px);
        --ui-hub-gap: clamp(12px, calc(0.9375vw), 18px);
        --ui-grid-gap: clamp(12px, calc(8px + 0.3125vw), 14px);
        --ui-tile-pad-y: clamp(14px, calc(6px + 0.625vw), 18px);
        --ui-tile-pad-x: clamp(16px, calc(4px + 0.9375vw), 22px);
        --ui-tile-pad-y-m: clamp(14px, calc(1.25vw - 2px), 22px);
        --ui-tile-pad-x-m: clamp(16px, calc(1.25vw), 24px);
        --ui-tile-gap: clamp(12px, calc(4px + 0.625vw), 16px);
        --ui-tile-icono: clamp(38px, calc(18px + 1.5625vw), 48px);
        --ui-tile-icono-m: clamp(38px, calc(6px + 2.5vw), 54px);
        --ui-tile-icono-svg: clamp(20px, calc(8px + 0.9375vw), 26px);
        --ui-tile-titulo: clamp(12.5px, calc(6.5px + 0.46875vw), 15.5px);
        --ui-tile-titulo-m: clamp(12.5px, calc(5.5px + 0.546875vw), 16px);
        --ui-tile-desc: clamp(11px, calc(7px + 0.3125vw), 13px);
        --ui-tile-desc-m: clamp(11px, calc(6px + 0.390625vw), 13.5px);
        --ui-tile-chevron: clamp(26px, calc(18px + 0.625vw), 30px);
        --ui-paq-valor: clamp(17px, calc(13px + 0.3125vw), 19px);
        --ui-paq-valor-fecha: clamp(14px, calc(11px + 0.234375vw), 15.5px);
        --ui-paq-etiqueta: clamp(11px, calc(9px + 0.15625vw), 12px);
        --ui-paq-sub: clamp(12.5px, calc(10.5px + 0.15625vw), 13.5px);
        --ui-paq-nota: clamp(12px, calc(10px + 0.15625vw), 13px);
        --ui-paq-detalles: clamp(12.5px, calc(10.5px + 0.15625vw), 13.5px);
    }
}

/* ===========================================================================
   Escala del login
   ===========================================================================

   Las siete pantallas de entrada (iniciar sesión, olvidé la clave, restablecer,
   cambiar clave, configurar 2FA, verificar 2FA y elegir apartamento) tienen cada
   una su propia hoja de estilos con sus tamaños en píxeles fijos. Eso las dejaba
   fuera de la escala general de la aplicación: con el navegador al 150 % —o en un
   portátil chico— la tarjeta, el logo y los botones se veían enormes.

   Mismas anclas y misma fórmula que la escala de interfaz de más arriba:
   1280px de ancho visible = el tamaño compacto, 1920px = el de siempre.

   EL NOMBRE DE CADA TOKEN ES SU TAMAÑO A 1920. O sea, --login-fz-16 vale 16px en
   un monitor sin zoom y se va achicando hasta 14px; --login-pad-40 vale 40px y
   baja hasta 30. Se nombran así a propósito: el mapa entre "lo que decía antes el
   CSS" y "lo que dice ahora" queda a la vista, y agregar una pantalla nueva es
   buscar el número que ya se estaba usando.

   Igual que la escala general, esto se enciende solo de 901px para arriba. En
   celular NO se toca nada, y hay un motivo concreto además del tamaño del dedo:
   Safari en iPhone hace zoom automático sobre un campo de texto cuya letra mide
   menos de 16px. Los campos de estas pantallas están justo en 16px, así que
   achicarlos en celular haría saltar la pantalla cada vez que alguien toca el
   campo de la contraseña. */
:root {
    --login-ancho-400: 400px;
    --login-logo-ancho: 200px;
    --login-logo-alto: 80px;
    --login-caja-56: 56px;
    --login-caja-32: 32px;
    --login-pad-40: 40px;
    --login-pad-36: 36px;
    --login-pad-42: 42px;
    --login-pad-16: 16px;
    --login-pad-14: 14px;
    --login-pad-13: 13px;
    --login-pad-12: 12px;
    --login-pad-11: 11px;
    --login-pad-10: 10px;
    --login-sep-28: 28px;
    --login-sep-26: 26px;
    --login-sep-24: 24px;
    --login-sep-20: 20px;
    --login-sep-18: 18px;
    --login-fz-30: 30px;
    --login-fz-26: 26px;
    --login-fz-24: 24px;
    --login-fz-22: 22px;
    --login-fz-21: 21px;
    --login-fz-20: 20px;
    --login-fz-16: 16px;
    --login-fz-15: 15px;
    --login-fz-145: 14.5px;
    --login-fz-14: 14px;
    --login-fz-135: 13.5px;
    --login-fz-13: 13px;
    --login-fz-125: 12.5px;
    --login-fz-12: 12px;
    --login-fz-11: 11px;
}

@media (min-width: 901px) {
    :root {
        --login-ancho-400: clamp(370px, calc(310px + 4.6875vw), 400px);
        --login-logo-ancho: clamp(150px, calc(50px + 7.8125vw), 200px);
        --login-logo-alto: clamp(60px, calc(20px + 3.125vw), 80px);
        --login-caja-56: clamp(38px, calc(2px + 2.8125vw), 56px);
        --login-caja-32: clamp(24px, calc(8px + 1.25vw), 32px);
        --login-pad-40: clamp(24px, calc(2.5vw - 8px), 40px);
        /* El relleno lateral CRECE cuando la pantalla se achica, al revés que el
           resto. No es un descuido: la tarjeta se ensanchó para que no se viera tan
           vertical, pero los campos y el botón son width:100%, así que se estiraban
           con ella. Sumando relleno a los lados la caja queda ancha y el contenido
           vuelve al ancho que tenía (256px a 1280, igual que antes). A 1920 no
           cambia: ahí sigue en 36px de siempre. */
        --login-pad-36: clamp(36px, calc(99px - 3.28125vw), 57px);
        --login-pad-42: clamp(32px, calc(12px + 1.5625vw), 42px);
        --login-pad-16: clamp(12px, calc(4px + 0.625vw), 16px);
        --login-pad-14: clamp(10px, calc(2px + 0.625vw), 14px);
        --login-pad-13: clamp(9px, calc(1px + 0.625vw), 13px);
        --login-pad-12: clamp(9px, calc(3px + 0.46875vw), 12px);
        --login-pad-11: clamp(8px, calc(2px + 0.46875vw), 11px);
        --login-pad-10: clamp(7.5px, calc(2.5px + 0.390625vw), 10px);
        --login-sep-28: clamp(17px, calc(1.71875vw - 5px), 28px);
        --login-sep-26: clamp(16px, calc(1.5625vw - 4px), 26px);
        --login-sep-24: clamp(15px, calc(1.40625vw - 3px), 24px);
        --login-sep-20: clamp(13px, calc(1.09375vw - 1px), 20px);
        --login-sep-18: clamp(11px, calc(1.09375vw - 3px), 18px);
        --login-fz-30: clamp(19px, calc(1.71875vw - 3px), 30px);
        --login-fz-26: clamp(17.5px, calc(0.5px + 1.328125vw), 26px);
        --login-fz-24: clamp(15.5px, calc(1.328125vw - 1.5px), 24px);
        --login-fz-22: clamp(15px, calc(1px + 1.09375vw), 22px);
        --login-fz-21: clamp(14.5px, calc(1.5px + 1.015625vw), 21px);
        --login-fz-20: clamp(14px, calc(2px + 0.9375vw), 20px);
        --login-fz-16: clamp(13px, calc(7px + 0.46875vw), 16px);
        --login-fz-15: clamp(12px, calc(6px + 0.46875vw), 15px);
        --login-fz-145: clamp(12px, calc(7px + 0.390625vw), 14.5px);
        --login-fz-14: clamp(11.5px, calc(6.5px + 0.390625vw), 14px);
        --login-fz-135: clamp(11.5px, calc(7.5px + 0.3125vw), 13.5px);
        --login-fz-13: clamp(11px, calc(7px + 0.3125vw), 13px);
        --login-fz-125: clamp(10.5px, calc(6.5px + 0.3125vw), 12.5px);
        --login-fz-12: clamp(10.5px, calc(7.5px + 0.234375vw), 12px);
        --login-fz-11: clamp(9.5px, calc(6.5px + 0.234375vw), 11px);
    }
}

/* Preferencia de tema explícita (página Configuración) — prima sobre prefers-color-scheme */
:root[data-theme="dark"] {
    --synbex-primary: #819df8;
    --synbex-primary-hover: #bfccf8;
    --synbex-primary-soft: rgba(44, 92, 252, 0.16);
    --synbex-danger: #f87171;
    --synbex-danger-soft: rgba(248, 113, 113, 0.12);
    --synbex-success: #4ade80;
    --synbex-success-hover: #86efac;
    --synbex-quorum-present: #60a5fa;
    --synbex-quorum-absent: #fbbf24;
    --synbex-text: #f3f4f6;
    --synbex-text-muted: #9ca3af;
    --synbex-border: #2e2e32;
    --synbex-surface: #1c1c1f;
    --synbex-bg: #121214;
    --synbex-shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 12px 32px -8px rgba(0, 0, 0, 0.5);
    --synbex-phone-link: #2dd4bf;
    --synbex-ambient-glow: rgba(255, 255, 255, 0.035);
}

:root[data-theme="light"] {
    --synbex-primary: #2c5cfc;
    --synbex-primary-hover: #254cd1;
    --synbex-primary-soft: #eef2ff;
    --synbex-danger: #dc2626;
    --synbex-danger-soft: #fef2f2;
    --synbex-success: #16a34a;
    --synbex-success-hover: #15803d;
    --synbex-quorum-present: #2563eb;
    --synbex-quorum-absent: #f59e0b;
    --synbex-text: #111827;
    --synbex-text-muted: #6b7280;
    --synbex-border: #d1d5dc;
    --synbex-surface: #eef0f3;
    --synbex-bg: #e3e5eb;
    --synbex-shadow: 0 1px 2px rgba(17, 24, 39, 0.04), 0 12px 32px -8px rgba(17, 24, 39, 0.12);
    --synbex-phone-link: #0d9488;
    --synbex-ambient-glow: rgba(17, 24, 39, 0.035);
}

/* Salvavidas global: ningún contenido normal necesita nunca deslizar la página
   completa hacia los lados — solo tarjetas/tablas puntuales con su propio
   overflow-x:auto. Si algo se pasa del ancho por error, esto evita que arrastre
   con él la barra de navegación/topbar entera en celular. */
html {
    overflow-x: hidden;
}

body {
    margin: 0;
    max-width: 100%;
    /* clip y no hidden: los dos impiden que la página se deslice hacia los lados,
       pero hidden convierte al body en su propio contenedor de scroll, y eso
       rompe position:sticky en TODA la app (la barra de acciones de los
       formularios quedaba quieta al final en vez de seguir la pantalla). clip
       recorta igual sin crear ese contenedor. hidden queda primero como
       respaldo para los navegadores que no conocen clip (Safari anterior a 16). */
    overflow-x: hidden;
    overflow-x: clip;
    font-family: var(--synbex-font);
    color: var(--synbex-text);
    background: var(--synbex-bg);
    touch-action: manipulation;
}

/* Barra de scroll delgada y del mismo tema de la app (en vez de la gris/blanca
   predeterminada del navegador) — aplica a cualquier contenedor con scroll
   interno (tarjetas, tablas, modales, menús desplegables, etc.), no solo a
   uno en particular, y cambia sola entre claro/oscuro con las mismas
   variables de siempre. Firefox usa scrollbar-color; el resto, ::-webkit-*. */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--synbex-border) transparent;
}

*::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

*::-webkit-scrollbar-track {
    background: transparent;
}

*::-webkit-scrollbar-thumb {
    background-color: var(--synbex-border);
    border-radius: 999px;
}

*::-webkit-scrollbar-thumb:hover {
    background-color: var(--synbex-text-muted);
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* ======================================
   Dashboard — clases compartidas entre páginas administrativas
   ====================================== */

.page-title {
    margin: 0 0 6px;
    font-size: 24px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--synbex-text);
}

.page-subtitle {
    margin: 0 0 24px;
    font-size: 14px;
    color: var(--synbex-text-muted);
}

.page-header-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 24px;
}

.stat-row {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 20px;
}

.stat-card {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 160px;
    padding: 18px 20px;
    background: var(--synbex-surface);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-lg);
    box-shadow: var(--synbex-shadow);
}

.stat-value {
    font-size: 28px;
    font-weight: 700;
    color: var(--synbex-text);
}

.stat-value.stat-active { color: #16a34a; }
.stat-value.stat-blocked { color: var(--synbex-danger); }

.stat-label {
    font-size: 13px;
    color: var(--synbex-text-muted);
}

.synbex-presence-tenant {
    margin-bottom: 20px;
}

.synbex-presence-tenant:last-child {
    margin-bottom: 0;
}

.synbex-presence-tenant-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 8px;
}

.synbex-presence-tenant-counts {
    font-size: 13px;
    color: var(--synbex-text-muted);
}

.synbex-presence-connected {
    color: #16a34a;
    font-weight: 600;
}

.synbex-presence-disconnected {
    color: var(--synbex-danger);
    font-weight: 600;
}

.stat-link {
    font-size: 14px;
    font-weight: 600;
    color: var(--synbex-primary);
    text-decoration: none;
}

.stat-link:hover {
    text-decoration: underline;
}

.synbex-card {
    background: var(--synbex-surface);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-lg);
    box-shadow: var(--synbex-shadow);
    padding: 24px;
}

.synbex-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
}

.synbex-table th {
    text-align: left;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--synbex-text-muted);
    padding: 0 12px 10px;
    border-bottom: 1px solid var(--synbex-border);
}

/* Consultas de ingreso — tantas columnas que se apretaban y el texto se partía
   en dos líneas por celda; pedido explícito: cada columna ocupa el ancho que
   necesite su contenido, y para verlas todas se desliza la tarjeta hacia la
   derecha (ya tiene overflow-x:auto) en vez de encogerlas. Más padding
   horizontal que el resto de tablas de la app — pedido explícito, los datos
   se sentían pegados unos con otros con el padding estándar (12px). Selector
   compuesto (no solo .consultas-table th/td) a propósito: así gana sin
   importar el orden en el archivo sobre el padding:12px/0 12px 10px de
   .synbex-table td/th, que aparecen más abajo (mismo criterio que ya usa
   .catalog-add-btn.areas-new-btn más arriba en este mismo archivo). */
.synbex-table.consultas-table th,
.synbex-table.consultas-table td {
    white-space: nowrap;
    padding-left: 22px;
    padding-right: 22px;
}

.sortable-th {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
    transition: color 0.15s ease;
}

/* Alerta cuando el visitante NO está en Blacklist/Cita/Frecuentes — pedido
   explícito, esa columna no debe quedar vacía. Gris/tenue a propósito: es
   solo informativo (primera visita o sin novedades), no una alerta real, así
   que no debe competir visualmente con los mensajes de Blacklist/Cita/
   Frecuentes que sí importa que resalten. Compartida entre Consultas.razor y
   la tabla "dentro del conjunto" de Index.razor — ninguna de las dos tiene
   este color definido en su propio .razor.css. */
.visitor-alert-neutral {
    color: var(--synbex-text-muted);
    font-style: italic;
}

.sortable-th:hover {
    color: var(--synbex-primary);
}

.synbex-table td {
    padding: 12px;
    border-bottom: 1px solid var(--synbex-border);
    color: var(--synbex-text);
    vertical-align: middle;
}

/* Tabla de Residentes — un nombre largo se envolvía en varias líneas dentro de
   la celda (se veía como una fila extra) en vez de dejar que la tarjeta
   scrollee horizontal, que es lo que .synbex-card ya hace en celular (ver
   @media max-width:700px más abajo). white-space:nowrap fuerza a que cada
   celda ocupe el ancho que necesite su contenido — con eso la tabla crece de
   verdad y el scroll horizontal hace su trabajo. Escrito solo para esta
   tabla (no en .synbex-table td, que es global y otras tablas sí necesitan
   que el texto se pueda partir, ej. columnas de comentarios en reportes). */
.residents-table td {
    white-space: nowrap;
}

/* No hay un "estándar" fijo para esto — son valores razonables: con 1 solo
   residente la tarjeta no se ve colapsada/rara (min-height), y con muchos
   (este conjunto de prueba tiene 300) no obliga a bajar toda la página para
   ver la tabla completa — se scrollea ADENTRO de la tarjeta en vez de afuera. */
.residents-table-card {
    min-height: 220px;
    max-height: 640px;
    overflow: auto;
}

/* El encabezado de columnas queda fijo arriba mientras se scrollea la lista
   adentro de la tarjeta — necesita su propio fondo para que las filas no se
   vean "pasar por debajo" del texto al hacer scroll. */
.residents-table thead th {
    position: sticky;
    top: 0;
    background: var(--synbex-surface);
    z-index: 1;
}

/* Editar/Reenviar/Desactivar/Eliminar en fila — antes eran 3-4 elementos
   sueltos dentro de la celda, que se apilaban uno encima del otro cuando no
   cabían en el ancho disponible (el wrap normal de contenido inline). Con
   flex forzado y sin wrap quedan siempre en horizontal. Pedido explícito:
   "más separados, mejor organizados" — ahora son dos grupos (editar/reenviar
   vs. desactivar/eliminar) con más aire entre sí y una línea divisoria antes
   del grupo que cambia el estado de la cuenta. */
.residents-actions {
    display: flex;
    align-items: center;
    gap: 18px;
    flex-wrap: nowrap;
}

.residents-actions-group {
    display: flex;
    align-items: center;
    gap: 10px;
}

.residents-actions-group-danger {
    padding-left: 18px;
    border-left: 1px solid var(--synbex-border);
}

/* En celular la fila se vuelve tarjeta (ver .synbex-table-stacked) y los 4
   botones quedaban peleando por ~300px: cada uno terminaba en 53-71px con el
   texto partido en varias líneas, y "Reenviar credenciales" era ilegible.

   Acá los dos grupos que ya existen pasan a ser dos filas de 2 botones cada
   una (Editar/Reenviar arriba, Desactivar/Eliminar abajo), que es el mismo
   agrupamiento de escritorio pero apilado. La línea divisoria vertical no
   tiene sentido cuando los grupos están uno sobre otro, así que pasa a ser
   separación horizontal. */
@media (max-width: 600px) {
    /* width:100% porque la celda de acciones también es flex: sin esto
       .residents-actions se queda del ancho de su contenido y las dos filas
       de botones no llegan al borde de la tarjeta. */
    .residents-actions {
        flex-direction: column;
        align-items: stretch;
        gap: 8px;
        width: 100%;
    }

    /* stretch en vez del center heredado: "Reenviar credenciales" ocupa dos
       líneas y queda de 44px, y con center el "Editar" de al lado se quedaba
       en 28px flotando en la mitad. Estirados, los dos botones de cada fila
       miden lo mismo. */
    .residents-actions-group {
        gap: 8px;
        align-items: stretch;
    }

    .residents-actions-group-danger {
        padding-left: 0;
        border-left: none;
    }

    /* flex-basis 0 = las dos mitades exactas. Sin el min-width:0 el botón no
       puede achicarse por debajo del ancho de su texto ("Reenviar
       credenciales") y se saldría de la tarjeta; con él, ese texto sí se
       parte en dos líneas, que acá está bien porque los dos botones de la
       fila crecen igual y quedan parejos.

       box-sizing explícito por lo mismo que en .facility-card-actions: el
       "Editar" es un <a> (content-box por defecto) y "Reenviar credenciales"
       un <button> (border-box), y sin igualarlos el enlace queda más ancho
       que el botón aunque los dos pidan la mitad. */
    .residents-actions .btn-synbex {
        flex: 1 1 0;
        min-width: 0;
        padding-left: 8px;
        padding-right: 8px;
        text-align: center;
        box-sizing: border-box;
    }
}

/* Círculo de ícono en el modal de confirmación (desactivar, etc.) — mismo
   molde que .resident-delete-icon pero en tono ámbar/advertencia en vez de
   rojo/peligro, para distinguir "esto se puede deshacer" de "esto no". */
.confirm-action-icon {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 auto 16px;
}

.confirm-action-icon svg {
    width: 32px;
    height: 32px;
}

.confirm-action-icon.is-warning {
    background: rgba(217, 119, 6, 0.14);
    color: #d97706;
}

.synbex-table tr:last-child td {
    border-bottom: none;
}

.tenant-row {
    cursor: pointer;
    transition: background 0.12s ease;
}

.tenant-row:hover {
    background: var(--synbex-primary-soft);
}

.tenant-row-chevron {
    text-align: right;
    color: var(--synbex-text-muted);
    font-size: 12px;
    width: 24px;
}

.tenant-expand-row td {
    padding: 0;
    border-bottom: 1px solid var(--synbex-border);
    background: var(--synbex-bg);
}

.tenant-expand-panel {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 18px;
    padding: 18px 20px;
}

.tenant-expand-group {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.tenant-expand-group-title {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--synbex-text-muted);
}

.tenant-expand-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.badge {
    display: inline-flex;
    align-items: center;
    padding: 3px 10px;
    font-size: 12px;
    font-weight: 600;
    border-radius: 999px;
}

/* Puntito de color antes del texto de cualquier insignia (pedido explícito,
   sobre una imagen de referencia: "● Listo para retiro"). background:currentColor
   en vez de un color propio — toma el mismo tono que ya tiene la insignia donde
   se ponga, así que un solo elemento sirve para verde, naranja, gris o rojo sin
   tener que repetirlo por cada variante. */
.badge-punto {
    width: 6px;
    height: 6px;
    margin-right: 6px;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
}

.badge-active {
    background: rgba(22, 163, 74, 0.12);
    color: #16a34a;
}

.badge-blocked {
    background: var(--synbex-danger-soft);
    color: var(--synbex-danger);
}

.badge-platform {
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.btn-synbex {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 8px 16px;
    font-size: 13.5px;
    font-weight: 600;
    font-family: inherit;
    border-radius: var(--synbex-radius-md);
    border: 1px solid transparent;
    cursor: pointer;
    text-decoration: none;
    transition: background 0.15s ease, border-color 0.15s ease;
}

.btn-synbex-primary {
    background: var(--synbex-primary);
    color: #fff;
}

.btn-synbex-primary:hover {
    background: var(--synbex-primary-hover);
}

.btn-synbex-secondary {
    background: transparent;
    color: var(--synbex-text);
    border-color: var(--synbex-border);
}

.btn-synbex-secondary:hover {
    background: var(--synbex-primary-soft);
}

.btn-synbex-danger {
    background: transparent;
    color: var(--synbex-danger);
    border-color: var(--synbex-danger);
}

.btn-synbex-danger:hover {
    background: var(--synbex-danger-soft);
}

.btn-synbex-sm {
    padding: 5px 12px;
    font-size: 12.5px;
}

.btn-synbex-lg {
    padding: 14px 28px;
    font-size: 18px;
}

/* Botones "Filtrar"/"Buscar"/"Exportar" al lado de campos .synbex-input en
   filas de filtro (reporte de limpieza, consultas de ingreso, ...) — mismo
   padding vertical (10px) y tamaño de letra (14px) que .synbex-input, para
   que el botón quede exactamente de la misma altura que el campo de al lado
   en vez de verse más bajito. Pedido explícito. */
.report-filter-btn {
    padding: 10px 22px;
    font-size: 14px;
}

.btn-synbex-success {
    background: var(--synbex-success);
    color: #fff;
}

.btn-synbex-success:hover {
    background: var(--synbex-success-hover);
}

/* min(320px, 100%): en celulares muy angostos (menos de ~360px de ancho) la tarjeta
   no cabe con 320px de mínimo y la cuadrícula se salía del panel por la derecha
   (Limpieza, pisos, apartamentosâ¦ todo lo hecho con .hub-grid). Así nunca exige
   más ancho del que hay. */
.hub-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
    gap: var(--ui-grid-gap);
}

.hub-tile {
    display: flex;
    align-items: center;
    gap: var(--ui-tile-gap);
    padding: var(--ui-tile-pad-y) var(--ui-tile-pad-x);
    background: var(--synbex-surface);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-lg);
    box-shadow: var(--synbex-shadow);
    text-decoration: none;
    color: var(--synbex-text);
    text-align: left;
    overflow: hidden;
    position: relative;
    transition: border-color 0.25s ease, transform 0.25s cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 0.25s ease;
}

.hub-tile::before {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--synbex-primary-soft);
    opacity: 0;
    transition: opacity 0.25s ease;
}

.hub-tile:hover {
    border-color: var(--synbex-primary);
    transform: translateY(-3px);
    box-shadow: 0 12px 28px -12px color-mix(in srgb, var(--synbex-primary) 45%, transparent);
}

.hub-tile:hover::before {
    opacity: 1;
}

.hub-tile:active {
    transform: translateY(-1px);
}

/* Estado elegido (ej. tipo de destino en "Nueva asignación" de Limpieza) —
   mismo tinte que el hover pero permanente, sin depender del mouse encima. */
.hub-tile.is-selected {
    border-color: var(--synbex-primary);
}

.hub-tile.is-selected::before {
    opacity: 1;
}

/* Cuando el "tile" es un <button> (ej. Configuración, que abre una ventana
   flotante en vez de navegar a otra página) en vez de un <a> — mismo look,
   solo se resetean las diferencias por defecto del navegador. */
button.hub-tile {
    width: 100%;
    font: inherit;
    cursor: pointer;
}

button.hub-tile:disabled {
    opacity: 0.6;
    cursor: default;
}

button.hub-tile:disabled:hover {
    border-color: var(--synbex-border);
    transform: none;
    box-shadow: var(--synbex-shadow);
}

button.hub-tile:disabled:hover::before {
    opacity: 0;
}

.hub-tile-icon {
    position: relative;
    flex-shrink: 0;
    width: var(--ui-tile-icono);
    height: var(--ui-tile-icono);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 26px;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* Variantes de color para distinguir tarjetas a simple vista (Mi unidad) —
   el resto de los hubs sigue usando el morado por defecto de arriba. */
.hub-tile-icon.is-teal {
    background: rgba(13, 148, 136, 0.12);
    color: var(--synbex-phone-link);
}

.hub-tile-icon.is-orange {
    background: rgba(217, 119, 6, 0.12);
    color: #d97706;
}

.hub-tile-icon.is-green {
    background: rgba(22, 163, 74, 0.12);
    color: var(--synbex-success);
}

.hub-tile-icon.is-red {
    background: var(--synbex-danger-soft);
    color: var(--synbex-danger);
}

.hub-tile-icon.is-slate {
    background: rgba(107, 114, 128, 0.14);
    color: var(--synbex-text-muted);
}

.hub-tile-icon.is-blue {
    background: rgba(37, 99, 235, 0.12);
    color: #2563eb;
}

.hub-tile:hover .hub-tile-icon {
    transform: scale(1.12) rotate(-4deg);
}

/* Para hubs con pocas opciones que igual quedan cortos de alto (ej. "Mi
   unidad" con 3, "Comunidad" con 5-6) — un poco más grandes que el tamaño
   normal, para llegar hasta abajo sin tener que deslizar. Misma clase para
   ambos hubs a propósito: deben verse del mismo tamaño. */
.hub-grid-medium .hub-tile {
    padding: var(--ui-tile-pad-y-m) var(--ui-tile-pad-x-m);
}

.hub-grid-medium .hub-tile-icon {
    width: var(--ui-tile-icono-m);
    height: var(--ui-tile-icono-m);
}

.hub-grid-medium .hub-tile-icon svg {
    width: var(--ui-tile-icono-svg);
    height: var(--ui-tile-icono-svg);
}

.hub-grid-medium .hub-tile-title {
    font-size: var(--ui-tile-titulo-m);
}

.hub-grid-medium .hub-tile-desc {
    font-size: var(--ui-tile-desc-m);
}

/* Notificación sin leer dentro de esa sección — punto naranja sobre el ícono +
   borde parpadeante en la tarjeta, mismo lenguaje visual que el resto de avisos
   parpadeantes de la app. */
.hub-tile.has-unread {
    border-color: #d97706;
    animation: hub-tile-unread-blink 1.4s ease-in-out infinite;
}

.hub-tile.has-unread .hub-tile-icon::after {
    content: "";
    position: absolute;
    top: -2px;
    right: -2px;
    width: 10px;
    height: 10px;
    border-radius: 999px;
    background: #d97706;
    border: 2px solid var(--synbex-surface);
}

@keyframes hub-tile-unread-blink {
    0%, 100% {
        box-shadow: 0 0 0 0 rgba(217, 119, 6, 0.45);
    }

    50% {
        box-shadow: 0 0 0 6px rgba(217, 119, 6, 0);
    }
}

@media (prefers-reduced-motion: reduce) {
    .hub-tile.has-unread {
        animation: none;
    }
}

.hub-tile-text {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.hub-tile-title {
    font-weight: 600;
    font-size: var(--ui-tile-titulo);
    color: var(--synbex-text);
}

/* Tarjeta "Limpiezas asignadas" en Cleaning/Index.razor — el número de
   pendientes va a la par del título, no debajo (a diferencia de hub-tile-desc). */
.hub-tile-title-row {
    display: flex;
    align-items: center;
    gap: 8px;
}

.hub-tile-desc {
    font-size: var(--ui-tile-desc);
    color: var(--synbex-text-muted);
    opacity: 0.85;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* En celular la tarjeta es angosta y las descripciones más largas quedaban
   cortadas con "â¦" (p. ej. la de Portería). Acá se dejan envolver en dos
   líneas: es la única forma de leerlas completas en ese ancho. Se limita a 2
   para que todas las tarjetas del hub conserven más o menos la misma altura. */
@media (max-width: 600px) {
    .hub-tile-desc {
        white-space: normal;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        line-clamp: 2;
        -webkit-box-orient: vertical;
    }
}

/* Check verde en la esquina (pedido explícito) — apartamento con limpieza HOY
   (hora Colombia) al navegar Torre â Piso â Apartamento, o piso completo al
   navegar Torre â Piso. .hub-tile ya es position:relative, así que solo hace
   falta anclar esto adentro (overflow:hidden de .hub-tile evita que se salga
   del borde redondeado de la tarjeta). */
.cleaning-done-badge {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 22px;
    height: 22px;
    border-radius: 999px;
    background: var(--synbex-success);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1;
}

.cleaning-done-badge svg {
    width: 14px;
    height: 14px;
}

.synbex-field {
    margin-bottom: 18px;
}

.synbex-label {
    display: block;
    margin-bottom: 6px;
    font-size: 13px;
    font-weight: 600;
    color: var(--synbex-text);
}

.synbex-input {
    width: 100%;
    box-sizing: border-box;
    padding: 10px 12px;
    font-size: 14.5px;
    font-family: inherit;
    color: var(--synbex-text);
    background: var(--synbex-surface);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-md);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

/* En celular, cualquier campo con letra menor a 16px hace que el navegador
   acerque la pantalla sola al tocarlo para escribir (y hay que alejar de nuevo
   después) — es un comportamiento del navegador, no algo que se pueda apagar
   desde JS/CSS directamente, así que la solución real es no bajar de 16px acá. */
@media (max-width: 600px) {
    .synbex-input {
        font-size: 16px;
    }
}

/* Fecha de inicio/fin del modal de reporte de limpieza — el input nativo de
   fecha no se achica bien con solo "width:100%" dentro de una ventana angosta
   (el navegador le da un ancho mínimo propio); con menos padding y letra más
   chica entra sin que una tape a la otra. */
.cleaning-date-input {
    padding: 8px 6px;
    font-size: 13px;
    min-width: 0;
}

/* Los 4 botones de rango (Esta semana/Este mes/Este año/Otro) dentro de la
   ventana flotante de reporte, en una cuadrícula de 2 columnas x 2 filas. */
.cleaning-modal-preset-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
}

.cleaning-modal-preset-btn {
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    padding: 13px 14px;
    font-size: 14px;
}

/* Solo en celular: Fecha de inicio arriba y Fecha fin abajo, cada una en su
   propia fila a todo lo ancho — en computadora/tablet siguen lado a lado. */
@media (max-width: 600px) {
    .cleaning-date-range-row {
        flex-direction: column;
    }
}

.synbex-input:focus {
    outline: none;
    border-color: var(--synbex-primary);
    box-shadow: 0 0 0 3px var(--synbex-primary-soft);
}

.synbex-input.invalid {
    border-color: var(--synbex-danger);
}

.synbex-validation-message {
    display: block;
    margin-top: 6px;
    font-size: 12.5px;
    color: var(--synbex-danger);
}

.synbex-form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 20px;
}

.synbex-checkbox-row {
    display: flex;
    gap: 24px;
    margin-bottom: 18px;
}

.synbex-checkbox {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    color: var(--synbex-text);
    cursor: pointer;
}

.synbex-checkbox input {
    width: 16px;
    height: 16px;
    accent-color: var(--synbex-primary);
    cursor: pointer;
}

.synbex-toggle {
    position: relative;
    display: inline-block;
    width: 40px;
    height: 22px;
    flex-shrink: 0;
}

.synbex-toggle input {
    opacity: 0;
    width: 0;
    height: 0;
}

.synbex-toggle-slider {
    position: absolute;
    inset: 0;
    background: var(--synbex-border);
    border-radius: 999px;
    cursor: pointer;
    transition: background 0.15s ease;
}

.synbex-toggle-slider::before {
    content: "";
    position: absolute;
    height: 16px;
    width: 16px;
    left: 3px;
    top: 3px;
    background: white;
    border-radius: 50%;
    transition: transform 0.15s ease;
}

.synbex-toggle input:checked + .synbex-toggle-slider {
    background: var(--synbex-primary);
}

.synbex-toggle input:checked + .synbex-toggle-slider::before {
    transform: translateX(18px);
}

.synbex-toggle-field {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px 0;
    border-bottom: 1px solid var(--synbex-border);
    font-size: 14px;
    color: var(--synbex-text);
}

/* Variante para filas donde la etiqueta debe quedar pegada al interruptor (ej.
   "Por correo"/"Por WhatsApp" anidados bajo "Recibe mensajes") en vez de
   estirarse a los extremos del contenedor. */
.synbex-toggle-field-compact {
    justify-content: flex-start;
    gap: 10px;
}

/* Asterisco rojo en el label de un campo obligatorio — pedido explícito, para
   toda la app (empezando por Agregar persona: Documento, Nombre, Apellido...). */
.required-asterisk {
    color: var(--synbex-danger);
    margin-left: 2px;
}

/* Modal "Configurar campos" (Entradas â por cada punto de entrada) — tabla
   con un encabezado fijo (Campo/Visible/Lista/Obligatorio, una sola vez) y
   una fila por campo, en vez de repetir esas tres etiquetas en cada tarjeta.
   Cada fila separada con su línea para que no se enreden con muchos campos. */
.field-entry-setting-table {
    display: flex;
    flex-direction: column;
    max-height: 60vh;
    overflow-y: auto;
    padding: 4px 6px 4px 2px;
}

.field-entry-setting-row {
    display: grid;
    grid-template-columns: 1fr 74px 74px 96px;
    align-items: center;
    column-gap: 14px;
    padding: 14px 4px;
    border-bottom: 1px solid var(--synbex-border);
}

.field-entry-setting-row:last-child {
    border-bottom: none;
}

.field-entry-setting-row > .synbex-toggle {
    justify-self: center;
}

.field-entry-setting-name {
    font-weight: 600;
    font-size: 14.5px;
    color: var(--synbex-text);
}

.field-entry-setting-header {
    padding: 0 4px 12px;
    border-bottom: 2px solid var(--synbex-border);
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--synbex-text-muted);
}

.field-entry-setting-header span:not(:first-child) {
    text-align: center;
}

/* La ventana más grande y con más aire a los lados — pedido explícito, se
   sentía muy angosta y muy pegada al borde con el diseño anterior (440px,
   una sola columna fija). */
/* Selector compuesto (no solo .entry-point-settings-modal) a propósito: así
   gana sin importar el orden en el archivo sobre el max-width:440px de
   .settings-modal-dialog, que aparece más abajo (mismo criterio que ya usa
   .catalog-add-btn.areas-new-btn más arriba en este mismo archivo). */
.settings-modal-dialog.entry-point-settings-modal {
    max-width: 860px;
}

.settings-modal-body.entry-point-settings-body {
    padding: 26px 30px;
}

@media (max-width: 720px) {
    .settings-modal-body.entry-point-settings-body {
        padding: 18px;
    }
}

.document-dropzone {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 32px 20px;
    border: 2px dashed var(--synbex-border);
    border-radius: var(--synbex-radius-lg);
    background: var(--synbex-bg);
    text-align: center;
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease;
}

.document-dropzone:hover {
    border-color: var(--synbex-primary);
    background: var(--synbex-primary-soft);
}

.document-dropzone input[type="file"] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
}

.document-dropzone-icon {
    font-size: 32px;
}

.document-dropzone-text {
    font-weight: 600;
    color: var(--synbex-text);
    font-size: 14.5px;
}

.document-dropzone-hint {
    font-size: 12.5px;
    color: var(--synbex-text-muted);
}

.import-progress {
    margin: 16px 0;
}

.import-progress progress {
    width: 100%;
    height: 10px;
    border-radius: 999px;
    overflow: hidden;
    accent-color: var(--synbex-primary);
}

.document-file-chip {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-top: 10px;
    padding: 8px 14px;
    background: var(--synbex-primary-soft);
    border-radius: var(--synbex-radius-md);
    font-size: 13.5px;
    color: var(--synbex-text);
}

/* Grid en vez de flex-wrap: con más de 3 fotos, flex-wrap las repartía mal en
   la fila de abajo (dependía del ancho sobrante de la última fila de arriba).
   Con grid, cada foto siempre cae en su propia celda del mismo tamaño. */
.cleaning-photo-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(100px, 120px));
    gap: 10px;
}

.cleaning-photo-thumb {
    width: 100%;
    aspect-ratio: 1;
    object-fit: cover;
    border-radius: 8px;
    cursor: zoom-in;
    transition: transform 0.15s ease;
}

.cleaning-photo-thumb:hover {
    transform: scale(1.04);
}

/* En el historial de limpieza (vista admin) las miniaturas se ven más chicas
   que en "Mi limpieza" del residente — ahí sí queremos que se noten grandes,
   pero en el historial hay muchas filas y no hace falta que ocupen tanto. */
.cleaning-detail-row .cleaning-photo-thumb {
    width: 70px;
    height: 70px;
    aspect-ratio: unset;
}

/* Cuadrícula fija de 2 columnas — con auto-flow por fila esto ya acomoda solo
   1Âª=arriba-izq, 2Âª=arriba-der, 3Âª=abajo-izq, 4Âª(botón "Ver todas")=abajo-der,
   sin necesitar posiciones a mano. Compartida entre el historial de admin
   (Cleaning/Index.razor) y "Mi limpieza" del residente (MyUnitCleaning.razor,
   tanto en las tarjetas por mes como en la ventana del día del calendario) —
   por eso vive acá y no en el CSS aislado de un solo componente. Tiene que
   quedar DESPUÃS de ".cleaning-detail-row .cleaning-photo-thumb" (arriba) para
   pisarle el tamaño de 70px con el 90px de acá cuando la grilla se usa dentro
   de esa fila (vista admin). */
.cleaning-photo-grid-preview {
    display: grid;
    grid-template-columns: repeat(2, 90px);
    gap: 8px;
}

.cleaning-photo-grid-preview .cleaning-photo-thumb {
    width: 90px;
    height: 90px;
}

.cleaning-photo-more {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    width: 90px;
    height: 90px;
    border-radius: 8px;
    border: 1px dashed var(--synbex-border);
    background: var(--synbex-bg);
    color: var(--synbex-text-muted);
    font-family: inherit;
    font-weight: 600;
    font-size: 11px;
    text-align: center;
    line-height: 1.2;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.cleaning-photo-more svg {
    width: 20px;
    height: 20px;
}

.cleaning-photo-more:hover {
    background: var(--synbex-primary-soft);
    border-color: var(--synbex-primary);
    color: var(--synbex-primary);
}

/* Ventana flotante con TODAS las fotos de una limpieza (ver OpenGallery) —
   vive dentro del mismo .photo-lightbox-backdrop que ya usaba el visor de una
   sola foto, solo que ahora envuelve una cuadrícula en vez de un <img> suelto. */
.cleaning-gallery-modal {
    position: relative;
    max-width: min(90vw, 900px);
    max-height: 90vh;
    overflow-y: auto;
    background: var(--synbex-surface);
    border-radius: 12px;
    padding: 20px;
    cursor: default;
}

.cleaning-gallery-close {
    position: absolute;
    top: 10px;
    right: 10px;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    border: none;
    background: var(--synbex-bg);
    color: var(--synbex-text);
    font-size: 15px;
    cursor: pointer;
    z-index: 1;
}

.cleaning-gallery-close:hover {
    background: var(--synbex-border);
}

.cleaning-gallery-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(200px, 100%), 1fr));
    gap: 14px;
    margin-top: 8px;
}

.cleaning-gallery-grid img {
    width: 100%;
    aspect-ratio: 1;
    object-fit: cover;
    border-radius: 10px;
    cursor: zoom-in;
    transition: transform 0.15s ease;
}

.cleaning-gallery-grid img:hover {
    transform: scale(1.03);
}

/* Clic en una foto DENTRO de la galería la abre en pantalla completa, encima de
   la propia galería (mismo .photo-lightbox-backdrop de siempre, apilado) — por
   eso necesita un z-index mayor al que ya trae .photo-lightbox-backdrop. */
.cleaning-gallery-fullscreen {
    z-index: 1001;
}

.cleaning-checklist {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.cleaning-checklist-item {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    color: var(--synbex-text);
}

.cleaning-checklist-icon {
    display: inline-flex;
    flex-shrink: 0;
    color: var(--synbex-text-muted);
}

.cleaning-checklist-icon.is-done {
    color: var(--synbex-success);
}

.photo-lightbox-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.85);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    cursor: zoom-out;
    padding: 24px;
}

.photo-lightbox-backdrop img {
    max-width: 92vw;
    max-height: 92vh;
    border-radius: 10px;
    box-shadow: var(--synbex-shadow);
    cursor: default;
}

/* ==========================================================================
   Visor de imagen con zoom (ImageLightbox.razor) — variante de arriba, solo
   para donde se pidió acercar (documentos y adjuntos de asamblea). El resto
   de pantallas que ya usaban .photo-lightbox-backdrop siguen exactamente
   igual: esto no les toca nada porque va detrás de ".is-zoomable".
   ========================================================================== */

.photo-lightbox-backdrop.is-zoomable img {
    cursor: zoom-in;
    transition: max-width 0.2s ease, max-height 0.2s ease;
}

/* Acercada: el fondo se vuelve desplazable (en vez de agrandar la imagen sin
   límite, lo que en una foto de muy alta resolución sería enorme e
   impráctico) y dejar de centrar dentro de él es lo que permite que el
   overflow se pueda recorrer completo con scroll, no solo lo que ya cabía. */
.photo-lightbox-backdrop.is-zoomable.is-zoomed {
    overflow: auto;
    align-items: flex-start;
    justify-content: flex-start;
    cursor: default;
}

.photo-lightbox-backdrop.is-zoomable.is-zoomed img {
    max-width: none;
    max-height: none;
    width: 200%;
    cursor: zoom-out;
    margin: auto;
}

.photo-lightbox-close {
    position: fixed;
    top: 18px;
    right: 18px;
    z-index: 1001;
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    font-size: 18px;
    cursor: pointer;
    backdrop-filter: blur(4px);
}

.photo-lightbox-close:hover {
    background: rgba(0, 0, 0, 0.75);
}

.synbex-booking-modal {
    max-width: 600px;
    width: 100%;
    max-height: calc(100vh - 40px);
    overflow-y: auto;
    cursor: default;
    transition: max-width 0.35s ease, padding 0.35s ease;
}

/* Pedido explícito: en escritorio se aprovecha más espacio (antes se quedaba
   fijo en 500px sin importar qué tan ancha fuera la pantalla) — mismo criterio
   de escalado que .cleaning-assign-modal-dialog/.assembly-create-modal-dialog. */
@media (min-width: 901px) {
    .synbex-booking-modal {
        max-width: 46vw;
    }
}

/* Aviso "Ya reservado ese día" — antes era un <p> suelto sin ningún ícono ni
   fondo, se perdía entre el resto del texto. Mismo lenguaje visual que
   .synbex-alert (ícono + fondo suave) pero en tono informativo, no de error. */
.booking-existing-note {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 14px;
    margin-bottom: 14px;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    font-size: 13.5px;
}

.booking-existing-note svg {
    flex-shrink: 0;
    width: 18px;
    height: 18px;
    margin-top: 1px;
}

.synbex-booking-modal-success {
    max-width: 260px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    padding: 32px 20px;
    text-align: center;
    animation: synbex-success-pop 0.3s ease;
}

.synbex-success-check {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    background: #16a34a;
    color: #fff;
    font-size: 32px;
    line-height: 64px;
    animation: synbex-check-pop 0.45s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Mismo círculo grande y centrado que synbex-success-check, pero en rojo y con
   el ícono de basurero — para las ventanas de confirmar eliminación. */
.resident-delete-icon {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    background: var(--synbex-danger-soft);
    color: var(--synbex-danger);
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 auto 16px;
}

.resident-delete-icon svg {
    width: 32px;
    height: 32px;
}

.synbex-success-text {
    margin: 0;
    font-weight: 600;
    font-size: 16px;
    color: var(--synbex-text, #1f2937);
}

@keyframes synbex-success-pop {
    from { transform: scale(0.85); opacity: 0; }
    to { transform: scale(1); opacity: 1; }
}

@keyframes synbex-check-pop {
    0% { transform: scale(0); opacity: 0; }
    60% { transform: scale(1.15); opacity: 1; }
    100% { transform: scale(1); }
}

/* ===== Formulario de limpieza (registrar una limpieza) — checklist + fotos =====
   Nombres propios (cleaning-checklist-form-*, cleaning-photo-upload-*), separados
   a propósito de .cleaning-checklist/.cleaning-checklist-item/.cleaning-photo-btn
   de más arriba: esas las sigue usando el historial de solo lectura del residente
   (MyUnitCleaning.razor, una lista <ul><li> con ícono fijo, no un formulario). */
.cleaning-checklist-progress-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 8px;
}

.cleaning-checklist-progress-label {
    font-size: 13px;
    font-weight: 700;
    color: var(--synbex-primary);
}

/* El porcentaje va DESPUÃS de "completado" (pedido explícito): queda
   "2 de 3 completado Â· 83%". Se separa con un punto medio puesto por CSS para
   no meter caracteres sueltos en el marcado. */
.cleaning-checklist-progress-percent::before {
    content: " Â· ";
    color: var(--synbex-text-muted);
}

.cleaning-checklist-progress-bar {
    height: 6px;
    border-radius: 999px;
    background: var(--synbex-border);
    overflow: hidden;
    margin-bottom: 14px;
}

/* Sin transition en width a propósito: Blazor Server actualiza este estilo
   parcheando el atributo "style" completo vía SignalR (no solo la propiedad),
   y en ese caso una transición CSS de width se puede quedar trabada en 0 —
   confirmado reproduciéndolo. La barra igual se ve bien sin animar. */
.cleaning-checklist-progress-fill {
    height: 100%;
    background: var(--synbex-primary);
    border-radius: 999px;
}

.cleaning-checklist-form {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 18px;
}

.cleaning-checklist-form-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-lg);
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
}

.cleaning-checklist-form-item:hover {
    border-color: var(--synbex-primary);
}

/* Se pinta por DOS caminos a propósito (pedido explícito de que marcar el
   checklist se sienta fluido):
     Â· :has(input:checked) â el navegador lo aplica en el mismo instante del
       toque, sin esperar nada;
     Â· .is-done â el estado ya confirmado por el servidor.
   Antes solo existía .is-done, así que cada marca se veía recién cuando volvía
   la respuesta por el circuito, y en celular con mala señal eso se siente
   trabado. Ahora el servidor solo confirma lo que ya se ve. */
.cleaning-checklist-form-item.is-done,
.cleaning-checklist-form-item:has(input:checked) {
    background: var(--synbex-primary-soft);
    border-color: var(--synbex-primary);
}

/* El checkbox nativo se mantiene (accesible por teclado/lector de pantalla),
   pero invisible — el círculo de al lado (cleaning-checklist-form-check) es lo
   que se ve, controlado por la clase is-done del label. */
.cleaning-checklist-form-item input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.cleaning-checklist-form-check {
    flex-shrink: 0;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    border: 2px solid var(--synbex-border);
    display: flex;
    align-items: center;
    justify-content: center;
    color: transparent;
    transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease, transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.cleaning-checklist-form-check svg {
    width: 15px;
    height: 15px;
}

.cleaning-checklist-form-item.is-done .cleaning-checklist-form-check,
.cleaning-checklist-form-item:has(input:checked) .cleaning-checklist-form-check {
    background: var(--synbex-primary);
    border-color: var(--synbex-primary);
    color: #fff;
    transform: scale(1.1);
}

.cleaning-checklist-form-label {
    font-size: 14.5px;
    font-weight: 500;
    color: var(--synbex-text);
}

.cleaning-checklist-form-item.is-done .cleaning-checklist-form-label,
.cleaning-checklist-form-item:has(input:checked) .cleaning-checklist-form-label {
    text-decoration: line-through;
    color: var(--synbex-text-muted);
}

/* Botones grandes de "Tomar foto"/"Galería" — mismo lenguaje visual que
   .hub-tile (tarjeta con sombra, ícono en círculo de color, se levanta al
   pasar el mouse) para que combine con el selector de destino de arriba en
   esta misma página, en vez del botón chico de antes. */
.cleaning-photo-upload-grid {
    display: flex;
    gap: 14px;
    flex-wrap: wrap;
    /* Centrado (pedido explícito) — con solo un botón visible (el caso más
       común: "Tomar foto" sin "Galería" cuando el conjunto exige cámara en
       vivo) se veía pegado a la izquierda en vez de centrado en la pantalla. */
    justify-content: center;
    margin: 20px 0 12px;
}

.cleaning-photo-upload-tile {
    position: relative;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    width: 140px;
    padding: 22px 16px;
    background: var(--synbex-surface);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-lg);
    box-shadow: var(--synbex-shadow);
    cursor: pointer;
    text-align: center;
    transition: border-color 0.25s ease, transform 0.25s cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 0.25s ease;
}

.cleaning-photo-upload-tile:hover {
    border-color: var(--synbex-primary);
    transform: translateY(-3px);
    box-shadow: 0 12px 28px -12px color-mix(in srgb, var(--synbex-primary) 45%, transparent);
}

/* Fondo azul sólido con ícono blanco (pedido explícito) en vez del celeste
   clarito de antes — solo afecta a "Tomar foto" (la cámara): "Galería" pisa
   esto con .is-teal, que es más específico. */
.cleaning-photo-upload-icon {
    width: 52px;
    height: 52px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-primary);
    color: #fff;
    transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.cleaning-photo-upload-icon svg {
    width: 26px;
    height: 26px;
}

.cleaning-photo-upload-icon.is-teal {
    background: rgba(13, 148, 136, 0.12);
    color: var(--synbex-phone-link);
}

.cleaning-photo-upload-tile:hover .cleaning-photo-upload-icon {
    transform: scale(1.12) rotate(-4deg);
}

.cleaning-photo-upload-label {
    font-weight: 600;
    font-size: 14px;
    color: var(--synbex-text);
}

.cleaning-photo-upload-tile input[type="file"] {
    position: absolute;
    inset: 0;
    opacity: 0;
    cursor: pointer;
    width: 100%;
}

.cleaning-photo-upload-tile.is-disabled {
    opacity: 0.6;
    pointer-events: none;
}

/* Encabezado "Destino: Apartamento X" al registrar una limpieza — antes era un
   .synbex-alert (fondo rosado de error + borde/texto azul encima, pedido
   explícito: se veía como un mensaje en rojo). Ahora es un título normal, sin
   fondo de alerta, con más espacio debajo antes del Checklist (pedido
   explícito) y el botón "â Volver" ahora vive AFUERA/arriba de este bloque
   (ver .cleaning-back-btn), no debajo del título. */
.cleaning-target-header {
    margin: 4px 0 34px;
}

.cleaning-target-kicker {
    display: block;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--synbex-text-muted);
}

.cleaning-target-title {
    margin: 2px 0 0;
    font-size: 21px;
    font-weight: 700;
    color: var(--synbex-primary);
}

/* Botón único "â Volver" (pedido explícito) — reemplaza lo que antes eran dos
   o tres botones distintos apilados ("Cambiar tipo", "Buscar de nuevo",
   "Cambiar destino"): uno solo, mismo azul que usa el resto de la app, con
   flecha. Se reusa en los dos momentos donde hace falta volver un paso: antes
   de elegir destino (GoBackOneStep) y ya con un destino elegido
   (ChangeTargetSelection) — ver Cleaning/Index.razor. */
.cleaning-back-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 14px;
    padding: 0;
    border: none;
    background: none;
    color: var(--synbex-primary);
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
}

.cleaning-back-btn:hover {
    color: var(--synbex-primary-hover);
}

.cleaning-back-btn svg {
    width: 17px;
    height: 17px;
}

/* Check verde centrado al guardar una limpieza (pedido explícito, reemplaza el
   mensaje rojo/alerta de antes) — se apaga solo a los 2 segundos, ver
   ShowSaveSuccess/HideSaveSuccessAfterDelayAsync en Cleaning/Index.razor. */
.cleaning-save-success-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(15, 17, 21, 0.4);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 300;
    animation: cleaning-save-success-fade 0.15s ease;
}

.cleaning-save-success-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    background: var(--synbex-surface);
    border-radius: var(--synbex-radius-lg);
    box-shadow: var(--synbex-shadow);
    padding: 34px 42px;
    text-align: center;
    max-width: 85vw;
}

.cleaning-save-success-icon {
    display: flex;
    color: #16a34a;
    animation: cleaning-save-success-pop 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.cleaning-save-success-icon svg {
    width: 56px;
    height: 56px;
}

.cleaning-save-success-title {
    font-size: 17px;
    font-weight: 700;
    color: var(--synbex-text);
}

.cleaning-save-success-detail {
    font-size: 12px;
    color: var(--synbex-text-muted);
}

@keyframes cleaning-save-success-pop {
    0% { transform: scale(0.4); opacity: 0; }
    60% { transform: scale(1.08); opacity: 1; }
    100% { transform: scale(1); }
}

@keyframes cleaning-save-success-fade {
    from { opacity: 0; }
    to { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .cleaning-save-success-icon,
    .cleaning-save-success-backdrop {
        animation: none;
    }
}

/* "0 de 6" al lado de "Fotos (obligatoria)" (pedido explícito) — chiquito y
   morado (--synbex-primary), el mismo tono que usa el resto de la app. */
.cleaning-photos-heading-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

.cleaning-photos-counter {
    flex-shrink: 0;
    font-size: 12px;
    font-weight: 700;
    color: var(--synbex-primary);
}

/* Confirmaciones "Â¿seguro que cancelas?" / "Â¿ya terminaste?" (pedido
   explícito) — mismo lenguaje visual liviano que .cleaning-save-success-*,
   sin encabezado con botón de cerrar (son solo dos opciones, no un formulario). */
.cleaning-confirm-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(15, 17, 21, 0.4);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 300;
    padding: 20px;
}

.cleaning-confirm-card {
    display: flex;
    flex-direction: column;
    gap: 18px;
    background: var(--synbex-surface);
    border-radius: var(--synbex-radius-lg);
    box-shadow: var(--synbex-shadow);
    padding: 24px;
    max-width: 360px;
    width: 100%;
}

.cleaning-confirm-text {
    margin: 0;
    font-size: 14.5px;
    line-height: 1.5;
    color: var(--synbex-text);
    text-align: center;
}

/* Ãcono del aviso flotante (error o confirmación) — mismo círculo que ya usa el
   check verde de "Limpieza registrada", en rojo cuando es un error. */
.cleaning-message-icon {
    display: flex;
    align-self: center;
    color: var(--synbex-primary);
}

.cleaning-message-icon.is-error {
    color: var(--synbex-danger);
}

.cleaning-message-icon svg {
    width: 42px;
    height: 42px;
}

/* Miniatura pendiente de subir (Cleaning/Index.razor) — pedido explícito: ya no
   una â chiquita en la esquina, sino un círculo translúcido con el basurero
   centrado, visible siempre en todas las fotos; un toque pregunta si eliminar
   (ver RequestDeletePhoto). */
.cleaning-pending-photo {
    position: relative;
    width: 100px;
    height: 100px;
    border-radius: 8px;
    overflow: hidden;
    cursor: pointer;
}

.cleaning-pending-photo-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.cleaning-pending-photo-delete {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 44px;
    height: 44px;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.55);
    display: flex;
    align-items: center;
    justify-content: center;
    animation: cleaning-pending-photo-delete-pop 0.15s ease;
}

.cleaning-pending-photo-delete svg {
    width: 22px;
    height: 22px;
    color: #fff;
}

@keyframes cleaning-pending-photo-delete-pop {
    from { transform: translate(-50%, -50%) scale(0.6); opacity: 0; }
    to { transform: translate(-50%, -50%) scale(1); opacity: 1; }
}

/* "Â¿Qué vas a limpiar?" — pedido explícito: centrado, más grande, en negrita y
   con el color de texto normal (antes usaba .page-subtitle, gris clarito, y se
   perdía por completo frente a las tres tarjetas de abajo). */
.cleaning-what-title {
    margin: 4px 0 20px;
    font-size: 22px;
    font-weight: 700;
    color: var(--synbex-text);
    text-align: center;
}

/* Pestaña "Pendientes" del limpiador: cada asignación es una tarjeta-botón
   completa (toda el área es clicable, no solo un link chiquito) que entra
   directo al flujo de checklist+fotos de siempre. */
.cleaning-pending-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.cleaning-pending-card {
    display: flex;
    align-items: center;
    gap: 14px;
    width: 100%;
    text-align: left;
    background: var(--synbex-bg);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-md);
    padding: 14px 16px;
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease;
}

.cleaning-pending-card:hover {
    border-color: var(--synbex-primary);
}

.cleaning-pending-card.is-overdue {
    border-color: var(--synbex-danger);
}

/* Vence HOY — alerta ámbar, un escalón antes de vencida (roja). Mismo tono que
   .asset-status-badge.is-due-soon, para quedar coherente con Equipos. */
.cleaning-pending-card.is-due-today {
    border-color: #d97706;
}

.cleaning-pending-card-icon {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.cleaning-pending-card-icon svg {
    width: 20px;
    height: 20px;
}

.cleaning-pending-card-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.cleaning-pending-card-title {
    font-weight: 600;
    font-size: 15px;
    color: var(--synbex-text);
}

.cleaning-pending-card-due {
    font-size: 13px;
    color: var(--synbex-text-muted);
}

.cleaning-pending-card-due.is-overdue {
    color: var(--synbex-danger);
    font-weight: 600;
}

.cleaning-pending-card-due.is-due-today {
    color: #d97706;
    font-weight: 600;
}

.cleaning-pending-card-due svg {
    width: 13px;
    height: 13px;
    vertical-align: -2px;
}

.cleaning-pending-card-notes {
    font-size: 13px;
    color: var(--synbex-text-muted);
    font-style: italic;
}

.cleaning-pending-card-arrow {
    flex-shrink: 0;
    color: var(--synbex-text-muted);
    display: flex;
}

.cleaning-pending-card-arrow svg {
    width: 18px;
    height: 18px;
}

/* Historial de limpieza del admin — pedido explícito: "que no sea como una
   tabla, sino más individual cada uno". Cada limpieza es su propia tarjeta
   expandible (ícono + destino + resumen), mismo lenguaje visual que
   .cleaning-pending-card de arriba (que ya es esta idea, del lado del
   limpiador) — la diferencia es que acá el "botón" es solo el resumen: la
   tarjeta se queda fija para poder mostrar el detalle debajo al expandir,
   en vez de navegar a otro lado. */
.cleaning-history-month-nav {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
}

.cleaning-history-search-row {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 18px;
    flex-wrap: wrap;
}

.cleaning-history-search-wrap {
    position: relative;
    flex: 1;
    min-width: 220px;
}

.cleaning-history-search-wrap > svg {
    position: absolute;
    left: 12px;
    top: 50%;
    transform: translateY(-50%);
    width: 16px;
    height: 16px;
    color: var(--synbex-text-muted);
    pointer-events: none;
}

.cleaning-history-search-wrap input {
    width: 100%;
    box-sizing: border-box;
    padding: 10px 12px 10px 36px;
    font-size: 14px;
    font-family: inherit;
    color: var(--synbex-text);
    background: var(--synbex-bg);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-md);
}

.cleaning-history-search-wrap input:focus {
    outline: none;
    border-color: var(--synbex-primary);
}

/* Selector compuesto (no solo .cleaning-history-count) a propósito: la clase
   base .tab-count está definida más abajo en este archivo, y con la misma
   especificidad (una sola clase cada una) el orden en el archivo decide —
   así gana esta sin importar dónde quede .tab-count. */
.tab-count.cleaning-history-count {
    flex-shrink: 0;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    font-weight: 700;
    white-space: nowrap;
}

.cleaning-history-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.cleaning-history-card {
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-bg);
    overflow: hidden;
    transition: border-color 0.15s ease;
}

.cleaning-history-card.is-expanded {
    border-color: var(--synbex-primary);
}

.cleaning-history-card-summary {
    display: flex;
    align-items: center;
    gap: 14px;
    width: 100%;
    text-align: left;
    background: transparent;
    border: none;
    padding: 14px 16px;
    cursor: pointer;
    font: inherit;
    color: inherit;
}

.cleaning-history-card-summary:hover {
    background: var(--synbex-primary-soft);
}

.cleaning-history-card-icon {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.cleaning-history-card-icon svg {
    width: 20px;
    height: 20px;
}

.cleaning-history-card-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.cleaning-history-card-title {
    font-weight: 600;
    font-size: 15px;
    color: var(--synbex-text);
}

.cleaning-history-card-meta {
    font-size: 12.5px;
    color: var(--synbex-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cleaning-history-card-checklist {
    flex-shrink: 0;
    font-size: 12.5px;
    font-weight: 700;
    padding: 3px 10px;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.12);
    color: var(--synbex-text-muted);
}

.cleaning-history-card-checklist.is-complete {
    background: rgba(22, 163, 74, 0.12);
    color: var(--synbex-success);
}

.cleaning-history-card-chevron {
    flex-shrink: 0;
    color: var(--synbex-text-muted);
    font-size: 12px;
    width: 16px;
    text-align: center;
}

/* Detalle expandido — antes era una fila de tabla sin ningún estilo propio
   (solo lo que heredaba del navegador); ahora vive dentro de la tarjeta,
   indentado para alinear con el texto del resumen (ancho del ícono + gap de
   .cleaning-history-card-summary de arriba: 40px + 14px + 16px â 70px). */
.cleaning-detail-row {
    padding: 14px 16px 16px 70px;
    border-top: 1px solid var(--synbex-border);
}

.cleaning-detail-comment {
    margin: 0 0 12px;
    font-size: 13.5px;
    color: var(--synbex-text);
}

.cleaning-detail-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
}

.cleaning-result-checklist {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 180px;
}

.cleaning-result-item {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13.5px;
    color: var(--synbex-text-muted);
}

.cleaning-result-item.is-done {
    color: var(--synbex-text);
}

.cleaning-result-icon {
    flex-shrink: 0;
    width: 18px;
    height: 18px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    font-size: 11px;
    background: rgba(0, 0, 0, 0.08);
    color: var(--synbex-text-muted);
}

.cleaning-result-item.is-done .cleaning-result-icon {
    background: var(--synbex-success);
    color: #fff;
}

@media (max-width: 640px) {
    .cleaning-detail-row {
        padding-left: 16px;
    }

    .cleaning-history-card-meta {
        white-space: normal;
    }
}

.cleaning-confirm-actions {
    display: flex;
    gap: 10px;
}

.cleaning-confirm-actions .btn-synbex {
    flex: 1;
    justify-content: center;
}

/* Guardar/Cancelar a 50/50 (pedido explícito) al registrar una limpieza. */
.cleaning-save-actions {
    display: flex;
    gap: 10px;
    margin-top: 8px;
}

.cleaning-save-actions .btn-synbex {
    flex: 1;
    justify-content: center;
}

@media (max-width: 480px) {
    .cleaning-photo-upload-tile {
        width: 120px;
        padding: 18px 12px;
    }
}

.destino-field {
    position: relative;
}

.destino-suggestions {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 20;
    margin-top: 4px;
    max-height: 220px;
    overflow-y: auto;
    background: var(--synbex-surface);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-md);
    box-shadow: var(--synbex-shadow);
}

.destino-suggestion-item {
    display: block;
    width: 100%;
    padding: 9px 12px;
    text-align: left;
    background: transparent;
    border: none;
    border-bottom: 1px solid var(--synbex-border);
    color: var(--synbex-text);
    font-family: inherit;
    font-size: 13.5px;
    cursor: pointer;
    transition: background 0.15s ease;
}

.destino-suggestion-item:last-child {
    border-bottom: none;
}

.destino-suggestion-item:hover {
    background: var(--synbex-primary-soft);
}

.destino-suggestion-item.is-selected {
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    font-weight: 600;
}

/* Selector de código de país del teléfono (ver CountryCodeSelect.razor) — botón
   angosto que solo muestra "+57" cerrado (antes un <select> nativo mostraba el
   nombre del país completo, y con países de nombre largo el texto se salía de
   la caja). Reutiliza el mismo look de lista desplegable que .destino-suggestions
   de arriba, solo que angosto (no se necesita todo el ancho del campo). */
.country-code-select {
    position: relative;
    flex-shrink: 0;
}

.country-code-trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 6px;
    width: 92px;
    cursor: pointer;
}

.country-code-chevron {
    font-size: 10px;
    color: var(--synbex-text-muted);
}

.country-code-list {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 20;
    margin-top: 4px;
    width: max-content;
    min-width: 100%;
    max-width: 240px;
    max-height: 240px;
    overflow-y: auto;
    background: var(--synbex-surface);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-md);
    box-shadow: var(--synbex-shadow);
}

.destino-warning-text {
    margin: 6px 0 0;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--synbex-danger);
}

.cleaning-chart {
    display: flex;
    align-items: flex-end;
    gap: 18px;
    overflow-x: auto;
    padding: 8px 4px 0;
    min-height: 190px;
}

.cleaning-chart-bar-wrap {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    min-width: 44px;
}

.cleaning-chart-count {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--synbex-text);
}

.cleaning-chart-bar {
    width: 28px;
    min-height: 4px;
    background: var(--synbex-primary);
    border-radius: 6px 6px 0 0;
}

/* Barras horizontales — SOLO para el reporte de limpieza (Report.razor añade
   esta clase junto con .cleaning-chart; el widget del panel de inicio y de
   Mis limpiezas siguen con las barras verticales de siempre, sin tocar). */
.cleaning-chart-horizontal {
    flex-direction: column;
    align-items: stretch;
    overflow-x: visible;
    gap: 10px;
    min-height: 0;
    /* Con pocos meses (a veces solo 1) la barra llenaba TODO el ancho de la
       tarjeta y se veía enorme — pedido explícito: que no pase de la mitad. */
    max-width: 50%;
    margin: 0 auto;
}

.cleaning-chart-horizontal .cleaning-chart-bar-wrap {
    flex-direction: row;
    align-items: center;
    gap: 10px;
    min-width: 0;
    width: 100%;
}

.cleaning-chart-horizontal .cleaning-chart-label {
    width: 70px;
    flex-shrink: 0;
    text-align: right;
}

.cleaning-chart-bar-track {
    flex: 1;
    height: 18px;
    background: var(--synbex-bg);
    border-radius: 6px;
    overflow: hidden;
}

.cleaning-chart-horizontal .cleaning-chart-bar {
    height: 100%;
    border-radius: 6px;
}

.cleaning-chart-horizontal .cleaning-chart-count {
    width: 26px;
    flex-shrink: 0;
    text-align: left;
}

.cleaning-chart-label {
    font-size: 12px;
    color: var(--synbex-text-muted);
    white-space: nowrap;
}

.cleaning-report-summary-row {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    align-items: stretch;
}

.cleaning-report-summary-row .stat-row {
    flex: 1 1 200px;
    gap: 10px;
    margin-bottom: 0;
    flex-wrap: nowrap;
}

.cleaning-report-summary-row .stat-card {
    min-width: 0;
    flex: 1;
    padding: 12px 14px;
}

/* Cada limpieza del reporte con su propio número y más aire entre una y otra
   (pedido explícito: antes quedaban muy pegadas, no se notaba dónde empezaba y
   terminaba cada una). La tarjeta interna (.cleaning-task-card) ya trae su
   propio margin-bottom de 16px; esta clase suma el resto del espacio. */
.cleaning-report-entry {
    margin-bottom: 22px;
}

.cleaning-report-entry-badge {
    display: inline-block;
    margin-bottom: 8px;
    padding: 3px 12px;
    border-radius: 999px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    font-size: 12px;
    font-weight: 700;
}

.cleaning-report-cta-btn {
    width: 100%;
    padding: 12px 20px;
    font-size: 15px;
    box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--synbex-glow-color, var(--synbex-primary)) 55%, transparent);
}

.cleaning-report-cta-btn:hover {
    box-shadow: 0 8px 20px -6px color-mix(in srgb, var(--synbex-glow-color, var(--synbex-primary)) 65%, transparent);
}

.cleaning-report-generate-btn {
    width: 100%;
    padding: 16px 20px;
    font-size: 16px;
}

/* En computadora, el calendario y el panel de reporte (presets + estadísticas +
   Generar reporte) se ven lado a lado; en móvil el panel queda oculto y solo
   se usa el botón "Generar reporte" de ancho completo, que abre la ventana
   flotante — pedido explícito: "móvil como está ahorita está bien". */
.cleaning-desktop-report-panel {
    display: none;
}

/* El botón de reporte va centrado arriba, en TODAS las resoluciones (pedido
   explícito: que en computadora funcione igual que en celular). Antes en
   computadora había un panel lateral con los mismos botones y el gráfico; se
   quitó porque la ventana flotante ya trae todo eso adentro — presets, rango a
   medida, resumen y gráfico — así que no se perdió nada, solo dejó de estar
   duplicado en dos sitios distintos según el ancho de pantalla. */
.cleaning-report-cta-btn {
    max-width: 340px;
    margin-left: auto;
    margin-right: auto;
}

@media (min-width: 900px) {
    /* El calendario pasa a ocupar el ancho completo: ya no comparte fila con
       el panel de reporte. */
    .cleaning-page-top .cleaning-calendar-card {
        margin-bottom: 16px;
    }

}

.cleaning-panel-header {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 14px;
}

.cleaning-panel-icon {
    width: 40px;
    height: 40px;
    flex-shrink: 0;
}

.cleaning-stat-card {
    flex-direction: row;
    align-items: center;
    text-align: left;
    gap: 12px;
}

.cleaning-stat-icon {
    width: 40px;
    height: 40px;
    border-radius: var(--synbex-radius-md);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.cleaning-stat-icon-purple {
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.cleaning-stat-icon-green {
    background: color-mix(in srgb, var(--synbex-success) 15%, transparent);
    color: var(--synbex-success);
}

.cleaning-desktop-report-panel .cleaning-chart {
    min-height: 0;
}

.cleaning-panel-chart-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-weight: 600;
    font-size: 14px;
    color: var(--synbex-text);
    margin-bottom: 6px;
}

.cleaning-panel-chart-period {
    font-size: 12px;
    font-weight: 600;
    color: var(--synbex-text-muted);
    background: var(--synbex-bg);
    padding: 3px 10px;
    border-radius: 999px;
}

.cleaning-panel-hint {
    margin: 8px 0 0;
    font-size: 12px;
    color: var(--synbex-text-muted);
    text-align: center;
}

.cleaning-calendar-nav-btn {
    width: 32px;
    height: 32px;
    padding: 0;
    border-radius: 50%;
}

.cleaning-report-summary-row .cleaning-chart-card {
    flex: 1 1 240px;
    margin: 0;
}

.cleaning-report-summary-row .cleaning-chart-card h3 {
    margin-bottom: 8px;
    font-size: 14px;
}

.cleaning-report-summary-row .cleaning-chart {
    min-height: 100px;
    gap: 10px;
    padding-top: 4px;
}

.report-light-theme {
    background: var(--synbex-surface);
    color: var(--synbex-text);
    border-radius: var(--synbex-radius-lg);
    padding: 16px;
}

.report-light-theme .stat-card {
    align-items: center;
    text-align: center;
}

/* La fila de tarjetas (Limpiezas registradas/Destinos atendidos/Checklist
   promedio) quedaba pegada a la izquierda como grupo aunque cada tarjeta ya
   estuviera centrada por dentro — pedido explícito: centrar todo el grupo. */
.report-light-theme .stat-row {
    justify-content: center;
}

/* La tarjeta de datos del reporte (tabla o mensaje de "sin resultados") no lleva
   borde/radio/sombra — solo la línea gris entre filas que ya trae .synbex-table.
   Las tarjetas de arriba (Entregas registradas/Pendientes/Entregadas) usan
   .stat-card, no .synbex-card, así que no las toca esta regla. */
.report-light-theme .synbex-card {
    border: none;
    border-radius: 0;
    box-shadow: none;
}

/* Al exportar/imprimir (window.print) el reporte siempre sale en tema claro,
   sin importar el tema elegido en pantalla — solo aplica dentro de @media print. */
@media print {
    .report-light-theme {
        --synbex-primary: #2c5cfc;
        --synbex-primary-hover: #254cd1;
        --synbex-primary-soft: #eef2ff;
        --synbex-danger: #dc2626;
        --synbex-danger-soft: #fef2f2;
        --synbex-success: #16a34a;
        --synbex-success-hover: #15803d;
        --synbex-text: #111827;
        --synbex-text-muted: #6b7280;
        --synbex-border: #e5e7eb;
        --synbex-surface: #ffffff;
        --synbex-bg: #f6f7fb;
        /* Más aire que en pantalla (16px) — al pasar el margen de @page a 0
           (ver más abajo) todo el respiro visual de la página impresa depende
           de este padding. Abajo lleva más (46px) para dejarle sitio al pie
           fijo (position:fixed, ver .print-report-footer) y que no tape la
           última fila de la tabla. */
        padding: 28px 24px 46px;
    }

    /* El navegador (Chrome) dibuja su PROPIO encabezado/pie en negro (fecha +
       título arriba, URL + "Página X de Y" abajo) dentro del margen de la
       hoja — nada de eso es contenido nuestro, así que ningún CSS de acá
       adentro lo toca. La única forma de quitarlo es dejarle el margen de la
       hoja en 0: sin espacio donde dibujarlo, Chrome no lo pinta. Pedido
       explícito del usuario (prefirió esto a mantener el número de página
       automático, que solo viene junto con ese texto negro). */
    @page {
        size: auto;
        margin: 0;
    }
}

/* Encabezado/pie compartido de TODOS los reportes imprimibles (Limpieza,
   Asistencia de asamblea, Resultados de votación, ...) — formato de referencia
   que trajo el usuario: barra de color arriba con el logo pegado a la
   izquierda y la fecha de generación debajo del logo, título y subtítulo(s)
   centrados debajo, y un pie fijo al fondo de cada hoja. Se define una sola
   vez acá (ver PrintReportHeader.razor / PrintReportFooter.razor en
   Components/Shared) para que quede igual en todos los reportes — no copiar
   este CSS a mano en cada página.
   La barra completa usa var(--synbex-primary) — el mismo índigo que las
   barras del gráfico — pedido explícito: "el celeste que está de lado a
   lado... ese color tiene que ser igual al del gráfico". La caja blanca
   detrás del logo (.print-report-logo-box) se queda BLANCA — eso NO cambia,
   solo la barra de fondo.
   Contenida dentro del padding de .report-light-theme en pantalla (esquinas
   redondeadas); al imprimir se pega a los 3 bordes de la hoja (ver el
   @media print más abajo). */
.print-report-header {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 8px;
    min-height: 72px;
    padding: 16px 24px;
    margin-bottom: 20px;
    border-radius: 12px;
    background: var(--synbex-primary);
    box-sizing: border-box;
}

.print-report-header-meta {
    font-size: 13.5px;
    font-weight: 700;
    color: #ffffff;
}

.print-report-header-brand {
    display: flex;
    align-items: center;
}

.print-report-logo-box {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: #fff;
    border-radius: 8px;
    padding: 10px 18px;
    box-sizing: border-box;
}

.print-report-logo {
    display: block;
    height: 64px;
    max-width: 260px;
    object-fit: contain;
}

/* Texto de respaldo cuando el conjunto no tiene logo subido — vive sobre la
   caja BLANCA (.print-report-logo-box), así que necesita color oscuro, no
   blanco como el resto del texto del encabezado. */
.print-report-wordmark {
    font-weight: 700;
    font-size: 30px;
    letter-spacing: 0.02em;
    color: var(--synbex-primary);
}

/* Solo al imprimir/exportar: la barra pasa a pegarse a los 3 bordes de la
   hoja (arriba, izquierda, derecha) sin margen blanco — pedido explícito:
   "que tenga margen cero... solamente blanco de lo celeste para abajo".
   Los márgenes negativos cancelan exactamente el padding de impresión de
   .report-light-theme (ver el otro @media print más arriba) — esquinas
   rectas porque toca el borde físico de la hoja, no tendría sentido
   redondearlas ahí. En pantalla (sin imprimir) se queda como tarjeta
   contenida con las 4 esquinas redondeadas. */
@media print {
    .print-report-header {
        margin: -28px -24px 20px;
        border-radius: 0;
        min-height: 96px;
    }
}

.print-report-title {
    margin: 0 0 4px;
    font-size: 30px;
    font-weight: 700;
    color: var(--synbex-text);
    text-align: center;
}

.print-report-subtitle {
    margin: 0 0 4px;
    font-size: 13.5px;
    color: var(--synbex-text-muted);
    text-align: center;
}

.print-report-section-heading {
    margin: 22px 0 10px;
    font-size: 15px;
    font-weight: 700;
    color: var(--synbex-text);
}

.print-report-footer {
    margin-top: 24px;
    padding-top: 12px;
    border-top: 1px solid var(--synbex-border);
    text-align: center;
    font-size: 11.5px;
    color: var(--synbex-text-muted);
}

/* Solo al imprimir: el pie queda fijo al fondo de CADA hoja (no solo al
   final del contenido) — pedido explícito: "que esté bastante abajo... en
   todas las hojas que siempre esté el pie". position:fixed es la única forma
   de lograr esto sin soporte de @page/margin-boxes (que Chrome no
   implementa): a diferencia del contenido normal, los elementos fixed SÃ se
   repiten en cada hoja impresa. El padding-bottom extra en .report-light-theme
   (ver el otro @media print más arriba) le deja espacio para no tapar la
   última fila de la tabla. */
@media print {
    .print-report-footer {
        position: fixed;
        left: 24px;
        right: 24px;
        bottom: 6px;
        margin-top: 0;
        border-top: 1px solid var(--synbex-border);
    }
}

/* Tabla de reportes: encabezados en mayúscula sobre fondo gris claro, como en
   la referencia — la etiqueta en el <th> se queda en minúscula/normal en el
   markup (por los enlaces de orden ââ) y esto solo la transforma visualmente. */
.print-report-table thead th {
    text-transform: uppercase;
    letter-spacing: 0.03em;
    font-size: 11px;
    color: var(--synbex-text-muted);
    background: var(--synbex-bg);
    /* .synbex-table th (global, usada en toda la app) trae padding-top: 0 —
       se nota bien ahí porque casi nunca tiene fondo propio, pero acá SÃ
       tiene fondo gris (línea de arriba) y el texto quedaba pegado al borde
       de arriba. Mismo padding arriba y abajo — pedido explícito — para que
       la letra quede centrada verticalmente en la celda. */
    padding: 10px 12px;
    vertical-align: middle;
}

/* Ãcono de orden (â²/â¼ con línea, ver SortIndicator en Report.razor) al lado
   del texto del encabezado — un poco más chico que el tamaño por defecto del
   ícono (26px) para que no domine sobre la letra. */
.sortable-th-icon {
    display: inline-flex;
    vertical-align: middle;
    margin-left: 4px;
}

.sortable-th-icon svg {
    width: 11px;
    height: 11px;
}

/* Sin límite de ancho, la tarjeta ocupaba todo el ancho de la página en
   computadora y cada casilla del calendario se veía enorme — se topa a un
   ancho cómodo de escritorio; en celular sigue ocupando el 100% normal. */
/* Entre 640 y 900 se topa a un ancho cómodo y va centrado. Desde 900 se
   ensancha para alinearse con las tarjetas de "Historial por mes" — ver el
   bloque de abajo, después de .cleaning-calendar-day. */
@media (min-width: 640px) and (max-width: 899.98px) {
    .cleaning-calendar-card {
        max-width: 380px;
        margin-left: auto;
        margin-right: auto;
    }
}

.cleaning-calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 4px;
    text-align: center;
}

.cleaning-calendar-weekday {
    font-size: 11px;
    color: var(--synbex-text-muted);
    font-weight: 600;
    padding-bottom: 4px;
}

.cleaning-calendar-day {
    position: relative;
    aspect-ratio: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    font-size: 13px;
    color: var(--synbex-text);
    background: var(--synbex-bg);
}

.cleaning-calendar-day.is-empty {
    background: transparent;
}

.cleaning-calendar-day.has-cleaning {
    background: var(--synbex-primary);
    color: #fff;
    font-weight: 700;
    cursor: pointer;
}

/* En computadora el calendario se ensancha hasta el mismo ancho que las
   tarjetas de "Historial por mes", para que las dos queden en la misma columna.
   Va DESPUÃS de las reglas de arriba a propósito: con la misma especificidad
   gana la última, y puesto más arriba el max-width de 380px y el aspect-ratio
   lo pisaban. */
@media (min-width: 900px) {
    .cleaning-calendar-card {
        max-width: none;
    }

    /* Ensanchar SIN crecer hacia abajo: las casillas eran cuadradas
       (aspect-ratio: 1), así que con la tarjeta más ancha habrían crecido en
       alto en la misma proporción. Se les fija la altura que ya tenían —51px,
       medido antes de tocar nada— y así solo se estiran a lo ancho. */
    .cleaning-calendar-day {
        aspect-ratio: auto;
        height: 51px;
    }
}

/* Ventana flotante grande que abre un día marcado del calendario (ver
   OnDayClick en MyUnitCleaning.razor) — más ancha y más alta que el modal
   chico de .settings-modal-dialog (440px) porque adentro va el detalle
   completo de la limpieza: fecha, quién la hizo, checklist y fotos, con
   espacio de sobra — pedido explícito, sobre todo en celular. */
.cleaning-day-modal {
    max-width: 760px;
    width: 100%;
}

@media (max-width: 700px) {
    .cleaning-day-modal {
        min-height: min(640px, calc(100vh - 24px));
        max-height: calc(100vh - 24px);
    }
}

/* Antes era texto plano en negrita ("muy negro, muy simple", pedido explícito
   de mejorarlo) — ahora cada dato tiene su propio ícono de color, mismo
   lenguaje visual que .account-field-row (Mi cuenta). */
.cleaning-day-detail-meta {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.cleaning-day-detail-meta-row {
    display: flex;
    align-items: flex-start;
    gap: 12px;
}

.cleaning-day-detail-meta-icon {
    flex-shrink: 0;
    width: 34px;
    height: 34px;
    margin-top: 1px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}

.cleaning-day-detail-meta-icon svg {
    width: 18px;
    height: 18px;
}

.cleaning-day-detail-meta-icon.is-blue {
    background: rgba(37, 99, 235, 0.12);
    color: #2563eb;
}

.cleaning-day-detail-meta-icon.is-purple {
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.cleaning-day-detail-meta-icon.is-orange {
    background: rgba(217, 119, 6, 0.14);
    color: #d97706;
}

.cleaning-day-detail-meta-label {
    display: block;
    font-size: 12px;
    font-weight: 600;
    color: var(--synbex-text-muted);
}

.cleaning-day-detail-meta-value {
    display: block;
    font-size: 14.5px;
    font-weight: 600;
    color: var(--synbex-text);
    margin-top: 1px;
}

/* "Checklist" / "Fotos" con su propio ícono de color en vez de solo texto en
   negrita — la línea arriba se queda para marcar el cambio de sección en una
   ventana tan alta. */
.cleaning-day-detail-section {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 22px 0 12px;
    padding-top: 16px;
    border-top: 1px solid var(--synbex-border);
    font-size: 14.5px;
    font-weight: 700;
    color: var(--synbex-text);
}

.cleaning-day-detail-section-icon {
    flex-shrink: 0;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}

.cleaning-day-detail-section-icon svg {
    width: 14px;
    height: 14px;
}

.cleaning-day-detail-section-icon.is-teal {
    background: rgba(13, 148, 136, 0.14);
    color: #0d9488;
}

.cleaning-day-detail-section-icon.is-purple {
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

/* Fotos más grandes que en la tarjeta compacta de la lista por mes (90px) —
   pedido explícito: "que no sea tan pequeño", acá hay más ancho disponible. */
.cleaning-day-detail .cleaning-photo-grid-preview {
    grid-template-columns: repeat(2, 130px);
}

.cleaning-day-detail .cleaning-photo-grid-preview .cleaning-photo-thumb,
.cleaning-day-detail .cleaning-photo-more {
    width: 130px;
    height: 130px;
}

.cleaning-calendar-caption {
    margin: 10px 0 0;
    font-size: 12px;
    color: var(--synbex-text-muted);
}

/* Calendario de Solicitudes de reserva: pendiente en amarillo, confirmada en
   verde (mismos tonos que .badge-pending/.badge-active), y cualquier fecha ya
   pasada en gris — sin importar el estado, porque ya no aplica ninguna accion. */
.cleaning-calendar-day.has-pending {
    background: rgba(217, 119, 6, 0.14);
    color: #d97706;
    font-weight: 700;
    cursor: pointer;
}

.cleaning-calendar-day.has-confirmed {
    background: rgba(22, 163, 74, 0.14);
    color: #16a34a;
    font-weight: 700;
    cursor: pointer;
}

.cleaning-calendar-day.is-past {
    background: rgba(107, 114, 128, 0.14);
    color: var(--synbex-text-muted);
}

/* Un dia pasado que SI tuvo una reserva (pendiente o confirmada) se distingue
   del resto de dias pasados (vacios) con un gris bien mas marcado. */
.cleaning-calendar-day.is-past-reserved {
    background: rgba(107, 114, 128, 0.4);
    color: var(--synbex-text);
    font-weight: 700;
    cursor: pointer;
}

.reservations-top-grid {
    display: flex;
    flex-direction: column;
    gap: 16px;
    margin-bottom: 20px;
}

/* "Todas" queda sola arriba, y Confirmadas/Pendientes/Finalizadas se reparten
   una fila aparte debajo — en vez de las cuatro pestañas repartidas en una
   sola fila (ver .tab-header, que sigue siendo la base: gap y margen). */
.reservations-tab-header {
    flex-direction: column;
}

.reservations-tab-row {
    display: flex;
    gap: 8px;
}

/* Pestañas de estado reutilizables (Mis reservas, Mis mantenimientos, y
   cualquier otra lista que se divida por estado): se reparten el ancho a
   partes iguales; la activa queda resaltada con el color primario. */
.tab-header {
    display: flex;
    gap: 8px;
    margin-bottom: 14px;
}


.tab-btn {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    border: 1px solid var(--synbex-border);
    background: var(--synbex-bg);
    color: var(--synbex-text-muted);
    font-weight: 600;
    font-size: 14px;
    padding: 10px 12px;
    border-radius: var(--synbex-radius-md);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

/* Fondo suave (no el mismo morado sólido de los botones de acción como
   "Nueva solicitud") para que la pestaña activa se distinga claramente de
   un botón de acción primario. */
.tab-btn.is-active {
    background: var(--synbex-primary-soft);
    border-color: var(--synbex-primary);
    color: var(--synbex-primary);
}

.tab-count {
    font-size: 12px;
    font-weight: 700;
    background: rgba(0, 0, 0, 0.12);
    border-radius: 999px;
    padding: 1px 7px;
    line-height: 1.5;
}

.tab-btn.is-active .tab-count {
    background: var(--synbex-primary);
    color: #fff;
}

/* En celular cuatro pestañas con su contador (Pendiente/Vencida/Completada/
   Cancelada) no caben en una fila, y con "flex: 1" (base 0) se repartían el
   ancho por igual y recortaban el texto. Con base "auto" cada pestaña mide lo
   que necesita y la fila envuelve SOLO cuando de verdad no cabe — las
   pantallas de tres pestañas que ya entraban siguen en una sola fila.
   Va después de .tab-btn a propósito: misma especificidad, gana la última. */
@media (max-width: 600px) {
    .tab-header {
        flex-wrap: wrap;
    }

    .tab-btn {
        flex: 1 1 auto;
        min-width: 0;
        padding: 10px 10px;
        gap: 5px;
    }
}

.tab-btn svg {
    width: 16px;
    height: 16px;
}

/* Pestaña "Pendientes" del admin — si hay pendientes y no es la pestaña activa,
   parpadea en naranja para llamar la atención, pero el fondo se queda gris
   (el mismo tab-btn de siempre); al hacerle clic y quedar activa, se ve como
   cualquier otra pestaña activa (azul), sin parpadeo. */
.tab-btn.has-pending-alert:not(.is-active) {
    border-color: #d97706;
    color: #d97706;
    animation: tab-pending-blink 1.4s ease-in-out infinite;
}

.tab-btn.has-pending-alert:not(.is-active) .tab-count {
    background: #d97706;
    color: #fff;
}

@keyframes tab-pending-blink {
    0%, 100% {
        box-shadow: 0 0 0 0 rgba(217, 119, 6, 0.5);
    }

    50% {
        box-shadow: 0 0 0 5px rgba(217, 119, 6, 0);
    }
}

@media (prefers-reduced-motion: reduce) {
    .tab-btn.has-pending-alert:not(.is-active) {
        animation: none;
    }
}

/* Selector de mes de "Mis paquetes" (pestaña Historial de retiros) — en
   escritorio se queda pegado a la derecha de las pestañas, como siempre. */
.packages-month-select {
    width: auto;
    margin-left: auto;
}

/* ==========================================================================
   Pestañas de Anuncios (rediseño, pedido explícito 2026-09-29, sobre una
   imagen de referencia) — clases PROPIAS de esta pantalla, no las .tab-* de
   arriba: esas las usan otras 14 pantallas y este look (una sola barra
   continua, línea vertical entre pestañas, solo el borde de ABAJO marca la
   activa) es solo para acá.
   ========================================================================== */
.announcement-tab-header {
    display: flex;
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-md);
    overflow: hidden;
}

.announcement-tab-btn {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 12px;
    background: var(--synbex-surface);
    color: var(--synbex-text-muted);
    font-weight: 600;
    font-size: 14px;
    border: none;
    border-bottom: 3px solid transparent;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

/* Línea vertical entre pestañas — border-left en vez de gap, para que quede
   pegada a la barra (una sola pieza) y no un espacio suelto entre botones. */
.announcement-tab-btn + .announcement-tab-btn {
    border-left: 1px solid var(--synbex-border);
}

.announcement-tab-btn.is-active {
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    border-bottom-color: var(--synbex-primary);
}

.announcement-tab-btn svg {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

.announcement-tab-btn.is-active .tab-count {
    background: var(--synbex-primary);
    color: #fff;
}

/* Pedido explícito (2026-09-30, ajuste): lo que parpadea es el CÍRCULO
   (background), no el número — antes escalaba todo el elemento y el
   número también se agrandaba. Ahora el número se queda quieto y solo el
   fondo alterna entre dos tonos del mismo anaranjado del botón Desactivar
   (#d97706), para que quede claro que es "lo mismo que pasó al desactivar". */
@keyframes tab-count-pulse {
    0%, 100% {
        background-color: #d97706;
    }

    50% {
        background-color: #fbbf24;
    }
}

.tab-count.is-pulsing {
    background-color: #d97706;
    color: #fff;
    animation: tab-count-pulse 900ms ease-in-out infinite;
}

@media (max-width: 600px) {
    .announcement-tab-btn {
        flex: 1 1 auto;
        min-width: 0;
        padding: 10px 8px;
        gap: 5px;
        font-size: 13px;
    }
}

@media (max-width: 600px) {
    /* En celular ya no cabe al lado de los dos botones — pasa a su propia
       fila debajo, a un 60% de ancho y centrado, en vez de ir al 100%. */
    .packages-tab-header {
        flex-wrap: wrap;
    }

    .packages-month-select {
        flex: 0 0 60%;
        margin: 10px auto 0;
    }
}

@media (max-width: 600px) {
    /* Con 3 pestañas (Mis mantenimientos) el texto+ícono no cabe cómodo con el
       tamaño de escritorio — se compacta un poco solo en móvil. */
    .tab-btn {
        font-size: 12.5px;
        padding: 9px 6px;
        gap: 4px;
    }

    .tab-btn svg {
        width: 16px;
        height: 16px;
    }

    /* Casos tiene 5 pestañas — no caben cómodas en una sola fila en celular.
       Se agrupan en cuadrícula de 2, con "Todos" (la más usada, va primera)
       ocupando la fila completa arriba: 1 / 2 / 2. */
    .casos-tab-header {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 8px;
    }

    .casos-tab-header .casos-tab-all,
    .casos-tab-header .casos-tab-month {
        grid-column: 1 / -1;
    }
}

.reservations-list-card {
    display: flex;
    flex-direction: column;
}

/* Antes tenía un tope fijo de 360px sin importar el tamaño de pantalla, lo que
   dejaba mucho espacio vacío debajo en celulares altos. Ahora el tope se
   calcula según el alto real de la pantalla (100dvh, que en móvil sí cuenta
   el navegador con su barra de direcciones) menos lo que ocupa el resto de la
   página arriba (header, título, pestañas) — así aprovecha lo que quede
   disponible sin importar el dispositivo. Si hay pocas reservas, la lista
   igual se encoge a su contenido (max-height, no height fija). */
.reservations-list-scroll {
    flex: 1;
    min-height: 220px;
    max-height: calc(100vh - 320px);
    max-height: calc(100dvh - 320px);
    overflow-y: auto;
}

@media (max-width: 600px) {
    .reservations-list-scroll {
        max-height: calc(100vh - 360px);
        max-height: calc(100dvh - 360px);
    }
}

.reservation-history-list {
    display: flex;
    flex-direction: column;
}

.reservation-history-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 0;
    border-bottom: 1px solid var(--synbex-border);
}

.reservation-history-row:last-child {
    border-bottom: none;
}

.reservation-history-info {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.reservation-history-title {
    font-weight: 600;
    color: var(--synbex-text);
}

.reservation-history-date {
    font-size: 13px;
    color: var(--synbex-text-muted);
}

.synbex-icon-text-btn {
    position: relative;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 270px;
    padding: 8px 12px;
    border-radius: 8px;
    border: 1px solid var(--synbex-border);
    background: var(--synbex-surface);
    color: var(--synbex-text);
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
    overflow: hidden;
}

.synbex-icon-text-btn:hover {
    border-color: var(--synbex-primary);
}

.synbex-icon-text-btn input[type="file"] {
    position: absolute;
    inset: 0;
    opacity: 0;
    cursor: pointer;
    width: 100%;
}

.synbex-photo-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 270px;
    height: 200px;
    border: 2px dashed var(--synbex-border);
    border-radius: 8px;
    background: var(--synbex-bg);
    color: var(--synbex-text-muted);
    font-size: 32px;
}

.synbex-input-currency {
    position: relative;
}

.synbex-input-currency-symbol {
    position: absolute;
    left: 12px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--synbex-text-muted);
    font-size: 14px;
    pointer-events: none;
}

.synbex-input-currency input {
    padding-left: 24px;
}

.reservation-request-item {
    padding: 10px 0;
    border-bottom: 1px solid var(--synbex-border);
}

/* ---- Documentos ---- */

.synbex-page-icon {
    width: 48px;
    height: 48px;
    border-radius: 12px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 22px;
    flex-shrink: 0;
}

.doc-file-icon {
    width: 40px;
    height: 40px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: #fff;
    flex-shrink: 0;
}

.doc-icon-pdf { background: #dc2626; }
.doc-icon-doc { background: #2563eb; }
.doc-icon-xls { background: #16a34a; }
.doc-icon-img { background: #9333ea; }
.doc-icon-generic { background: var(--synbex-text-muted); }

/* ---- Componentes reutilizables: fila de acciones con ícono, menú de "más
   opciones" (â®) y paginación. Usados por Documentos, Contactos y cualquier
   otra tabla con el mismo patrón. ---- */

.synbex-row-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 6px;
    position: relative;
}

.synbex-icon-btn {
    width: 34px;
    height: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--synbex-radius-md);
    border: 1px solid var(--synbex-border);
    background: var(--synbex-surface);
    color: var(--synbex-text);
    cursor: pointer;
    font-size: 15px;
    text-decoration: none;
    box-sizing: border-box;
}

/* Los íconos de línea de HubIcons vienen a 26px por defecto (pensados para las
   tarjetas grandes de los hubs) — acá el botón es chico, así que se achican al
   tamaño que ya usaba el emoji que reemplazan. */
.synbex-icon-btn svg {
    width: 18px;
    height: 18px;
}

.synbex-icon-btn:hover {
    background: var(--synbex-primary-soft);
    border-color: var(--synbex-primary);
}

.synbex-icon-btn-whatsapp {
    color: #25d366;
}

/* ======================================
   Pantalla de "procesando" a pantalla completa. Global (no scoped) a propósito:
   la usan DOS componentes distintos — LoadingOverlay (mensaje fijo) y
   BulkSendOverlay (con contador de correos enviados) —, y el CSS aislado de uno
   no alcanza el markup del otro. Mismo motivo por el que las reglas de
   MobileTabBar viven acá y no en su .razor.css.
   ====================================== */
.loading-overlay {
    position: fixed;
    inset: 0;
    z-index: 1500;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(15, 23, 42, 0.45);
    backdrop-filter: blur(2px);
}

.loading-overlay-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
    padding: 32px 40px;
    background: var(--synbex-surface);
    border-radius: var(--synbex-radius-lg);
    box-shadow: var(--synbex-shadow);
    max-width: 320px;
    text-align: center;
}

.loading-overlay-spinner {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 4px solid var(--synbex-primary-soft);
    border-top-color: var(--synbex-primary);
    animation: loading-overlay-spin 0.8s linear infinite;
}

.loading-overlay-message {
    margin: 0;
    font-size: 14px;
    font-weight: 600;
    color: var(--synbex-text);
}

@keyframes loading-overlay-spin {
    to {
        transform: rotate(360deg);
    }
}

@media (max-width: 420px) {
    .loading-overlay-card {
        max-width: 260px;
        padding: 28px 30px;
    }
}

.synbex-menu-wrapper {
    position: relative;
}

.synbex-menu-overlay {
    position: fixed;
    inset: 0;
    z-index: 40;
}

.synbex-menu {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    z-index: 50;
    min-width: 190px;
    background: var(--synbex-surface);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-md);
    box-shadow: var(--synbex-shadow);
    padding: 6px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* display:flex (antes block) — pedido explícito, reemplazar los emoji de
   Documentos (â­/â/ð/âï¸/ðï¸) por íconos de línea de HubIcons.Get(); un SVG
   inline dentro de un <button>/<a> normal (display:block) no se alinea bien
   con el texto de al lado (queda un poco arriba o abajo por la línea base),
   con flex+gap quedan centrados en la misma fila sin importar qué item los
   use (con ícono o solo texto, como los que no se tocaron). */
.synbex-menu-item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    text-align: left;
    padding: 8px 10px;
    border-radius: 8px;
    border: none;
    background: none;
    color: var(--synbex-text);
    font-size: 13.5px;
    cursor: pointer;
    text-decoration: none;
    box-sizing: border-box;
    white-space: nowrap;
}

.synbex-menu-item svg {
    width: 15px;
    height: 15px;
    flex-shrink: 0;
}

.synbex-menu-item:hover {
    background: var(--synbex-primary-soft);
}

.synbex-menu-item-danger {
    color: var(--synbex-danger);
}

.synbex-menu-item-danger:hover {
    background: var(--synbex-danger-soft);
}

.synbex-pagination {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid var(--synbex-border);
}

.synbex-pagination-pages {
    display: flex;
    align-items: center;
    gap: 4px;
}

.synbex-pagination-btn {
    min-width: 32px;
    height: 32px;
    padding: 0 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--synbex-radius-md);
    border: 1px solid var(--synbex-border);
    background: var(--synbex-surface);
    color: var(--synbex-text);
    cursor: pointer;
    font-size: 13.5px;
    box-sizing: border-box;
}

.synbex-pagination-btn:hover:not(:disabled) {
    background: var(--synbex-primary-soft);
    border-color: var(--synbex-primary);
}

.synbex-pagination-btn:disabled {
    opacity: 0.45;
    cursor: default;
}

.synbex-pagination-btn-active {
    background: var(--synbex-primary);
    border-color: var(--synbex-primary);
    color: #fff;
}

/* ---- Contactos ---- */

.contact-phone-link {
    color: var(--synbex-phone-link);
    text-decoration: underline;
}

/* El ícono de teléfono queda inline junto al número en la tabla de escritorio
   (antes era el emoji ð) — sin esto sale al tamaño grande por defecto de
   HubIcons (26px), casi del alto de la fila. */
.contact-phone-link svg {
    width: 14px;
    height: 14px;
    vertical-align: -2px;
}

/* Antes el correo salía sin clase propia, heredando el azul por defecto del
   navegador para los enlaces — pedido explícito: en tema oscuro ese azul casi
   no se distingue del fondo. --synbex-primary ya es el índigo de marca de
   toda la app (botones, ítem activo del menú) y cambia solo entre temas. */
.contact-email-link {
    color: var(--synbex-primary);
    text-decoration: underline;
}

/* Botones de Llamar/WhatsApp: más grandes y horizontales que el resto de los
   íconos de acción (â®, Ver, Descargar en Documentos), que se quedan como están. */
.contact-action-btn {
    width: 56px;
    height: 42px;
    font-size: 19px;
}

.contact-icon {
    width: 40px;
    height: 40px;
    border-radius: 999px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 17px;
    flex-shrink: 0;
}

.contact-icon-plumbing { background: rgba(147, 51, 234, 0.16); color: #9333ea; }
.contact-icon-security { background: var(--synbex-primary-soft); color: var(--synbex-primary); }
.contact-icon-admin { background: rgba(22, 163, 74, 0.14); color: #16a34a; }
.contact-icon-electric { background: rgba(217, 119, 6, 0.16); color: #d97706; }
.contact-icon-emergency { background: var(--synbex-danger-soft); color: var(--synbex-danger); }
.contact-icon-generic { background: rgba(107, 114, 128, 0.16); color: var(--synbex-text-muted); }

.badge-purple {
    background: rgba(147, 51, 234, 0.14);
    color: #9333ea;
}

.badge-warning {
    background: rgba(217, 119, 6, 0.14);
    color: #d97706;
}

.badge-neutral {
    background: rgba(107, 114, 128, 0.16);
    color: var(--synbex-text-muted);
}

/* Tarjetas de móvil (Contactos, Documentos, y cualquier otra tabla que
   necesite el mismo tratamiento): ocultas por default — escritorio/tablet usan
   la tabla de siempre —, se activan solo bajo el breakpoint de abajo. */
.mobile-cards {
    display: none;
}

@media (max-width: 600px) {
    .table-wrapper {
        display: none;
    }

    .mobile-cards {
        display: flex;
        flex-direction: column;
        gap: 14px;
    }
}

    .mobile-card {
        display: flex;
        flex-direction: column;
        text-align: left;
        gap: 12px;
        max-width: 380px;
        width: 100%;
        margin: 0 auto;
        box-sizing: border-box;
        position: relative;
    }

    .mobile-card-header {
        display: flex;
        align-items: center;
        gap: 12px;
    }

    .mobile-card-icon {
        width: 46px;
        height: 46px;
        border-radius: 999px;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 20px;
        flex-shrink: 0;
    }

    .mobile-card-headertext {
        display: flex;
        flex-direction: column;
        gap: 5px;
        min-width: 0;
    }

    .mobile-card-title {
        font-weight: 700;
        font-size: 16px;
        color: var(--synbex-text);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .mobile-card-subtitle {
        font-size: 12px;
        color: var(--synbex-text-muted);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .mobile-card-row {
        display: flex;
        align-items: center;
        gap: 10px;
        padding-top: 12px;
        border-top: 1px solid var(--synbex-border);
    }

    .mobile-card-row-icon {
        width: 30px;
        height: 30px;
        border-radius: 8px;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 13px;
        background: var(--synbex-bg);
        flex-shrink: 0;
    }

    /* Reemplaza a los emoji (ð/ð¥ en Documentos) que traía este mismo
       casillero — sin esto salían al tamaño por defecto de HubIcons (26px),
       casi tan grandes como el casillero mismo. */
    .mobile-card-row-icon svg {
        width: 16px;
        height: 16px;
    }

    .mobile-card-row-body {
        display: flex;
        flex-direction: column;
        gap: 2px;
        min-width: 0;
        flex: 1;
    }

    .mobile-card-label {
        font-size: 11px;
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: 0.04em;
        color: var(--synbex-text-muted);
    }

    .mobile-card-row-body a,
    .mobile-card-row-body span {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    /* Botones grandes con texto (Llamar/WhatsApp, Ver/Descargar) — distintos de
       los botones de ícono solo que usa la tabla de escritorio. */
    .mobile-card-buttons {
        display: flex;
        gap: 10px;
        margin-top: 2px;
    }

    .mobile-card-btn {
        flex: 1;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 8px;
        padding: 12px;
        border-radius: var(--synbex-radius-md);
        font-weight: 600;
        font-size: 14px;
        text-decoration: none;
        border: none;
        box-sizing: border-box;
    }

    /* Ãcono de teléfono inline en el botón grande "Llamar" (antes era el
       emoji ð) — mismo motivo que el resto de los HubIcons inline: sin esto
       sale a 26px por defecto, más alto que el propio botón. */
    .mobile-card-btn svg {
        width: 16px;
        height: 16px;
        vertical-align: -3px;
    }

    .mobile-card-btn-indigo {
        background: var(--synbex-primary);
        color: #fff;
    }

    .mobile-card-btn-green {
        background: #25d366;
        color: #fff;
    }

    .mobile-card-btn-disabled {
        opacity: 0.4;
        pointer-events: none;
    }

    /* Pedido explícito (revierte la isla flotante que tenía antes): ya NO es
       position:fixed — vuelve al flujo normal, como una tarjeta más debajo
       de la última de la lista, solo con un margen propio arriba para que no
       quede pegada. Y solo se ven los botones de página — ni el texto
       "Mostrando X a Y de Z" ni el selector de "N por página" (este último ya
       venía oculto en celular de antes); GetPageNumbers() en cada page.razor
       ya solo genera los botones que hacen falta según el total real de
       páginas, nunca botones de más "vacíos". */
    .mobile-card-pagination {
        margin-top: 4px;
    }

    .mobile-card-pagination .page-subtitle {
        display: none;
    }

    .mobile-card-pagination .synbex-pagination {
        flex-direction: column;
        margin-top: 0;
        padding-top: 0;
        border-top: none;
    }

    /* Con muchas páginas (números de dos/tres cifras, o el máximo de 7 botones
       de GetPageNumbers() todos angostos) la fila puede no caber completa en
       una pantalla angosta — pedido explícito: que no se salgan por la
       derecha. flex-wrap:wrap los pasa a una segunda línea (centrada, como la
       primera) en vez de desbordar o esconderse fuera del panel. */
    .mobile-card-pagination .synbex-pagination-pages {
        justify-content: center;
        flex-wrap: wrap;
        row-gap: 6px;
    }

    .mobile-card-pagination .synbex-page-size-select {
        display: none;
    }

    /* Pedido explícito: sin flechas â¹ âº — solo los números de página, y se
       salta directo a la que sea tocando su botón. En el markup de cada
       page.razor (Contactos/Documentos) las flechas SIEMPRE son el primer y
       el último hijo de ".synbex-pagination-pages" (el @foreach de números
       va entero en el medio, sin envoltorio propio), así que first-child/
       last-child las ubica sin importar cuántos números haya — no hace falta
       una clase aparte ni tocar el .razor. */
    .mobile-card-pagination .synbex-pagination-pages > .synbex-pagination-btn:first-child,
    .mobile-card-pagination .synbex-pagination-pages > .synbex-pagination-btn:last-child {
        display: none;
    }

.documents-search-wrapper,
.contacts-search-wrapper {
    position: relative;
    flex: 1;
    min-width: 180px;
}

.documents-search-wrapper .documents-search-input,
.contacts-search-wrapper .contacts-search-input {
    padding-left: 38px;
    box-sizing: border-box;
    width: 100%;
}

.documents-search-icon,
.contacts-search-icon {
    position: absolute;
    left: 13px;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    opacity: 0.55;
    pointer-events: none;
}

/* Antes esto era el emoji ð a font-size:14px — ahora es el SVG de línea de
   HubIcons.Get("search"), que sin un tamaño propio sale al 26px por defecto
   (pensado para las tarjetas grandes de los hubs), demasiado grande para un
   ícono suelto dentro de un campo de texto. */
.documents-search-icon svg,
.contacts-search-icon svg {
    width: 15px;
    height: 15px;
}

.documents-filters-toggle-icon svg {
    width: 13px;
    height: 13px;
}

/* Estrella junto al título en la fila de la tabla (escritorio) cuando el
   documento es favorito — reemplaza al "â­ " que iba antes del título. */
.documents-favorite-star svg {
    width: 15px;
    height: 15px;
}

.documents-filters-toggle {
    display: none;
    align-items: center;
    gap: 6px;
    padding: 10px 16px;
    border-radius: 999px;
    border: 1px solid var(--synbex-border);
    background: var(--synbex-surface);
    color: var(--synbex-primary);
    font-family: inherit;
    font-weight: 600;
    font-size: 14px;
    cursor: pointer;
    white-space: nowrap;
}

.documents-filters-extra {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
}

@media (max-width: 600px) {
    /* Selector compuesto (no ".documents-page-header" a secas) a propósito:
       ".catalog-header-row" define su propio "align-items: flex-start" más
       abajo en este archivo, y por venir después en el orden del CSS le
       ganaba a este override aunque la media query sí aplicara — el título
       y el ícono quedaban pegados a la izquierda en vez de centrados. Con el
       selector compuesto (dos clases) la especificidad ya no depende del
       orden. */
    .catalog-header-row.documents-page-header {
        flex-direction: column;
        align-items: center;
        justify-content: center;
        text-align: center;
    }

    .documents-page-title {
        font-size: 28px;
        text-align: center;
    }

    /* Pedido explícito: título centrado pero la descripción de abajo
       completamente a la izquierda, y el ícono de documentos pasa de estar a
       la izquierda del título a su propia columna a la derecha. row-reverse
       deja el ícono al final (visualmente a la derecha) y el bloque de texto
       ocupa el resto del ancho a la izquierda; ".documents-header-desc"
       necesita su propio text-align porque si no hereda el "center" del
       ".documents-page-header" de arriba. */
    /* Ãcono a la IZQUIERDA y, a su derecha, título y descripción uno debajo del
       otro. Antes era row-reverse, que dejaba el ícono a la derecha. */
    .documents-page-header .documents-header-left {
        flex-direction: row;
        align-items: flex-start;
        width: 100%;
    }

    /* El contenedor centra su contenido; el bloque de texto tiene que volver a
       alinearse a la izquierda para quedar pegado al ícono. */
    .documents-page-header .documents-header-text,
    .documents-page-header .documents-page-title {
        text-align: left;
    }

    .documents-page-header .documents-header-text {
        flex: 1;
        min-width: 0;
    }

    .documents-page-header .documents-header-desc {
        text-align: left;
    }

    /* flex-basis 100%, no width:100%: como el ítem trae "flex: 1 1 0%", la
       base de 0% le ganaba al width y la búsqueda se quedaba en la misma fila
       que el selector y el botón de Filtros — entre los tres dejaban el
       selector en 58px, sin poder leer la categoría. Con la base al 100% la
       búsqueda ocupa su propia fila y los otros dos se reparten la siguiente. */
    .documents-search-wrapper {
        flex-basis: 100%;
    }

    .documents-search-input {
        font-size: 16px;
        padding-top: 12px;
        padding-bottom: 12px;
        max-width: 100% !important;
    }

    /* "Central, margen automático" — pedido explícito: pasa de compartir fila
       con el botón de Filtros a ocupar su propia fila centrada. El wrapper
       (no el <select> directo) es el que fuerza el salto de línea con
       flex-basis:100% — si ese 100% se lo ponemos al <select> junto con su
       propio max-width, el navegador calcula su "tamaño hipotético" ya
       recortado por el max-width para decidir si cabe junto al botón de
       Filtros en la misma línea, y sí cabía: el salto de línea nunca pasaba.
       Con el wrapper sin max-width, el 100% se respeta de verdad y el select
       (que sí trae su propio max-width en línea) queda centrado adentro. */
    .documents-category-wrapper {
        flex-basis: 100%;
        display: flex;
        justify-content: center;
    }

    .documents-category-select {
        border-radius: 999px;
    }

    .documents-filters-toggle {
        display: inline-flex;
    }

    .documents-filters-extra {
        display: none;
        width: 100%;
    }

    .documents-filters-extra.documents-filters-extra-open {
        display: flex;
    }
}

@media (max-width: 600px) {
    /* Mismo tratamiento que Documentos (ver comentarios arriba en ese
       bloque) para que las dos pantallas queden estructuradas igual: título
       centrado, descripción a la izquierda, ícono en su propia columna a la
       derecha — pedido explícito de mantener ambas consistentes. Selector
       compuesto por el mismo motivo de especificidad que ".documents-page-header". */
    .catalog-header-row.contacts-page-header {
        flex-direction: column;
        align-items: center;
        justify-content: center;
        text-align: center;
    }

    .contacts-page-title {
        font-size: 28px;
        text-align: center;
    }

    .contacts-page-header .contacts-header-left {
        flex-direction: row-reverse;
        align-items: flex-start;
        width: 100%;
    }

    .contacts-page-header .contacts-header-text {
        flex: 1;
        min-width: 0;
    }

    .contacts-page-header .contacts-header-desc {
        text-align: left;
    }

    .contacts-search-wrapper {
        flex-basis: 100%;
    }

    .contacts-search-input {
        font-size: 16px;
        padding-top: 12px;
        padding-bottom: 12px;
        max-width: 100% !important;
    }

    /* "Central, margen automático" — mismo patrón que ".documents-category-wrapper":
       el wrapper (sin max-width) es el que fuerza el salto de línea con
       flex-basis:100%, y el <select> de adentro (que sí trae su propio
       max-width en línea) queda centrado. */
    .contacts-category-wrapper {
        flex-basis: 100%;
        display: flex;
        justify-content: center;
    }

    .contacts-category-select {
        border-radius: 999px;
    }
}

.reservation-request-item:last-child {
    border-bottom: none;
    padding-bottom: 0;
}

.cleaning-calendar-check {
    position: absolute;
    bottom: 1px;
    right: 3px;
    font-size: 8px;
    line-height: 1;
}

.cleaning-month-group summary.cleaning-month-header {
    cursor: pointer;
    font-weight: 600;
    color: var(--synbex-text);
    list-style: none;
}

.cleaning-month-group summary.cleaning-month-header::-webkit-details-marker {
    display: none;
}

.cleaning-month-group summary.cleaning-month-header::before {
    content: "â¸";
    display: inline-block;
    margin-right: 8px;
    color: var(--synbex-primary);
    transition: transform 0.15s ease;
}

.cleaning-month-group[open] summary.cleaning-month-header::before {
    transform: rotate(90deg);
}

.synbex-alert {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 14px;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-danger-soft);
    color: var(--synbex-danger);
    font-size: 13.5px;
    line-height: 1.4;
}

.synbex-alert-success {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 14px;
    border-radius: var(--synbex-radius-md);
    background: rgba(22, 163, 74, 0.12);
    color: #16a34a;
    font-size: 13.5px;
    line-height: 1.4;
}

@media (max-width: 640px) {
    .synbex-form-grid {
        grid-template-columns: 1fr;
    }
}

/* Una tabla ancha (muchas columnas) tiene que scrollear DENTRO de su propia
   tarjeta, nunca desbordarla — antes esto solo pasaba en pantallas angostas
   (dentro del @media de abajo); una tabla con suficientes columnas se salía
   igual de la tarjeta en computadora. Sin condición de ancho a propósito. */
.synbex-card:has(> table.synbex-table),
.synbex-card:has(> .synbex-table-wrap) {
    overflow-x: auto;
}

@media (max-width: 700px) {
    .page-header-row {
        flex-wrap: wrap;
    }

    .synbex-card {
        padding: 16px;
    }

    /* Las tablas no se achican de forma legible en pantallas angostas — se deja
       que conserven un ancho mínimo en vez de aplastar las columnas (el scroll
       horizontal de arriba se encarga de que igual se puedan ver todas).
       Ojo: bajo 600px esto ya no aplica, porque ahí la tabla se apila como
       tarjetas y no necesita ancho mínimo (ver "Tablas apiladas" más abajo). */
    .synbex-table {
        min-width: 520px;
    }
}

@media (max-width: 480px) {
    .page-title {
        font-size: 20px;
    }

    .synbex-card {
        padding: 14px;
    }
}

/* Fila de botones de acción arriba de un listado (Importar / + Agregarâ¦).
   En escritorio van centrados y con ancho parejo; en celular se reparten el
   ancho disponible en vez de conservar un mínimo fijo que no cabía y empujaba
   la página hacia la derecha. */
.page-actions-row {
    display: flex;
    justify-content: center;
    gap: 10px;
    margin-bottom: 16px;
}

.page-actions-row .btn-synbex {
    min-width: 180px;
    justify-content: center;
}

@media (max-width: 600px) {
    .page-actions-row .btn-synbex {
        flex: 1;
        min-width: 0;
    }
}

/* Formulario de dos columnas (campos a la izquierda, foto a la derecha).
   La columna de la foto tiene ancho fijo y no encoge, así que en celular las
   dos tienen que apilarse: si no, la de los campos queda de unos pocos píxeles
   de ancho. */
.form-split-row {
    display: flex;
    gap: 20px;
    align-items: stretch;
}

.form-split-main {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.form-split-aside {
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

@media (max-width: 700px) {
    .form-split-row {
        flex-direction: column;
    }

    /* La foto deja de tener ancho fijo y se ajusta al ancho de la tarjeta. */
    .form-split-aside,
    .form-split-aside img {
        width: 100% !important;
    }
}

/* ======================================
   Tablas apiladas (celular)

   En pantallas angostas una tabla no cabe, y el scroll horizontal dejaba los
   botones de Acciones escondidos a la derecha — en móvil casi nadie descubre
   que hay que deslizar. Acá cada fila pasa a ser una tarjeta que entra completa
   en el ancho de la pantalla, con el nombre de la columna al lado de su dato.

   La clase .synbex-table-stacked la pone responsive-tables.js, no el markup:
   ese script es el que le copia a cada celda su data-label. Si el JS no corre,
   nada de esto aplica y la tabla se queda con el scroll de siempre en vez de
   quedar a medio armar. Una tabla que necesite seguir siendo tabla acá se marca
   con data-no-stack en el <table>.
   ====================================== */

@media (max-width: 600px) {
    .synbex-table-stacked,
    .synbex-table-stacked tbody,
    .synbex-table-stacked tr,
    .synbex-table-stacked td {
        display: block;
        width: auto;
    }

    /* El ancho mínimo de más arriba es justo lo que forzaba el scroll — acá la
       tabla ya no necesita desbordar, así que se anula. */
    .synbex-table-stacked {
        min-width: 0 !important;
    }

    /* El encabezado se reemplaza por la etiqueta de cada celda (::before). */
    .synbex-table-stacked thead {
        display: none;
    }

    .synbex-table-stacked tr {
        border: 1px solid var(--synbex-border);
        border-radius: var(--synbex-radius-lg);
        padding: 12px 14px;
        margin-bottom: 12px;
        background: var(--synbex-surface);
    }

    .synbex-table-stacked tr:last-child {
        margin-bottom: 0;
    }

    /* !important en alineación y borde porque varias tablas traen
       style="text-align:right" pegado en el <td>, y en tarjeta eso descuadra. */
    .synbex-table-stacked td {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: 14px;
        padding: 7px 0 !important;
        border: none !important;
        text-align: left !important;
        white-space: normal !important;
        overflow-wrap: anywhere;
    }

    .synbex-table-stacked td + td {
        border-top: 1px solid var(--synbex-border) !important;
    }

    .synbex-table-stacked td::before {
        content: attr(data-label);
        flex-shrink: 0;
        font-size: 11px;
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: 0.04em;
        color: var(--synbex-text-muted);
    }

    /* Celda sin etiqueta (la de acciones, o una columna sin título): el dato
       ocupa todo el ancho en vez de quedar empujado a la derecha por un hueco. */
    .synbex-table-stacked td[data-label=""]::before {
        display: none;
    }

    /* Acciones: los botones bajan a una fila propia y se reparten el ancho
       completo, que era el punto de todo esto — que se vean sin deslizar. */
    .synbex-table-stacked td.is-actions {
        gap: 8px;
        padding-top: 12px !important;
    }

    .synbex-table-stacked td.is-actions .btn-synbex {
        flex: 1;
        justify-content: center;
        margin: 0 !important;
    }

    /* Fila de "todavía no hay registros": un solo <td> con colspan, que el
       script deja sin etiquetar a propósito — se muestra como texto suelto,
       sin borde de tarjeta. */
    .synbex-table-stacked tr:has(> td[colspan]) {
        border: none;
        background: none;
        padding: 0;
    }

    .synbex-table-stacked td[colspan] {
        display: block;
        text-align: center !important;
    }

    .synbex-table-stacked td[colspan]::before {
        display: none;
    }
}

.synbex-announcements-panel {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-bottom: 24px;
}

.synbex-announcement-card {
    background: var(--synbex-primary-soft);
    color: var(--synbex-text);
    justify-content: space-between;
    align-items: center;
}

.synbex-priority-badge {
    display: inline-block;
    margin-left: 8px;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    background: var(--synbex-danger-soft);
    color: var(--synbex-danger);
    white-space: nowrap;
    vertical-align: middle;
}

/* Like/dislike de anuncios: compartido entre la ventana flotante del inicio
   (Dashboard/Index.razor) y la lista de solo lectura de Comunidad
   (MyAnnouncements.razor) — por eso va acá y no en el CSS aislado de
   Dashboard/Index.razor.css, que solo aplica a ese componente. Privados a
   propósito (pedido explícito, 2026-09-28): sin ningún número al lado, solo el
   ícono relleno o no para quien reacciona. */
.announcement-modal-reactions {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 20px;
    margin-top: 18px;
}

.announcement-modal-reaction {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 999px;
    border: 1px solid var(--synbex-border);
    background: none;
    color: var(--synbex-text-muted);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.announcement-modal-reaction svg {
    width: 20px;
    height: 20px;
}

.announcement-modal-reaction:hover {
    border-color: var(--synbex-primary);
    color: var(--synbex-primary);
}

/* Mismo criterio que la reacción de Mantenimiento/Casos: relleno en el color de
   acento sin importar si es like o dislike — el ícono ya distingue cuál es. En
   la ventana flotante --synbex-primary cambia según el tipo de aviso (rojo si es
   importante); acá en Comunidad usa el azul normal de la app. */
.announcement-modal-reaction.is-active {
    background: var(--synbex-primary);
    border-color: var(--synbex-primary);
    color: #fff;
}

.synbex-document-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.badge-pending {
    background: rgba(217, 119, 6, 0.12);
    color: #d97706;
}

.synbex-case-list {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.synbex-case-card {
    display: flex;
    gap: 16px;
    align-items: flex-start;
}

/* Casos propios (los que reportó quien está viendo la lista) resaltados entre
   los del resto del conjunto — borde + fondo suave con el color primario. */
.synbex-case-card-mine {
    border-color: var(--synbex-primary);
    background: var(--synbex-primary-soft);
}

/* Ãconos de línea (HubIcons) puestos junto a texto normal, en vez de emoji —
   nacen a 26x26 (pensados para las tarjetas grandes de los hubs), acá se
   achican y se alinean con la línea de base del texto que los acompaña. */
.synbex-inline-icon {
    display: inline-flex;
    vertical-align: -3px;
}

.synbex-inline-icon svg {
    width: 15px;
    height: 15px;
}

.synbex-case-icon {
    flex-shrink: 0;
}

.synbex-case-title {
    font-size: 17px;
}

/* Grid de 3 columnas (contenido 55% / línea divisoria / metadatos) con filas
   explícitas, para que "Creado el" quede a la misma altura que la ubicación, y
   "Asignado a" a la misma altura que "Reportado por" — algo que con dos
   columnas flex independientes (una por texto de arriba a abajo, otra igual)
   no se puede lograr, porque no comparten las mismas filas. */
.synbex-case-grid {
    flex: 1;
    min-width: 0;
    display: grid;
    grid-template-columns: minmax(0, 55%) 1px minmax(0, 1fr);
    column-gap: 16px;
    row-gap: 8px;
    align-items: center;
}

.synbex-case-divider {
    grid-column: 2;
    grid-row: 1 / -1;
    background: var(--synbex-border);
}

/* Ubicación, Reportado por, Creado el y Asignado a comparten esta misma clase
   a propósito: misma letra y tamaño para las cuatro, en una sola línea cada
   una (etiqueta + valor), en vez de la etiqueta arriba y el valor abajo.
   Reemplaza también el margin-bottom:24px que trae .page-subtitle (bueno para
   separar un subtítulo de página del contenido de abajo, pero acá adentro de
   la tarjeta solo hace falta el espacio parejo que ya da el row-gap del grid). */
.synbex-case-line {
    margin: 0;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 14px;
    color: var(--synbex-text-muted);
    white-space: nowrap;
}

/* El valor (no la etiqueta) va en negrita, mismo color — así se distingue sin
   necesitar un segundo tono de gris. */
.synbex-case-line strong {
    font-weight: 600;
    color: inherit;
}

.synbex-case-col-title {
    grid-column: 1;
    grid-row: 1;
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.synbex-case-col-desc {
    grid-column: 1;
    grid-row: 2;
    margin: 0 0 6px;
}

.synbex-case-col-location {
    grid-column: 1;
    grid-row: 3;
}

.synbex-case-col-reporter {
    grid-column: 1;
    grid-row: 4;
}

.synbex-case-col-comment {
    grid-column: 1;
    grid-row: 5;
}

.synbex-case-col-reactions {
    grid-column: 1;
    grid-row: 6;
}

.synbex-case-meta-label {
    font-size: 12px;
    font-weight: 600;
    color: var(--synbex-text-muted);
}

/* "Â¿Se resolvió bien?" va a la derecha (columna de metadatos), separado de los
   likes/dislikes del caso en sí — antes salían pegados en la misma fila y se
   confundían como si fueran el mismo par de botones. */
.synbex-case-resolution {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
}

.synbex-case-col-resolution {
    grid-column: 3;
    grid-row: 5;
}

.synbex-case-resolution-buttons {
    display: flex;
    gap: 8px;
}

.synbex-case-comment {
    background: var(--synbex-primary-soft);
    color: var(--synbex-text);
    margin: 6px 0;
}

.synbex-case-col-created {
    grid-column: 3;
    grid-row: 3;
}

.synbex-case-col-assigned {
    grid-column: 3;
    grid-row: 4;
}

@media (max-width: 640px) {
    .synbex-case-card {
        flex-wrap: wrap;
    }

    .synbex-case-grid {
        grid-template-columns: minmax(0, 1fr);
        flex-basis: 100%;
    }

    .synbex-case-divider {
        display: none;
    }

    .synbex-case-line {
        white-space: normal;
    }

    .synbex-case-col-title,
    .synbex-case-col-desc,
    .synbex-case-col-location,
    .synbex-case-col-reporter,
    .synbex-case-col-comment,
    .synbex-case-col-reactions,
    .synbex-case-col-created,
    .synbex-case-col-assigned,
    .synbex-case-col-resolution {
        grid-column: 1;
    }

    .synbex-case-col-location { grid-row: 3; }
    .synbex-case-col-created { grid-row: 4; }
    .synbex-case-col-reporter { grid-row: 5; }
    .synbex-case-col-assigned { grid-row: 6; }
    .synbex-case-col-resolution { grid-row: 7; }
    .synbex-case-col-comment { grid-row: 8; }
    .synbex-case-col-reactions { grid-row: 9; }
}

.synbex-case-reactions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 4px;
}

/* Botón "Agregar nuevo caso del conjunto" — grande y centrado a propósito,
   en vez del botoncito chico de siempre en la esquina del encabezado: es la
   acción principal de esta pantalla. */
.casos-new-case-row {
    display: flex;
    justify-content: center;
    margin: 28px 0;
}

.casos-new-case-btn {
    width: 70%;
    max-width: 520px;
    box-sizing: border-box;
    text-align: center;
    justify-content: center;
}

@media (max-width: 640px) {
    .casos-new-case-btn {
        width: 100%;
    }
}

/* Máximo 3 tarjetas por fila — con flexbox + justify-content:center en vez de
   CSS grid, para que si hay 1 o 2 áreas (menos de 3) queden centradas en la
   fila en vez de pegadas a la izquierda; con 3 llenan la fila completa. */
.synbex-area-grid {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 20px;
}

.synbex-area-card {
    flex: 0 1 calc(33.333% - 14px);
    max-width: 380px;
    min-width: 300px;
}

@media (max-width: 640px) {
    .synbex-area-card {
        flex-basis: 100%;
        max-width: 420px;
    }
}

/* Botón "Solicitudes" de Ãreas comunes — un poco más ancho/alto que un botón
   normal, y cuando hay solicitudes pendientes se pone naranja y parpadea, con
   una pastilla mostrando cuántas hay, para que sea imposible no notarlo. */
.areas-requests-btn {
    position: relative;
    box-sizing: border-box;
    width: 190px;
    height: 46px;
    padding: 12px 26px;
    font-size: 15px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    /* Sin solicitudes pendientes .has-pending no aplica, y sin esto el botón se
       queda solo con el "border: 1px solid transparent" de .btn-synbex — al lado
       del morado sólido de "+ Nueva área" se veía como si solo fuera texto suelto,
       sin ningún borde ni fondo. Pedido explícito: mismo color que el resto de la
       marca, para que se note que es un botón real. */
    color: var(--synbex-primary);
    background: var(--synbex-primary-soft);
    border-color: var(--synbex-primary);
}

.areas-requests-btn:not(.has-pending):hover {
    background: var(--synbex-primary-soft);
    filter: brightness(0.95);
}

/* Mismo ancho y alto que .areas-requests-btn (arriba), solo para "+ Nueva área"
   — no se toca .catalog-add-btn porque ese es compartido con otras 16 pantallas.
   El selector compuesto (no solo .areas-new-btn) es a propósito: así gana sin
   importar el orden en el archivo sobre el margin-top:4px de .catalog-add-btn. */
.catalog-add-btn.areas-new-btn {
    box-sizing: border-box;
    width: 190px;
    height: 46px;
    padding: 12px 26px;
    font-size: 15px;
    justify-content: center;
    margin: 0;
}

.areas-requests-btn.has-pending {
    background: #d97706;
    border-color: #d97706;
    color: #fff;
    animation: areas-requests-blink 1.4s ease-in-out infinite;
}

.areas-requests-btn.has-pending:hover {
    background: #b45309;
    border-color: #b45309;
}

.areas-requests-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: 999px;
    background: #fff;
    color: #d97706;
    font-size: 12.5px;
    font-weight: 800;
}

@keyframes areas-requests-blink {
    0%, 100% {
        box-shadow: 0 0 0 0 rgba(217, 119, 6, 0.55);
    }

    50% {
        box-shadow: 0 0 0 6px rgba(217, 119, 6, 0);
    }
}

@media (prefers-reduced-motion: reduce) {
    .areas-requests-btn.has-pending {
        animation: none;
    }
}

.synbex-area-card {
    padding: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.synbex-area-photo {
    width: 100%;
    height: 140px;
    object-fit: cover;
    display: block;
    cursor: zoom-in;
}

.synbex-area-body {
    padding: 16px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    flex: 1;
}

.synbex-calendar-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 14px;
}

.synbex-calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 4px;
}

.synbex-calendar-weekday {
    text-align: center;
    font-size: 12px;
    font-weight: 600;
    color: var(--synbex-text-muted);
    padding: 4px 0;
}

.synbex-calendar-day {
    aspect-ratio: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    border-radius: var(--synbex-radius-md);
    font-size: 13px;
    font-family: inherit;
    border: 1px solid transparent;
    background: transparent;
    color: var(--synbex-text);
    position: relative;
}

.synbex-calendar-day-empty {
    background: transparent;
}

.synbex-calendar-day-disabled {
    color: var(--synbex-text-muted);
    opacity: 0.4;
}

.synbex-calendar-day-available {
    background: var(--synbex-primary-soft);
    cursor: pointer;
    transition: background 0.15s ease;
}

.synbex-calendar-day-available:hover {
    background: var(--synbex-primary);
    color: #fff;
}

/* ======================================
   Accesibilidad — barra flotante (AccessibilityWidget)
   El tamaño de texto y la escala de grises se aplican solo sobre
   #synbex-app-content (ver App.razor), no sobre <html>/<body>, para que la
   propia barra de accesibilidad nunca cambie de tamaño ni de color junto
   con el contenido que controla.
   ====================================== */

html.a11y-underline-links a {
    text-decoration: underline !important;
}

html.a11y-readable-font,
html.a11y-readable-font * {
    font-family: Arial, Helvetica, sans-serif !important;
    letter-spacing: 0.02em !important;
    line-height: 1.6 !important;
}

html.a11y-reduce-motion *,
html.a11y-reduce-motion *::before,
html.a11y-reduce-motion *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
}

#synbex-app-content.a11y-grayscale {
    filter: grayscale(1);
}

/* Misma especificidad que :root[data-theme] (pseudo-clase + atributo) — con
   selector de tipo "html" en vez de ":root" esta regla pierde siempre contra
   el tema claro/oscuro sin importar el orden en el archivo. */
:root[data-a11y-contrast="high"] {
    --synbex-primary: #ffff00;
    --synbex-primary-hover: #ffff66;
    --synbex-primary-soft: #333300;
    --synbex-text: #ffffff;
    --synbex-text-muted: #ffff00;
    --synbex-border: #ffffff;
    --synbex-surface: #000000;
    --synbex-bg: #000000;
}

.synbex-calendar-day-selected {
    background: var(--synbex-primary);
    color: #fff;
}

.synbex-calendar-day-booked {
    background: var(--synbex-danger-soft);
    color: var(--synbex-danger);
}

/* El día de hoy se marca con un anillo — independiente de si además está
   disponible, reservado o (si el conjunto lo permitiera) en el pasado, para
   que la persona ubique "dónde estamos" de un vistazo sin importar el resto
   del estado del día. */
.synbex-calendar-day-today {
    box-shadow: inset 0 0 0 2px var(--synbex-primary);
    font-weight: 700;
}

.synbex-calendar-x {
    font-size: 11px;
}

.synbex-calendar-dot {
    font-size: 8px;
    line-height: 1;
    margin-top: 2px;
}

/* Resumen del área al reservar (foto + datos) — foto baja (100px) porque acá
   solo importan nombre/descripción/tipo, no es la ficha completa del área
   (esa vive en /dashboard/areas-comunes). En celular se apilan en vez de ir
   lado a lado. */
/* "Selecciona una fecha para reservar" arriba del calendario — usa el mismo
   acento morado que ya cambia de tono entre claro/oscuro en toda la app
   (var(--synbex-primary)), así que ya sale llamativo en los dos temas sin
   necesidad de un color aparte para cada uno. */
.booking-calendar-instruction {
    margin: 0 0 10px;
    text-align: center;
    font-weight: 600;
    color: var(--synbex-primary);
}

@media print {
    html,
    body {
        background: #ffffff !important;
    }

    body * {
        visibility: hidden;
    }

    .print-area,
    .print-area * {
        visibility: visible;
    }

    .print-area {
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
    }

    .no-print {
        display: none !important;
    }

    * {
        -webkit-print-color-adjust: exact !important;
        print-color-adjust: exact !important;
        color-adjust: exact !important;
    }

    .stat-card,
    .cleaning-chart-bar-wrap,
    .cleaning-task-card,
    .synbex-card:not(:has(table)) {
        break-inside: avoid;
        page-break-inside: avoid;
    }

    /* La tarjeta que envuelve la tabla del reporte sí debe poder partirse entre
       paginas — si no, al no caber completa se empuja entera a la pagina
       siguiente y deja un hueco enorme al final de la anterior. Las filas no
       se parten a la mitad, y el encabezado se repite en cada pagina. */
    .synbex-table thead {
        display: table-header-group;
    }

    .synbex-table tr {
        break-inside: avoid;
        page-break-inside: avoid;
    }

    /* Los encabezados ordenables usan white-space:nowrap para que la flechita de
       orden no salte de linea en pantalla — pero en una tabla de 7 columnas eso
       fuerza a la tabla a ser mas ancha que la pagina impresa, empujando el borde
       derecho de la tarjeta fuera del area imprimible (por eso "desaparecia").
       table-layout:fixed obliga a la tabla a respetar el 100% del ancho de la
       tarjeta, y el texto que no quepa se parte en vez de ensanchar la columna. */
    .synbex-table {
        table-layout: fixed;
    }

    .synbex-table th,
    .synbex-table td {
        white-space: normal;
        overflow-wrap: break-word;
        word-break: break-word;
    }
}

/* ======================================
   Novedades — feed de actividad reciente en el inicio del residente
   ====================================== */

.synbex-novedades-feed {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.synbex-novedad-card {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
    background: var(--synbex-surface);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-lg);
    box-shadow: var(--synbex-shadow);
    text-decoration: none;
    color: inherit;
    transition: background 0.15s ease, transform 0.1s ease;
}

.synbex-novedad-card:hover {
    background: var(--synbex-primary-soft);
    transform: translateY(-1px);
}

.synbex-novedad-icon {
    font-size: 22px;
    flex-shrink: 0;
    color: var(--synbex-primary);
}

.synbex-novedad-icon svg {
    width: 20px;
    height: 20px;
}

.synbex-novedad-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 1;
    min-width: 0;
}

.synbex-novedad-kind {
    font-size: 11.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--synbex-primary);
}

.synbex-novedad-title {
    font-size: 14.5px;
    font-weight: 600;
    color: var(--synbex-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.synbex-novedad-subtitle {
    font-size: 12.5px;
    color: var(--synbex-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.synbex-novedad-date {
    font-size: 12.5px;
    color: var(--synbex-text-muted);
    flex-shrink: 0;
    white-space: nowrap;
}

/* Insertado por JS (tenant-logo.js) en el login cuando hay un logo guardado en
   el navegador de una sesión anterior — va en el CSS global (no en el .razor.css
   con scope de Blazor) porque un elemento creado por JS después de cargar la
   página no lleva el atributo de scope, así que el CSS aislado no lo alcanzaría. */
.login-brand-logo {
    max-width: 200px;
    margin: 0 auto;
    display: block;
}

/* Barra de navegación inferior en móvil (MobileTabBar.razor) — se define acá,
   en el CSS global, y no en el .razor.css aislado del componente: el <nav> se
   renderiza como un hijo flex más de `.dashboard` (junto al sidebar y al
   contenido), así que necesita `position:fixed` para salir de ese flujo, y
   puesto en el CSS global no hay ninguna duda de que la regla se aplica. */
.mobile-tabbar {
    display: none;
}

/* Modo "solo registro" (ej. Visitantes en celular): oculta el header de
   arriba y la barra de navegación de abajo (viven en otros componentes con
   su propio CSS aislado, por eso la regla va acá) para que la página activa
   llene toda la pantalla — ver Synbex.toggleFocusMode en fullscreen.js. */
body.synbex-focus-mode .dashboard-header,
body.synbex-focus-mode .dashboard-sidebar,
body.synbex-focus-mode .dashboard-nav-backdrop,
body.synbex-focus-mode .mobile-tabbar {
    display: none !important;
}

body.synbex-focus-mode .dashboard-main {
    padding-bottom: 16px !important;
}

/* El input nativo type="date" no se achica bien con solo width:100% dentro de
   un campo angosto — el navegador le impone un ancho mínimo propio y el
   contenido (ej. el día) queda cortado a la derecha. Mismo motivo que
   .cleaning-date-input, de uso general para cualquier campo de fecha. */
.synbex-date-input {
    min-width: 0;
    padding-right: 6px;
}

@media (max-width: 600px) {
    /* Isla flotante: separada de los tres bordes (abajo y los dos laterales),
       con esquinas redondeadas — a propósito ya NO llega de lado a lado ni
       queda pegada al fondo, así se ve como un elemento aparte flotando sobre
       el contenido, no como una barra fija de borde a borde.
       .is-compact (ver mobile-tabbar-scroll.js) la angosta al deslizar hacia
       abajo (como el menú inferior de Instagram) y la devuelve a su ancho
       normal al deslizar hacia arriba — pedido explícito: antes achicaba el
       padding/ícono/letra (la isla se veía más baja), ahora todo eso se queda
       IGUAL en los dos estados; lo único que cambia es el ancho (más inset a
       los lados), centrada siempre porque left/right suben lo mismo. El fondo es
       semi-transparente con blur (pedido explícito) en vez de opaco — color-mix
       sobre --synbex-surface para que seguir respetando el tema claro/oscuro. */
    .mobile-tabbar {
        display: flex;
        position: fixed;
        left: 14px;
        right: 14px;
        bottom: calc(14px + env(safe-area-inset-bottom, 0px));
        width: auto;
        box-sizing: border-box;
        z-index: 60;
        background: color-mix(in srgb, var(--synbex-surface) 78%, transparent);
        backdrop-filter: blur(14px);
        -webkit-backdrop-filter: blur(14px);
        border: 1px solid var(--synbex-border);
        border-radius: 20px;
        padding: 6px 2px;
        box-shadow: 0 4px 16px rgba(0, 0, 0, 0.14);
        transition: left 0.25s ease, right 0.25s ease;
    }

    /* Con el menú desplegable de la izquierda abierto, la isla se esconde (no
       tiene sentido verla flotando encima del cajón) — y vuelve a aparecer
       sola en cuanto se cierra. .dashboard-nav-toggle-input es el checkbox
       (sin JS) que ya controla ese cajón, ver DashboardLayout.razor. */
    .dashboard-nav-toggle-input:checked ~ .mobile-tabbar {
        display: none;
    }

    /* Angostada más suave que antes (pedido explícito: "un poquito más pequeño,
       pero no tanto") — antes 54px de inset a cada lado (40px más angosta que
       el normal de 14px), ahora solo 12px más angosta. */
    .mobile-tabbar.is-compact {
        left: 26px;
        right: 26px;
    }

    /* Solo íconos, sin texto (pedido explícito) — más padding vertical que antes
       porque ya no hay una segunda línea de letra que reparta el alto de la
       pestaña; el nombre de cada una sigue viviendo como aria-label en el NavLink
       (ver MobileTabBar.razor) para lectores de pantalla. */
    .mobile-tabbar-item {
        flex: 1 1 0;
        min-width: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 11px 1px;
        text-decoration: none;
        color: var(--synbex-text-muted);
        background: transparent;
        border: none;
        font-family: inherit;
        cursor: pointer;
    }

    .mobile-tabbar-icon {
        display: flex;
        line-height: 1;
        position: relative;
    }

    .mobile-tabbar-icon svg {
        display: block;
        width: 25px;
        height: 25px;
    }

    /* Notificación sin leer en alguna de las secciones de esta pestaña — punto
       naranja parpadeante sobre el ícono (el "abajo" del celular ya lo cubre la
       barra misma, que vive pegada al fondo de la pantalla). */
    .mobile-tabbar-item.has-unread .mobile-tabbar-icon::after {
        content: "";
        position: absolute;
        top: -1px;
        right: -3px;
        width: 8px;
        height: 8px;
        border-radius: 999px;
        background: #d97706;
        border: 1.5px solid var(--synbex-surface);
        animation: mobile-nav-unread-blink 1.4s ease-in-out infinite;
    }

    @media (prefers-reduced-motion: reduce) {
        .mobile-tabbar-item.has-unread .mobile-tabbar-icon::after {
            animation: none;
        }
    }

    @keyframes mobile-nav-unread-blink {
        0%, 100% {
            box-shadow: 0 0 0 0 rgba(217, 119, 6, 0.55);
        }

        50% {
            box-shadow: 0 0 0 5px rgba(217, 119, 6, 0);
        }
    }

    .mobile-tabbar-item.active {
        color: var(--synbex-primary);
    }

    /* Pastilla semi-transparente detrás del ícono activo (pedido explícito) —
       horizontal (más ancha que alta, esquinas redondeadas en vez de círculo
       perfecto: "como un rectángulo con los bordes redondeados") — padding +
       margin negativo del mismo tamaño para que el fondo se vea más grande sin
       empujar el alto de la barra (el padding normal habría estirado también a
       los demás ítems, porque .mobile-tabbar es flex sin align-items:center â
       los hijos se estiran parejo). */
    /* Azul clarito (--synbex-primary-soft, el mismo que ya usan los cuadritos de
       ícono del resto de la app) en tema claro — pedido explícito: en tema
       oscuro se pisa más abajo con blanco transparente (ver los bloques de
       [data-theme]/prefers-color-scheme después de este @media), porque ese
       mismo primary-soft en oscuro sale morado, no el efecto "vidrio" que se
       quería ahí. */
    .mobile-tabbar-item.active .mobile-tabbar-icon {
        padding: 6px 16px;
        margin: -6px -16px;
        border-radius: 14px;
        background: var(--synbex-primary-soft);
        border: 1px solid color-mix(in srgb, var(--synbex-primary) 30%, transparent);
    }

    .dashboard-main {
        padding-bottom: 120px;
    }
}

@media (prefers-color-scheme: dark) {
    .mobile-tabbar-item.active .mobile-tabbar-icon {
        background: rgba(255, 255, 255, 0.22);
        border-color: rgba(255, 255, 255, 0.35);
    }
}

[data-theme="dark"] .mobile-tabbar-item.active .mobile-tabbar-icon {
    background: rgba(255, 255, 255, 0.22);
    border-color: rgba(255, 255, 255, 0.35);
}

[data-theme="light"] .mobile-tabbar-item.active .mobile-tabbar-icon {
    background: var(--synbex-primary-soft);
    border-color: color-mix(in srgb, var(--synbex-primary) 30%, transparent);
}

/* Modal flotante genérico (fondo desenfocado + tarjeta centrada) — usado por
   SettingsMenu (Tema/Mi cuenta) y por el reporte de Mi limpieza. Global porque
   más de un componente lo necesita. */
.settings-modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(15, 17, 21, 0.55);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 200;
    padding: 20px;
}

.settings-modal-dialog {
    width: 100%;
    max-width: 440px;
    max-height: calc(100vh - 40px);
    overflow-y: auto;
    background: var(--synbex-surface);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-lg);
    box-shadow: var(--synbex-shadow);
    animation: settings-modal-in 0.15s ease;
}

/* Modal de "Nueva asamblea" (y de "Nueva votación", que reusa esta misma
   clase) — más ancho que el estándar (440px) para que la información de la
   asamblea, participación y quórum entren cómodas, pero sin llegar a ocupar
   toda la pantalla. En escritorio usa 59.5% del ancho de pantalla (70% menos
   un 15%, a pedido); en celular/tablet se queda con 612px como techo (en la
   práctica no se nota, porque en pantallas angostas el modal ya llena el
   ancho disponible bastante antes de llegar a ese límite). */
.assembly-create-modal-dialog {
    max-width: 612px;
}

@media (min-width: 901px) {
    .assembly-create-modal-dialog {
        max-width: 59.5vw;
    }
}

/* Modal de "Nueva asignación" (Asignar limpiezas) — mismo criterio de ancho
   que .assembly-create-modal-dialog: más espacio que el estándar de 440px
   para que quepan las secciones (destino, personal, detalles) sin sentirse
   apretadas, sin llegar a ocupar toda la pantalla en escritorio. */
.cleaning-assign-modal-dialog {
    max-width: 560px;
}

@media (min-width: 901px) {
    .cleaning-assign-modal-dialog {
        max-width: 54vw;
    }
}

@keyframes settings-modal-in {
    from {
        opacity: 0;
        transform: scale(0.96) translateY(6px);
    }

    to {
        opacity: 1;
        transform: scale(1) translateY(0);
    }
}

.settings-modal-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    padding: 16px 18px;
    border-bottom: 1px solid var(--synbex-border);
}

/* Variante con ícono a la izquierda del título/subtítulo (ej. "Nueva asamblea")
   — .catalog-icon-box es el mismo cuadrito redondeado que ya usa el encabezado
   de cada sub-página, reutilizado acá para que combine. */
.settings-modal-header-info {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
}

.settings-modal-title {
    margin: 0;
    font-size: 16px;
    font-weight: 700;
    color: var(--synbex-text);
}

.settings-modal-subtitle {
    margin: 4px 0 0;
    font-size: 13px;
    color: var(--synbex-text-muted);
}

.settings-modal-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border: none;
    border-radius: var(--synbex-radius-md);
    background: transparent;
    color: var(--synbex-text-muted);
    font-size: 14px;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.settings-modal-close:hover {
    background: var(--synbex-primary-soft);
    color: var(--synbex-text);
}

.settings-modal-body {
    padding: 18px;
}

/* Encabezado de "Mi cuenta": inicial(es) del nombre en un círculo, dentro de
   su propia tarjeta, junto al nombre completo y el rol/apartamento debajo. A
   propósito SIN ícono de cámara ni forma de subir foto — el residente no
   puede ponerse una foto de perfil. */
.account-header {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 16px;
    margin-bottom: 20px;
    background: var(--synbex-bg);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-lg);
}

.account-avatar {
    flex-shrink: 0;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    font-weight: 700;
    font-size: 18px;
}

.account-name {
    font-weight: 700;
    font-size: 15px;
    color: var(--synbex-text);
}

.account-role-line {
    font-size: 13px;
    color: var(--synbex-text-muted);
    margin-top: 2px;
}

.account-divider {
    border: none;
    border-top: 1px solid var(--synbex-border);
    margin: 16px 0;
}

/* Encabezado de sección (Información de contacto / Preferencias de
   notificaciones): ícono en caja redondeada + título + una línea de
   explicación debajo — a diferencia de .catalog-section-header (ícono suelto,
   sin subtítulo), que se usa en otras pantallas y no convenía tocar. */
.account-section-header {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    margin: 24px 0 14px;
}

.account-section-header:first-of-type {
    margin-top: 0;
}

.account-section-icon {
    flex-shrink: 0;
    width: 38px;
    height: 38px;
    border-radius: var(--synbex-radius-md);
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.account-section-icon svg {
    width: 19px;
    height: 19px;
}

.account-section-header h3 {
    margin: 0;
    font-size: 15px;
    color: var(--synbex-text);
}

.account-section-header p {
    margin: 2px 0 0;
    font-size: 12.5px;
    color: var(--synbex-text-muted);
}

/* Cada campo (correo, celular, teléfono) con su propio ícono a la izquierda,
   alineado con la etiqueta y la caja del campo juntas. */
.account-field-row {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    margin-bottom: 16px;
}

.account-field-icon {
    flex-shrink: 0;
    width: 34px;
    height: 34px;
    margin-top: 2px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.account-field-icon svg {
    width: 16px;
    height: 16px;
}

.account-field-body {
    flex: 1;
    min-width: 0;
}

/* Correo: mismo aspecto de caja que los demás campos (igual que en el
   diseño), pero no editable — es un <p>, no un <input>. */
/* Lista de personal de Limpieza seleccionable en "Nueva asignación" — fila
   completa clicable (envuelta en <label>) en vez del checkbox suelto de
   .synbex-checkbox-row, para que se note de un vistazo quién quedó marcado. */
.cleaning-assign-staff-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.cleaning-assign-staff-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    background: var(--synbex-bg);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-md);
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease;
}

.cleaning-assign-staff-row:hover {
    border-color: var(--synbex-primary);
}

.cleaning-assign-staff-row.is-selected {
    background: var(--synbex-primary-soft);
    border-color: var(--synbex-primary);
}

.cleaning-assign-staff-icon {
    flex-shrink: 0;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.cleaning-assign-staff-icon svg {
    width: 15px;
    height: 15px;
}

.cleaning-assign-staff-name {
    flex: 1;
    min-width: 0;
    font-weight: 600;
    font-size: 14.5px;
    color: var(--synbex-text);
}

.cleaning-assign-staff-row input[type="checkbox"] {
    flex-shrink: 0;
    width: 18px;
    height: 18px;
    accent-color: var(--synbex-primary);
    cursor: pointer;
}

.account-readonly-value {
    margin: 0;
    box-sizing: border-box;
    padding: 10px 12px;
    font-size: 14.5px;
    color: var(--synbex-text);
    background: var(--synbex-bg);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-md);
}

.account-channel-hint {
    font-size: 12px;
    color: var(--synbex-text-muted);
}

.account-toggle-row {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 14px;
    margin-bottom: 12px;
    background: var(--synbex-bg);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-md);
}

.account-toggle-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

.account-toggle-desc {
    margin: 4px 0 0;
    font-size: 12.5px;
    color: var(--synbex-text-muted);
}

/* Panel "cristalizado": mismo fondo que ya tiene el modal detrás, con un
   tinte blanco translúcido apenas encima (aclara un poco en oscuro, casi
   imperceptible en claro) y borde visible — envuelve cada sección
   (Información de contacto / Preferencias de notificaciones) para que se
   distingan como paneles aparte, con el mismo estilo entre sí. */
.account-panel {
    padding: 16px;
    margin-bottom: 20px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-lg);
}

.account-panel:last-of-type {
    margin-bottom: 0;
}

.account-panel .account-toggle-row:last-child {
    margin-bottom: 0;
}

/* Fijos abajo sin importar cuánto se suba o baje (pedido explícito) — sticky en
   vez de fixed a propósito: este mismo panel se usa tanto en la página completa
   de "Mi cuenta" (donde pega al fondo del viewport al hacer scroll) como dentro
   del modal de SettingsMenu (donde .settings-modal-dialog es su propio
   contenedor con scroll) — position:fixed se saldría del modal, sticky respeta
   el contorno de cualquiera de los dos. El fondo sólido + la línea de arriba
   son necesarios porque el contenido de atrás sigue pasando por debajo. */
/* Pedido explícito: ya NO flota pegada abajo del todo — queda como cualquier
   otro bloque, justo debajo del panel de notificaciones (donde ya vive en el
   marcado), sin position:sticky ni offset para la isla del menú móvil. */
.account-actions {
    display: flex;
    gap: 10px;
    margin-top: 22px;
    padding-top: 14px;
    border-top: 1px solid var(--synbex-border);
}

.account-actions .btn-synbex {
    flex: 1;
    justify-content: center;
}

.account-actions .btn-synbex svg {
    width: 16px;
    height: 16px;
}

.cleaning-report-modal-actions {
    display: flex;
    gap: 10px;
}

.cleaning-report-modal-actions .btn-synbex {
    flex: 1;
}

@media screen {
    .cleaning-report-preview.is-collapsed {
        display: none;
    }
}

/* Tarjeta móvil de "Mis mantenimientos" / "Mis reservas" — ícono de estado
   (arriba a la izquierda, sin fondo) coloreado según el estado del caso. */
.maintenance-status-icon {
    display: inline-flex;
    line-height: 1;
}

.maintenance-status-icon.is-pending {
    color: #d97706;
}

.maintenance-status-icon.is-inprogress {
    color: var(--synbex-primary);
}

.maintenance-status-icon.is-completed,
.maintenance-status-icon.is-confirmed {
    color: var(--synbex-success);
}

.maintenance-status-icon.is-rejected {
    color: var(--synbex-danger);
}

/* Franja superior de la tarjeta móvil: el mismo color que hay detrás del texto
   del badge (Pendiente/En progreso/Finalizado, o Pendiente/Confirmada/Rechazada
   en reservas) ahora cubre todo el ancho de esa primera fila (ícono + estado),
   y el resto de la tarjeta sigue blanca. Se estira con margen negativo para
   tapar el padding de .synbex-card y llegar hasta los bordes/esquinas
   redondeadas de arriba. Clase compartida por Mantenimiento y Reservas. */
.mobile-card-status-band {
    margin: -16px -16px 0;
    padding: 14px 16px;
    border-radius: var(--synbex-radius-lg) var(--synbex-radius-lg) 0 0;
}

.mobile-card-status-band.is-pending {
    background: rgba(217, 119, 6, 0.12);
}

.mobile-card-status-band.is-inprogress {
    background: var(--synbex-primary-soft);
}

.mobile-card-status-band.is-completed,
.mobile-card-status-band.is-confirmed {
    background: rgba(22, 163, 74, 0.12);
}

.mobile-card-status-band.is-rejected {
    background: var(--synbex-danger-soft);
}

/* El badge ya no necesita su propio fondo — la franja entera ya lleva ese
   mismo color, así que acá solo se ve como texto en negrita de color. */
.mobile-card-status-band .badge {
    background: transparent;
    padding: 0;
}

@media (max-width: 480px) {
    .mobile-card-status-band {
        margin: -14px -14px 0;
    }
}

@media (max-width: 600px) {
    .maintenance-new-request-btn {
        width: 100%;
        justify-content: center;
        padding: 14px 20px;
        font-size: 15px;
        margin-top: 4px;
    }
}

/* Ãcono redondo de "Mis mantenimientos": un solo ícono estándar (la llave
   inglesa) para todas las tarjetas — solo el color de fondo cambia según el
   estado, reutilizando los mismos tonos de .mobile-card-status-band. */
.mobile-card-icon.is-pending {
    background: rgba(217, 119, 6, 0.12);
    color: #d97706;
}

.mobile-card-icon.is-inprogress {
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.mobile-card-icon.is-completed {
    background: rgba(22, 163, 74, 0.12);
    color: var(--synbex-success);
}

/* Fila de fechas de la tarjeta de mantenimiento: "Solicitada" y "Atención
   estimada" una debajo de la otra. */
.maintenance-date-row {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding-top: 12px;
    border-top: 1px solid var(--synbex-border);
}

.maintenance-date-col {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
}

.maintenance-date-value {
    font-size: 12.5px;
}

@media (max-width: 600px) {
    /* Más espacio entre cada tarjeta de mantenimiento, a pedido del usuario. */
    .maintenance-mobile-cards {
        gap: 22px;
    }
}

/* ======================================
   Diseño "Catálogo" — patrón reutilizable para pantallas simples de
   administración que agregan/eliminan ítems de una lista corta (campos de
   registro de visitantes, checklist de limpieza, puntos de entrada, etc.):
   encabezado con ícono + título + pastilla informativa a la derecha, tarjeta
   "Agregar nuevo X" con título en índigo + línea divisoria + grid de
   formulario + botón degradado, y tarjeta "X registrados" con pastilla de
   conteo + tabla con un ícono cuadrado por fila. Global (no scoped) a
   propósito, para reusarlo tal cual en varias páginas sin duplicar CSS.
   ====================================== */
.catalog-header-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 20px;
}

/* Botón "Volver" de BotonVolver.razor — sale en TODA pantalla que esté dentro de
   un menú de botones grandes, y a dónde lleva lo decide RutasDeVuelta.cs.

   Antes solo salía en celular, con el argumento de que en computador está el menú
   lateral. No alcanzaba: el menú lleva al menú principal, no al hub del que se
   entró, así que para devolverse un escalón había que adivinar. Ahora sale
   siempre, y encadenado: de reservar un área se vuelve al listado de áreas, y de
   ahí a Reservas y visitas.

   Discreto a propósito: es un paso atrás, no la acción principal de la pantalla.
   Por eso va con borde y gris en vez de relleno de color, y solo se tiñe al pasar
   el mouse. El tamaño sale de los tokens de la escala de interfaz, así que se
   achica solo en pantallas chicas o con zoom, igual que el resto. */
.boton-volver {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    width: fit-content;
    margin-bottom: 14px;
    padding: var(--ui-boton-pad-y) var(--ui-boton-pad-x);
    border: 1px solid var(--synbex-border);
    border-radius: 999px;
    background: var(--synbex-surface);
    color: var(--synbex-text-muted);
    font-weight: 600;
    font-size: var(--ui-boton-fz);
    line-height: 1;
    text-decoration: none;
    transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}

.boton-volver:hover {
    color: var(--synbex-primary);
    border-color: var(--synbex-primary);
    background: var(--synbex-primary-soft);
}

.boton-volver-icono {
    display: flex;
}

.boton-volver-icono svg {
    width: var(--ui-volver-icono);
    height: var(--ui-volver-icono);
}

/* En celular gana tamaño de toque sin volverse un botón de color: sigue siendo
   secundario, pero el dedo lo acierta. */
@media (max-width: 600px) {
    .boton-volver {
        padding: 9px 16px;
        font-size: 13.5px;
    }

    .boton-volver-icono svg {
        width: 16px;
        height: 16px;
    }
}

.catalog-header-left {
    display: flex;
    align-items: center;
    gap: 16px;
}

.catalog-icon-box {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 52px;
    height: 52px;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

/* Mismos colores que ya usan las tarjetas grandes de los hubs (hub-tile-icon),
   para que el ícono del encabezado de cada sub-página combine con el color que
   ya tenía su tarjeta en Comunidad/Ãreas comunes/etc. */
.catalog-icon-box.is-orange {
    background: rgba(217, 119, 6, 0.12);
    color: #d97706;
}

.catalog-icon-box.is-teal {
    background: rgba(13, 148, 136, 0.12);
    color: var(--synbex-phone-link);
}

.catalog-icon-box.is-green {
    background: rgba(22, 163, 74, 0.12);
    color: var(--synbex-success);
}

.catalog-icon-box.is-red {
    background: var(--synbex-danger-soft);
    color: var(--synbex-danger);
}

.catalog-icon-box.is-slate {
    background: rgba(107, 114, 128, 0.14);
    color: var(--synbex-text-muted);
}

/* Equipos/Asamblea/WhatsApp en "Mejorar plan" — colores nuevos porque los 5 de
   arriba ya estaban tomados por los otros módulos y cada tarjeta necesita el
   suyo propio para distinguirse de un vistazo. */
.catalog-icon-box.is-purple {
    background: rgba(147, 51, 234, 0.12);
    color: #9333ea;
}

.catalog-icon-box.is-indigo {
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

/* Verde de WhatsApp (su propio color de marca) — a propósito distinto del
   is-green de Reservas para que no se confundan en la misma grilla. */
.catalog-icon-box.is-emerald {
    background: rgba(37, 211, 102, 0.16);
    color: #1da851;
}

/* Parqueaderos en "Mejorar plan" — distinto del is-teal de Aseo para que no se
   confundan en la misma grilla. */
.catalog-icon-box.is-cyan {
    background: rgba(8, 145, 178, 0.12);
    color: #0891b2;
}

/* Equipos (/dashboard/equipos) — fila por equipo, con acciones y paneles
   internos (marcar hecho / historial) que se despliegan sin navegar a otra
   página. Global (no scoped) porque .asset-status-badge también lo usa la
   gráfica de resumen del inicio (Dashboard/Index.razor). */
.asset-row-card {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.asset-row-main {
    display: flex;
    align-items: flex-start;
    gap: 14px;
}

.asset-row-icon {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: var(--synbex-radius-md);
    background: rgba(107, 114, 128, 0.14);
    color: var(--synbex-text-muted);
}

.asset-row-icon svg {
    width: 20px;
    height: 20px;
}

.asset-row-info {
    flex: 1;
    min-width: 0;
}

.asset-row-name {
    font-weight: 600;
    font-size: 15px;
}

.asset-row-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    color: var(--synbex-text-muted);
    font-size: 13px;
    margin-top: 2px;
}

.asset-row-notes {
    font-size: 13px;
    color: var(--synbex-text-muted);
    margin-top: 4px;
}

.asset-row-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.asset-row-actions .btn-synbex svg {
    width: 14px;
    height: 14px;
}

.asset-inline-panel {
    border-top: 1px solid var(--synbex-border);
    padding-top: 12px;
}

/* Pill de estado (Vencido/Por vencer/Al día) — fondo suave + ícono/texto en el
   color de acento, nunca color solo (siempre acompañado del ícono y la
   etiqueta), mismos tonos rojo/ámbar/verde que ya usa el resto de Synbex. */
.asset-status-badge {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 12px;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
}

.asset-status-badge svg {
    width: 14px;
    height: 14px;
}

.asset-status-badge.is-overdue {
    background: var(--synbex-danger-soft);
    color: var(--synbex-danger);
}

.asset-status-badge.is-due-soon {
    background: rgba(217, 119, 6, 0.12);
    color: #d97706;
}

.asset-status-badge.is-ok {
    background: rgba(22, 163, 74, 0.12);
    color: var(--synbex-success);
}

/* Reusado por el bloque de asignaciones de limpieza (Pendientes/Completados/
   Vencidos) — is-overdue/is-ok se comparten tal cual, is-pending es nuevo. */
.asset-status-badge.is-pending {
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

/* Barra de equipos dentro de "General" (inicio de TenantAdmin/FacilityAdmin) —
   3 segmentos con ancho proporcional al conteo (flex-grow puesto inline por
   Index.razor), separados por un hueco de 2px (el color de fondo de la
   tarjeta se ve entre segmentos) en vez de un borde, para que cada segmento
   se distinga sin depender solo del color. */
.asset-maintenance-block {
    border-top: 1px solid var(--synbex-border);
    margin-top: 14px;
    padding-top: 14px;
}

.asset-summary-bar {
    display: flex;
    gap: 2px;
    height: 14px;
    border-radius: 999px;
    overflow: hidden;
    margin-top: 4px;
}

.asset-summary-bar-segment {
    min-width: 4px;
}

.asset-summary-bar-segment.is-overdue {
    background: var(--synbex-danger);
}

.asset-summary-bar-segment.is-due-soon {
    background: #d97706;
}

.asset-summary-bar-segment.is-ok {
    background: var(--synbex-success);
}

.asset-summary-bar-segment.is-pending {
    background: var(--synbex-primary);
}

.asset-summary-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 14px;
}

.asset-summary-legend .asset-status-badge {
    text-decoration: none;
    transition: filter 0.15s ease;
}

.asset-summary-legend .asset-status-badge:hover {
    filter: brightness(0.95);
}

/* Ãcono ("logo") del encabezado de Ãreas comunes — oculto solo en celular a
   pedido del usuario. Clase propia (no .catalog-icon-box a secas) para no
   afectar las demás páginas que reusan ese mismo componente visual. */
@media (max-width: 600px) {
    .areas-comunes-icon-box {
        display: none;
    }
}

/* Tarjetas de Conjuntos: horizontales y una por fila (a diferencia de
   synbex-area-grid, que agrupa varias tarjetas cuadradas por fila) — con un
   avatar de la inicial del nombre en vez de una foto. Si esta prueba gusta,
   se replica en Usuarios y Pisos. */
/* Pedido explícito: los paneles de Conjuntos ocupan demasiado ancho en
   escritorio — se limita el ancho de TODO el bloque (botón "Nuevo conjunto"
   + lista, para que ambos queden alineados) y se centra en la página. En
   celular no alcanza ese 60% ni de cerca, así que se vuelve a 100% ahí. */
.facility-page-inner {
    max-width: 50%;
    margin: 0 auto;
}

@media (max-width: 900px) {
    .facility-page-inner {
        max-width: 100%;
    }
}

.facility-list {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.facility-card {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 16px 20px;
}

.facility-avatar {
    font-size: 22px;
    font-weight: 700;
}

.facility-card-body {
    flex: 1;
    min-width: 0;
}

.facility-card-header {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.facility-card-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    flex-shrink: 0;
}

/* Botón de acción con borde azul + sombreado "levantado" (no tan plano) —
   pedido explícito, pensado para reusarse en toda la sección de administrar
   Empresas/Conjuntos (y similares): se agrega ENCIMA de .btn-synbex
   .btn-synbex-secondary (no lo reemplaza), así el texto/tamaño se queda
   igual y solo cambia color de borde/texto + la sombra. */
.catalog-action-btn {
    border-color: var(--synbex-primary);
    color: var(--synbex-primary);
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.35);
    transition: box-shadow 0.15s ease, transform 0.15s ease, background 0.15s ease;
}

.catalog-action-btn:hover {
    box-shadow: 0 3px 6px rgba(0, 0, 0, 0.18), 0 1px 3px rgba(0, 0, 0, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.35);
}

.catalog-action-btn:active {
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15), inset 0 1px 2px rgba(0, 0, 0, 0.15);
    transform: translateY(1px);
}

@media (max-width: 640px) {
    .facility-card {
        flex-wrap: wrap;
    }

    /* box-sizing explícito: sin él, el padding lateral se SUMA al 100% y la
       fila de botones terminaba más ancha que la propia tarjeta, que era lo
       que empujaba la página hacia la derecha en celular.

       El sangrado de 72px que alineaba los botones con el nombre (a la derecha
       del avatar) se quitó a propósito: en un celular de 360px dejaba apenas
       222px útiles y "Desactivar" se caía a una segunda fila. Sin él hay 294px
       y los tres botones entran en una sola línea, que es lo que importa acá. */
    .facility-card-actions {
        width: 100%;
        padding-left: 0;
        box-sizing: border-box;
    }

    /* Reparto parejo del ancho en vez de que cada botón mida lo que mide su
       texto: con flex-basis 0 todos quedan del mismo tamaño y ninguno empuja al
       siguiente hacia abajo.

       El min-width es el que decide cuántos caben por fila, y el wrap se queda
       a propósito: Pisos/Unidades/Edificios/Conjuntos tienen 3 botones (3Ã78 +
       16 de espacios = 250 dentro de 294 â una sola fila, medido), pero
       Conjuntos como SuperAdmin tiene un cuarto ("Módulos": 4Ã78 + 24 = 336 >
       294) y ahí baja "Desactivar" solo a una segunda fila a lo ancho — que
       para una acción destructiva está bien, mejor que cuatro botones
       espachurrados.

       nowrap en el texto para que "Desactivar" nunca se parta en dos líneas y
       deje ese botón más alto que los demás.

       box-sizing explícito porque acá conviven <a class="btn-synbex"> y
       <button class="btn-synbex">: el navegador le da border-box al <button>
       y content-box al <a>, así que con el mismo flex-basis:0 los enlaces
       terminaban 10px más anchos que el botón (96 vs 86 medidos). Igualarlos
       acá y no en .btn-synbex a secas para no cambiar el ancho de los botones
       del resto de la app. */
    .facility-card-actions .btn-synbex {
        flex: 1 1 0;
        min-width: 78px;
        padding-left: 8px;
        padding-right: 8px;
        white-space: nowrap;
        box-sizing: border-box;
    }
}

/* Logo de empresa en la pantalla de login — tenant-logo.js lo aplica con
   JavaScript puro (todavía no hay sesión con la que Blazor sepa el logo desde
   el servidor), por eso esta regla vive acá y no en Login.razor.css: el
   aislamiento de CSS con ámbito de Blazor NUNCA calza con un elemento creado
   por document.createElement(), solo con markup que el propio Razor compiló.
   Mismo tratamiento que el logo del sidebar (.dashboard-brand-logo-box) —
   caja blanca redondeada, nunca la imagen suelta. Solo aparece si el conjunto
   tiene logo propio; sin logo, el "S" morado de siempre se queda igual.

   Pedido explícito, bug real: la versión anterior REEMPLAZABA el <div id=
   "login-brand-mark"> entero por uno nuevo sin id (mark.replaceWith(...)),
   metiendo una <img> donde el servidor esperaba el texto "S". Blazor nunca
   pudo reconciliar eso contra el HTML que vuelve a mandar en cada navegación
   entre /login y /login/olvide-clave (el tipo de hijo cambió de texto a
   elemento) y lo dejaba pegado ahí para siempre, sin id — con lo que
   tenant-logo.js ya no lo volvía a encontrar en la vuelta siguiente, y el
   "Synbex" de al lado se quedaba visible sin ocultar (el bug de "aparecen
   los dos"). Ahora el mismo <div id="login-brand-mark"> de siempre se queda
   igual (mismo texto "S" adentro, nunca se toca) y solo se le agrega esta
   clase + una custom property con el logo — un cambio de clase/estilo en
   línea sobre el MISMO nodo, que Blazor sí sincroniza bien en cada
   navegación (igual que ya hacía con el "Synbex" de al lado). */
/* "#login-brand-mark.has-logo", no ".login-brand-mark.has-logo" a secas —
   Login.razor.css trae su propia ".login-brand-mark" con el atributo "b-xxxx"
   que Blazor le agrega (mismo puntaje de especificidad que dos clases), y esa
   hoja de estilos "Synbex.Web.styles.css" carga DESPUÃS de este app.css en
   App.razor — en un empate de especificidad gana la que carga más tarde, así
   que sin el id esta regla nunca le ganaba (el "S" morado de siempre se
   quedaba encima del logo, aunque la clase "has-logo" sí estuviera puesta).
   El id sube la especificidad por encima de cualquier empate posible. */
#login-brand-mark.has-logo {
    /* Sin esto se queda con el 56x56 fijo del "S" morado (mucho más chico que
       el logo de antes, que llegaba a 80px de alto y 200px de ancho) y pegado
       a la izquierda de la fila (el "S" nunca necesitó centrarse porque
       "Synbex" al lado ya no ocupa espacio con el logo puesto — display:none
       — pero .login-brand sigue siendo flex con align-items normal, no
       centra solo). margin:0 auto en un hijo de flex centra SOLO este
       elemento sin tocar la regla de .login-brand ni el caso sin logo (mismo
       truco que usaba el .login-brand-logo-box de antes).

       width fijo (200px), no auto — antes el ancho salía solo del tamaño
       natural de la <img> (una imagen real tiene su propio ancho intrínseco
       según su relación de aspecto); background-image en un <div> NUNCA hace
       eso, así que con width:auto la caja se encogía al ancho mínimo del
       texto "S" invisible + el padding — un rectángulo angosto y alto que
       con border-radius:20px se veía como una píldora. Con un ancho fijo
       queda una caja pareja pase lo que pase con la forma del logo real —
       background-size:contain lo acomoda adentro sin deformarlo. */
    width: var(--login-logo-ancho);
    height: var(--login-logo-alto);
    margin: 0 auto;
    box-sizing: border-box;
    background: #fff;
    border: 1px solid var(--synbex-border);
    border-radius: 20px;
    background-image: var(--login-brand-logo-url);
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
    background-origin: content-box;
    padding: 10px;
    color: transparent;
}

/* Botón "elegir imagen" del logo de empresa (TenantForm.razor) — un input de
   archivo normal es feo e inconsistente entre navegadores, así que se oculta
   (mismo truco que .landing-contact-honeypot: fuera de la vista pero sigue en
   el DOM, foco por teclado y lector de pantalla funcionan) y un <label>
   asociado por "for" hace de botón, con el ícono de galería (HubIcons "image")
   como único contenido — no hace falta texto, ya se entiende qué hace. */
.tenant-logo-picker-input {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

.tenant-logo-picker-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    flex-shrink: 0;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.tenant-logo-picker-btn:hover {
    background: var(--synbex-primary);
    color: #fff;
}

.tenant-logo-picker-btn.is-busy {
    pointer-events: none;
    opacity: 0.6;
}

.tenant-logo-picker-btn svg {
    width: 22px;
    height: 22px;
}

/* Tarjetas de Empresas (SuperAdmin): mismo lenguaje visual que .facility-card
   (avatar + nombre + badge), pero apiladas en vertical — abajo del encabezado va
   el panel de acciones agrupadas por categoría (General/Conjunto/Comunicación/
   Comunidad), siempre visible en vez de aparecer solo al hacer clic, porque son
   demasiadas para caber en una sola fila horizontal como las de Conjuntos. */
.tenant-card {
    flex-direction: column;
    align-items: stretch;
    gap: 14px;
}

.tenant-card-top {
    display: flex;
    align-items: center;
    gap: 16px;
}

.tenant-card-logo {
    object-fit: contain;
    padding: 6px;
    background: var(--synbex-surface);
    border: 1px solid var(--synbex-border);
}

.tenant-card .tenant-expand-panel {
    padding: 14px 0 0;
    border-top: 1px solid var(--synbex-border);
}

@media (max-width: 640px) {
    .tenant-card-top {
        flex-wrap: wrap;
    }
}

.catalog-info-pill {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    border-radius: 999px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
}

.catalog-info-pill svg {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

.catalog-section-header {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 14px;
}

.catalog-section-header svg {
    width: 20px;
    height: 20px;
    color: var(--synbex-primary);
    flex-shrink: 0;
}

.catalog-section-header h3 {
    margin: 0;
    color: var(--synbex-primary);
    font-size: 16px;
}

.catalog-section-divider {
    height: 1px;
    background: var(--synbex-primary-soft);
    margin-bottom: 20px;
}

.catalog-add-btn {
    background: linear-gradient(135deg, var(--synbex-primary), #819df8);
    color: #fff;
    font-weight: 700;
    border: none;
    margin-top: 4px;
}

.catalog-add-btn:hover {
    filter: brightness(1.08);
}

.catalog-list-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 16px;
}

/* Botón verde "+ Agregar" que expande/colapsa el formulario de alta — mismo
   patrón que Limpieza â Checklist (.checklist-add-btn ahí), generalizado acá
   para reusarlo en toda Portería (Campos de registro, Entradas, Visitantes
   frecuentes/con cita/no deseados): clic para abrir el formulario, clic de
   nuevo para cerrarlo. */
.catalog-add-toggle-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    min-width: 110px;
    height: 32px;
    padding: 0 14px;
    flex-shrink: 0;
    border-radius: var(--synbex-radius-md);
    /* Verde sobrio (#16a34a, el mismo que la app usa para "éxito"). Antes era
       #1ed11e, un verde muy encendido que se pidió bajar de tono. Va fijo y no
       como variable de tema: en modo oscuro esa variable aclara el verde y el
       texto blanco encima perdería contraste. */
    border: 1px solid #16a34a;
    background: #16a34a;
    color: #ffffff;
    font-size: 13.5px;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}

/* Al pasar por encima se oscurece y el resplandor crece. Antes se volvía morado
   claro con letra morada, que con el verde nuevo parecía otro botón distinto. */
.catalog-add-toggle-btn:hover {
    background: #15803d;
    border-color: #15803d;
    color: #ffffff;
}

/* Buscador de las listas de administración (Frecuentes, Citas, No deseados) —
   pedido explícito: antes ocupaba el 100% del ancho de la tarjeta, ahora la
   mitad (50%), y con más separación abajo para que no quede pegado al
   encabezado de la tabla de abajo. */
.catalog-search-field {
    margin-bottom: 28px;
}

.catalog-search-input {
    width: 50%;
}

.catalog-count-pill {
    padding: 5px 12px;
    border-radius: 999px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    font-size: 12.5px;
    font-weight: 700;
    white-space: nowrap;
}

.catalog-row-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    flex-shrink: 0;
}

.catalog-row-icon svg {
    width: 18px;
    height: 18px;
}

.catalog-row-label {
    display: flex;
    align-items: center;
    gap: 12px;
    font-weight: 700;
    text-transform: uppercase;
    font-size: 13px;
    letter-spacing: 0.02em;
}

.catalog-delete-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.catalog-delete-btn svg {
    width: 14px;
    height: 14px;
}

/* Tope de ancho para las páginas de "Diseño Catálogo": en computadora, todo el
   contenido (encabezado + tarjetas) ocupa como máximo el 60% del panel — en
   celular/tablet angosto no tendría sentido, así que ahí se deja a todo el
   ancho disponible. Global (no scoped) para poder reusarlo en Campos,
   Entradas, Visitantes frecuentes, Citas, No deseados, Permisos y Reportes. */
.catalog-page-container {
    width: 100%;
}

@media (min-width: 900px) {
    /* Columna de contenido con ancho MÃXIMO en píxeles, no un porcentaje (pedido
       explícito: en un portátil de 1366 entraba mucha menos información que en un
       monitor de 1920).

       Con 60 % el ancho dependía del monitor: 1.152 px en una pantalla de 1920 pero
       apenas 678 px en una de 1280, o sea que en la pantalla chica —justo donde hace
       falta aprovechar— se desperdiciaba casi la mitad del panel y todo se veía
       apretado y grande. Con min() la columna usa casi todo el panel mientras la
       pantalla es chica, y deja de crecer al llegar a 1.180 px, que es el ancho
       cómodo para leer. Es el patrón estándar de "columna de contenido". */
    .catalog-page-container {
        max-width: min(92%, 1180px);
        margin: 0 auto;
    }

    /* Variante ancha para las pantallas de Contabilidad (pedido explícito): tienen
       varias columnas de cifras y gráficos, y al 60 % quedaban apretadas. */
    .catalog-page-container.es-ancho {
        max-width: 90%;
    }

    /* Variante del inicio del residente (pedido explícito 2026-10-04: "está
       reducido, un poco más ancho, a un 85 %"). Sin el tope de 1180px del general:
       en un monitor grande los paneles ocupaban solo ~60-70 % del espacio y quedaban
       chicos; con 85 % crecen con la pantalla. En celular no aplica (este bloque
       empieza en 900px). */
    .catalog-page-container.inicio-ancho {
        max-width: 85%;
    }
}

/* Variante angosta para las pantallas personales de "Mi hogar" (Mis paquetes,
   Mi limpieza, Mis mantenimientos, Mis parqueaderos, Visitantes frecuentes, Mi
   edificio) — pedido explícito: "está muy grande el panel a lo ancho... quiero
   que el ancho sea parecido al ancho que se usa para resoluciones grandes".

   El 92 % del ancho general (arriba) resuelve bien las pantallas con TABLAS —
   ahí sí hace falta el espacio—, pero en una lista personal de paquetes o de
   parqueaderos ese mismo 92 % se sentía de borde a borde en cualquier resolución
   media, porque el tope de 1180px solo entra a jugar pasados los 1683px de
   ancho. Con un porcentaje más bajo, el margen a los lados aparece antes: a
   1280px (150 % de zoom en un monitor de 1920, el caso que reportó el pedido)
   el panel general ocupaba unos 1177px con apenas 51px de aire a cada lado;
   con esta variante ocupa 896px y quedan 192px a cada lado — el mismo aire que
   ya se ve en una pantalla grande sin zoom, no solo en ella.

   El tope en píxeles (1180) se deja IGUAL que el general a propósito: en un
   monitor de verdad grande esta variante se ve exactamente como ya se veía
   antes, nada cambia ahí. Lo único distinto es el porcentaje, que es lo que
   hacía que "se sintiera ancho" en las resoluciones de en medio. */
@media (min-width: 900px) {
    .catalog-page-container.pagina-mi-hogar {
        max-width: min(70%, 1180px);
    }

    /* Título y subtítulo un poco más chicos en estas pantallas (pedido
       explícito). Se reusan los tokens de la escala de "hub" en vez de números
       sueltos: ya están hechos para achicarse solos con el zoom (--ui-hub-titulo
       va de 24 a 19px, --ui-hub-sub de 14 a 12,5px entre 1920 y 1280), así que
       esto queda consistente con el resto de la aplicación sin inventar una
       escala nueva. */
    .catalog-page-container.pagina-mi-hogar .page-title {
        font-size: var(--ui-hub-titulo);
    }

    .catalog-page-container.pagina-mi-hogar .page-subtitle {
        font-size: var(--ui-hub-sub);
    }
}

/* Lista de "una tarjeta por elemento" para catálogos donde cada fila tiene
   suficiente información propia como para no aplastarla en columnas de tabla —
   empezó en Anuncios (a pedido del usuario: quería cada anuncio individual, no
   una tabla, igual en computadora que en celular) y queda pensada para reusarse
   en cualquier otra pantalla de "Diseño Catálogo" que lo necesite. */
.catalog-item-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

/* Tarjeta de anuncio en la pantalla de administración (Anuncios) — pedido
   explícito: rediseño completo, "más moderno... como un ícono". Clases
   propias, sin tocar .catalog-item-card (genérico, no se usa en ningún otro
   lado hoy pero se deja intacto por si acaso) ni .synbex-announcement-card
   (esa es la tarjeta que ve el RESIDENTE en su propio dashboard — cosa
   aparte, no se toca). */
.announcement-admin-list {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

/* Pedido explícito (2026-09-30): fila arriba de la lista — cuántos hay en
   la pestaña actual a la izquierda, selector de mes (solo Inactivos) pegado
   del todo a la derecha. "margin-left:auto" es lo que lo empuja allá aunque
   el texto de la izquierda sea corto; sin el selector (Activos/Próximos) el
   texto se queda solo a la izquierda. */
.announcement-admin-summary-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 16px;
}

.announcement-admin-month-picker {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
}

/* Rediseño (pedido explícito, 2026-09-29, sobre una imagen de referencia): dos
   columnas separadas por una línea vertical (izquierda: título/vigencia/dirigido
   a/mensaje; derecha: estado/vistas-likes-dislikes), con una franja de
   color a la izquierda de toda la tarjeta — el mismo color del ícono
   (azul normal, rojo si es IsPriority). Los botones (Editar/Duplicar/
   Desactivar) ya NO viven dentro de esas columnas (ajuste 2026-09-30): la
   tarjeta pasa a apilar verticalmente "top" (ícono + las dos columnas) y
   luego la fila de acciones, de ancho completo. */
.announcement-admin-card {
    display: flex;
    flex-direction: column;
    padding: 18px;
    border-left: 4px solid var(--synbex-primary);
}

.announcement-admin-card.is-priority {
    border-left-color: var(--synbex-danger);
}

.announcement-admin-top {
    display: flex;
    align-items: stretch;
    gap: 16px;
}

.announcement-admin-icon {
    flex-shrink: 0;
    width: 44px;
    height: 44px;
    border-radius: var(--synbex-radius-md);
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.announcement-admin-icon.is-priority {
    background: var(--synbex-danger-soft);
    color: var(--synbex-danger);
}

.announcement-admin-icon svg {
    width: 22px;
    height: 22px;
}

/* Pequeña mayúscula gris arriba de cada dato (Título/Vigencia/Dirigido a/
   Mensaje/Vistas/Me gusta/No me gusta) — misma idea que .mobile-card-label. */
.announcement-admin-field-label {
    display: block;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--synbex-text-muted);
    margin-bottom: 2px;
}

/* Pedido explícito (ajuste 2026-09-29, y de nuevo 2026-09-30): las dos
   secciones de la tarjeta (izquierda: título/vigencia/dirigido a/mensaje;
   derecha: estado/vistas-likes/botones) reparten el ancho 60/40 — más aire
   para los botones, que ahora van en una sola fila dentro del panel. */
.announcement-admin-left {
    flex: 6 1 0%;
    min-width: 0;
}

.announcement-admin-title {
    font-weight: 700;
    font-size: 15.5px;
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}

/* Pedido explícito (2026-09-30): dos y dos — a la izquierda Título y
   Mensaje, a la derecha Dirigido a y Vigencia — en vez de Título arriba con
   Vigencia/Dirigido a en una franja angosta debajo. */
.announcement-admin-columns {
    display: flex;
    align-items: stretch;
    gap: 20px;
}

.announcement-admin-col {
    flex: 1 1 0%;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.announcement-admin-col-divider {
    flex-shrink: 0;
    width: 1px;
    background: var(--synbex-border);
    align-self: stretch;
}

.announcement-admin-field-group {
    margin-bottom: 12px;
}

.announcement-admin-field-group:last-child {
    margin-bottom: 0;
}

@media (max-width: 700px) {
    .announcement-admin-columns {
        flex-direction: column;
        gap: 0;
    }

    .announcement-admin-col-divider {
        display: none;
    }
}

.announcement-admin-meta-value {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    color: var(--synbex-text);
}

.announcement-admin-meta-value svg {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    color: var(--synbex-text-muted);
}

.announcement-admin-meta-sep {
    color: var(--synbex-border);
}

.announcement-admin-message {
    font-size: 13.5px;
    line-height: 1.5;
    color: var(--synbex-text);
    margin: 0;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.announcement-admin-divider {
    flex-shrink: 0;
    width: 1px;
    background: var(--synbex-border);
}

/* Pedido explícito (ajuste 2026-09-30): todo el panel (estado/vistas/me
   gusta/no me gusta) centrado en vez de pegado a la derecha — antes el
   ícono y el número de cada estadística quedaban alineados a la izquierda
   porque text-align no afecta el contenido de una fila flex. */
.announcement-admin-right {
    flex: 4 1 0%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
    text-align: center;
}

.announcement-admin-status {
    flex-shrink: 0;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 11.5px;
    font-weight: 700;
    white-space: nowrap;
}

.announcement-admin-status.is-active {
    background: color-mix(in srgb, var(--synbex-success) 15%, transparent);
    color: var(--synbex-success);
}

.announcement-admin-status.is-upcoming {
    background: rgba(37, 99, 235, 0.12);
    color: #2563eb;
}

.announcement-admin-status.is-inactive {
    background: rgba(107, 114, 128, 0.14);
    color: var(--synbex-text-muted);
}

.announcement-admin-stats {
    display: flex;
    gap: 16px;
}

.announcement-admin-stat {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.announcement-admin-stat-value {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 5px;
    font-size: 13px;
    font-weight: 600;
    color: var(--synbex-text);
}

.announcement-admin-stat-value svg {
    width: 15px;
    height: 15px;
    flex-shrink: 0;
    color: var(--synbex-text-muted);
}

/* "Ver quiénes" — se le quita el look nativo de <button> y se le agrega el
   subrayado que avisa que se puede tocar. */
.announcement-admin-viewers-toggle {
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    text-decoration: underline;
    font: inherit;
    font-size: 12.5px;
    color: var(--synbex-text-muted);
}

.announcement-admin-viewers-toggle:hover {
    color: var(--synbex-primary);
}

.announcement-admin-viewers {
    padding: 10px 12px;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-bg);
    font-size: 12.5px;
    color: var(--synbex-text-muted);
    text-align: left;
    width: 100%;
}

/* Pedido explícito (ajuste 2026-09-30, otra vez): los tres botones viven
   DENTRO del panel derecho, debajo de Vistas/Me gusta/No me gusta, en UNA
   sola fila (no apilados) — el panel ahora es 40% del ancho de la tarjeta,
   suficiente para los tres. "width:100%" en vez de "flex-basis:100%" a
   propósito: así el mismo valor sirve sin importar si el panel padre está en
   modo fila o columna (ver el gotcha ya documentado más abajo). */
.announcement-admin-actions {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px;
    width: 100%;
}

/* Mismo formato que los botones grandes de Documentos (Ver/Descargar) —
   color sólido + resplandor a juego, ícono a la izquierda. Página propia
   (no reutiliza .mobile-card-btn) porque esa clase solo trae su estilo
   completo dentro de @media (max-width:600px) salvo que cada pantalla lo
   reactive para escritorio; acá se necesita a cualquier ancho. */
.announcement-admin-action-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 10px 14px;
    border-radius: var(--synbex-radius-md);
    font-weight: 600;
    font-size: 13px;
    text-decoration: none;
    border: none;
    color: #fff;
    cursor: pointer;
    box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--synbex-glow-color, var(--synbex-primary)) 55%, transparent);
    transition: box-shadow 0.15s ease, background 0.15s ease;
}

.announcement-admin-action-btn:hover {
    box-shadow: 0 8px 20px -6px color-mix(in srgb, var(--synbex-glow-color, var(--synbex-primary)) 65%, transparent);
}

.announcement-admin-action-btn svg {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

.announcement-admin-action-btn.is-edit {
    background: var(--synbex-primary);
}

/* Pedido explícito (2026-09-30): los tres colores se quedan dentro de la
   misma paleta que ya usa el resto de la app (azul/verde), en vez de sumar
   un color nuevo — Duplicar pasa a verde (el mismo "success" de Activar y
   de "Descargar" en Documentos). */
.announcement-admin-action-btn.is-duplicate {
    --synbex-glow-color: var(--synbex-success);
    background: var(--synbex-success);
}

/* Pedido explícito (2026-09-30): Desactivar deja de ser rojo puro — pasa a
   un anaranjado rojizo, el mismo #d97706 que ya usa el ícono de Anuncios
   (catalog-icon-box.is-orange) — no es un color nuevo, ya estaba en la
   paleta de la propia pantalla. */
.announcement-admin-action-btn.is-deactivate {
    --synbex-glow-color: #d97706;
    background: #d97706;
}

.announcement-admin-action-btn.is-activate {
    --synbex-glow-color: var(--synbex-success);
    background: var(--synbex-success);
}

/* Pantalla mediana (laptop angosto, ventana achicada): la columna derecha fija
   ya no alcanza cómoda para vistas/likes — se deja de fijar el ancho y se
   acomoda en fila propia debajo del contenido, en vez de forzar el apilado
   total de celular. */
@media (max-width: 1100px) {
    .announcement-admin-top {
        flex-wrap: wrap;
    }

    .announcement-admin-left {
        flex-basis: 100%;
    }

    .announcement-admin-divider {
        width: 100%;
        height: 1px;
        margin: 4px 0;
    }

    .announcement-admin-right {
        flex-basis: 100%;
        flex-direction: row;
        flex-wrap: wrap;
        align-items: center;
        justify-content: space-between;
        text-align: left;
    }
}

/* En celular ya no hay fila para vistas/likes lado a lado — se apilan uno
   debajo del otro. */
@media (max-width: 700px) {
    .announcement-admin-right {
        flex-direction: column;
        align-items: center;
        text-align: center;
    }

    .announcement-admin-stats {
        width: 100%;
        justify-content: center;
    }
}

.announcement-admin-load-more {
    display: flex;
    justify-content: center;
    margin-top: 6px;
}

.catalog-item-card {
    padding: 16px;
}

.catalog-item-card-title {
    font-weight: 700;
    font-size: 15.5px;
    margin-bottom: 10px;
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

/* fila, no columna: en computadora los 3 datos quedan uno al lado del otro; en
   celular, angosto, se van a la línea siguiente solos — mismo marcado para
   ambos tamaños, sin duplicar el bloque por breakpoint. */
.catalog-item-card-fields {
    display: flex;
    flex-wrap: wrap;
    column-gap: 24px;
    row-gap: 6px;
    margin-bottom: 14px;
}

.catalog-item-card-field {
    display: flex;
    gap: 6px;
    font-size: 13.5px;
    line-height: 1.4;
}

.catalog-item-card-field-label {
    color: var(--synbex-text-muted);
    flex-shrink: 0;
}

.catalog-item-card-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    padding-top: 12px;
    border-top: 1px solid var(--synbex-border);
}

/* Sección bloqueada porque el conjunto no tiene ese módulo contratado
   (ver ModuleErrorBoundary.razor). Mismo lenguaje visual que catalog-icon-box. */
.module-blocked-card {
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
}

.module-blocked-icon {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 52px;
    height: 52px;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.module-blocked-card .btn-synbex {
    margin-left: auto;
}

/* Botón "Mejorar plan" al pie del menú lateral: solo lo ven los administradores de
   empresa/conjunto y solo cuando les falta algún módulo (ver DashboardLayout). */
.dashboard-upgrade-btn {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    margin-bottom: 12px;
    border-radius: var(--synbex-radius-md);
    border: 1px solid color-mix(in srgb, #d97706 45%, transparent);
    background: color-mix(in srgb, #d97706 12%, transparent);
    color: #b45309;
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
    transition: background 0.15s ease, border-color 0.15s ease;
}

.dashboard-upgrade-btn:hover {
    background: color-mix(in srgb, #d97706 22%, transparent);
    border-color: #d97706;
}

.dashboard-upgrade-icon {
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
}

.dashboard-upgrade-icon svg {
    width: 18px;
    height: 18px;
}

.dashboard-upgrade-count {
    margin-left: auto;
    flex-shrink: 0;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: 999px;
    background: #d97706;
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

@media (prefers-color-scheme: dark) {
    .dashboard-upgrade-btn { color: #fbbf24; }
}

[data-theme="dark"] .dashboard-upgrade-btn { color: #fbbf24; }
[data-theme="light"] .dashboard-upgrade-btn { color: #b45309; }

/* Pantalla "Mejorar plan": una tarjeta por módulo que le falta al conjunto. */
.upgrade-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
    gap: 16px;
    margin-bottom: 20px;
}

.upgrade-card {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.upgrade-card-header {
    display: flex;
    align-items: flex-start;
    gap: 12px;
}

.upgrade-card-title {
    margin: 0;
    font-size: 17px;
    font-weight: 700;
    color: var(--synbex-text);
}

.upgrade-card-tagline {
    margin: 4px 0 0;
    font-size: 13px;
    color: var(--synbex-text-muted);
}

.upgrade-card-badge {
    margin-left: auto;
    flex-shrink: 0;
}

.upgrade-card-features {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.upgrade-card-features li {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-size: 13px;
    color: var(--synbex-text);
}

.upgrade-card-check {
    flex-shrink: 0;
    display: inline-flex;
    color: #16a34a;
}

.upgrade-card-check svg {
    width: 16px;
    height: 16px;
}

.upgrade-contact-card {
    display: flex;
    align-items: flex-start;
    gap: 16px;
}

/* ==========================================================================
   Pantalla de Monitoreo (solo SuperAdmin)
   ========================================================================== */
.monitor-status {
    display: flex;
    flex-direction: column;
    gap: 18px;
    margin-bottom: 20px;
    border-left: 4px solid var(--synbex-border);
}

.monitor-status.is-good { border-left-color: #16a34a; }
.monitor-status.is-bad { border-left-color: #dc2626; }

.monitor-status-headline {
    display: flex;
    align-items: flex-start;
    gap: 12px;
}

.monitor-status-light {
    flex-shrink: 0;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    margin-top: 5px;
}

.monitor-status.is-good .monitor-status-light {
    background: #16a34a;
    box-shadow: 0 0 0 4px rgba(22, 163, 74, 0.18);
}

.monitor-status.is-bad .monitor-status-light {
    background: #dc2626;
    box-shadow: 0 0 0 4px rgba(220, 38, 38, 0.18);
    animation: monitor-alert-pulse 1.6s ease-in-out infinite;
}

@keyframes monitor-alert-pulse {
    0%, 100% { box-shadow: 0 0 0 4px rgba(220, 38, 38, 0.18); }
    50% { box-shadow: 0 0 0 9px rgba(220, 38, 38, 0.05); }
}

@media (prefers-reduced-motion: reduce) {
    .monitor-status.is-bad .monitor-status-light { animation: none; }
}

.monitor-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    gap: 14px;
    padding-top: 16px;
    border-top: 1px solid var(--synbex-border);
}

.monitor-stat {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.monitor-stat-number {
    font-size: 26px;
    font-weight: 700;
    line-height: 1.1;
    color: var(--synbex-text);
    font-variant-numeric: tabular-nums;
}

.monitor-stat-number.is-bad { color: #dc2626; }
.monitor-stat-number.is-warn { color: #d97706; }

.monitor-stat-label {
    font-size: 12px;
    color: var(--synbex-text-muted);
}

.monitor-disk {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid var(--synbex-border);
}

.monitor-disk-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
}

.monitor-disk-label {
    font-size: 13px;
    color: var(--synbex-text-muted);
}

.monitor-disk-value {
    font-size: 13px;
    font-weight: 600;
    color: var(--synbex-text);
    font-variant-numeric: tabular-nums;
}

.monitor-disk-value.is-warn { color: #d97706; }
.monitor-disk-value.is-bad { color: #dc2626; }

.monitor-disk-bar {
    height: 8px;
    border-radius: 999px;
    background: var(--synbex-border);
    overflow: hidden;
}

.monitor-disk-bar-fill {
    height: 100%;
    border-radius: 999px;
    background: #16a34a;
    transition: width 0.3s ease;
}

.monitor-disk-bar-fill.is-warn { background: #d97706; }
.monitor-disk-bar-fill.is-bad { background: #dc2626; }

.monitor-disk-updated {
    font-size: 11px;
    color: var(--synbex-text-muted);
}

.monitor-tabs {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 16px;
}

.monitor-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.monitor-entry {
    border-left: 3px solid var(--synbex-border);
    padding: 12px 16px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.monitor-entry.level-error { border-left-color: #dc2626; }
.monitor-entry.level-advertencia { border-left-color: #d97706; }
.monitor-entry.level-actividad { border-left-color: #64748b; }

.monitor-entry-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    font-size: 12px;
}

.monitor-entry-time {
    font-weight: 600;
    color: var(--synbex-text);
    white-space: nowrap;
}

.monitor-entry-source {
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    font-weight: 600;
}

.monitor-entry-meta { color: var(--synbex-text-muted); }

/* Al final de la fila de arriba (margin-left:auto la empuja a la derecha, ya
   que .monitor-entry-head es flex-wrap), para "limpiar" un registro puntual
   sin esperar a la limpieza automática por antigÃ¼edad. */
.monitor-entry-delete-btn {
    margin-left: auto;
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    padding: 4px;
    cursor: pointer;
    color: var(--synbex-text-muted);
    border-radius: var(--synbex-radius-md);
    transition: background 0.15s ease, color 0.15s ease;
}

.monitor-entry-delete-btn svg {
    width: 16px;
    height: 16px;
}

.monitor-entry-delete-btn:hover {
    background: var(--synbex-danger-soft);
    color: var(--synbex-danger);
}

/* Pantalla de Correos: "Reintentar ahora" en un correo que ya agotó los reintentos
   automáticos (típicamente después de corregir la configuración SMTP). Va justo antes
   del botón de eliminar, mismo tamaño de texto que el resto de la fila de meta. */
.monitor-entry-retry-btn {
    margin-left: auto;
    padding: 3px 10px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    border: none;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.monitor-entry-retry-btn:hover {
    background: var(--synbex-primary);
    color: #fff;
}

/* Cuando el botón de reintentar ya empujó todo a la derecha, el de eliminar no debe
   volver a hacerlo (quedaría un hueco raro entre ambos). */
.monitor-entry-retry-btn + .monitor-entry-delete-btn {
    margin-left: 0;
}

/* Flechas subir/bajar de las opciones de una lista desplegable (modal "Listas
   desplegables" en Campos de registro) — mismo lenguaje visual ámbar que las
   flechas de Pisos tenían, para que se note que reordena y no confundirse con
   el resto de acciones (eliminar en rojo, agregar en azul). */
.field-option-order-arrows {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex-shrink: 0;
}

.field-option-order-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 20px;
    padding: 0;
    background: var(--synbex-primary-soft);
    border: 1px solid var(--synbex-primary);
    border-radius: 6px;
    color: var(--synbex-primary);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.field-option-order-btn svg {
    width: 12px;
    height: 12px;
}

.field-option-order-btn:hover:not(:disabled) {
    background: var(--synbex-primary);
    border-color: var(--synbex-primary);
    color: #fff;
}

.field-option-order-btn:disabled {
    opacity: 0.35;
    cursor: default;
}

/* "Conjunto" arriba del panel "Agregar nuevo campo" (Campos de registro) —
   centrado, con la etiqueta en el morado/índigo de la app. Con un solo
   conjunto el <select> queda deshabilitado (una sola opción, ya elegida);
   con varios, sirve para cambiar de conjunto sin reabrir el modal inicial. */
.field-facility-context {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    margin-bottom: 20px;
}

.field-facility-context-label {
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--synbex-primary);
}

.field-facility-context-select {
    max-width: 320px;
    text-align: center;
    text-align-last: center;
}

.field-facility-context-select:disabled {
    opacity: 1;
    background: var(--synbex-surface);
    color: var(--synbex-text);
    font-weight: 600;
    cursor: default;
}

/* Cantidad de opciones de un campo (Campos de registro) — texto chico y gris,
   antes del botón "Listas", en vez de la pastilla de color que tenía junto
   al nombre del campo (pedido explícito: "que no grite", más discreto). */
.field-options-count {
    font-size: 12.5px;
    color: var(--synbex-text-muted);
    white-space: nowrap;
}

.monitor-entry-message {
    margin: 0;
    font-size: 14px;
    color: var(--synbex-text);
    word-break: break-word;
}

.monitor-entry-details summary {
    cursor: pointer;
    font-size: 12px;
    color: var(--synbex-text-muted);
}

.monitor-entry-details pre {
    margin: 8px 0 0;
    padding: 10px 12px;
    border-radius: 8px;
    background: var(--synbex-surface-alt, rgba(127, 127, 127, 0.1));
    font-size: 11px;
    line-height: 1.5;
    overflow-x: auto;
    white-space: pre-wrap;
    word-break: break-word;
}

/* Barra de nivel de seguridad de la contraseña (ver password-strength.js).
   Solo informa: nunca impide guardar. */
/* Siempre ocupa su espacio (visibility, no display:none) — antes desaparecía
   del todo mientras el campo estaba vacío y aparecía de golpe al escribir,
   empujando todo lo de abajo y dando un salto visual. */
.pwd-strength {
    display: flex;
    visibility: hidden;
    flex-direction: column;
    gap: 5px;
    margin-top: 8px;
}

.pwd-strength.is-visible { visibility: visible; }

.pwd-strength-track {
    height: 5px;
    border-radius: 999px;
    background: var(--synbex-border);
    overflow: hidden;
}

.pwd-strength-fill {
    height: 100%;
    width: 0;
    border-radius: 999px;
    transition: width 0.2s ease, background-color 0.2s ease;
}

/* display:block + line-height/min-height fijos (no "normal") — un <span> vacío
   puede medir un pelo distinto a uno con texto según la fuente, y eso era lo
   que seguía corriendo "Al menos 10 caracteres" hacia abajo un par de
   píxeles al aparecer el mensaje. Con altura fija, es exactamente la misma
   con o sin texto. */
.pwd-strength-label {
    display: block;
    font-size: 12px;
    font-weight: 600;
    line-height: 15px;
    min-height: 15px;
}

.pwd-strength[data-level="insegura"] .pwd-strength-fill { background: #dc2626; }
.pwd-strength[data-level="insegura"] .pwd-strength-label { color: #dc2626; }

.pwd-strength[data-level="normal"] .pwd-strength-fill { background: #d97706; }
.pwd-strength[data-level="normal"] .pwd-strength-label { color: #d97706; }

.pwd-strength[data-level="segura"] .pwd-strength-fill { background: #16a34a; }
.pwd-strength[data-level="segura"] .pwd-strength-label { color: #16a34a; }

@media (prefers-reduced-motion: reduce) {
    .pwd-strength-fill { transition: none; }
}

/* Aviso "confirma tu correo" en el panel (ver DashboardLayout). Solo informa, no
   bloquea nada — se puede cerrar y reaparece en el próximo inicio de sesión. */
.dashboard-email-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin: 0 24px 12px;
}

.dashboard-email-banner-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-shrink: 0;
}

.dashboard-email-banner-close {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 16px;
    line-height: 1;
    color: inherit;
    opacity: 0.6;
    padding: 4px;
}

/* Barra de carga global (action-loading.js) — una franja fina arriba de todo
   que aparece apenas se hace clic en cualquier botón/envío de formulario, y
   desaparece sola cuando el servidor responde. z-index por encima incluso del
   toast-host, para que nunca quede tapada. */
#synbex-loading-bar {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 3px;
    z-index: 3000;
    overflow: hidden;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s ease;
}

#synbex-loading-bar.synbex-loading-bar-active {
    opacity: 1;
}

.synbex-loading-bar-fill {
    height: 100%;
    width: 40%;
    background: var(--synbex-primary);
    border-radius: 0 2px 2px 0;
    animation: synbex-loading-bar-slide 1.1s ease-in-out infinite;
}

@keyframes synbex-loading-bar-slide {
    0% {
        margin-left: -40%;
    }

    100% {
        margin-left: 100%;
    }
}

@media (prefers-reduced-motion: reduce) {
    .synbex-loading-bar-fill {
        animation: none;
        width: 100%;
        margin-left: 0;
    }
}

.dashboard-email-banner-close:hover { opacity: 1; }

/* Tutorial guiado (TutorialOverlay.razor) — el "agujero de luz" es un solo div
   posicionado exactamente sobre el elemento a explicar, con un box-shadow
   gigante que oscurece todo lo demás (truco clásico: la sombra "rellena" el
   resto de la pantalla, dejando transparente solo el rectángulo del propio
   div). pointer-events:none en el agujero para que nunca tape un clic real
   por accidente — el tutorial se navega solo con sus propios botones. */
.tutorial-spotlight {
    z-index: 2500;
    border-radius: 10px;
    box-shadow: 0 0 0 9999px rgba(15, 17, 21, 0.65);
    pointer-events: none;
    transition: top 0.2s ease, left 0.2s ease, width 0.2s ease, height 0.2s ease;
}

.tutorial-tooltip {
    z-index: 2501;
    background: var(--synbex-surface);
    border: 1px solid var(--synbex-border);
    border-radius: 12px;
    box-shadow: 0 12px 32px -8px rgba(0, 0, 0, 0.35);
    padding: 18px 20px;
    transition: top 0.2s ease, left 0.2s ease;
}

.tutorial-tooltip-step {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--synbex-primary);
    margin-bottom: 6px;
}

.tutorial-tooltip-title {
    margin: 0 0 8px;
    font-size: 16px;
    font-weight: 700;
    color: var(--synbex-text);
}

.tutorial-tooltip-text {
    margin: 0;
    font-size: 14px;
    line-height: 1.5;
    color: var(--synbex-text-muted);
}

.tutorial-tooltip-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-top: 16px;
}

.tutorial-tooltip-nav {
    display: flex;
    gap: 8px;
}

.tutorial-btn {
    border: none;
    border-radius: 8px;
    padding: 8px 14px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.tutorial-btn-skip {
    background: none;
    color: var(--synbex-text-muted);
    padding-left: 0;
}

.tutorial-btn-skip:hover {
    color: var(--synbex-text);
    text-decoration: underline;
}

.tutorial-btn-secondary {
    background: var(--synbex-bg);
    border: 1px solid var(--synbex-border);
    color: var(--synbex-text);
}

.tutorial-btn-secondary:hover {
    background: var(--synbex-border);
}

.tutorial-btn-primary {
    background: var(--synbex-primary);
    color: #fff;
}

.tutorial-btn-primary:hover {
    filter: brightness(1.08);
}

@media (max-width: 480px) {
    .tutorial-tooltip {
        width: calc(100vw - 32px) !important;
        left: 16px !important;
    }
}

/* ==========================================================================
   Asamblea — modo pantalla completa (AssemblyLayout) y sala (Room.razor)
   ========================================================================== */

.assembly-shell {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    background: var(--synbex-bg);
}

.assembly-topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 20px;
    background: var(--synbex-surface);
    border-bottom: 1px solid var(--synbex-border);
    position: sticky;
    top: 0;
    z-index: 20;
}

.assembly-topbar-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    font-weight: 600;
    color: var(--synbex-text);
}

.assembly-topbar-mark {
    width: 28px;
    height: 28px;
    border-radius: 8px;
    background: var(--synbex-primary);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    font-weight: 700;
}

.assembly-topbar-title {
    font-size: 15px;
}

/* Rojo fijo a propósito (no var(--synbex-text)) — así se ve rojo tanto en tema
   claro como oscuro, sin depender de la paleta que cambia con el tema. */
.assembly-logout-btn {
    border: 1px solid #dc2626;
    background: transparent;
    color: #dc2626;
    padding: 8px 16px;
    border-radius: 8px;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
}

.assembly-logout-btn:hover:not(:disabled) {
    background: rgba(220, 38, 38, 0.1);
}

.assembly-logout-btn:disabled {
    opacity: 0.6;
    cursor: default;
}

.assembly-body {
    flex: 1;
    /* box-sizing: border-box es clave acá — sin esto, el padding se suma
       por fuera del width:100%, y en mobile eso es justo lo que causaba el
       desliz horizontal (28px de más, el doble del padding). */
    box-sizing: border-box;
    padding: 20px;
    max-width: 720px;
    margin: 0 auto;
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.assembly-card {
    background: var(--synbex-surface);
    border: 1px solid var(--synbex-border);
    border-radius: 14px;
    padding: 18px;
}

.assembly-card-title {
    margin: 0 0 4px 0;
    font-size: 21px;
    color: var(--synbex-text);
    text-align: center;
}

/* Debajo del título "Votaciones" — cuenta rápida de cuántas ya finalizaron,
   cuántas están en curso y cuántas faltan. A propósito chico y discreto (es
   informativo, no un dato central de la pantalla). */
.assembly-voting-summary-label {
    margin: 0 0 14px;
    text-align: center;
    font-size: 12.5px;
    color: var(--synbex-text-muted);
}

.assembly-card-subtitle {
    margin: 0 0 12px 0;
    color: var(--synbex-text-muted);
    font-size: 14px;
}

.assembly-badge {
    display: inline-flex;
    align-items: center;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
}

.assembly-badge-live {
    background: #dcfce7;
    color: #166534;
}

.assembly-badge-scheduled {
    background: #e0e7ff;
    color: #3730a3;
}

.assembly-badge-closed {
    background: #f3f4f6;
    color: #4b5563;
}

:root[data-theme="dark"] .assembly-badge-live,
@media (prefers-color-scheme: dark) {
    .assembly-badge-live {
        background: #14532d;
        color: #bbf7d0;
    }

    .assembly-badge-scheduled {
        background: #312e81;
        color: #c7d2fe;
    }

    .assembly-badge-closed {
        background: #1f2937;
        color: #d1d5db;
    }
}

:root[data-theme="dark"] .assembly-badge-scheduled {
    background: #312e81;
    color: #c7d2fe;
}

:root[data-theme="dark"] .assembly-badge-closed {
    background: #1f2937;
    color: #d1d5db;
}

/* Medidor de quórum */
.assembly-quorum-meter {
    height: 12px;
    border-radius: 999px;
    background: var(--synbex-bg);
    border: 1px solid var(--synbex-border);
    overflow: hidden;
    margin: 8px 0;
}

.assembly-quorum-fill {
    height: 100%;
    background: var(--synbex-primary);
    transition: width 0.4s ease;
}

.assembly-quorum-fill.is-reached {
    background: #16a34a;
}

.assembly-quorum-numbers {
    display: flex;
    justify-content: space-between;
    font-size: 13px;
    color: var(--synbex-text-muted);
}

.assembly-quorum-reached-tag {
    color: #16a34a;
    font-weight: 600;
}

/* Votación */
.assembly-voting-session {
    border: 1px solid var(--synbex-border);
    border-radius: 12px;
    padding: 14px;
    margin-top: 10px;
}

/* Abierta ahora mismo — borde y franja verde a la izquierda para que el
   administrador la identifique de un vistazo entre varias votaciones. */
.assembly-voting-session.is-open {
    border-color: #16a34a;
    border-left-width: 4px;
    background: color-mix(in srgb, #16a34a 6%, var(--synbex-surface));
}

/* Deshabilitada — atenuada, sin importar en qué Status haya quedado. */
.assembly-voting-session.is-disabled {
    opacity: 0.6;
}

.assembly-voting-session-disabled-tag {
    margin: 0 0 8px;
    color: var(--synbex-danger);
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
}

.assembly-voting-session-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 6px;
}

.assembly-voting-session-title {
    margin: 0;
    font-size: 15px;
    font-weight: 600;
}

.assembly-voting-session-desc {
    color: var(--synbex-text-muted);
    font-size: 13px;
    margin: 0 0 10px 0;
}

.assembly-option-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.assembly-option-btn {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    box-sizing: border-box;
    text-align: left;
    padding: 12px 18px;
    border: 1px solid var(--synbex-border);
    border-radius: 10px;
    background: var(--synbex-surface);
    cursor: pointer;
    font-size: 14px;
    color: var(--synbex-text);
}

.assembly-option-btn:hover:not(:disabled) {
    border-color: var(--synbex-primary);
}

.assembly-option-btn:disabled {
    cursor: default;
    opacity: 0.7;
}

.assembly-option-btn.is-mine {
    border-color: var(--synbex-primary);
    background: color-mix(in srgb, var(--synbex-primary) 10%, var(--synbex-surface));
}

.assembly-option-result-bar {
    flex: 1;
    min-width: 0;
    height: 8px;
    border-radius: 999px;
    background: var(--synbex-bg);
    overflow: hidden;
}

.assembly-option-result-fill {
    height: 100%;
    background: var(--synbex-primary);
}

.assembly-option-result-pct {
    flex-shrink: 0;
    font-size: 13px;
    font-weight: 600;
    min-width: 42px;
    text-align: right;
}

/* Tablero de asistencia / bitácora (administrador) */
.assembly-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
}

.assembly-table th,
.assembly-table td {
    padding: 8px 10px;
    border-bottom: 1px solid var(--synbex-border);
    text-align: left;
}

.assembly-table th {
    color: var(--synbex-text-muted);
    font-weight: 600;
    font-size: 12px;
    text-transform: uppercase;
}

.assembly-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 999px;
    background: #d1d5db;
}

.assembly-dot.is-connected {
    background: #16a34a;
}

.assembly-admin-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 10px;
}

/* Tablero de asistencia — con un conjunto grande, la tabla sola podía terminar
   más alta que la pantalla entera. Alto máximo con scroll propio adentro. */
.assembly-table-scroll {
    margin-top: 14px;
    max-height: 420px;
    overflow-y: auto;
    overflow-x: auto;
}

/* Controles de cada votación — separados en "áreas" a propósito: los de arriba
   (abrir/detener/iniciar/reiniciar/finalizar + ver resultados) son las
   acciones normales del día a día; Deshabilitar/Habilitar queda en su propia
   fila con una rayita arriba, para no confundirlo con los de iniciar/cerrar. */
.assembly-voting-controls {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 10px;
}

.assembly-voting-controls-group {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.assembly-voting-controls-danger-zone {
    padding-top: 10px;
    border-top: 1px dashed var(--synbex-border);
}

.assembly-btn {
    display: inline-flex;
    align-items: center;
    padding: 8px 14px;
    border-radius: 8px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    border: 1px solid var(--synbex-border);
    background: var(--synbex-surface);
    color: var(--synbex-text);
    text-decoration: none;
}

.assembly-btn:hover:not(:disabled) {
    filter: brightness(0.97);
}

.assembly-btn:disabled {
    opacity: 0.6;
    cursor: default;
}

.assembly-btn-primary {
    background: var(--synbex-primary);
    border-color: var(--synbex-primary);
    color: #fff;
}

.assembly-btn-danger {
    background: #dc2626;
    border-color: #dc2626;
    color: #fff;
}

/* "+ Nueva votación" — a propósito un color distinto (violeta vivo, no el
   índigo de .assembly-btn-primary) para que resalte más entre los botones de
   la fila de acciones del encabezado. */
.assembly-btn-accent {
    background: linear-gradient(135deg, #9333ea, #c026d3);
    border-color: #9333ea;
    color: #fff;
}

/* Ventana de confirmación propia de Synbex (reemplaza a confirm() del
   navegador) — chica, centrada, mismo backdrop que .settings-modal-backdrop. */
.assembly-confirm-dialog {
    width: 100%;
    max-width: 380px;
    background: var(--synbex-surface);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-lg);
    box-shadow: var(--synbex-shadow);
    padding: 26px 24px;
    text-align: center;
    animation: settings-modal-in 0.15s ease;
}

.assembly-confirm-message {
    margin: 0;
    font-size: 15px;
    line-height: 1.5;
    color: var(--synbex-text);
}

.assembly-confirm-actions {
    display: flex;
    gap: 10px;
    margin-top: 20px;
}

.assembly-confirm-actions .assembly-btn {
    flex: 1;
    justify-content: center;
}

@media (max-width: 640px) {
    .assembly-body {
        padding: 14px;
    }
}

/* Tarjeta "Convocatoria y quórum" — dona + info, lado a lado en desktop, apiladas en celular */
.assembly-quorum-layout {
    display: flex;
    align-items: center;
    gap: 24px;
    flex-wrap: wrap;
}

.assembly-quorum-info {
    flex: 1;
    min-width: 200px;
}

.assembly-donut {
    flex-shrink: 0;
    width: 132px;
    height: 132px;
    border-radius: 50%;
    background: conic-gradient(var(--synbex-primary) calc(var(--pct) * 1%), var(--synbex-border) 0);
    display: flex;
    align-items: center;
    justify-content: center;
}

.assembly-donut.is-reached {
    background: conic-gradient(#16a34a calc(var(--pct) * 1%), var(--synbex-border) 0);
}

.assembly-donut-hole {
    width: 96px;
    height: 96px;
    border-radius: 50%;
    background: var(--synbex-surface);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

.assembly-donut-value {
    font-size: 20px;
    font-weight: 700;
    color: var(--synbex-text);
}

.assembly-donut-label {
    font-size: 11px;
    color: var(--synbex-text-muted);
}

/* Versión grande del mismo donut, solo para la pantalla de quórum de abajo
   (.assembly-quorum-display) — pensada para proyectar/mostrarle a la sala.

   Acá el resto del aro NO es el gris de "vacío" que usa el donut chico: es el
   color de los ausentes (pedido explícito). O sea que el gráfico muestra las
   dos partes de verdad —quiénes están y quiénes faltan— en vez de un arco
   flotando sobre una pista neutra. */
.assembly-donut-lg {
    width: 300px;
    height: 300px;
    background: conic-gradient(var(--synbex-quorum-present) calc(var(--pct) * 1%), var(--synbex-quorum-absent) 0);
}

/* Con el quórum alcanzado la parte de presentes pasa a verde: es la señal de
   que ya se llegó, y se mueve junto con la cifra de arriba. Los ausentes se
   quedan siempre en su amarillo anaranjado. */
.assembly-donut-lg.is-reached {
    background: conic-gradient(var(--synbex-success) calc(var(--pct) * 1%), var(--synbex-quorum-absent) 0);
}

.assembly-donut-hole-lg {
    width: 224px;
    height: 224px;
}

.assembly-donut-hole-lg .assembly-donut-value {
    font-size: 46px;
}

.assembly-donut-hole-lg .assembly-donut-label {
    font-size: 15px;
}

/* Pantalla grande de quórum (botón "Quórum" en Room.razor) — a propósito
   ocupa el 100% de la pantalla (no una ventana flotante): pensada para
   proyectar/compartir pantalla durante la asamblea, el porcentaje tiene que
   verse desde lejos. */
.assembly-quorum-fullpage {
    position: fixed;
    inset: 0;
    z-index: 250;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--synbex-bg);
    overflow-y: auto;
    padding: 32px 20px;
    animation: settings-modal-in 0.15s ease;
}

.assembly-quorum-display {
    position: relative;
    width: 100%;
    max-width: 480px;
    text-align: center;
}

/* Botón "Volver" pegado a la esquina de la PANTALLA, no del bloque de
   contenido. El bloque va centrado verticalmente, así que posicionándolo
   contra él terminaba a la altura del título y, en celular, montado encima
   del nombre de la asamblea (medido: arrancaba dentro del texto).

   Va arriba a la izquierda, que es donde se espera un "volver", y se queda
   quieto aunque el contenido se desplace. */
.assembly-quorum-display-back {
    position: fixed;
    top: 14px;
    left: 14px;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 14px 8px 10px;
    border: 1px solid var(--synbex-border);
    border-radius: 999px;
    background: var(--synbex-surface);
    color: var(--synbex-text);
    font: inherit;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
}

.assembly-quorum-display-back:hover {
    border-color: var(--synbex-primary);
    color: var(--synbex-primary);
}

.assembly-quorum-display-back svg {
    width: 16px;
    height: 16px;
}

.assembly-quorum-display-title {
    margin: 0 0 4px;
    font-size: 22px;
    font-weight: 700;
    color: var(--synbex-text);
}

.assembly-quorum-display-status {
    margin: 0;
    font-size: 14px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

.assembly-quorum-display-status.is-reached {
    color: #16a34a;
}

.assembly-quorum-display-status.is-not-reached {
    color: var(--synbex-danger);
}

.assembly-quorum-display-stats {
    display: flex;
    justify-content: center;
    gap: 40px;
    margin: 22px 0 28px;
}

.assembly-quorum-display-stat {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.assembly-quorum-display-stat-value {
    font-size: 34px;
    font-weight: 800;
    line-height: 1.1;
    color: var(--synbex-text);
}

/* Cada cifra con el color de su parte del gráfico. El total se queda con el
   color de texto normal: no es una porción del gráfico, es la suma de las
   otras dos. */
.assembly-quorum-display-stat-value.is-present {
    color: var(--synbex-quorum-present);
}

.assembly-quorum-display-stat-value.is-present.is-reached {
    color: var(--synbex-success);
}

.assembly-quorum-display-stat-value.is-absent {
    color: var(--synbex-quorum-absent);
}

.assembly-quorum-display-stat-label {
    font-size: 12.5px;
    color: var(--synbex-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

.assembly-quorum-display > .assembly-donut-lg {
    margin: 0 auto;
}

@media (max-width: 480px) {
    /* En celular el gráfico sigue siendo el elemento grande de la pantalla,
       pero tiene que entrar a lo ancho junto con las tres cifras. */
    .assembly-donut-lg {
        width: 240px;
        height: 240px;
    }

    .assembly-donut-hole-lg {
        width: 180px;
        height: 180px;
    }

    .assembly-donut-hole-lg .assembly-donut-value {
        font-size: 38px;
    }

    .assembly-quorum-display-stats {
        gap: 26px;
    }

    .assembly-quorum-display-stat-value {
        font-size: 28px;
    }
}

/* Fila de una opción en el formulario "Nueva votación" — texto e imagen en
   filas separadas (no una junto a la otra) para que el input de archivo
   nativo, que no se achica bien, nunca empuje el formulario más ancho que el
   modal. */
.assembly-option-form-row {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-bottom: 12px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--synbex-border);
}

.assembly-option-form-row:last-of-type {
    border-bottom: none;
}

.assembly-option-form-top {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* Salvavidas contra desbordes horizontales — ninguna tarjeta debería poder
   empujar el ancho del panel, pase lo que pase con textos largos. Ojo: NO se
   le pone overflow-x:hidden a .assembly-option-btn — eso fue justo lo que
   recortaba el "100%" del resultado por el borde derecho antes; acá alcanza
   con que el texto pueda achicarse/partirse. */
.assembly-card,
.assembly-voting-session {
    overflow-x: hidden;
}

.assembly-option-btn span {
    min-width: 0;
    overflow-wrap: break-word;
}

/* Bloque de asistencia fusionado dentro de la tarjeta de Convocatoria y quórum
   (antes era una tarjeta aparte) — misma línea separadora que .assembly-live-stats. */
.assembly-attendance-block {
    border-top: 1px solid var(--synbex-border);
    margin-top: 14px;
    padding-top: 14px;
}

/* Resumen en vivo de una votación (administrador) */
.assembly-live-stats {
    border-top: 1px solid var(--synbex-border);
    margin-top: 10px;
    padding-top: 14px;
}

.assembly-live-stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
    gap: 10px;
}

.assembly-live-stat {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: 10px 8px;
    border: 1px solid var(--synbex-border);
    border-radius: 10px;
    background: var(--synbex-bg);
}

.assembly-live-stat-value {
    font-size: 20px;
    font-weight: 700;
    color: var(--synbex-text);
}

.assembly-live-stat-label {
    font-size: 12px;
    color: var(--synbex-text-muted);
    text-align: center;
}

/* Lista de "quiénes ya votaron" — solo para votaciones "Por fecha", ver Room.razor. */
.assembly-participants-details {
    margin-top: 12px;
}

.assembly-participants-details summary {
    cursor: pointer;
    font-size: 13px;
    font-weight: 600;
    color: var(--synbex-primary);
}

.assembly-participants-list {
    list-style: none;
    margin: 10px 0 0;
    padding: 0;
    max-height: 220px;
    overflow-y: auto;
    border: 1px solid var(--synbex-border);
    border-radius: 10px;
}

.assembly-participants-list li {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    font-size: 13px;
    border-bottom: 1px solid var(--synbex-border);
}

.assembly-participants-list li:last-child {
    border-bottom: none;
}

.assembly-participant-unit {
    font-weight: 700;
    flex-shrink: 0;
}

.assembly-participant-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--synbex-text-muted);
}

.assembly-participant-time {
    flex-shrink: 0;
    font-size: 11.5px;
    color: var(--synbex-text-muted);
}

/* Encabezado de la sala — todo apilado y centrado: insignia de estado arriba
   del todo, después el nombre, la descripción, y por último los botones de
   acción (ver .assembly-header-centered-action). */
.assembly-header-card {
    text-align: center;
}

.assembly-header-centered-action {
    margin-top: 14px;
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    gap: 10px;
}

.assembly-header-title {
    margin: 4px 0 6px;
    font-size: 26px;
    font-weight: 700;
    color: var(--synbex-text);
}

.assembly-header-desc {
    margin: 0 0 8px;
    color: var(--synbex-text-muted);
    font-size: 14px;
}

/* Apartamento/cupo de quien mira — a propósito discreta y fuera de la tarjeta
   del encabezado (pedido explícito: el encabezado solo debe mostrar estado +
   nombre + descripción). Chiquita, centrada, como una notita al margen. */
.assembly-unit-note {
    margin: -4px 0 14px;
    text-align: center;
    font-size: 12px;
    color: var(--synbex-text-muted);
}

@media (max-width: 480px) {
    .assembly-header-title {
        font-size: 21px;
    }
}

/* Wizard de votación del residente — una pregunta a la vez, opciones grandes,
   y las pantallas de confirmación/espera/agradecimiento que la reemplazan
   según el momento. */
.assembly-wizard-card {
    text-align: center;
    padding: 32px 20px;
}

.assembly-vote-big-title {
    margin: 0 0 8px;
    font-size: 22px;
    font-weight: 700;
    color: var(--synbex-text);
}

.assembly-vote-big-desc {
    margin: 0 0 20px;
    color: var(--synbex-text-muted);
    font-size: 14px;
}

.assembly-vote-big-options {
    display: flex;
    flex-direction: column;
    gap: 14px;
    margin-top: 20px;
}

.assembly-vote-big-option {
    display: flex;
    align-items: center;
    gap: 14px;
    width: 100%;
    box-sizing: border-box;
    text-align: left;
    padding: 20px 22px;
    border: 2px solid var(--synbex-border);
    border-radius: 14px;
    background: var(--synbex-surface);
    cursor: pointer;
    font-size: 18px;
    font-weight: 600;
    color: var(--synbex-text);
    transition: border-color 0.15s ease, transform 0.1s ease;
}

.assembly-vote-big-option:hover:not(:disabled) {
    border-color: var(--synbex-primary);
}

.assembly-vote-big-option:active:not(:disabled) {
    transform: scale(0.99);
}

.assembly-vote-big-option:disabled {
    cursor: default;
    opacity: 0.6;
}

/* Marca la opción actual en la pantalla de "Cambiar opinión" (ver
   _changingVoteSessionId en Room.razor) — mismo acento que .is-mine en el
   listado chico del administrador. */
.assembly-vote-big-option.is-selected {
    border-color: var(--synbex-primary);
    background: color-mix(in srgb, var(--synbex-primary) 8%, var(--synbex-surface));
}

.assembly-vote-big-option-tag {
    margin-left: auto;
    flex-shrink: 0;
    font-size: 12px;
    font-weight: 700;
    color: var(--synbex-primary);
}

/* "Voto realizado" — reemplaza a las opciones dentro de la MISMA tarjeta
   cuando ya votó (el título/descripción/cronómetro de arriba no se tocan).
   Solo aparece después de que el servidor confirmó el guardado (ver
   CastVoteAsync en Room.razor), nunca antes. */
.assembly-vote-done-option {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding: 24px 20px;
}

.assembly-vote-done-option-icon {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background: #16a34a;
    color: #fff;
    font-size: 28px;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
}

.assembly-vote-done-option-label {
    margin: 0;
    text-align: center;
    font-size: 18px;
    font-weight: 700;
    color: var(--synbex-text);
}

/* Esperando la próxima votación (o esperando la primera) */
.assembly-vote-waiting {
    padding: 40px 20px;
}

.assembly-vote-waiting h2 {
    margin: 0 0 8px;
    font-size: 20px;
    font-weight: 700;
    color: var(--synbex-text);
}

.assembly-vote-waiting p {
    margin: 0;
}

/* Gracias por votar — pantalla final cuando ya no queda nada pendiente */
.assembly-vote-done {
    padding: 48px 20px;
}

.assembly-vote-done-title {
    margin: 0 0 8px;
    font-size: 26px;
    font-weight: 700;
    color: var(--synbex-text);
}

.assembly-vote-done-sub {
    margin: 0;
    color: var(--synbex-text-muted);
    font-size: 15px;
}

/* Cronómetro de una votación "Por tiempo" — centrado y grande tanto para el
   administrador (dentro de la tarjeta de la votación) como para el residente
   (arriba de las opciones grandes). */
.assembly-countdown-badge {
    display: block;
    width: fit-content;
    margin: 4px auto 14px;
    padding: 10px 26px;
    border-radius: 999px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    font-size: 28px;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    text-align: center;
}

.assembly-countdown-badge-center {
    /* Ya queda centrado por defecto — esta clase solo se deja para no romper el
       markup existente que la sigue pasando. */
}

/* Pausado: mismo look, en ámbar en vez del color de acento — para que de un
   vistazo se note que el cronómetro no está corriendo. */
.assembly-countdown-badge.is-paused {
    background: #fef3c7;
    color: #b45309;
}

:root[data-theme="dark"] .assembly-countdown-badge.is-paused,
@media (prefers-color-scheme: dark) {
    .assembly-countdown-badge.is-paused {
        background: #451a03;
        color: #fcd34d;
    }
}

/* ======================================
   Modal "Nueva votación" — organizado por secciones, dos columnas en
   escritorio (Información general | Horario), Opciones abajo a todo el ancho.
   Usa la misma clase .assembly-create-modal-dialog que "Nueva asamblea" (ver
   más arriba) para que ambos modales midan siempre lo mismo de ancho.
   ====================================== */
.assembly-voting-modal-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 20px;
}

/* Recién a partir de acá entra la segunda columna — antes (celular, tablet
   angosta) las dos secciones van una debajo de la otra, en el orden natural
   del formulario, sin tener que tocar nada más. Sin align-items:start a
   propósito: el default (stretch) hace que ambas columnas midan lo mismo de
   alto, la que tenga más contenido (ej. "Horario" con fecha) manda — así no
   queda una más chica que la otra. */
@media (min-width: 760px) {
    .assembly-voting-modal-grid {
        grid-template-columns: 1fr 1fr;
    }

    /* Además de que ambas columnas midan lo mismo entre sí (align-items:
       stretch de arriba), reservamos de entrada el alto que necesita la
       modalidad más alta ("Por fecha", con 6 campos) — así cambiar de
       modalidad en el selector no hace que el panel salte/crezca de golpe:
       ya venía con ese tamaño reservado desde que se abrió el modal. */
    .assembly-voting-modal-grid .assembly-form-section {
        min-height: 432px;
    }
}

.assembly-form-section {
    padding: 14px 16px;
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-lg);
    background: var(--synbex-bg);
}

.assembly-form-section-title {
    margin: 0 0 12px;
    font-size: 13px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: var(--synbex-text-muted);
}

/* Variante con ícono — solo para los 3 títulos principales de "Nueva asamblea"
   (Información de la asamblea / Participación y votos / Quórum), a propósito
   sin mayúsculas ni gris apagado como la variante de arriba: acá el título
   entero (ícono + texto) va en el color primario, más parecido a un
   encabezado real que a una etiqueta de formulario. */
.assembly-form-section-title.has-icon {
    display: flex;
    align-items: center;
    gap: 8px;
    text-transform: none;
    letter-spacing: normal;
    font-size: 14.5px;
    color: var(--synbex-primary);
}

.assembly-form-section-title.has-icon svg {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}

.assembly-form-section-full {
    margin-top: 20px;
}

/* Aviso/botón de "copiar fechas de la última por fecha" — reforzado para que
   sea visible y explícito, en vez del autorrelleno silencioso que había antes. */
.assembly-copy-window-box {
    margin-bottom: 12px;
}

.assembly-copy-window-note {
    display: block;
    padding: 8px 12px;
    border-radius: 8px;
    background: color-mix(in srgb, var(--synbex-primary) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--synbex-primary) 25%, transparent);
    font-size: 12.5px;
    color: var(--synbex-text-muted);
    line-height: 1.4;
}

/* Primera/Segunda/Ãltima convocatoria — tres columnas iguales a propósito
   (a diferencia de .synbex-form-grid, que es de a dos) para que las tres
   quepan en una sola fila con el modal más ancho, en vez de que la tercera
   se caiga a una segunda línea. En celular sigue apilando, como cualquier
   otra grilla del formulario. */
.assembly-quorum-percent-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 12px;
}

@media (max-width: 560px) {
    .assembly-quorum-percent-grid {
        grid-template-columns: 1fr;
    }
}

/* Reemplaza las flechitas nativas del navegador en los campos de porcentaje
   (feas y minúsculas, distintas en cada navegador) por un par de botones
   propios, con el mismo lenguaje visual (color primario) que el resto de
   Synbex. El input nativo sigue aceptando flechas de teclado/scroll — solo se
   le esconde el spinner que dibuja el navegador. */
.assembly-number-stepper {
    position: relative;
    display: flex;
}

.assembly-number-stepper .synbex-input {
    padding-right: 34px;
    -moz-appearance: textfield;
}

.assembly-number-stepper .synbex-input::-webkit-outer-spin-button,
.assembly-number-stepper .synbex-input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.assembly-number-stepper-btns {
    position: absolute;
    right: 4px;
    top: 4px;
    bottom: 4px;
    width: 22px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.assembly-number-stepper-btn {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: none;
    border-radius: 4px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.assembly-number-stepper-btn:hover {
    background: var(--synbex-primary);
    color: #fff;
}

.assembly-number-stepper-btn svg {
    width: 10px;
    height: 10px;
}

/* Botón "i" de info junto a un label — muestra su explicación al pasar el
   mouse (CSS puro) o al hacer clic/tocar (clase .is-open que controla Blazor,
   para que también funcione en celular sin hover). */
.assembly-info-label {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 6px;
}

.assembly-info-wrap {
    position: relative;
    display: inline-flex;
}

.assembly-info-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.assembly-info-btn:hover {
    background: var(--synbex-primary);
    color: #fff;
}

.assembly-info-btn svg {
    width: 13px;
    height: 13px;
}

/* Se abre hacia ARRIBA del ícono (no hacia abajo) — este campo suele estar
   cerca del final del formulario, y abriendo hacia abajo la tarjeta quedaba
   fuera del área visible del modal (había que hacer scroll adentro del modal
   para verla, lo que se sentía como si el panel "creciera"/se reacomodara). */
.assembly-info-popover {
    display: none;
    position: absolute;
    left: 0;
    bottom: calc(100% + 8px);
    z-index: 5;
    width: 320px;
    max-width: 80vw;
    padding: 14px 16px;
    background: var(--synbex-surface);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-md);
    box-shadow: var(--synbex-shadow);
    font-size: 13px;
    line-height: 1.5;
    color: var(--synbex-text);
    text-align: left;
}

.assembly-info-wrap:hover .assembly-info-popover,
.assembly-info-popover.is-open {
    display: block;
}

.assembly-info-popover p {
    margin: 0 0 8px;
}

.assembly-info-popover p:last-child {
    margin-bottom: 0;
}

/* Fila de "Un solo voto por apartamento" — interruptor más grande y con más
   presencia que el checkbox nativo de antes, mismo lenguaje visual que
   .synbex-toggle (usado en Accesibilidad) pero más grande, ya que acá es la
   única opción de toda la sección y merece más protagonismo. */
.assembly-toggle-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    cursor: pointer;
}

.assembly-toggle-row-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.assembly-toggle-lg {
    width: 52px;
    height: 30px;
}

.assembly-toggle-lg .synbex-toggle-slider::before {
    height: 24px;
    width: 24px;
}

.assembly-toggle-lg input:checked + .synbex-toggle-slider::before {
    transform: translateX(22px);
}

/* "Tus respuestas" — lista de votaciones que el residente ya respondió y
   todavía puede cambiar, con un botón "Cambiar opinión" por fila. */
.assembly-my-answers {
    margin-top: 16px;
}

.assembly-my-answers-title {
    margin: 0 0 10px;
    font-size: 14px;
    font-weight: 700;
    color: var(--synbex-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

.assembly-my-answer-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 10px 0;
    border-top: 1px solid var(--synbex-border);
}

.assembly-my-answer-row:first-of-type {
    border-top: none;
}

.assembly-my-answer-title {
    font-size: 14px;
    color: var(--synbex-text);
}

/* Pantalla "Confirmá tu votación" de una "por fecha" con varias preguntas
   juntas (ver ShowBatchReview en Room.razor) — a diferencia de
   .assembly-my-answer-row (filas separadas por una simple línea), acá cada
   pregunta va en su propia tarjeta demarcada, para que quede claro dónde
   empieza y termina cada una al revisar varias respuestas de un vistazo. */
.assembly-batch-review-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-bottom: 4px;
}

.assembly-batch-review-item {
    border: 1px solid var(--synbex-border);
    border-radius: 12px;
    padding: 14px 16px;
    background: var(--synbex-surface);
}

.assembly-batch-review-item-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 8px;
}

.assembly-batch-review-item-index {
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: var(--synbex-text-muted);
}

.assembly-batch-review-item-title {
    margin: 0 0 8px;
    font-size: 15px;
    font-weight: 600;
    color: var(--synbex-text);
}

.assembly-batch-review-item-answer {
    margin: 0;
    font-size: 14px;
    font-weight: 700;
    color: var(--synbex-primary);
}

/* Botón "Confirmar votación" de la pantalla de revisión — a propósito grande y
   arriba de todo (antes del desglose de respuestas), pedido explícito para que
   sea lo primero que la persona ve al llegar, no algo que tenga que buscar
   después de leer todas las preguntas. */
.assembly-batch-confirm-btn {
    width: 100%;
    justify-content: center;
    padding: 18px 22px;
    font-size: 17px;
    margin-bottom: 18px;
    box-shadow: 0 6px 16px color-mix(in srgb, var(--synbex-primary) 35%, transparent);
}

/* Check verde + texto tras confirmar un lote "por fecha" — mismo lenguaje
   visual que .synbex-booking-modal-success (reserva de áreas comunes), ver
   .synbex-success-check/.synbex-success-text más arriba. */
.assembly-batch-success {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    padding: 40px 20px;
    text-align: center;
    animation: synbex-success-pop 0.3s ease;
}

/* ---- Pasos previos de la asamblea (Room.razor) ----
   Lo primero que ve quien entra: qué tiene que revisar antes de votar (el
   quórum, los documentos que subió el administrador). Pedido explícito: los
   completados en verde transparente con un check a la derecha, y los que
   faltan en blanco con un ícono de pendiente.

   El verde es el mismo que ya usan las pestañas de "Ingresados" en Visitantes
   — no se inventa un color nuevo para esto. */
.assembly-steps-card {
    text-align: left;
}

.assembly-steps-title {
    margin: 0 0 4px;
    font-size: 20px;
    font-weight: 700;
    color: var(--synbex-text);
    text-align: center;
}

.assembly-step {
    display: flex;
    align-items: center;
    gap: 14px;
    width: 100%;
    padding: 14px 16px;
    margin-bottom: 10px;
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-surface);
    color: var(--synbex-text);
    font: inherit;
    text-align: left;
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease, transform 0.1s ease;
    box-sizing: border-box;
}

/* Un paso con archivo no es un botón entero: adentro conviven "abrir" y
   "descargar", y un enlace no puede ir dentro de otro. Así que la fila pasa a
   ser el contenedor y cada acción tiene su propia zona clicable. */
.assembly-step-doc {
    cursor: default;
    padding: 0 16px 0 0;
}

.assembly-step-main {
    display: flex;
    align-items: center;
    gap: 14px;
    /* Base 0 y no "auto": con auto reclama el ancho de su contenido, así que un
       título o un nombre de archivo largo dejaba al check sin lugar y lo tiraba
       a la línea de abajo, torcido a la izquierda (reportado). Con base 0 se
       encoge lo que haga falta —el texto ya se corta con puntos suspensivos— y
       el check se queda siempre en su lugar. */
    flex: 1 1 0;
    min-width: 0;
    padding: 14px 0 14px 16px;
    border: none;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.assembly-step-download {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    /* Sin esto el relleno y el borde se suman al ancho calculado y el botón se
       pasaba del borde de la tarjeta al ocupar su propia línea en celular. */
    box-sizing: border-box;
    padding: 5px 10px;
    border: 1px solid var(--synbex-border);
    border-radius: 999px;
    background: var(--synbex-surface);
    color: var(--synbex-text-muted);
    font-size: 12px;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
}

/* El ícono venía al tamaño de HubIcons (18px) y hacía el botón más alto de lo
   necesario para lo chico que es el texto. */
.assembly-step-download svg {
    width: 15px;
    height: 15px;
}

.assembly-step-download:hover {
    border-color: var(--synbex-primary);
    color: var(--synbex-primary);
}

.assembly-step.is-done .assembly-step-download {
    border-color: rgba(21, 128, 61, 0.45);
}

.assembly-step:hover {
    border-color: var(--synbex-primary);
}

.assembly-step:active {
    transform: scale(0.995);
}

.assembly-step.is-done {
    background: rgba(21, 128, 61, 0.12);
    border-color: #15803d;
}

.assembly-step-icon,
.assembly-step-mark {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    color: var(--synbex-text-muted);
}

.assembly-step-icon {
    width: 38px;
    height: 38px;
    border-radius: 10px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.assembly-step.is-done .assembly-step-icon {
    background: rgba(21, 128, 61, 0.16);
    color: #15803d;
}

/* El texto se queda con todo el espacio libre: así el check de la derecha
   queda siempre pegado al borde, esté el título corto o largo. */
.assembly-step-text {
    flex: 1 1 auto;
    min-width: 0;
}

/* Un nombre de archivo largo no puede empujar al botón de descargar encima
   del texto (pasaba en celular): se corta con puntos suspensivos. */
.assembly-step-text strong,
.assembly-step-text .page-subtitle {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.assembly-step.is-done .assembly-step-mark {
    color: #15803d;
}

/* Es la acción principal de la pantalla — de acá se pasa a votar, que es a lo
   que la persona vino. Va más grande que el resto y con sombra del color de la
   marca para que se despegue de los pasos de arriba (pedido explícito). */
.assembly-steps-continue {
    width: 100%;
    justify-content: center;
    margin-top: 14px;
    padding: 16px 20px;
    font-size: 17px;
    font-weight: 700;
    letter-spacing: 0.01em;
    border-radius: var(--synbex-radius-md);
    box-shadow: 0 6px 18px color-mix(in srgb, var(--synbex-primary) 38%, transparent);
    transition: transform 0.12s ease, box-shadow 0.15s ease;
}

.assembly-steps-continue:hover {
    box-shadow: 0 8px 22px color-mix(in srgb, var(--synbex-primary) 48%, transparent);
}

.assembly-steps-continue:active {
    transform: translateY(1px);
    box-shadow: 0 4px 12px color-mix(in srgb, var(--synbex-primary) 34%, transparent);
}

.assembly-steps-reopen {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-top: 14px;
}

/* Elegir qué se agrega (votación o documento) en el modal del administrador. */
.assembly-create-kind {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
    margin-bottom: 18px;
}

.assembly-create-kind-btn {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-surface);
    color: var(--synbex-text);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.assembly-create-kind-btn.is-active {
    border-color: var(--synbex-primary);
    background: var(--synbex-primary-soft);
}

.assembly-create-kind-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 34px;
    height: 34px;
    border-radius: 9px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.assembly-create-kind-btn.is-active .assembly-create-kind-icon {
    background: var(--synbex-primary);
    color: #fff;
}

/* Lista de documentos en el panel del administrador. */
.assembly-agenda-admin-row {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    padding: 14px 0;
    border-bottom: 1px solid var(--synbex-border);
}

.assembly-agenda-admin-row:last-child {
    border-bottom: none;
}

.assembly-agenda-admin-info {
    display: flex;
    align-items: center;
    gap: 12px;
    flex: 1 1 220px;
    min-width: 0;
}

.assembly-agenda-admin-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 36px;
    height: 36px;
    border-radius: 10px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.assembly-agenda-admin-files {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.assembly-agenda-file {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 10px;
    border: 1px solid var(--synbex-border);
    border-radius: 999px;
    font-size: 13px;
    color: var(--synbex-text-muted);
    text-decoration: none;
    max-width: 260px;
}

.assembly-agenda-file span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.assembly-agenda-file:hover {
    border-color: var(--synbex-primary);
    color: var(--synbex-primary);
}

/* Un documento abierto por quien entra a la sala. */
.assembly-agenda-file-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 18px;
    text-align: left;
}

.assembly-agenda-file-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    padding: 12px 14px;
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-md);
}

.assembly-agenda-file-name {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.assembly-agenda-file-actions {
    display: inline-flex;
    gap: 8px;
    flex-shrink: 0;
}

@media (max-width: 600px) {
    .assembly-create-kind {
        grid-template-columns: 1fr;
    }

    .assembly-agenda-file-row {
        flex-direction: column;
        align-items: stretch;
    }
}

/* En celular no hay botón de descargar, a propósito.

   Safari de iPhone decide por su cuenta qué hacer con un archivo: si reconoce
   el tipo (PDF, imágenes) ignora la orden de descargar del servidor y lo abre
   en su visor igual. El atributo "download" de los enlaces —el método estándar
   de la web— directamente no está soportado en iOS. O sea que ahí un botón de
   descargar no puede cumplir lo que promete.

   Lo que sí funciona en el teléfono es lo que ya hacen Gmail, Drive y
   WhatsApp: mostrar el documento y dejar que el propio sistema lo guarde
   desde Compartir â "Guardar en Archivos". Por eso en celular queda un solo
   botón, el de abrir. En computadora el botón sigue estando y ahí sí baja el
   archivo de un clic. */
@media (max-width: 520px) {
    .assembly-step-download,
    .assembly-agenda-download {
        display: none;
    }
}

/* ---- Páginas de error (NotFound.razor / Error.razor) ----
   Mismo lenguaje visual que Login.razor (fondo con degradado, tarjeta blanca
   redondeada), pero contenido centrado en vez de un formulario. Van acá en
   vez de en un .razor.css propio de cada página porque las dos comparten
   exactamente esta misma caja — así no se duplica el CSS entre ambas. */
.error-page {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background:
        radial-gradient(circle at 15% 10%, var(--synbex-primary-soft), transparent 45%),
        radial-gradient(circle at 85% 90%, var(--synbex-primary-soft), transparent 45%),
        var(--synbex-bg);
    box-sizing: border-box;
}

.error-card {
    width: 100%;
    max-width: 440px;
    background: var(--synbex-surface);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-lg);
    box-shadow: var(--synbex-shadow);
    padding: 44px 36px;
    box-sizing: border-box;
    text-align: center;
}

.error-icon-box {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 72px;
    height: 72px;
    margin: 0 auto 20px;
    border-radius: 20px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.error-icon-box.is-danger {
    background: var(--synbex-danger-soft);
    color: var(--synbex-danger);
}

.error-icon-box svg {
    width: 34px;
    height: 34px;
}

.error-title {
    margin: 0 0 8px;
    font-size: 22px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--synbex-text);
}

.error-subtitle {
    margin: 0 0 28px;
    font-size: 14.5px;
    line-height: 1.5;
    color: var(--synbex-text-muted);
}

.error-actions {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    flex-wrap: wrap;
}

.error-request-id {
    margin: 20px 0 0;
    font-size: 11.5px;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    color: var(--synbex-text-muted);
}

/* "Mi edificio" del residente — fichas de solo lectura, una por ámbito: los datos
   de ESTA torre y los del conjunto entero.

   Por qué separadas y no una sola lista: salen de dos tablas distintas (Building y
   Facility) y en un trámite piden cosas distintas de cada una. Mezcladas, el
   residente tenía que adivinar si "Ãrea construida" era de su torre o de todo el
   conjunto. Con el título y la etiqueta arriba, cada dato dice de dónde es sin
   tener que explicarlo.

   Si de un lado no hay nada registrado, ese bloque no se dibuja (lo decide la
   página) y queda una ficha sola, no un panel vacío. */
.ficha-bloque {
    margin-bottom: 18px;
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-lg);
    background: var(--synbex-surface);
    box-shadow: var(--synbex-shadow);
    overflow: hidden;
}

.ficha-bloque-encabezado {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: var(--ui-tile-pad-y) var(--ui-tile-pad-x);
    border-bottom: 1px solid var(--synbex-border);
    background: var(--synbex-bg);
}

.ficha-bloque-titulo {
    margin: 0;
    font-size: var(--ui-tile-titulo);
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--synbex-text);
}

.ficha-bloque-etiqueta {
    flex-shrink: 0;
    padding: 4px var(--ui-etiqueta-pad-x);
    border-radius: 999px;
    font-size: var(--ui-etiqueta-fz);
    font-weight: 600;
    color: var(--synbex-primary);
    background: var(--synbex-primary-soft);
}

.ficha-bloque-etiqueta.es-conjunto {
    color: var(--synbex-success);
    background: rgba(22, 163, 74, 0.12);
}

/* Cuatro por fila en computador. La cuenta de columnas es fija a propósito y no
   con auto-fit: la rayita que separa un dato del siguiente se dibuja con
   border-left y hay que apagarla en el primero de CADA fila, y eso solo se puede
   saber con :nth-child si se sabe cuántas columnas hay. */
.ficha-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    padding: 6px 0;
}

.ficha-dato {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 14px var(--ui-tile-pad-x);
    border-left: 1px solid var(--synbex-border);
}

.ficha-dato:nth-child(4n + 1) {
    border-left: none;
}

.ficha-dato-label {
    font-size: var(--ui-tile-desc);
    color: var(--synbex-text-muted);
}

.ficha-dato-valor {
    font-size: var(--ui-hub-sub);
    font-weight: 700;
    color: var(--synbex-text);
    word-break: break-word;
}

/* La nota va de lado a lado, debajo de la cuadrícula: es texto libre y en una
   columna angosta se leería peor que el resto. */
.ficha-nota {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 14px var(--ui-tile-pad-x) 18px;
    border-top: 1px solid var(--synbex-border);
}

.ficha-nota-texto {
    margin: 0;
    white-space: pre-wrap;
}

@media (max-width: 1100px) {
    .ficha-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .ficha-dato:nth-child(4n + 1) {
        border-left: 1px solid var(--synbex-border);
    }

    .ficha-dato:nth-child(3n + 1) {
        border-left: none;
    }
}

@media (max-width: 760px) {
    .ficha-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .ficha-dato:nth-child(3n + 1) {
        border-left: 1px solid var(--synbex-border);
    }

    .ficha-dato:nth-child(2n + 1) {
        border-left: none;
    }
}

/* En celular, uno por renglón: la rayita de la izquierda ya no separa nada, y en
   su lugar va una línea arriba que marca dónde empieza cada dato. */
@media (max-width: 480px) {
    .ficha-grid {
        grid-template-columns: minmax(0, 1fr);
    }

    /* Los tres selectores con :nth-child pesan más que un ".ficha-dato" solo, así
       que apagar la rayita de la izquierda exige repetirlos: si no, el cuarto dato
       se quedaba con su borde izquierdo aunque ya no hubiera nada al lado. */
    .ficha-dato,
    .ficha-dato:nth-child(2n + 1),
    .ficha-dato:nth-child(3n + 1),
    .ficha-dato:nth-child(4n + 1) {
        border-left: none;
        border-top: 1px solid var(--synbex-border);
    }

    .ficha-dato:first-child {
        border-top: none;
    }
}

/* ==========================================================================
   Pantalla de carga centrada al cambiar de pestaña/página (pedido explícito:
   "que me salga una animación de carga en el puro centro de la pantalla,
   grande, moderna, antes de que cargue, para que la persona vea que está
   cargando").

   Convive con la barra fina de arriba (#synbex-loading-bar), que se queda
   para las acciones DENTRO de una misma página (guardar, marcar entregado,
   etc.). Este overlay sale SOLO cuando se navega de verdad a otra ruta —
   ver action-loading.js: es ahí donde se decide cuál de los dos mostrar.

   Vive pegado a <body> (fuera de #synbex-app-content, lo único que Blazor
   re-renderiza al navegar), así que el render de la página nueva nunca lo
   borra a mitad de camino. */
#synbex-page-loader {
    position: fixed;
    inset: 0;
    z-index: 3500;
    display: flex;
    align-items: center;
    justify-content: center;
    /* color-mix sobre el fondo del tema para que se vea bien igual en claro
       que en oscuro, sin tener que declarar dos colores a mano. */
    background: color-mix(in srgb, var(--synbex-bg) 72%, transparent);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.18s ease, visibility 0s linear 0.18s;
    pointer-events: none;
}

#synbex-page-loader.is-active {
    opacity: 1;
    visibility: visible;
    transition: opacity 0.18s ease, visibility 0s;
    /* Bloquea clics mientras carga: evita que se dispare dos veces la misma
       navegación si la persona vuelve a tocar por impaciencia. */
    pointer-events: auto;
}

.synbex-page-loader-box {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 18px;
}

/* Anillo doble: un aro de fondo tenue + un arco en degradado girando encima.
   El arco se hace con "mask" (dos capas: una cónica que dibuja el degradado y
   una radial que le vacía el centro) en vez de un borde de color — así el
   trazo queda parejo y con las puntas redondeadas, que es lo que le da el
   aire moderno frente al típico spinner de borde punteado. */
.synbex-page-loader-ring {
    width: 84px;
    height: 84px;
    border-radius: 50%;
    background:
        conic-gradient(from 0deg,
            transparent 0deg,
            color-mix(in srgb, var(--synbex-primary) 25%, transparent) 90deg,
            var(--synbex-primary) 300deg,
            var(--synbex-primary) 360deg);
    -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 7px), #000 calc(100% - 6px));
    mask: radial-gradient(farthest-side, transparent calc(100% - 7px), #000 calc(100% - 6px));
    animation: synbex-page-loader-spin 0.85s linear infinite;
}

/* Aro de fondo: el mismo tamaño y grosor, en gris tenue, para que el arco de
   arriba se lea como "avance" sobre una pista y no como algo flotando solo. */
.synbex-page-loader-ring-wrap {
    position: relative;
    width: 84px;
    height: 84px;
}

.synbex-page-loader-ring-wrap::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 50%;
    border: 6px solid var(--synbex-border);
    opacity: 0.55;
}

.synbex-page-loader-ring {
    position: absolute;
    inset: 0;
}

.synbex-page-loader-text {
    font-size: 14px;
    font-weight: 600;
    letter-spacing: 0.01em;
    color: var(--synbex-text-muted);
    animation: synbex-page-loader-pulse 1.4s ease-in-out infinite;
}

@keyframes synbex-page-loader-spin {
    to {
        transform: rotate(360deg);
    }
}

@keyframes synbex-page-loader-pulse {
    0%, 100% { opacity: 0.55; }
    50% { opacity: 1; }
}

/* Mismo criterio que la barra fina de arriba y que el resto de la app: quien
   pidió menos movimiento (ajuste del sistema o el widget de accesibilidad) ve
   el indicador quieto, no girando. */
@media (prefers-reduced-motion: reduce) {
    .synbex-page-loader-ring,
    .synbex-page-loader-text {
        animation: none;
    }
}

.a11y-reduce-motion .synbex-page-loader-ring,
.a11y-reduce-motion .synbex-page-loader-text {
    animation: none;
}

/* ==========================================================================
   Escáner de cédula a pantalla completa (Visitors/Index.razor).

   Antes era un recuadro chico dentro de una ventana flotante; ahora la cámara
   ocupa toda la pantalla (pedido explícito), como cualquier escáner de
   documentos de celular. El marco amarillo que sigue al documento NO se
   dibuja acá: lo pinta idScanner.js sobre el <canvas>, cuadro por cuadro,
   según dónde detecte los bordes. */
.id-scanner-fullscreen {
    position: fixed;
    inset: 0;
    /* Por encima de todo lo demás de la app, incluida la barra inferior de
       celular y las ventanas flotantes. */
    z-index: 4000;
    background: #000;
    overflow: hidden;
}

.id-scanner-video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
}

/* object-fit:cover llena la pantalla completa sin deformar la imagen (recorta
   por el lado que sobra). idScanner.js hace exactamente esta misma cuenta al
   ubicar el marco, para que caiga justo encima del documento. */
.id-scanner-video {
    object-fit: cover;
    background: #000;
}

/* El lienzo del marco va encima del video y no intercepta toques: el único
   control es el botón de cerrar. */
.id-scanner-close {
    position: absolute;
    top: max(16px, env(safe-area-inset-top));
    left: 16px;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    border: none;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    /* Encima del lienzo del marco, si no el canvas se lo comería. */
    z-index: 2;
}

.id-scanner-close:hover {
    background: rgba(0, 0, 0, 0.75);
}

/* Textos abajo, sobre un degradado oscuro para que se lean sin importar qué
   esté enfocando la cámara. */
.id-scanner-caption {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 40px 20px max(28px, env(safe-area-inset-bottom));
    text-align: center;
    background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent);
    color: #fff;
    z-index: 2;
    pointer-events: none;
}

.id-scanner-title {
    font-size: 17px;
    font-weight: 700;
    margin-bottom: 6px;
}

/* Este texto lo cambia idScanner.js: pasa de "Acerca el reverso de la cédula"
   a "Documento detectado..." cuando el marco se fija. */
.id-scanner-hint {
    font-size: 14px;
    font-weight: 600;
    color: #facc15;
}

.id-scanner-sub {
    font-size: 12px;
    margin-top: 6px;
    color: rgba(255, 255, 255, 0.75);
}

/* Botón de linterna del escáner. Arranca oculto y solo se muestra si la
   cámara del equipo de verdad la tiene (lo decide idScanner.js al abrir). */
.id-scanner-torch {
    position: absolute;
    /* Arriba a la derecha (pedido explicito). Centrado al medio quedaba encima
       de la guia y del circulo de progreso, que le robaban el toque: los dos
       viven en el centro con z-index propio, asi que el boton no llegaba a
       recibir el clic. Arriba no se pisa con nada. */
    top: max(16px, env(safe-area-inset-top));
    right: 16px;
    min-width: 68px;
    padding: 14px 10px;
    border-radius: 18px;
    border: none;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    cursor: pointer;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    z-index: 5;
}

.id-scanner-torch.is-on {
    background: #facc15;
    color: #111;
}

.id-scanner-torch-icon {
    display: flex;
    align-items: center;
    justify-content: center;
}

/* El SVG viene de HubIcons como HTML crudo (MarkupString), asi que el
   aislamiento de CSS de Blazor no lo alcanza — por eso esta regla vive aca en
   app.css, que es global, y no en un .razor.css de pagina. */
.id-scanner-torch-icon svg {
    width: 30px;
    height: 30px;
}

.id-scanner-torch-label {
    font-size: 11px;
    font-weight: 600;
    margin-top: 4px;
}

/* Línea de diagnóstico: discreta a propósito, no es para el guarda sino para
   poder entender por qué no lee cuando falla en un celular que no tenemos. */
.id-scanner-diag {
    margin-top: 8px;
    font-size: 10px;
    color: rgba(255, 255, 255, 0.45);
    letter-spacing: 0.02em;
}

/* Botón de lectura de pasaportes. Va debajo del de flash, en el mismo borde
   derecho. Apagado se ve discreto; encendido, en el mismo amarillo que la
   guía, para que quede claro que está activo. */
.id-scanner-mrz {
    position: absolute;
    top: calc(50% + 78px);
    right: 14px;
    min-width: 68px;
    padding: 12px 8px;
    border-radius: 18px;
    border: none;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    z-index: 2;
}

.id-scanner-mrz.is-on {
    background: #facc15;
    color: #111;
}

.id-scanner-mrz:disabled {
    opacity: 0.6;
    cursor: default;
}

.id-scanner-mrz-icon {
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Mismo motivo que el ícono del flash: el SVG viene de HubIcons como HTML
   crudo, así que la regla tiene que vivir acá en el CSS global. */
.id-scanner-mrz-icon svg {
    width: 26px;
    height: 26px;
}

.id-scanner-mrz-label {
    font-size: 10px;
    font-weight: 600;
    margin-top: 3px;
    text-align: center;
    line-height: 1.2;
}

/* Guía fija del escáner: solo un marco con esquinas, con la proporción de una
   cédula. Reemplaza al recuadro amarillo que seguía al documento (se quitó por
   pedido explícito). No detecta nada — orienta dónde poner el documento; la
   lectura trabaja sobre el cuadro completo igual. */
.id-scanner-guide {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    /* Más grande (pedido explícito, 2026-09-17). Solo visual: la lectura usa casi
       toda la imagen igual. */
    width: 94%;
    max-width: 760px;
    aspect-ratio: 1.586;
    pointer-events: none;
    border-radius: 14px;
    box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.35);
    z-index: 1;
}

.id-scanner-corner {
    position: absolute;
    width: 34px;
    height: 34px;
    border: 4px solid rgba(255, 255, 255, 0.9);
}

.id-scanner-corner.tl { top: -2px; left: -2px; border-right: none; border-bottom: none; border-radius: 14px 0 0 0; }
.id-scanner-corner.tr { top: -2px; right: -2px; border-left: none; border-bottom: none; border-radius: 0 14px 0 0; }
.id-scanner-corner.bl { bottom: -2px; left: -2px; border-right: none; border-top: none; border-radius: 0 0 0 14px; }
.id-scanner-corner.br { bottom: -2px; right: -2px; border-left: none; border-top: none; border-radius: 0 0 14px 0; }

/* Indicador circular grande y semitransparente, en el centro exacto de la
   pantalla (pedido explícito). Muestra el avance de preparación del lector de
   texto y, ya listo, cuántas lecturas lleva hechas. */
.id-scanner-ring-wrap {
    position: absolute;
    /* Le da el alto/ancho del anillo para que el texto de adentro se
       pueda centrar contra el mismo recuadro. */
    width: 148px;
    height: 148px;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    pointer-events: none;
    z-index: 2;
}

/* El anillo se dibuja con un degradado cónico sobre --ring-percent y se le
   vacía el centro con una máscara — el mismo truco del anillo de Paquetería,
   pero semitransparente para no tapar el documento. */
.id-scanner-ring {
    width: 148px;
    height: 148px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: conic-gradient(rgba(255, 255, 255, 0.92) calc(var(--ring-percent) * 1%),
                               rgba(255, 255, 255, 0.18) 0);
    -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 8px), #000 calc(100% - 7px));
    mask: radial-gradient(farthest-side, transparent calc(100% - 8px), #000 calc(100% - 7px));
}

/* Ya cargado, el anillo gira despacio para que se note que sigue trabajando. */
.id-scanner-ring.is-scanning {
    animation: id-scanner-ring-spin 2.4s linear infinite;
}

@keyframes id-scanner-ring-spin {
    to { transform: rotate(360deg); }
}

/* El texto del centro NO debe girar con el anillo, así que vive en su propia
   capa por encima, sin la animación. */
.id-scanner-ring-inner {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    color: #fff;
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}

.id-scanner-ring-value {
    font-size: 30px;
    font-weight: 700;
    line-height: 1;
}

.id-scanner-ring-label {
    font-size: 11px;
    font-weight: 600;
    opacity: 0.8;
}

@media (prefers-reduced-motion: reduce) {
    .id-scanner-ring.is-scanning { animation: none; }
}

/* Campo de fecha en celular: el control nativo de fecha trae su propia letra
   (más grande que la del resto de campos) y un ancho mínimo propio del
   navegador — por eso el texto se salía del panel y no se veía del mismo
   tamaño que Documento y Nombre. Se le fuerza la misma tipografía y tamaño
   que a los demás campos, y se le permite encogerse. */
@media (max-width: 600px) {
    .synbex-input.synbex-date-input {
        /* El mismo tamano que Documento y Nombre (pedido explicito: se veia
           distinto). Antes se bajaba a 14px para que entrara; ya no hace
           falta, porque box-sizing + min-width:0 son los que evitan que se
           desborde. */
        font-size: 16px;
        font-family: inherit;
        width: 100%;
        min-width: 0;
        max-width: 100%;
        box-sizing: border-box;
        /* 2px (el mínimo que ya se confirmó que no se sale) se veía pegado al
           borde del panel, sin nada del aire que sí tiene el lado izquierdo —
           pedido explícito, que se vea parejo. Este valor deja algo de
           respiro sin volver a acercarse al punto donde se salía. */
        padding-right: 6px;
        /* Defensa extra: en celular de verdad (reportado en Consultas, no se
           reproduce igual en el navegador de escritorio con el que se prueba
           acá) el widget interno del navegador puede seguir pintando más
           ancho que la caja aunque min-width:0 ya lo deje encoger — pasa más
           con el formato de 12 horas (con AM/PM, más ancho que 24 horas) según
           el idioma del teléfono. overflow:hidden recorta visualmente
           cualquier resto que se pase, en vez de dejarlo salirse del panel
           por la derecha. */
        overflow: hidden;
    }

    /* El iconito del calendario que dibuja el navegador también ocupa: se le
       recorta el margen Y el tamaño (por defecto ronda 20px) para que no
       empuje el texto fuera de la caja — antes solo se le quitaba el margen,
       que no alcanzaba en pantallas angostas de verdad. Un pelín de margen a
       la izquierda del ícono lo separa del texto sin agregarle nada a la
       derecha (que es lo que empujaría el borde de nuevo). */
    .synbex-input.synbex-date-input::-webkit-calendar-picker-indicator {
        width: 16px;
        height: 16px;
        margin: 0 0 0 2px;
        padding: 0;
    }
}

/* Aviso de documento vencido en el registro de visitantes. Mismo lenguaje que
   la alerta de menor de edad que ya existía al lado, pero en rojo: es un dato
   que el guarda tiene que ver sí o sí antes de dejar entrar a alguien. */
.visitor-expired-alert {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 8px;
    padding: 10px 12px;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-danger-soft);
    color: var(--synbex-danger);
    font-size: 13px;
    font-weight: 700;
}

.visitor-expired-alert svg {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}

/* Vencimiento vencido en la tabla de Consultas: se marca en rojo para que
   salte a la vista al revisar el historial. */
.consultas-expired {
    color: var(--synbex-danger);
    font-weight: 700;
}

/* Botón de crear al pie de cada bloque de "Mis reservas". Va siempre visible,
   haya o no reservas (pedido explícito): es la acción que trae a esa pantalla.
   A lo ancho en celular, y del tamaño de su texto en pantallas grandes. */
.reservations-new-btn {
    display: inline-flex;
    justify-content: center;
    margin-top: 16px;
    text-decoration: none;
}

@media (max-width: 600px) {
    .reservations-new-btn {
        display: flex;
        width: 100%;
    }
}

/* ==========================================================================
   Pase con QR de una cita
   ========================================================================== */

.appointment-qr-dialog {
    max-width: 380px;
}

.appointment-qr-body {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 14px;
}

/* Fondo blanco fijo y no var(--synbex-surface): en tema oscuro un QR sobre
   fondo negro no lo lee ninguna cámara. */
.appointment-qr-image {
    width: 220px;
    height: 220px;
    max-width: 100%;
    padding: 10px;
    border-radius: var(--synbex-radius-md);
    background: #ffffff;
}

.appointment-qr-data {
    display: flex;
    flex-direction: column;
    gap: 2px;
    font-size: 14px;
}

.appointment-qr-note {
    margin: 0;
    font-size: 13px;
    color: var(--synbex-text-muted);
}

.appointment-qr-share {
    width: 100%;
    justify-content: center;
}

.appointment-qr-btn {
    white-space: nowrap;
}

.appointment-qr-btn svg {
    width: 16px;
    height: 16px;
}

/* Formulario de alta que se despliega DENTRO de la tarjeta de la lista (el
   botón verde de la cabecera lo abre). Sin línea separadora abajo: mientras
   está abierto la lista se esconde, así que no hay nada de qué separarlo — la
   raya quedaba flotando sola al final de la tarjeta. */
.catalog-inline-form {
    padding-top: 4px;
}

/* Se usa para alternar lista/formulario en las pantallas con "+ Agregar": cuando
   el alta está abierta, la lista se oculta y viceversa — nunca las dos a la vez.
   Con !important porque cualquier regla de clase le ganaría al display:none que
   el navegador aplica por defecto a [hidden], y el bloque quedaría visible. */
[hidden] {
    display: none !important;
}

/* ==========================================================================
   Varias limpiezas el mismo día
   ========================================================================== */

/* Encabezado que corta entre una limpieza y la siguiente dentro del panel del
   día. Solo aparece cuando hubo más de una: con una sola sería ruido. La línea
   de arriba es lo que marca dónde termina la anterior. */
.cleaning-day-split {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 26px 0 14px;
    padding-top: 18px;
    border-top: 2px solid var(--synbex-border);
    font-size: 14px;
    font-weight: 700;
}

/* La primera no lleva línea arriba: no hay nada antes de qué separarla. */
.cleaning-day-split:first-of-type {
    margin-top: 4px;
    padding-top: 0;
    border-top: 0;
}

.cleaning-day-split-number {
    flex-shrink: 0;
    width: 26px;
    height: 26px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--synbex-primary);
    color: #fff;
    font-size: 13px;
}

.cleaning-day-split-label {
    flex: 1 1 auto;
    min-width: 0;
}

/* La hora es el dato que de verdad distingue una de otra. */
.cleaning-day-split-time {
    flex-shrink: 0;
    font-weight: 600;
    font-size: 13px;
    color: var(--synbex-text-muted);
}

/* ==========================================================================
   Panel del día de limpieza: horizontal en computadora, apilado en celular
   ========================================================================== */

/* En celular es una columna (apilado), que es lo que entra en una pantalla
   angosta. La versión en columnas vive en la media query de abajo. */
.cleaning-day-detail {
    display: grid;
    gap: 4px;
}

/* Cada bloque (Datos / Checklist / Fotos) es una celda de la grilla. Se anula
   el margen de arriba del primer título: como cada bloque empieza con uno, si
   no en columnas quedarían desalineados entre sí. */
.cleaning-day-block > .cleaning-day-detail-section:first-child {
    margin-top: 0;
}

/* 820px y no 900: por debajo de eso tres columnas dejan el checklist y las
   fotos demasiado angostos y se lee peor que apilado. */
@media (min-width: 820px) {
    .cleaning-day-modal {
        max-width: 1040px;
    }

    /* En computadora (pedido explícito con un diseño de ejemplo, 2026-09-17): los
       datos en una FILA arriba, de lado a lado, y debajo Checklist y Fotos como dos
       tarjetas una al lado de la otra. */
    .cleaning-day-detail {
        grid-template-columns: 1fr 1fr;
        grid-template-areas:
            "datos datos"
            "checklist fotos";
        gap: 18px;
        /* stretch (y no "start"): las tarjetas de Checklist y Fotos quedan del MISMO
           alto, la que manda es la más alta (pedido explícito). */
        align-items: stretch;
    }

    .cleaning-day-datos { grid-area: datos; }
    .cleaning-day-checklist { grid-area: checklist; }
    .cleaning-day-fotos { grid-area: fotos; }

    /* El título "Datos" no va: los tres datos ya se explican solos con su ícono y su
       rótulo, y el ejemplo que pidió el usuario no lo lleva. */
    .cleaning-day-datos > .cleaning-day-detail-section {
        display: none;
    }

    /* Los tres datos en fila, separados por una línea vertical. */
    .cleaning-day-datos .cleaning-day-detail-meta {
        flex-direction: row;
        gap: 0;
        padding: 4px 0 18px;
        border-bottom: 1px solid var(--synbex-border);
    }

    .cleaning-day-datos .cleaning-day-detail-meta-row {
        flex: 1 1 0;
        min-width: 0;
        padding: 0 22px;
    }

    .cleaning-day-datos .cleaning-day-detail-meta-row + .cleaning-day-detail-meta-row {
        border-left: 1px solid var(--synbex-border);
    }

    .cleaning-day-datos .cleaning-day-detail-meta-row:first-child {
        padding-left: 0;
    }

    /* La línea de arriba de cada título separaba secciones cuando iban una
       debajo de otra. En columnas ya no separa nada: son paralelas, no
       consecutivas — y una raya sobre cada columna se lee como un borde suelto. */
    .cleaning-day-block > .cleaning-day-detail-section {
        padding-top: 0;
        border-top: 0;
        margin-top: 0;
    }

}

/* ==========================================================================
   Botón de acción principal de una pantalla
   ========================================================================== */

/* Nació como el "Generar reporte" de Mi limpieza y se generalizó acá para
   reusarlo en las demás pantallas (pedido explícito: que sean todos iguales).
   Está partido en dos clases a propósito:

     .synbex-cta-btn          â SOLO la apariencia (tamaño, tipografía, sombra,
                                separación del ícono). Sirve para botones que
                                ya tienen su lugar y no hay que moverlos.
     .synbex-cta-btn-centered â además lo coloca: bloque centrado y a un ancho
                                cómodo, como el de Mi limpieza.

   Así una pantalla puede tomar el estilo sin heredar la posición. */
.synbex-cta-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 12px 20px;
    font-size: 15px;
    font-weight: 700;
    box-sizing: border-box;
    box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--synbex-glow-color, var(--synbex-primary)) 55%, transparent);
}

.synbex-cta-btn:hover {
    box-shadow: 0 8px 20px -6px color-mix(in srgb, var(--synbex-glow-color, var(--synbex-primary)) 65%, transparent);
}

/* Tamaño fijo del ícono. Los de HubIcons vienen a 26px porque están pensados
   como ícono de sección; a ese tamaño el botón crecía 6px de alto y dejaba de
   ser igual al de Mi limpieza (que usa uno de 18). Acá manda el botón. */
.synbex-cta-btn svg {
    flex-shrink: 0;
    width: 18px;
    height: 18px;
}

.synbex-cta-btn-centered {
    display: flex;
    width: 100%;
    max-width: 340px;
    margin-left: auto;
    margin-right: auto;
}

/* ==========================================================================
   Resplandor de color en más botones
   ========================================================================== */

/* El mismo efecto del botón principal, extendido a los botones que se pidieron.
   La clave es --synbex-glow-color: el resplandor toma el color DEL BOTÃN, no
   siempre el morado — si no, un botón rojo con halo morado se ve mal. Cada
   familia de abajo fija el suyo; sin fijarlo cae al primario.

   Va al final del archivo a propósito: con la misma especificidad gana la
   última regla, y más arriba las definiciones originales lo pisaban. */
.catalog-add-toggle-btn,
.reservations-new-btn,
.assembly-logout-btn {
    box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--synbex-glow-color, var(--synbex-primary)) 55%, transparent);
}

.catalog-add-toggle-btn:hover,
.reservations-new-btn:hover,
.assembly-logout-btn:hover:not(:disabled) {
    box-shadow: 0 8px 20px -6px color-mix(in srgb, var(--synbex-glow-color, var(--synbex-primary)) 65%, transparent);
}

/* El botón verde "+ Agregar" lleva halo verde (su color y el tono están
   definidos arriba, en .catalog-add-toggle-btn). */
.catalog-add-toggle-btn {
    --synbex-glow-color: #16a34a;
}

/* Los dos de "Mis reservas" (área común y registrar visita) son azules: se
   quedan con el resplandor primario, solo hay que dárselo. */

/* "Salir de la asamblea": rojo, así que el halo va en rojo. */
.assembly-logout-btn {
    --synbex-glow-color: #dc2626;
    transition: background 0.15s ease, box-shadow 0.15s ease;
}

/* Los de "Mis reservas" tampoco tenían transición propia. */
.reservations-new-btn {
    transition: box-shadow 0.15s ease;
}

/* ==========================================================================
   Tarjeta de contacto en celular: más aire entre líneas
   ========================================================================== */

/* Acotado a .contact-card a propósito. El molde .mobile-card-row lo comparten
   otras cuatro pantallas (Documentos, Mis paquetes, Paquetería, Mis reservas) y
   el pedido fue solo para Contactos — tocar la clase compartida las habría
   cambiado todas de una. */
@media (max-width: 600px) {
    /* Antes cada fila solo tenía 12px arriba y nada abajo, así que teléfono,
       correo y notas quedaban pegados unos a otros. */
    .contact-card .mobile-card-row {
        padding-top: 18px;
        padding-bottom: 8px;
    }

    /* Y el dato pegado a su etiqueta ("TELÃFONO" sobre el número). */
    .contact-card .mobile-card-row-body {
        gap: 5px;
    }

    /* Nombre y rol también respiran más. */
    .contact-card .mobile-card-headertext {
        gap: 8px;
    }

    .contact-card .mobile-card-header {
        padding-bottom: 6px;
    }

    /* Los botones de abajo, un poco más separados de la última fila. */
    .contact-card .mobile-card-buttons {
        margin-top: 6px;
    }
}

/* ==========================================================================
   Documentos: botones Ver / Descargar en celular
   ========================================================================== */

@media (max-width: 600px) {
    /* Mismo resplandor que el resto de botones de la app, cada uno en su color
       (ver --synbex-glow-color más arriba). */
    .mobile-card-btn-indigo,
    .mobile-card-btn-green {
        box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--synbex-glow-color, var(--synbex-primary)) 55%, transparent);
        transition: box-shadow 0.15s ease, background 0.15s ease;
    }

    .mobile-card-btn-indigo:hover,
    .mobile-card-btn-green:hover {
        box-shadow: 0 8px 20px -6px color-mix(in srgb, var(--synbex-glow-color, var(--synbex-primary)) 65%, transparent);
    }

    /* "Descargar" pasa al mismo verde de los botones de agregar (#16a34a).
       Antes era #25d366, el verde de WhatsApp — correcto en la tarjeta de un
       contacto, fuera de lugar en un documento. */
    .mobile-card-btn-green {
        --synbex-glow-color: #16a34a;
        background: #16a34a;
    }

    /* El ícono va a la izquierda del texto y a un tamaño que no agrande el
       botón: los de HubIcons vienen a 26px, pensados como ícono de sección. */
    .mobile-card-btn svg {
        flex-shrink: 0;
        width: 18px;
        height: 18px;
    }
}

/* ==========================================================================
   Documentos: línea de tamaño y fecha de subida
   ========================================================================== */

.documents-meta-line {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 8px;
    font-size: 12.5px;
    color: var(--synbex-text-muted);
}

.documents-meta-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

/* Los íconos de HubIcons vienen a 26px (pensados como ícono de sección); acá
   van chicos, al tamaño del texto que acompañan. */
.documents-meta-item svg {
    flex-shrink: 0;
    width: 15px;
    height: 15px;
}

/* La línea vertical que separa tamaño de fecha (y, más abajo, la que separa la
   fecha de "Visibilidad" — mismo elemento, mismo aspecto). */
.documents-meta-divider {
    width: 1px;
    height: 14px;
    background: var(--synbex-border);
}

/* Visibilidad: pedido explícito (2026-09-27, corregido el mismo día) de que
   viva EN LA MISMA fila de tamaño/fecha (.documents-meta-line), no aparte —
   así hereda el mismo font-size y color sin tener que repetirlos. Tamaño y
   fecha se agrupan en .documents-meta-fixed-group, que nunca envuelve (la
   razón original del nowrap de la línea completa: que no se separen entre
   sí); es la línea COMO TODO la que ahora sí puede envolver — ver más abajo,
   junto al bloque de "Tamaño y fecha SIEMPRE en la misma fila" — para que 1 a
   4 píldoras de ancho variable bajen a un segundo renglón en celular en vez
   de desbordar la tarjeta. */
.documents-meta-fixed-group {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    flex-wrap: nowrap;
    flex-shrink: 0;
}

/* Una píldora por destinatario (Dueños/Residentes/Recepción/Limpieza, o una
   sola "Todos") — mismo molde redondeado que .badge-neutral (gris, sin
   llamar la atención, pedido explícito: "no quiero algo muy colorido"), pero
   con la letra al tamaño de .documents-meta-item en vez del tamaño normal de
   insignia, para que se lea como parte del mismo renglón de datos chicos. */
.documents-audience-badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 9px;
    border-radius: 999px;
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1.4;
    color: var(--synbex-text-muted);
    background: rgba(107, 114, 128, 0.16);
}

/* La etiqueta de categoría se ajustaba al ancho de la tarjeta y dejaba un
   espacio vacío enorme después del texto ("Informe mensual              ").
   Con align-self la caja mide lo que mide su contenido. */
.mobile-card-headertext .badge {
    align-self: flex-start;
    width: auto;
}

/* ==========================================================================
   Documentos en celular: filtros a la par y datos en la columna derecha
   ========================================================================== */

@media (max-width: 600px) {
    /* Categorías y meses comparten fila, mitad y mitad. Antes cada uno ocupaba
       su propio renglón y la lista quedaba empujada muy abajo. */
    .documents-category-wrapper,
    .documents-period-wrapper {
        flex: 1 1 0;
        min-width: 0;
    }

    .documents-category-select,
    .documents-period-select {
        width: 100%;
        max-width: none;
    }

    /* La línea de tamaño y fecha vive ahora dentro de la columna del título, así
       que arranca donde arranca el título y no debajo del recuadro del archivo.
       Se deja envolver por si el nombre del mes y el tamaño no entran juntos. */
    .mobile-card-headertext .documents-meta-line {
        margin-top: 6px;
    }
}

/* ==========================================================================
   Documentos en computadora: las mismas tarjetas del celular, de tres en tres
   ========================================================================== */

/* Pedido explícito: que escritorio se vea igual que celular, con el mismo
   orden, pero aprovechando el ancho — tres tarjetas por fila en vez de una
   tabla. Los estilos de .mobile-card* ya no viven dentro de la media query de
   celular (se sacaron para poder reusarlos acá); lo único que sigue siendo por
   ancho es CUÃL de las dos vistas se muestra.

   Va acotado a .documents-cards: las otras pantallas con tarjetas de celular
   (Contactos, Mis paquetes, Paquetería, Mis reservas) siguen con su tabla en
   computadora, sin cambios. */
@media (min-width: 601px) {
    .documents-table-wrapper {
        display: none;
    }

    .documents-cards {
        display: grid;
        grid-template-columns: 1fr;
        gap: 14px;
        align-items: start;
    }

    /* Con tres columnas el ancho de cada tarjeta es un tercio: el título largo
       tiene que poder cortarse en varias líneas en vez de desbordar. */
    .documents-cards .mobile-card-title {
        white-space: normal;
    }
}


/* ==========================================================================
   Documentos: ajustes finos de la tarjeta
   ========================================================================== */

/* El recuadro del archivo (PDF) y la estrella van pegados ARRIBA, no centrados
   verticalmente: con el título en dos líneas quedaban flotando a media altura.
   Acotado a Documentos para no cambiar la alineación de Contactos y las demás
   pantallas que comparten este mismo molde de tarjeta. */
.documents-cards .mobile-card-header {
    align-items: flex-start;
}

/* Tamaño y fecha SIEMPRE en la misma fila. Antes se envolvían y la fecha caía
   sola a la línea de abajo. Esta regla es de Documentos, pero la clase
   .documents-meta-line también la usa Contactos (ver .contacts-meta-line) —
   por eso queda SIN acotar a .documents-cards, para no cambiar ese otro uso. */
.documents-meta-line {
    flex-wrap: nowrap;
}

/* Ahora que "Visibilidad" vive en esta misma línea, con 1 a 4 píldoras de
   ancho variable, nowrap ya no alcanza — en celular se saldría de la
   tarjeta. Acá SÍ se vuelve a permitir el wrap, pero acotado a
   .documents-cards (no toca Contactos): tamaño y fecha no se separan porque
   van agrupados aparte en .documents-meta-fixed-group (nowrap propio); lo
   único que baja a un segundo renglón si no cabe es la visibilidad. Más
   específico que la regla de arriba (3 clases vs. 1), así que gana sin
   necesidad de !important. */
.documents-cards .mobile-card-headertext .documents-meta-line {
    flex-wrap: wrap;
}

/* En computadora la tarjeta es una franja horizontal: una por fila, más baja,
   con los mismos datos y el mismo diseño. Los botones se van a la derecha en
   vez de quedar abajo ocupando alto de más. */
@media (min-width: 601px) {
    .documents-cards .mobile-card {
        flex-direction: row;
        align-items: center;
        gap: 18px;
    }

    .documents-cards .mobile-card-header {
        flex: 1 1 auto;
        min-width: 0;
    }

    /* Con todo el ancho disponible el título ya no necesita partirse. */
    .documents-cards .mobile-card-title {
        white-space: nowrap;
    }

    .documents-cards .mobile-card-buttons {
        flex: 0 0 auto;
        width: auto;
        margin-top: 0;
    }

    .documents-cards .mobile-card-btn {
        flex: 0 0 auto;
        min-width: 130px;
    }
}

/* .mobile-card trae max-width:380px (venía pensado para celular y quedó
   aplicándose a todos los anchos al sacar el bloque de su media query). En la
   franja horizontal la tarjeta tiene que ocupar todo el ancho disponible. */
@media (min-width: 601px) {
    .documents-cards .mobile-card {
        max-width: none;
        width: 100%;
    }

    /* Sin esto el bloque de título/categoría/datos se encoge a 0 y desaparece:
       sus hijos son nowrap con overflow oculto. */
    .documents-cards .mobile-card-headertext {
        flex: 1 1 auto;
        min-width: 0;
    }
}

/* ==========================================================================
   Documentos: tamaño + fecha en un solo renglón (celular) y estrella de
   favorito al lado de "Descargar" (computadora)
   ========================================================================== */

/* En celular el tamaño y la fecha se partían en dos renglones. flex-wrap:nowrap
   (arriba) solo evita que se separen entre sí; lo que se seguía partiendo era el
   TEXTO de adentro ("Subido el 24 ago / 2026"). Con nowrap en cada dato y un
   poco más de ancho prestado —la línea se mete bajo el hueco que deja la
   estrella del encabezado, que está arriba y no estorba— los dos entran juntos
   sin salirse de la tarjeta. */
@media (max-width: 600px) {
    .documents-cards .mobile-card-headertext .documents-meta-line {
        gap: 8px;
        margin-right: -52px;
    }

    .documents-cards .documents-meta-item {
        white-space: nowrap;
    }
}

/* La estrella de la fila de botones existe solo para computadora: en celular
   manda la del encabezado (arriba a la derecha) y esta se oculta. */
.documents-fav-btn {
    display: none;
}

@media (min-width: 601px) {
    /* En la franja horizontal la estrella vive junto a "Descargar", así que la
       del encabezado sobra: dejarla sería la misma acción dos veces. */
    .documents-header-fav {
        display: none;
    }

    /* Angosta a lo ancho (solo la estrella) pero del mismo alto que Ver y
       Descargar: align-self:stretch la estira al alto de la fila, así no hay que
       repetir a mano el alto de los otros botones. Sin fondo ni borde, y en
       amarillo cuando el documento ya es favorito. */
    .documents-fav-btn {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        flex: 0 0 auto;
        align-self: stretch;
        padding: 0 10px;
        background: none;
        border: 1px solid var(--synbex-border);
        border-radius: var(--synbex-radius-md);
        color: var(--synbex-text-muted);
        cursor: pointer;
    }

    .documents-fav-btn svg {
        display: block;
        width: 20px;
        height: 20px;
    }

    .documents-fav-btn:hover {
        border-color: #eab308;
        color: #eab308;
    }

    /* Marcado: el botón entero se pinta de amarillo y la estrella queda en
       blanco, para que se lea de un vistazo cuál es favorito. */
    .documents-fav-btn.is-favorite {
        background: #eab308;
        border-color: #eab308;
        color: #fff;
    }
}

/* El menú de tres puntos: en celular vive en el encabezado (documents-header-menu
   — ahí hay sitio, la fila de botones ya tiene Ver/Descargar apretados); en
   computadora se pasa a la fila de botones, después de la estrella
   (documents-buttons-menu) — pedido explícito 2026-09-27, para que la fila quede
   Ver / Descargar / ★ / ⋮ en ese orden en vez de los tres puntos sueltos arriba
   del todo. Mismo truco que la estrella: el botón se repite porque el marcado de
   un contenedor no se puede mover a otro solo con CSS. */
.documents-buttons-menu {
    display: none;
}

@media (min-width: 601px) {
    .documents-header-menu {
        display: none;
    }

    .documents-buttons-menu {
        display: inline-flex;
    }

    /* .synbex-icon-btn trae 34x34 fijo (pensado para la tabla de escritorio,
       donde no hay una fila de botones altos que igualar) — acá se estira al
       alto de Ver/Descargar en vez de quedar chico y descuadrado, igual que ya
       hace .documents-fav-btn al lado. */
    .documents-buttons-menu .synbex-icon-btn {
        width: auto;
        height: auto;
        align-self: stretch;
        padding: 0 10px;
    }
}

/* El verde de "Descargar", el resplandor y el tamaño de los íconos estaban
   escritos solo dentro de la media query de celular, porque hasta ahora estas
   tarjetas solo existían ahí. En computadora salían con el verde de WhatsApp,
   sin resplandor y con los íconos grandes. Se repiten acotados a Documentos —
   Contactos y las demás siguen usando su tabla en computadora. */
@media (min-width: 601px) {
    .documents-cards .mobile-card-btn-indigo,
    .documents-cards .mobile-card-btn-green {
        box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--synbex-glow-color, var(--synbex-primary)) 55%, transparent);
        transition: box-shadow 0.15s ease, background 0.15s ease;
    }

    .documents-cards .mobile-card-btn-indigo:hover,
    .documents-cards .mobile-card-btn-green:hover {
        box-shadow: 0 8px 20px -6px color-mix(in srgb, var(--synbex-glow-color, var(--synbex-primary)) 65%, transparent);
    }

    .documents-cards .mobile-card-btn-green {
        --synbex-glow-color: #16a34a;
        background: #16a34a;
    }

    .documents-cards .mobile-card-btn svg {
        flex-shrink: 0;
        width: 18px;
        height: 18px;
    }
}

@media (max-width: 600px) {
    /* Red de seguridad: en pantallas muy angostas (320px) con un tamaño y un mes
       largos, el renglón se recorta contra el borde de la tarjeta en vez de
       salirse de ella. */
    .documents-cards .mobile-card-headertext .documents-meta-line {
        overflow: hidden;
    }

    .documents-cards .documents-meta-item {
        min-width: 0;
    }
}

/* En los teléfonos más angostos (320px) el renglón completo no entra a 12.5px.
   Baja un punto la letra en vez de cortar la fecha a la mitad. */
@media (max-width: 360px) {
    .documents-cards .documents-meta-line {
        gap: 6px;
        font-size: 11.5px;
    }

    .documents-cards .documents-meta-item svg {
        width: 14px;
        height: 14px;
    }
}

/* ==========================================================================
   Documentos: estrella marcada y aire alrededor de los botones en celular
   ========================================================================== */

/* La estrella del encabezado (la de celular) se marca igual que la de
   computadora: fondo amarillo lleno y la estrella en blanco. Sin marcar queda
   como está —solo el borde del botón y la estrella en su color. */
.documents-header-fav.is-favorite,
.documents-header-fav.is-favorite:hover {
    background: #eab308;
    border-color: #eab308;
    color: #fff;
}

@media (max-width: 600px) {
    /* Ver y Descargar quedaban pegados al renglón del peso y la fecha, y casi
       tocando el borde de abajo de la tarjeta. */
    .documents-cards .mobile-card-buttons {
        margin-top: 14px;
        margin-bottom: 6px;
    }
}

/* ==========================================================================
   Documentos: favorito del administrador y nombre completo al tocarlo
   ========================================================================== */

@media (max-width: 600px) {
    /* En celular el encabezado es angosto: al administrador, que además del
       favorito tiene el menú de tres puntos, se le deja SOLO el menú y el
       favorito se le pasa adentro. El residente sigue con su estrella.
       Se distingue con :has() —"este encabezado trae menú"— porque quien
       administra y quién no es cosa del rol, pero apretar solo en celular es
       cosa del ancho. */
    .documents-cards .mobile-card-header:has(.synbex-menu-wrapper) .documents-header-fav {
        display: none;
    }
}

@media (min-width: 601px) {
    /* En computadora sobra espacio: el administrador se queda con la estrella al
       lado de "Descargar" y con los tres puntos, así que el renglón de favorito
       del menú sale sobrando. */
    .documents-cards .documents-menu-fav {
        display: none;
    }
}

/* El nombre del documento se corta cuando no entra; tocándolo se despliega
   completo. El cursor y el resaltado al pasar por encima son lo único que
   avisa que se puede tocar. */
.documents-card-title {
    cursor: pointer;
}

.documents-card-title.is-expanded {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
}

/* ==========================================================================
   Reservar un área común: foto de fondo + panel montado encima
   ========================================================================== */

/* La foto tiene que llegar a los bordes de la pantalla, pero .dashboard-main le
   mete relleno a todo el contenido (28px, 16px o 12px según el ancho). El bloque
   se lo come con márgenes negativos del MISMO valor: si alguna vez cambian esos
   rellenos en DashboardLayout.razor.css, hay que cambiarlos también acá. */
.booking-immersive {
    margin: -28px -28px -28px;
}

/* El marco: es lo único que tiene un alto de verdad (el que manda el CSS de
   reposo, o el que le puso el arrastre). Estático a propósito — ver el
   comentario de booking-hero-drag.js — así Blazor nunca lo repinta. */
.booking-hero-frame {
    position: relative;
    height: 300px;
}

/* Sin foto no hay nada que mostrar: queda una franja más baja. */
.booking-hero-frame.booking-hero-empty {
    height: 150px;
}

/* La foto en sí: llena el marco entero (lo que antes lograba con su propio
   alto, ahora lo hereda del marco de afuera). */
.booking-hero {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center;
    background-color: var(--synbex-primary-soft);
    cursor: zoom-in;
}

.booking-hero.booking-hero-empty {
    background-image: linear-gradient(135deg, var(--synbex-primary), color-mix(in srgb, var(--synbex-primary) 55%, #000));
    cursor: default;
}

/* Un velo oscuro arriba para que la flecha de volver se lea sobre cualquier
   foto, clara u oscura. */
.booking-hero::after {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 120px;
    background: linear-gradient(to bottom, rgba(0, 0, 0, 0.45), transparent);
    pointer-events: none;
}

.booking-hero-back {
    position: absolute;
    top: 16px;
    left: 16px;
    z-index: 2;
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.45);
    color: #fff;
    text-decoration: none;
    backdrop-filter: blur(4px);
}

.booking-hero-back svg {
    width: 20px;
    height: 20px;
}

.booking-hero-back:hover {
    background: rgba(0, 0, 0, 0.65);
}

/* El panel sube sobre la foto —de ahí el margen negativo— y solo redondea las
   esquinas de arriba, para que se lea como una hoja que tapa la imagen. */
.booking-hero-panel {
    position: relative;
    margin-top: -30px;
    background: var(--synbex-surface);
    border-radius: 26px 26px 0 0;
    padding: 26px 28px 40px;
    min-height: 55vh;
    box-shadow: 0 -8px 24px -12px rgba(0, 0, 0, 0.35);
}

/* En pantallas anchas el contenido no se estira de lado a lado: se centra en
   una columna cómoda de leer, igual que el resto de la app. */
.booking-hero-panel-inner {
    max-width: 760px;
    margin: 0 auto;
}

.booking-hero-titlerow {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

.booking-hero-title {
    margin: 0;
    font-size: 24px;
    font-weight: 700;
    color: var(--synbex-text);
}

.booking-hero-desc {
    margin: 10px 0 0;
    color: var(--synbex-text-muted);
    line-height: 1.55;
}

/* El calendario ya no es una tarjeta suelta sobre el fondo de la página: vive
   dentro del panel, así que se queda sin marco propio y solo se centra. */
.booking-calendar-card {
    max-width: 480px;
    margin: 22px auto 0;
    background: transparent;
    border: none;
    box-shadow: none;
    padding: 0;
}

@media (max-width: 900px) {
    .booking-immersive {
        margin: -16px -16px -16px;
    }

    .booking-hero-frame {
        height: 240px;
    }

    .booking-hero-panel {
        padding: 22px 18px 36px;
    }
}

@media (max-width: 480px) {
    .booking-immersive {
        margin: -12px -12px -84px;
    }

    .booking-hero-frame {
        height: 210px;
    }

    .booking-hero-title {
        font-size: 21px;
    }
}

/* El panel llega hasta abajo del todo: se come el relleno inferior de
   .dashboard-main (28px, 16px, o 84px en celular, que es el hueco de la barra
   de abajo) y lo devuelve como relleno propio, para que el fondo del panel siga
   de largo pero el contenido no quede tapado por la barra. */
.booking-hero-panel {
    padding-bottom: 60px;
}

@media (max-width: 600px) {
    .booking-immersive {
        margin-bottom: -84px;
    }

    .booking-hero-panel {
        padding-bottom: 104px;
    }
}

/* ==========================================================================
   Reservar: arrastrar el panel para agrandar la foto
   ========================================================================== */

/* La barrita del borde del panel. touch-action:none es lo que evita que el
   navegador se lleve el gesto como scroll de la página; va solo acá, en la
   franja del tirador, para no romper el desplazamiento normal dentro del
   panel. */
.booking-hero-handle {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 22px;
    margin: -14px 0 6px;
    cursor: grab;
    touch-action: none;
}

.booking-hero-handle:active {
    cursor: grabbing;
}

.booking-hero-handle span {
    display: block;
    width: 46px;
    height: 5px;
    border-radius: 3px;
    background: var(--synbex-border);
}

/* Solo al soltar: durante el arrastre la altura tiene que seguir al dedo sin
   retraso, así que la transición se prende y se apaga desde el JS. */
.booking-hero-snapping {
    transition: height 0.25s ease;
}

/* Pasado el 40% la foto deja de estar recortada y se ve entera. El fondo oscuro
   rellena lo que sobra a los lados o arriba y abajo. El JS pone esta clase en
   el MARCO (.booking-hero-frame), pero el fondo que hay que cambiar es el de
   la foto de adentro (.booking-hero) — de ahí el selector por descendencia. */
.booking-hero-frame.booking-hero-full .booking-hero {
    background-size: contain;
    background-repeat: no-repeat;
    background-color: #101013;
}

/* ==========================================================================
   Reservar en computadora: dos columnas — foto y datos a la izquierda,
   calendario a la derecha
   ========================================================================== */

/* Dos columnas: a la izquierda (70%) la foto con el nombre, la duración y la
   descripción; a la derecha (30%) solo el calendario. En celular no aplica nada
   de esto: ahí las dos columnas se apilan y se siguen viendo como una sola hoja
   (ver .booking-col-right más abajo). */
@media (min-width: 901px) {
    .booking-immersive {
        max-width: 1200px;
        margin: -28px auto -28px;
        display: grid;
        grid-template-columns: 7fr 3fr;
        gap: 24px;
        align-items: start;
    }

    /* Las esquinas redondeadas y el recorte se mudaron acá: ahora la "hoja" con
       la foto es solo esta columna, no todo el bloque. */
    .booking-col-left {
        border-radius: 0 0 26px 26px;
        overflow: hidden;
    }

    /* Más alta que antes: con el 70% del ancho, a 340px la foto volvía a verse
       como una franja. */
    .booking-hero-frame {
        height: 420px;
    }

    /* El panel de la izquierda ya solo lleva el texto, así que mide lo que mide
       su contenido en vez de estirarse media pantalla. */
    .booking-hero-panel {
        min-height: 0;
        padding-bottom: 26px;
    }

    .booking-hero-panel-inner {
        max-width: none;
    }

    /* La columna del calendario es su propia tarjeta, pegada arriba. */
    .booking-col-right {
        background: var(--synbex-surface);
        border-radius: 26px;
        padding: 22px 14px;
    }

    /* Los 100px de aire de abajo son para poder deslizar en el teléfono; acá
       solo dejarían un hueco. */
    .booking-calendar-card {
        margin-bottom: 0;
        max-width: none;
    }
}

/* ==========================================================================
   Reservar: encabezado del panel centrado, con la duración a la derecha
   ========================================================================== */

/* El nombre del área va centrado y la etiqueta de duración ("Por horas" / "Por
   día") se sale de la fila y se pega al borde derecho, a la altura del título.
   Va en posición absoluta a propósito: si se quedara dentro del flujo, empujaría
   el título y este dejaría de estar centrado respecto del panel. */
.booking-hero-titlerow {
    position: relative;
    display: block;
    text-align: center;
    padding-right: 92px;
    padding-left: 92px;
}

.booking-hero-titlerow .badge {
    position: absolute;
    right: 0;
    top: 50%;
    transform: translateY(-50%);
    margin: 0;
}

/* La descripción acompaña al título centrado, y de acá al calendario van los
   30px pedidos. */
.booking-hero-desc {
    margin: 10px 0 30px;
    text-align: center;
}

/* El calendario arranca justo después de esos 30px, y debajo quedan 100px de
   aire para poder deslizar con comodidad. */
.booking-calendar-card {
    margin-top: 0;
    margin-bottom: 100px;
}

/* Con esos 100px propios, el panel ya no necesita el relleno grande de abajo
   que antes despejaba la barra inferior del celular. */
.booking-hero-panel {
    padding-bottom: 24px;
}

@media (max-width: 600px) {
    .booking-hero-panel {
        padding-bottom: 24px;
    }
}

/* En celular se aprieta un poco el hueco reservado a cada lado para que al
   nombre le quede más aire. */
@media (max-width: 600px) {
    .booking-hero-titlerow {
        padding-left: 84px;
        padding-right: 84px;
    }
}

/* Solo en pantallas diminutas (menos de 330px) la etiqueta baja debajo del
   título: ahí ya no queda ancho útil para el nombre. */
@media (max-width: 330px) {
    .booking-hero-titlerow {
        padding: 0;
    }

    .booking-hero-titlerow .badge {
        position: static;
        display: inline-block;
        transform: none;
        margin: 8px 0 0;
    }
}

/* ==========================================================================
   Ventana de reserva: fecha en palabras y reloj en los selectores de hora
   ========================================================================== */

/* La fecha completa, centrada, entre el encabezado de la ventana y el bloque
   de horario. */
.booking-modal-date {
    margin: 18px 0 20px;
    text-align: center;
    font-weight: 600;
    font-size: 16px;
    color: var(--synbex-text);
}

/* El <select> nativo trae su propia flechita; se le quita y en su lugar va un
   reloj. El ícono se dibuja aparte, encima del campo, para que tome el color
   del tema (con background-image quedaría de un gris fijo en claro y oscuro). */
.booking-time-input {
    position: relative;
}

.booking-time-select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    padding-right: 40px;
}

.booking-time-icon {
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    pointer-events: none;
    color: var(--synbex-text-muted);
}

.booking-time-icon svg {
    width: 18px;
    height: 18px;
}

/* ==========================================================================
   Reservar en celular: las dos columnas apiladas se leen como una sola hoja
   ========================================================================== */

/* El calendario salió del panel para poder mandarlo a la columna derecha en
   computadora. En celular cae justo debajo, así que lleva el mismo fondo y el
   mismo relleno lateral: pegados y del mismo color, se ven como un solo panel,
   igual que antes de partirlo en dos. */
@media (max-width: 900px) {
    /* Los 30px que separan la descripción del calendario los pone ACÃ el panel,
       como relleno, y no la descripción como margen: con el panel sin relleno
       abajo ese margen se escapaba fuera del panel (colapso de márgenes) y
       dejaba una franja del fondo de la página entre las dos partes de la hoja. */
    .booking-hero-panel {
        min-height: 0;
        padding-bottom: 30px;
    }

    .booking-hero-desc {
        margin-bottom: 0;
    }

    .booking-col-right {
        background: var(--synbex-surface);
        padding: 0 18px 24px;
        /* Que la hoja pase de la mitad de la pantalla aunque el mes tenga pocas
           filas — antes ese trabajo lo hacía el min-height del panel. */
        min-height: 40vh;
        box-sizing: border-box;
    }
}

/* ==========================================================================
   Reservar: carrusel de fotos, ficha del área y flechas del mes
   ========================================================================== */

/* Contador "1 / 3" abajo a la izquierda de la foto. */
.booking-photo-count {
    position: absolute;
    left: 14px;
    bottom: 14px;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 6px 12px;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    font-size: 12.5px;
    font-weight: 600;
    backdrop-filter: blur(4px);
}

.booking-photo-count svg {
    width: 15px;
    height: 15px;
}

.booking-photo-nav {
    position: absolute;
    right: 14px;
    bottom: 14px;
    z-index: 2;
    display: flex;
    gap: 10px;
}

.booking-photo-arrow {
    width: 38px;
    height: 38px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    cursor: pointer;
    backdrop-filter: blur(4px);
}

.booking-photo-arrow svg {
    width: 18px;
    height: 18px;
}

.booking-photo-arrow:hover {
    background: rgba(0, 0, 0, 0.78);
}

/* El ícono redondo del área es GENERAL para todas (una sola ola, no una piscina
   ni una cancha): el catálogo de áreas es libre y no hay ícono por tipo. En
   celular no se dibuja, para no romper el título centrado. */
.booking-area-icon {
    display: none;
}

.booking-area-place {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    margin: 6px 0 0;
    color: var(--synbex-text-muted);
    font-size: 14px;
}

.booking-area-place svg {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

/* Dos copias de la ubicación (ver el comentario en BookingCalendar.razor):
   en computadora se ve la de junto al nombre; en celular, la de después de
   la descripción. Por defecto (celular) se esconde la de computadora; el
   media query de más abajo hace lo contrario a partir de 901px. */
.booking-area-place-desktop {
    display: none;
}

/* "Uso exclusivo" va siempre; el horario solo en las áreas por horas (en las de
   día completo esas horas no gobiernan nada). Cuando no está, no queda ni el
   separador: la fila se arma sola con lo que haya. */
.booking-area-facts {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 16px;
    margin-top: 18px;
    padding-top: 16px;
    border-top: 1px solid var(--synbex-border);
}

.booking-area-fact {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    color: var(--synbex-text);
}

/* En computadora el rótulo y el valor siguen leyéndose como una sola frase
   ("Horario: 6:00 a.m. - 10:00 p.m."), igual que antes de separarlos en dos
   <span> — el ":" que los une ahora lo pone este pseudo-elemento en vez de
   estar escrito a mano en el marcado, para poder quitarlo en celular (ver
   más abajo) sin dos copias del texto. */
.booking-area-fact-label::after {
    content: ": ";
}

.booking-area-fact-icon svg,
.booking-area-fact svg {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    color: var(--synbex-text-muted);
}

.booking-area-fact-divider {
    width: 1px;
    height: 20px;
    background: var(--synbex-border);
}

/* Flechas redondas del mes, en vez de los botones "â Anterior" / "Siguiente â¶":
   con la columna del calendario al 30% del ancho, aquellos no entraban en una
   línea con el nombre del mes. */
.booking-month-arrow {
    width: 38px;
    height: 38px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--synbex-border);
    border-radius: 50%;
    background: var(--synbex-surface);
    color: var(--synbex-text);
    cursor: pointer;
}

.booking-month-arrow svg {
    width: 18px;
    height: 18px;
}

.booking-month-arrow:hover {
    background: var(--synbex-primary-soft);
    border-color: var(--synbex-primary);
}

/* La palabra "Volver" solo aparece en computadora; en celular manda la flecha
   redonda sobre la foto, como hasta ahora. */
.booking-back-text {
    display: none;
}

/* El enlace de volver ya no vive dentro de la foto, así que el ancla de su
   posición absoluta pasa a ser el bloque entero. */
.booking-immersive {
    position: relative;
}

/* ==========================================================================
   Reservar en computadora: dos tarjetas con brillo, ficha en fila
   ========================================================================== */

@media (min-width: 901px) {
    /* Arriba de las dos columnas va la pastilla "â Volver", ocupando su propia
       fila; por eso el bloque ya no se pega al borde de arriba. */
    .booking-immersive {
        margin-top: 0;
    }

    .booking-hero-back {
        position: static;
        grid-column: 1 / -1;
        justify-self: start;
        width: auto;
        height: auto;
        gap: 8px;
        padding: 9px 18px 9px 13px;
        border-radius: 999px;
        background: var(--synbex-surface);
        border: 1px solid var(--synbex-border);
        color: var(--synbex-text);
        font-weight: 600;
        backdrop-filter: none;
    }

    .booking-hero-back:hover {
        background: var(--synbex-primary-soft);
        border-color: var(--synbex-primary);
    }

    .booking-back-text {
        display: inline;
    }

    /* La foto deja de tener el panel montado encima: son dos tarjetas separadas,
       una debajo de la otra. */
    .booking-col-left {
        display: flex;
        flex-direction: column;
        gap: 16px;
        border-radius: 0;
        overflow: visible;
    }

    .booking-hero-frame {
        height: 340px;
        border-radius: 22px;
        overflow: hidden;
    }

    .booking-hero-panel {
        margin-top: 0;
        border-radius: 22px;
        padding: 24px;
    }

    /* Sin panel montado no hay nada que arrastrar. */
    .booking-hero-handle {
        display: none;
    }

    /* Ficha del área en fila: ícono redondo, nombre y lugar, y la etiqueta de
       duración al final. */
    .booking-area-head {
        display: flex;
        align-items: center;
        gap: 16px;
        padding: 0;
        text-align: left;
    }

    .booking-area-head .badge {
        position: static;
        transform: none;
        margin-left: auto;
    }

    .booking-area-icon {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 62px;
        height: 62px;
        flex-shrink: 0;
        border-radius: 50%;
        background: linear-gradient(140deg, var(--synbex-primary), color-mix(in srgb, var(--synbex-primary) 55%, #000));
        color: #fff;
        box-shadow: 0 10px 22px -12px var(--synbex-primary);
    }

    .booking-area-icon svg {
        width: 30px;
        height: 30px;
    }

    .booking-area-headtext {
        min-width: 0;
    }

    .booking-hero-title,
    .booking-hero-desc {
        text-align: left;
    }

    .booking-hero-desc {
        margin: 14px 0 0;
    }

    .booking-area-place {
        justify-content: flex-start;
    }

    /* Acá, y no antes: en computadora vuelve a verse la ubicación junto al
       nombre y se esconde la copia de después de la descripción. */
    .booking-area-place-desktop {
        display: flex;
    }

    .booking-area-place-mobile {
        display: none;
    }

    .booking-area-facts {
        justify-content: flex-start;
        gap: 22px;
    }

    /* El brillo pedido, con NUESTRO morado (var(--synbex-primary)) y no el azul
       de la referencia: un degradado suave desde arriba, un borde teñido y un
       halo por debajo. Va en las tarjetas de contenido —la ficha del área y el
       calendario—, no en el fondo de la pantalla. */
    .booking-hero-panel,
    .booking-col-right,
    .booking-hero-frame {
        background-color: var(--synbex-surface);
        border: 1px solid color-mix(in srgb, var(--synbex-primary) 26%, transparent);
        box-shadow:
            0 20px 45px -28px color-mix(in srgb, var(--synbex-primary) 70%, transparent),
            inset 0 1px 0 color-mix(in srgb, #fff 14%, transparent);
    }

    .booking-hero-panel,
    .booking-col-right {
        background-image: linear-gradient(
            165deg,
            color-mix(in srgb, var(--synbex-primary) 13%, transparent) 0%,
            transparent 60%);
    }
}

@media (max-width: 900px) {
    /* En celular el panel se monta 30px sobre la foto: los controles del
       carrusel suben para no quedar tapados a medias por ese borde. */
    .booking-photo-count,
    .booking-photo-nav {
        bottom: 48px;
    }

    /* Pedido explícito: "en paneles, de dos en dos" — cada dato pasa de ser
       una frase corrida en una fila a su propia caja, con el rótulo en
       negrita arriba y el valor debajo (como la referencia que se pasó). Sin
       separador: cada caja ya se distingue sola por el fondo. */
    .booking-area-facts {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 10px;
    }

    .booking-area-fact {
        align-items: flex-start;
        gap: 10px;
        background: var(--synbex-primary-soft);
        border-radius: 14px;
        padding: 12px 14px;
    }

    .booking-area-fact-icon {
        display: inline-flex;
        flex-shrink: 0;
        margin-top: 2px;
    }

    .booking-area-fact-text {
        display: flex;
        flex-direction: column;
        gap: 2px;
    }

    .booking-area-fact-label {
        font-weight: 700;
    }

    /* El ":" que en computadora pega el rótulo al valor en la misma frase acá
       no hace falta: cada uno queda en su propia línea. */
    .booking-area-fact-label::after {
        content: none;
    }

    .booking-area-fact-value {
        color: var(--synbex-text-muted);
    }

    .booking-area-fact-divider {
        display: none;
    }
}

/* ==========================================================================
   Ãreas comunes: las fotos del formulario
   ========================================================================== */

/* Las miniaturas van una al lado de otra debajo del botón de agregar, igual que
   en limpieza. El resto del aspecto (botón, contador, basurero, aviso) lo ponen
   las clases .cleaning-photo-* que ya existen, reusadas tal cual. */
.area-photo-thumbs {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

/* ==========================================================================
   Reservar: el panel del calendario a la misma altura que el de la izquierda
   ========================================================================== */

/* La tarjeta del calendario se ordena como columna en los dos tamaños; lo único
   que cambia es dónde queda la instrucción. */
.booking-calendar-card {
    display: flex;
    flex-direction: column;
}

@media (max-width: 900px) {
    /* Pedido explícito: la instrucción pasa a ir PRIMERO, arriba del mes (antes
       iba entre el mes y los días). En el marcado sigue al final de todo, así
       que se reacomoda acá en vez de duplicarla. */
    .booking-calendar-card .booking-calendar-instruction {
        order: -2;
        margin: 14px 0;
    }

    .booking-calendar-card .synbex-calendar-header {
        order: -1;
    }
}

@media (min-width: 901px) {
    /* Las dos columnas terminan a la misma altura: la del calendario se estira
       hasta donde llega la ficha del área. */
    .booking-immersive {
        align-items: stretch;
    }

    .booking-col-right {
        display: flex;
        flex-direction: column;
    }

    .booking-calendar-card {
        flex: 1;
    }

    /* margin-top:auto empuja la instrucción al fondo del panel: ocupa el hueco
       que queda debajo de los días en vez de dejarlo vacío. */
    .booking-calendar-instruction {
        margin: auto 0 0;
        padding-top: 22px;
    }
}

/* ==========================================================================
   Mis reservas: aviso de cuándo se va la reserva de la lista
   ========================================================================== */

/* Discreto a propósito: es un dato de mantenimiento de la lista, no una alerta,
   así que va en cursiva y en el gris del texto secundario, sin ícono ni color
   de aviso que compitan con el estado de la reserva. */
.reservation-expiry-note {
    margin: 6px 0 0;
    font-size: 12.5px;
    color: var(--synbex-text-muted);
    font-style: italic;
}

/* ==========================================================================
   Mis reservas: filas con foto, y la sección de visitas esperadas
   ========================================================================== */

/* El aviso de los 15 días, arriba a la derecha del encabezado. */
.misreservas-header {
    align-items: flex-start;
    gap: 16px;
}

.misreservas-note {
    display: flex;
    align-items: center;
    gap: 10px;
    max-width: 340px;
    padding: 12px 14px;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-primary-soft);
    border: 1px solid color-mix(in srgb, var(--synbex-primary) 30%, transparent);
    font-size: 12.5px;
    line-height: 1.45;
    color: var(--synbex-text);
}

.misreservas-note svg {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
    color: var(--synbex-primary);
}

/* Encabezado de cada sección (Ãreas comunes / Visitas esperadas). */
.misreservas-section-head {
    display: flex;
    align-items: center;
    gap: 14px;
    margin: 26px 0 12px;
}

.misreservas-section-title {
    margin: 0;
    font-size: 18px;
    font-weight: 700;
    color: var(--synbex-text);
}

.misreservas-section-count {
    margin-left: auto;
}

/* Una fila = una reserva o una visita. Sirve igual en computadora y en celular:
   en pantalla angosta se acomoda en dos renglones (ver más abajo). */
.reserva-rows {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.reserva-row {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 12px;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-bg);
    border: 1px solid var(--synbex-border);
}

/* La miniatura del área. El tamaño en pantalla es 120x76; al servidor se le pide
   a 180px de ancho (el doble, para que se vea nítida en pantallas retina) en vez
   de la foto original de 1920px. */
.reserva-row-photo {
    width: 120px;
    height: 76px;
    flex-shrink: 0;
    border-radius: 10px;
    object-fit: cover;
    background: var(--synbex-surface);
}

.reserva-row-photo-empty,
.reserva-row-initials {
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--synbex-text-muted);
}

/* Las visitas no tienen foto: van con las iniciales en un redondel. */
.reserva-row-initials {
    width: 52px;
    height: 52px;
    border-radius: 50%;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    font-weight: 700;
    font-size: 16px;
}

.reserva-row-main {
    display: flex;
    flex-direction: column;
    gap: 5px;
    flex: 1 1 auto;
    min-width: 0;
}

.reserva-row-title {
    font-weight: 700;
    font-size: 15.5px;
    color: var(--synbex-text);
}

.reserva-row-line {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--synbex-text-muted);
}

.reserva-row-line svg {
    flex-shrink: 0;
    color: var(--synbex-text-muted);
}

.reserva-row-state {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    flex: 0 0 auto;
}

.reserva-row-state .reservation-expiry-note {
    margin: 0;
}

.reserva-row-actions {
    display: flex;
    gap: 8px;
    flex: 0 0 auto;
}

.reserva-row-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    justify-content: center;
    white-space: nowrap;
}

.reserva-row-btn svg {
    flex-shrink: 0;
}

@media (max-width: 700px) {
    /* En celular la fila se parte: foto y datos arriba, estado y botones abajo. */
    .reserva-row {
        flex-wrap: wrap;
    }

    .reserva-row-state,
    .reserva-row-actions {
        flex-basis: 100%;
    }

    .reserva-row-state {
        flex-direction: row;
        align-items: center;
        flex-wrap: wrap;
        gap: 10px;
    }

    .reserva-row-btn {
        flex: 1 1 0;
    }

    .misreservas-note {
        max-width: none;
    }
}

/* La fecha y la hora no se parten en dos renglones: el ancho lo cede la columna
   del nombre, que sí puede recortarse. */
.reserva-row-line {
    white-space: nowrap;
}

/* El aviso se va a la derecha del encabezado en vez de caer debajo del título. */
.misreservas-note {
    margin-left: auto;
}

/* Fila del interruptor de parqueadero en la ventana de "Volver a invitar" —
   mismo molde que el de "Mis citas". */
.renovar-parqueo-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-top: 14px;
}

/* ==========================================================================
   Pase de visita: la invitación que se le manda al visitante
   ========================================================================== */

.pase-dialog {
    max-width: 420px;
    background: transparent;
    box-shadow: none;
    padding: 0;
    position: relative;
    /* La invitación es alta: en un teléfono hay que poder llegar al botón de
       compartir sin que se salga de la pantalla. */
    max-height: 88vh;
    overflow-y: auto;
    margin-top: 46px;
}

.pase-close {
    position: sticky;
    top: 0;
    margin-left: auto;
    margin-bottom: -34px;
    display: block;
    z-index: 2;
    color: #fff;
}

/* La tarjeta: banda de color arriba con el logo, y los datos abajo en blanco.
   Es el mismo diseño que se dibuja en la imagen que se comparte, para que lo
   que ve el residente sea lo que le llega a su visita. */
.pase-card {
    border-radius: 22px;
    overflow: hidden;
    background: var(--synbex-surface);
    box-shadow: 0 24px 60px -30px rgba(0, 0, 0, 0.6);
}

.pase-top {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 26px 24px 22px;
    background: linear-gradient(150deg, var(--synbex-primary), color-mix(in srgb, var(--synbex-primary) 60%, #000));
    color: #fff;
    text-align: center;
}

/* El logo va sobre un panel blanco redondeado, igual que en la imagen que se
   comparte: muchos logos son oscuros o transparentes y sobre la banda de color
   se perdían. */
.pase-logo {
    max-height: 56px;
    max-width: 190px;
    object-fit: contain;
    margin-bottom: 10px;
    padding: 12px 18px;
    border-radius: 14px;
    background: #fff;
    box-sizing: content-box;
}

.pase-tenant {
    font-size: 19px;
    font-weight: 700;
}

.pase-facility {
    font-size: 13.5px;
    opacity: 0.85;
}

.pase-body {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 22px 24px 26px;
}

.pase-kicker {
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--synbex-primary);
}

.pase-name {
    margin: 10px 0 2px;
    font-size: 23px;
    font-weight: 700;
    color: var(--synbex-text);
}

.pase-doc {
    font-size: 13.5px;
    color: var(--synbex-text-muted);
}

/* Cada dato con su rótulo arriba: sin eso, "20 de septiembre" suelto no le dice
   a la visita si es la fecha de la cita o la de emisión del pase. */
.pase-datos {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px 12px;
    width: 100%;
    margin: 20px 0 4px;
    padding: 18px 0;
    border-top: 1px solid var(--synbex-border);
    border-bottom: 1px solid var(--synbex-border);
}

.pase-dato {
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.pase-dato-label {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--synbex-text-muted);
}

.pase-dato-valor {
    font-size: 14px;
    color: var(--synbex-text);
}

.pase-qr {
    width: 210px;
    height: 210px;
    margin: 20px 0 14px;
    border-radius: 12px;
    background: #fff;
    padding: 10px;
    box-sizing: border-box;
}

.pase-pie {
    margin: 0;
    font-size: 13px;
    line-height: 1.5;
    color: var(--synbex-text-muted);
}

.pase-share {
    width: 100%;
    margin-top: 16px;
    justify-content: center;
}

@media (max-width: 480px) {
    .pase-datos {
        grid-template-columns: 1fr;
    }
}

/* ==========================================================================
   Portería: ventana de "cita confirmada para hoy"
   ========================================================================== */

/* Verde y con un visto grande, deliberadamente distinta de la de "la cita no es
   para hoy" (que es de aviso): si las dos se parecieran, el guarda las leería
   como la misma alerta y dejaría de mirarlas. */
.visitor-cita-modal {
    max-width: 420px;
    text-align: center;
    padding: 30px 26px 26px;
    border-top: 5px solid #16a34a;
}

.visitor-cita-icon {
    width: 76px;
    height: 76px;
    margin: 0 auto 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: color-mix(in srgb, #16a34a 18%, transparent);
    color: #16a34a;
}

.visitor-cita-icon svg {
    width: 40px;
    height: 40px;
}

.visitor-cita-title {
    margin: 0;
    font-size: 22px;
    font-weight: 700;
    color: var(--synbex-text);
}

.visitor-cita-when {
    margin: 6px 0 20px;
    font-size: 15px;
    font-weight: 600;
    color: #16a34a;
}

.visitor-cita-datos {
    display: flex;
    flex-direction: column;
    gap: 12px;
    text-align: left;
    padding: 16px 18px;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-bg);
}

.visitor-cita-dato {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 14px;
}

.visitor-cita-label {
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--synbex-text-muted);
    flex-shrink: 0;
}

.visitor-cita-valor {
    font-size: 14.5px;
    font-weight: 600;
    color: var(--synbex-text);
    text-align: right;
}

/* Solo aparece cuando el guarda TIENE que hacer algo con el parqueadero. */
.visitor-cita-aviso {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 14px 0 0;
    padding: 12px 14px;
    border-radius: var(--synbex-radius-md);
    background: color-mix(in srgb, var(--synbex-warning, #f59e0b) 16%, transparent);
    border: 1px solid color-mix(in srgb, var(--synbex-warning, #f59e0b) 40%, transparent);
    font-size: 13px;
    text-align: left;
    color: var(--synbex-text);
}

.visitor-cita-aviso svg {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
}

.visitor-cita-btn {
    width: 100%;
    margin-top: 20px;
    justify-content: center;
}

/* El mismo aviso del parqueadero de la cita, pero dentro del formulario: la
   ventana de bienvenida se cierra sola y esto tiene que quedar a la vista. */
.visitor-parking-warning {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 8px 0 0;
    font-size: 12.5px;
    color: var(--synbex-text);
}

.visitor-parking-warning svg {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    color: var(--synbex-warning, #f59e0b);
}

/* Las tres variantes de la ventana del pase comparten el molde y solo cambian de
   color, para que el guarda las distinga de un vistazo sin leer:
     Â· verde  â la cita es para hoy, todo bien;
     Â· rojo   â el pase ya venció (la cita era de un día que ya pasó);
     Â· ámbar  â la persona se adelantó (la cita es más adelante).
   El color va en el borde de arriba, el redondel del ícono y la línea de fecha. */
.visitor-cita-modal.is-vencida {
    border-top-color: var(--synbex-danger, #dc2626);
}

.visitor-cita-modal.is-vencida .visitor-cita-icon {
    background: color-mix(in srgb, var(--synbex-danger, #dc2626) 18%, transparent);
    color: var(--synbex-danger, #dc2626);
}

.visitor-cita-modal.is-vencida .visitor-cita-when {
    color: var(--synbex-danger, #dc2626);
}

.visitor-cita-modal.is-adelantada {
    border-top-color: var(--synbex-warning, #f59e0b);
}

.visitor-cita-modal.is-adelantada .visitor-cita-icon {
    background: color-mix(in srgb, var(--synbex-warning, #f59e0b) 20%, transparent);
    color: var(--synbex-warning, #f59e0b);
}

.visitor-cita-modal.is-adelantada .visitor-cita-when {
    color: var(--synbex-warning, #f59e0b);
}

.visitor-cita-nota {
    margin: 16px 0 0;
    font-size: 13.5px;
    color: var(--synbex-text-muted);
}

.visitor-cita-acciones {
    display: flex;
    gap: 10px;
    margin-top: 18px;
}

.visitor-cita-acciones .btn-synbex {
    flex: 1;
    justify-content: center;
}

/* ==========================================================================
   Portería: "Â¿a dónde va?" — buscador de apartamento o área común
   ========================================================================== */

.visitor-destino-field {
    position: relative;
}

/* Lo ya elegido: se ve como una etiqueta con su ícono y una X para cambiarlo,
   en vez de dejar el texto editable —que invitaría a escribir a mano algo que
   no existe. */
.visitor-destino-elegido {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 11px 12px;
    border-radius: var(--synbex-radius-md);
    border: 1px solid var(--synbex-primary);
    background: var(--synbex-primary-soft);
    font-weight: 600;
    color: var(--synbex-text);
}

.visitor-destino-elegido svg {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
    color: var(--synbex-primary);
}

.visitor-destino-cambiar {
    margin-left: auto;
    border: none;
    background: none;
    color: var(--synbex-text-muted);
    font-size: 15px;
    cursor: pointer;
    padding: 0 4px;
}

/* La lista de coincidencias. Va por encima de lo que sigue (no empuja el
   formulario hacia abajo mientras se escribe) y con tope de alto propio. */
.visitor-destino-lista {
    position: absolute;
    left: 0;
    right: 0;
    z-index: 30;
    max-height: 260px;
    overflow-y: auto;
    margin-top: 4px;
    padding: 6px;
    border-radius: var(--synbex-radius-md);
    border: 1px solid var(--synbex-border);
    background: var(--synbex-surface);
    box-shadow: var(--synbex-shadow);
}

.visitor-destino-opcion {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 10px 10px;
    border: none;
    border-radius: 8px;
    background: none;
    text-align: left;
    color: var(--synbex-text);
    font-size: 14px;
    cursor: pointer;
}

.visitor-destino-opcion:hover {
    background: var(--synbex-primary-soft);
}

.visitor-destino-opcion svg {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    color: var(--synbex-text-muted);
}

.visitor-destino-vacio {
    margin: 6px 0 0;
    font-size: 12.5px;
    color: var(--synbex-text-muted);
}

/* ==========================================================================
   Aviso de "no se pudo cargar la pantalla" (ver js/blank-guard.js)
   ========================================================================== */

#synbex-blank-guard {
    position: fixed;
    inset: 0;
    z-index: 3000;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 16px;
    padding: 30px;
    text-align: center;
    background: var(--synbex-bg);
    color: var(--synbex-text);
}

#synbex-blank-guard p {
    margin: 0;
    max-width: 320px;
    font-size: 15px;
    line-height: 1.5;
    color: var(--synbex-text-muted);
}

#synbex-blank-guard button {
    padding: 12px 28px;
    border: none;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-primary);
    color: #fff;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
}

/* La hora de la cita, dentro de la ventana verde de "cita para hoy". El día se
   queda en el color de la ventana; solo la hora cambia:
     Â· verde â llegó dentro de la hora anterior o posterior a la de su cita;
     Â· ámbar â se corrió más que eso. No es un error (la cita ES de hoy), así que
       el tono es tierra y no un naranja chillón de alerta. */
.visitor-cita-hora {
    font-weight: 700;
}

.visitor-cita-hora.is-fuera-de-hora {
    color: #d08a2c;
}

/* El aviso del parqueadero en positivo: cuando ya quedó resuelto no es una
   advertencia, así que va en verde y no en ámbar. */
.visitor-cita-aviso.is-ok {
    background: color-mix(in srgb, #16a34a 14%, transparent);
    border-color: color-mix(in srgb, #16a34a 38%, transparent);
}

.visitor-cita-aviso.is-ok svg {
    color: #16a34a;
}

/* ==========================================================================
   Filas que se despliegan (visitas esperadas)
   ========================================================================== */

/* Con detalle, la fila deja de ser una caja horizontal para pasar a ser una
   columna: arriba el resumen y debajo, si se abre, el detalle. */
.reserva-row.tiene-detalles {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
}

.reserva-row-top {
    display: flex;
    align-items: center;
    gap: 16px;
}

/* Fecha y hora en el mismo renglón: arriba va un resumen, no la ficha completa. */
.reserva-row-lines {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 18px;
}

.reserva-row-toggle {
    width: 34px;
    height: 34px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 50%;
    background: none;
    color: var(--synbex-text-muted);
    cursor: pointer;
    transition: transform 0.18s ease;
}

.reserva-row-toggle svg {
    width: 20px;
    height: 20px;
}

.reserva-row-toggle:hover {
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

/* La misma flecha girada, para no cargar dos íconos. */
.reserva-row.is-abierto .reserva-row-toggle {
    transform: rotate(180deg);
}

.reserva-row-detalles {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px 24px;
    margin-top: 14px;
    padding-top: 16px;
    border-top: 1px solid var(--synbex-border);
}

.reserva-detalle {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
}

/* Ãcono en su cuadrito, como en el resto de la app. */
.reserva-detalle-icono {
    width: 38px;
    height: 38px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 11px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.reserva-detalle-icono svg {
    width: 19px;
    height: 19px;
}

.reserva-detalle-label {
    display: block;
    font-size: 12px;
    font-weight: 600;
    color: var(--synbex-primary);
}

.reserva-detalle-valor {
    display: block;
    font-size: 14px;
    color: var(--synbex-text);
    word-break: break-word;
}

@media (max-width: 700px) {
    /* En celular el detalle va en una sola columna y el resumen se parte, igual
       que el resto de la fila. */
    .reserva-row-detalles {
        grid-template-columns: 1fr;
    }

    .reserva-row-top {
        flex-wrap: wrap;
    }
}

/* En la fila desplegable el nombre no parte en dos renglones: recorta con
   puntos suspensivos y deja el ancho para la fecha y los botones. */
.reserva-row.tiene-detalles .reserva-row-title {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* El estado puede encogerse en vez de empujar a los botones fuera de la fila. */
.reserva-row.tiene-detalles .reserva-row-state {
    min-width: 0;
    flex-shrink: 1;
}

.reserva-row.tiene-detalles .reserva-row-actions {
    margin-left: auto;
}

/* ==========================================================================
   Contabilidad
   ========================================================================== */

.contabilidad-filtros {
    display: flex;
    align-items: flex-end;
    gap: 14px;
    flex-wrap: wrap;
}

.contabilidad-filtros .synbex-field {
    flex: 1 1 220px;
    max-width: 280px;
}

.contabilidad-imprimir {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

.contabilidad-imprimir svg {
    width: 18px;
    height: 18px;
}

/* Arriba: el número grande y, al lado, el desglose por área. */
.contabilidad-resumen {
    display: grid;
    grid-template-columns: minmax(240px, 320px) 1fr;
    gap: 16px;
    margin-bottom: 20px;
    align-items: start;
}

.contabilidad-total {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.contabilidad-total-label {
    font-size: 12.5px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--synbex-text-muted);
}

/* El dato que se viene a buscar: grande y sin nada que compita al lado. */
.contabilidad-total-valor {
    font-size: 34px;
    font-weight: 700;
    line-height: 1.1;
    color: var(--synbex-text);
}

.contabilidad-total-detalle {
    font-size: 13px;
    color: var(--synbex-text-muted);
}

.contabilidad-area-fila {
    display: grid;
    grid-template-columns: 1fr auto auto;
    gap: 14px;
    align-items: center;
    padding: 9px 0;
    border-bottom: 1px solid var(--synbex-border);
}

.contabilidad-area-fila:last-child {
    border-bottom: none;
}

.contabilidad-area-nombre {
    font-weight: 600;
    color: var(--synbex-text);
}

.contabilidad-area-cantidad {
    font-size: 12.5px;
    color: var(--synbex-text-muted);
}

.contabilidad-area-monto {
    font-weight: 700;
    color: var(--synbex-text);
    min-width: 90px;
    text-align: right;
}

@media (max-width: 800px) {
    .contabilidad-resumen {
        grid-template-columns: 1fr;
    }
}

/* ==========================================================================
   Ventana del residente: autorizar una visita
   ========================================================================== */

.visitor-approval-datos {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    gap: 10px 18px;
    margin: 4px 0 14px;
}

.visitor-approval-dato {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 13.5px;
    color: var(--synbex-text-muted);
}

.visitor-approval-dato svg {
    width: 17px;
    height: 17px;
    flex-shrink: 0;
}

/* El check para guardarla en la lista propia. */
.visitor-approval-frecuente {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    margin-top: 12px;
    font-size: 14px;
    color: var(--synbex-text);
    cursor: pointer;
}

.visitor-approval-frecuente input {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}

.visitor-approval-frecuente-tipo {
    margin-top: 12px;
    padding: 14px;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-bg);
    text-align: left;
}

/* ==========================================================================
   Portería: panel de autorizaciones del día
   ========================================================================== */

/* Panel aparte del de registro, justo debajo y con su propio aire arriba. */
.autorizaciones-panel {
    margin-top: 10px;
    margin-bottom: 18px;
    padding: 0;
    overflow: hidden;
}

.autorizaciones-reintentar {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    white-space: nowrap;
}

.autorizaciones-reintentar svg {
    width: 15px;
    height: 15px;
    flex-shrink: 0;
}

@media (max-width: 700px) {
    /* En celular el estado y el botón bajan a su propio renglón. */
    .autorizaciones-fila {
        flex-wrap: wrap;
    }

    .autorizaciones-reintentar {
        flex: 1 1 100%;
        justify-content: center;
    }
}

.autorizaciones-cabecera {
    display: flex;
    align-items: center;
    gap: 14px;
    width: 100%;
    padding: 16px 18px;
    border: none;
    background: none;
    color: var(--synbex-text);
    text-align: left;
    cursor: pointer;
}

.autorizaciones-icono {
    width: 40px;
    height: 40px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 12px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.autorizaciones-icono svg {
    width: 20px;
    height: 20px;
}

.autorizaciones-titulo {
    flex: 1 1 auto;
    min-width: 0;
    font-weight: 700;
    font-size: 15.5px;
}

.autorizaciones-detalle {
    display: block;
    margin-top: 2px;
    font-size: 12.5px;
    font-weight: 500;
    color: var(--synbex-text-muted);
}

.autorizaciones-flecha {
    flex-shrink: 0;
    display: flex;
    color: var(--synbex-text-muted);
    transition: transform 0.18s ease;
}

.autorizaciones-flecha svg {
    width: 20px;
    height: 20px;
}

.autorizaciones-flecha.is-abierto {
    transform: rotate(180deg);
}

.autorizaciones-lista {
    border-top: 1px solid var(--synbex-border);
}

.autorizaciones-fila {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 13px 18px;
    border-bottom: 1px solid var(--synbex-border);
}

.autorizaciones-fila:last-child {
    border-bottom: none;
}

.autorizaciones-fila-datos {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.autorizaciones-nombre {
    font-weight: 600;
    color: var(--synbex-text);
}

.autorizaciones-sub {
    font-size: 12.5px;
    color: var(--synbex-text-muted);
}

/* "3 solicitudes": cuántas veces se le avisó al apartamento por esta visita
   (2026-10-02). Discreto, al lado de la hora del último aviso. */
.autorizaciones-veces {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 8px;
    border-radius: 999px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    font-size: 11.5px;
    font-weight: 700;
    white-space: nowrap;
}

/* --------------------------------------------------------------------------
   Portería Â· panel de autorizaciones: dos listas, avatar y acciones
   -------------------------------------------------------------------------- */

/* Separador de las dos listas ("Pendientes de ingresar" / "Ya ingresaron").
   Pegajoso no: la lista puede crecer, pero el panel entero se pliega. */
.autorizaciones-grupo {
    padding: 10px 18px 8px;
    background: var(--synbex-bg-soft, rgba(148, 163, 184, 0.09));
    border-bottom: 1px solid var(--synbex-border);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--synbex-text-muted);
}

/* Avatar de iniciales, igual que el de las tarjetas de Conjuntos/Edificios: es
   texto y color, sin ninguna imagen que cargar. El color sale del nombre, así
   que la misma persona siempre aparece del mismo color sin guardar nada. */
.autorizaciones-avatar {
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    font-weight: 700;
    font-size: 14px;
    letter-spacing: 0.02em;
}

.autorizaciones-avatar.is-orange {
    background: rgba(217, 119, 6, 0.12);
    color: #d97706;
}

.autorizaciones-avatar.is-teal {
    background: rgba(13, 148, 136, 0.12);
    color: var(--synbex-phone-link);
}

.autorizaciones-avatar.is-green {
    background: rgba(22, 163, 74, 0.12);
    color: var(--synbex-success);
}

.autorizaciones-avatar.is-red {
    background: var(--synbex-danger-soft);
    color: var(--synbex-danger);
}

.autorizaciones-avatar.is-slate {
    background: rgba(107, 114, 128, 0.14);
    color: var(--synbex-text-muted);
}

/* "Volver a solicitar acceso" en el mismo ámbar que "Limpiar" del formulario de
   arriba — pedido explícito, para que el par de acciones del guarda se lea igual
   en las dos partes de la pantalla. */
.autorizaciones-panel .autorizaciones-reintentar {
    background: #d97706;
    border-color: #d97706;
    color: #fff;
    font-weight: 700;
}

.autorizaciones-panel .autorizaciones-reintentar:hover:not(:disabled) {
    background: #b45309;
    border-color: #b45309;
}

/* "Realizar ingreso" en el mismo verde del botón Ingreso del formulario: es la
   misma acción, y termina el trámite en vez de repetirlo. */
.autorizaciones-ingresar {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    white-space: nowrap;
    background: var(--synbex-success);
    border-color: var(--synbex-success);
    color: #fff;
    font-weight: 700;
}

.autorizaciones-ingresar:hover:not(:disabled) {
    background: #15803d;
    border-color: #15803d;
}

.autorizaciones-ingresar svg {
    width: 15px;
    height: 15px;
    flex-shrink: 0;
}

/* Lo que ya entró queda a media luz: sigue consultable, pero no compite con lo
   que todavía está pendiente. */
.autorizaciones-fila.is-ingresada .autorizaciones-nombre,
.autorizaciones-fila.is-ingresada .autorizaciones-sub {
    color: var(--synbex-text-muted);
}

@media (max-width: 700px) {
    .autorizaciones-ingresar {
        flex: 1 1 100%;
        justify-content: center;
    }
}

/* --------------------------------------------------------------------------
   Portería Â· dejar entrar sin autorización, y la columna Autorización
   -------------------------------------------------------------------------- */

/* Los dos botones de una solicitud sin respuesta ("volver a solicitar" y
   "dejar entrar") comparten renglón y se reparten el ancho en celular. */
.autorizaciones-acciones {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

/* Gris pizarra a propósito: NO es el verde de "Ingreso". Dejar entrar sin que
   el apartamento conteste es una excepción que el guarda asume, y el botón no
   debería invitar a usarse como si fuera el camino normal. */
.autorizaciones-dejar-entrar {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    white-space: nowrap;
    background: var(--synbex-text-muted);
    border-color: var(--synbex-text-muted);
    color: #fff;
    font-weight: 700;
}

.autorizaciones-dejar-entrar:hover:not(:disabled) {
    background: #475569;
    border-color: #475569;
}

.autorizaciones-dejar-entrar svg {
    width: 15px;
    height: 15px;
    flex-shrink: 0;
}

@media (max-width: 700px) {
    .autorizaciones-acciones {
        flex: 1 1 100%;
    }

    .autorizaciones-acciones .btn-synbex {
        flex: 1 1 0;
        justify-content: center;
    }
}

/* Ventana de confirmación de "dejar entrar igualmente". */
.visitor-confirm-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: rgba(15, 23, 42, 0.55);
}

.visitor-confirm-card {
    width: 100%;
    max-width: 430px;
    padding: 26px 24px 22px;
    border-radius: var(--synbex-radius-lg, 16px);
    background: var(--synbex-surface, #fff);
    box-shadow: 0 20px 50px rgba(15, 23, 42, 0.25);
    text-align: center;
}

.visitor-confirm-icono {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 52px;
    height: 52px;
    margin-bottom: 14px;
    border-radius: 50%;
    background: rgba(217, 119, 6, 0.12);
    color: #d97706;
}

.visitor-confirm-icono svg {
    width: 26px;
    height: 26px;
}

.visitor-confirm-titulo {
    margin: 0 0 10px;
    font-size: 17px;
    font-weight: 700;
    color: var(--synbex-text);
}

.visitor-confirm-texto {
    margin: 0 0 12px;
    font-size: 13.5px;
    line-height: 1.5;
    color: var(--synbex-text-muted);
}

.visitor-confirm-acciones {
    display: flex;
    gap: 10px;
    margin-top: 18px;
}

.visitor-confirm-acciones .btn-synbex {
    flex: 1 1 0;
    justify-content: center;
}

/* Columna "Autorización" de Consultas: un color por caso, para poder barrer la
   tabla con la vista y ver de quién salió cada permiso. */
.consultas-autorizacion {
    display: inline-block;
    padding: 2px 9px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
}

.consultas-autorizacion.is-autorizado {
    background: rgba(22, 163, 74, 0.12);
    color: var(--synbex-success);
}

.consultas-autorizacion.is-porteria {
    background: rgba(217, 119, 6, 0.14);
    color: #b45309;
}

.consultas-autorizacion.is-rechazado {
    background: var(--synbex-danger-soft);
    color: var(--synbex-danger);
}

.consultas-autorizacion.is-neutro {
    background: none;
    color: var(--synbex-text-muted);
}

/* Una lectura no es un ingreso: se muestra a media luz para que no se confunda
   al barrer la tabla, pero con todos sus datos disponibles. */
.consultas-row-lectura td {
    opacity: 0.72;
    font-style: italic;
}

/* --------------------------------------------------------------------------
   Portería Â· "Ya ingresaron" plegable y reintentar como ícono
   -------------------------------------------------------------------------- */

/* La cabecera "Ya ingresaron" es un botón que ocupa el renglón entero — texto,
   contador y flecha — para poder tocarla en cualquier parte y no tener que
   apuntarle a la flechita. Hereda el aspecto de .autorizaciones-grupo. */
.autorizaciones-grupo-btn {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    border: none;
    border-bottom: 1px solid var(--synbex-border);
    text-align: left;
    cursor: pointer;
    font-family: inherit;
}

.autorizaciones-grupo-btn:hover {
    background: rgba(148, 163, 184, 0.16);
}

/* Cuántos hay, para saberlo sin desplegar. */
.autorizaciones-grupo-cuenta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: 999px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: 0;
}

.autorizaciones-grupo-flecha {
    display: flex;
    margin-left: auto;
    color: var(--synbex-text-muted);
    transition: transform 0.18s ease;
}

.autorizaciones-grupo-flecha svg {
    width: 18px;
    height: 18px;
}

.autorizaciones-grupo-flecha.is-abierto {
    transform: rotate(180deg);
}

/* Reintentar quedó solo con el ícono: el renglón ya lleva avatar, tres líneas
   de datos y el estado. Cuadrado, para que se lea como botón de acción y no
   como una pastilla de texto a medio cortar. */
.autorizaciones-panel .autorizaciones-reintentar {
    width: 34px;
    height: 34px;
    padding: 0;
    justify-content: center;
    gap: 0;
}

.autorizaciones-panel .autorizaciones-reintentar svg {
    width: 16px;
    height: 16px;
}

@media (max-width: 700px) {
    /* En celular el ícono no se estira: el que crece es "Dejar entrar". */
    .autorizaciones-acciones .autorizaciones-reintentar {
        flex: 0 0 auto;
    }
}

/* --------------------------------------------------------------------------
   Portería Â· cabecera del panel de autorizaciones, solo el título centrado
   -------------------------------------------------------------------------- */

/* El título va centrado en el renglón completo, con la flecha pegada a la
   derecha SIN empujarlo: por eso la flecha se posiciona encima en vez de ocupar
   lugar en el flujo — si fuera un hermano más del flex, el título quedaría
   centrado respecto al espacio sobrante, no respecto al panel. */
.autorizaciones-panel .autorizaciones-cabecera {
    position: relative;
    justify-content: center;
    gap: 0;
    padding: 16px 46px;
}

.autorizaciones-panel .autorizaciones-titulo {
    flex: 0 1 auto;
    text-align: center;
}

.autorizaciones-panel .autorizaciones-flecha {
    position: absolute;
    top: 50%;
    right: 18px;
    transform: translateY(-50%);
}

.autorizaciones-panel .autorizaciones-flecha.is-abierto {
    transform: translateY(-50%) rotate(180deg);
}

/* "Pendientes" es un div y "Ya ingresaron" un button, pero los dos son el mismo
   renglón: texto a la izquierda, contador a la derecha. */
.autorizaciones-grupo {
    display: flex;
    align-items: center;
    gap: 10px;
}

.autorizaciones-grupo .autorizaciones-grupo-cuenta {
    margin-left: auto;
}

/* En "Ya ingresaron" el contador va pegado al texto y la flecha cierra el
   renglón, así que ahí NO se empuja hasta el borde. */
.autorizaciones-grupo-btn .autorizaciones-grupo-cuenta {
    margin-left: 0;
}

/* --------------------------------------------------------------------------
   Portería Â· "Dejar entrar igualmente" en azul
   -------------------------------------------------------------------------- */

/* Iba en gris pizarra y se leía como si estuviera desactivado. Azul sólido: se
   ve activo, y sigue sin confundirse con Ingreso (verde), Salida (rojo) ni
   Limpiar/reintentar (ámbar) — que era el motivo de no usar ninguno de esos. */
.autorizaciones-panel .autorizaciones-dejar-entrar,
.visitor-confirm-acciones .autorizaciones-dejar-entrar {
    background: #2563eb;
    border-color: #2563eb;
    color: #fff;
}

.autorizaciones-panel .autorizaciones-dejar-entrar:hover:not(:disabled),
.visitor-confirm-acciones .autorizaciones-dejar-entrar:hover:not(:disabled) {
    background: #1d4ed8;
    border-color: #1d4ed8;
}

/* --------------------------------------------------------------------------
   Inicio del residente Â· visitas de hoy en la portería
   -------------------------------------------------------------------------- */

/* Mismo molde que .assembly-live-banner (blanco con acento a la izquierda) para
   que se reconozca como el mismo tipo de aviso, pero acá el acento es el morado
   de la marca y adentro va una lista en vez de un solo renglón. */
.visitas-hoy-banner {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    margin-bottom: 18px;
    padding: 16px 18px;
    border: 1px solid var(--synbex-border);
    border-left: 4px solid var(--synbex-primary);
    border-radius: var(--synbex-radius-lg, 16px);
    background: var(--synbex-surface, #fff);
    box-shadow: 0 2px 10px rgba(15, 23, 42, 0.06);
}

.visitas-hoy-banner-icon {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 12px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.visitas-hoy-banner-icon svg {
    width: 20px;
    height: 20px;
}

.visitas-hoy-banner-body {
    flex: 1 1 auto;
    min-width: 0;
}

.visitas-hoy-banner-kicker {
    display: block;
    margin-bottom: 8px;
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--synbex-primary);
}

.visitas-hoy-fila {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 9px 0;
    border-top: 1px solid var(--synbex-border);
}

/* La primera no lleva línea: el separador es entre visitas, no debajo del
   título. */
.visitas-hoy-fila:first-of-type {
    border-top: none;
    padding-top: 0;
}

.visitas-hoy-datos {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.visitas-hoy-nombre {
    font-weight: 700;
    font-size: 14.5px;
    color: var(--synbex-text);
}

.visitas-hoy-sub {
    font-size: 12.5px;
    color: var(--synbex-text-muted);
}

.visitas-hoy-btn {
    flex-shrink: 0;
    white-space: nowrap;
}

@media (max-width: 560px) {
    /* En celular el botón baja a su propio renglón y ocupa el ancho: es la
       acción, no un adorno al costado. */
    .visitas-hoy-fila {
        flex-wrap: wrap;
    }

    .visitas-hoy-btn {
        flex: 1 1 100%;
    }
}

/* --------------------------------------------------------------------------
   Portería Â· aviso grande al centro (ingreso / salida / autorización)
   -------------------------------------------------------------------------- */

/* El fondo se desenfoca donde el navegador puede; donde no —equipos y navegadores
   viejos— queda el velo oscuro solo, que ya cumple. Nunca depende del blur para
   que el mensaje se lea. */
.visitor-flash-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1400;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: rgba(15, 23, 42, 0.42);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
    /* Deja pasar los clics: el aviso no espera respuesta y se va solo. */
    pointer-events: none;
    animation: visitor-flash-fondo 0.16s ease-out;
}

/* Aparece chico y crece hasta su tamaño. Es UNA transformación de escala más la
   opacidad — las dos cosas que cualquier equipo viejo mueve en la tarjeta gráfica
   sin recalcular el diseño de la página. A propósito no se animan sombras,
   tamaños ni márgenes, que es lo que pone lento un equipo lento. */
.visitor-flash-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    min-width: 240px;
    padding: 34px 42px;
    border-radius: 20px;
    background: var(--synbex-surface, #fff);
    box-shadow: 0 24px 60px rgba(15, 23, 42, 0.28);
    text-align: center;
    animation: visitor-flash-entrada 0.22s cubic-bezier(0.2, 0.9, 0.3, 1.2);
    will-change: transform, opacity;
}

.visitor-flash-icono {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 62px;
    height: 62px;
    border-radius: 50%;
}

.visitor-flash-icono svg {
    width: 30px;
    height: 30px;
}

.visitor-flash-texto {
    font-size: 20px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--synbex-text);
}

/* Verde: se hizo. */
.visitor-flash-card.is-ok .visitor-flash-icono {
    background: rgba(22, 163, 74, 0.13);
    color: var(--synbex-success);
}

/* Ãmbar: no se hizo todavía, falta que el apartamento conteste. */
.visitor-flash-card.is-espera .visitor-flash-icono {
    background: rgba(217, 119, 6, 0.15);
    color: #b45309;
}

.visitor-flash-card.is-espera .visitor-flash-texto {
    color: #b45309;
}

@keyframes visitor-flash-fondo {
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes visitor-flash-entrada {
    from {
        opacity: 0;
        transform: scale(0.82);
    }
    to {
        opacity: 1;
        transform: scale(1);
    }
}

/* Quien pidió menos animación en su sistema no la ve — el aviso igual sale. */
@media (prefers-reduced-motion: reduce) {
    .visitor-flash-backdrop,
    .visitor-flash-card {
        animation: none;
    }
}

/* --------------------------------------------------------------------------
   Portería Â· ajustes del aviso central
   -------------------------------------------------------------------------- */

/* Más desenfoque, pedido expreso. El velo baja un poco a la vez: con el fondo
   más borroso ya no hace falta oscurecerlo tanto para que el mensaje resalte. */
.visitor-flash-backdrop {
    background: rgba(15, 23, 42, 0.34);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
}

/* La sombra grande y difusa se leía como un halo sucio alrededor de la tarjeta,
   sobre todo con el fondo borroso detrás. Se cambia por uno corto y pegado —
   levanta la tarjeta del fondo sin dibujarle un aura. */
.visitor-flash-card {
    box-shadow: 0 6px 20px rgba(15, 23, 42, 0.16);
}

/* Salida en rojo: mismo criterio que el botón Salida del formulario, para que
   ingreso y salida se distingan de lejos sin leer el texto. */
.visitor-flash-card.is-salida .visitor-flash-icono {
    background: rgba(185, 28, 28, 0.13);
    color: #b91c1c;
}

.visitor-flash-card.is-salida .visitor-flash-texto {
    color: #b91c1c;
}

/* --------------------------------------------------------------------------
   Portería Â· foto del visitante
   -------------------------------------------------------------------------- */

/* Cámara en vivo flotante. Se arrastra por toda la pantalla y al soltarla se pega
   al borde más cercano (ver visitor-camera.js). Position fixed y no absolute: el
   guarda la deja donde le sirve y ahí se queda, aunque desplace la página. */
.visitor-live-camera {
    position: fixed;
    z-index: 1100;
    width: 220px;
    border-radius: 14px;
    overflow: hidden;
    background: #0f172a;
    box-shadow: 0 8px 24px rgba(15, 23, 42, 0.3);
    cursor: grab;
    /* La transición hace visible el "imán" al borde; se apaga mientras se arrastra
       para que siga al puntero sin retraso. */
    transition: left 0.18s ease, top 0.18s ease;
    user-select: none;
}

.visitor-live-camera.is-arrastrando {
    cursor: grabbing;
    transition: none;
}

.visitor-live-camera-video {
    display: block;
    width: 100%;
    height: auto;
    /* La imagen sale espejada como en un espejo: es lo que uno espera al verse a sí
       mismo, y solo afecta la vista previa — la foto guardada va sin espejar. */
    transform: scaleX(-1);
}

.visitor-live-camera-etiqueta {
    display: block;
    padding: 5px 10px;
    background: rgba(15, 23, 42, 0.9);
    color: #e2e8f0;
    font-size: 11.5px;
    font-weight: 600;
    text-align: center;
}

/* Ventana de foto manual. */
.visitor-foto-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1300;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: rgba(15, 23, 42, 0.6);
}

.visitor-foto-card {
    width: 100%;
    max-width: 460px;
    padding: 20px;
    border-radius: 18px;
    background: var(--synbex-surface, #fff);
    box-shadow: 0 10px 30px rgba(15, 23, 42, 0.22);
    text-align: center;
}

.visitor-foto-titulo {
    margin: 0 0 14px;
    font-size: 17px;
    font-weight: 700;
    color: var(--synbex-text);
}

/* "El apartamento la va a ver antes de autorizar": la foto al pedir permiso. */
.visitor-foto-nota {
    margin: -8px 0 14px;
    font-size: 13px;
    color: var(--synbex-text-muted);
}

.visitor-foto-video,
.visitor-foto-preview {
    display: block;
    width: 100%;
    border-radius: 12px;
    background: #0f172a;
}

.visitor-foto-video {
    transform: scaleX(-1);
}

.visitor-foto-acciones {
    display: flex;
    gap: 10px;
    margin-top: 16px;
}

.visitor-foto-acciones .btn-synbex {
    flex: 1 1 0;
    justify-content: center;
}

/* La foto en el detalle de Consultas. */
.consultas-foto-fila {
    display: flex;
    justify-content: center;
    margin-bottom: 14px;
}

.consultas-foto {
    width: 160px;
    height: 160px;
    object-fit: cover;
    border-radius: 12px;
    border: 1px solid var(--synbex-border);
    cursor: zoom-in;
}

.consultas-foto-grande-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1500;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background: rgba(15, 23, 42, 0.82);
    cursor: zoom-out;
}

.consultas-foto-grande {
    max-width: 100%;
    max-height: 100%;
    border-radius: 12px;
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
}

/* Bloque de foto en "Configurar entrada". */
.entry-point-photo-block {
    padding-bottom: 16px;
    margin-bottom: 16px;
    border-bottom: 1px solid var(--synbex-border);
}

.entry-point-photo-title {
    margin: 0 0 10px;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--synbex-text-muted);
}

@media (max-width: 900px) {
    /* La cámara flotante es de computadora. En celular el guarda toma la foto
       desde la ventana, que ya ocupa la pantalla entera. */
    .visitor-live-camera {
        display: none;
    }
}

/* Selector de cámara dentro del panel flotante. Ocupa el mismo renglón donde iba
   la etiqueta, para no hacer el panel más alto. */
.visitor-live-camera-select {
    display: block;
    width: 100%;
    padding: 6px 8px;
    border: none;
    border-radius: 0;
    background: rgba(15, 23, 42, 0.92);
    color: #e2e8f0;
    font-size: 11.5px;
    font-weight: 600;
    /* Cursor normal y no la manito de arrastrar: acá sí se hace clic. */
    cursor: pointer;
}

.visitor-live-camera-select:focus {
    outline: 2px solid var(--synbex-primary);
    outline-offset: -2px;
}

/* Botón grande de "Tomar foto" en celular — mismo molde que el de limpieza: el
   input va escondido dentro del label, así que tocar el recuadro entero abre la
   cámara del teléfono. */
.visitor-foto-tomar {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 30px 20px;
    border: 2px dashed var(--synbex-border);
    border-radius: 14px;
    background: var(--synbex-bg-soft, rgba(148, 163, 184, 0.08));
    color: var(--synbex-primary);
    cursor: pointer;
}

.visitor-foto-tomar input {
    display: none;
}

.visitor-foto-tomar.is-disabled {
    opacity: 0.6;
    pointer-events: none;
}

.visitor-foto-tomar-icono svg {
    width: 34px;
    height: 34px;
}

.visitor-foto-tomar-texto {
    font-size: 15px;
    font-weight: 700;
    color: var(--synbex-text);
}

/* El check de aceptar, para que se lea como "esta foto sirve". */
.visitor-foto-aceptar {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

.visitor-foto-aceptar svg {
    width: 17px;
    height: 17px;
}

/* La foto del detalle, más chica: va arriba de todo y no debe empujar los datos
   fuera de la pantalla — es una referencia, no el contenido principal. Un clic la
   abre en grande, que es donde se mira de verdad. */
.consultas-foto {
    width: 110px;
    height: 110px;
}

.consultas-foto-fila {
    margin-bottom: 16px;
}

/* --------------------------------------------------------------------------
   Tipo de visitante (pastilla de color)
   -------------------------------------------------------------------------- */

/* Global y no con alcance a una página: lo usan Consultas y la lista de
   "visitantes dentro del conjunto", y la misma cosa tiene que verse igual en las
   dos. En la tabla va SOLO el tipo; el motivo está en el detalle.

   Cada tipo tiene SU color y ninguno se repite: el color es lo que permite barrer
   la tabla sin leer, y dos tipos del mismo color anulan justamente eso. */
/* Todas las pastillas del mismo ancho y el texto al centro: como bloque ocupan
   el ancho entero de su celda, y el ancho de la columna ya lo fija el texto más
   largo. Así no hay que adivinar un ancho fijo —cambiaría cada vez que cambie una
   etiqueta— y ninguna queda más corta que la de al lado. */
.consultas-tipo {
    display: block;
    width: 100%;
    box-sizing: border-box;
    text-align: center;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
}

/* Rojo: es el único que hay que ver desde el otro lado de la portería. */
.consultas-tipo.is-no-deseado {
    background: var(--synbex-danger-soft);
    color: var(--synbex-danger);
}

/* Ãmbar: la cita se avisó de antemano. Queda lejos del rojo del no deseado y no
   se confunde con la calma del verde del frecuente. */
.consultas-tipo.is-cita {
    background: rgba(217, 119, 6, 0.16);
    color: #b45309;
}

/* Verde: alguien de la casa ya dijo que esta persona puede entrar. */
.consultas-tipo.is-frecuente {
    background: rgba(22, 163, 74, 0.12);
    color: var(--synbex-success);
}

/* Celeste claro con la letra azul: no es una persona, es un carro. Suave a
   propósito — el que tiene que saltar a la vista es el rojo del no deseado. */
.consultas-tipo.is-vehiculo {
    background: rgba(56, 189, 248, 0.20);
    color: #1d4ed8;
}

/* Morado: el apartamento dijo que no y la persona no entró. Antes era ámbar,
   que ahora es el de la cita. */
.consultas-tipo.is-rechazado {
    background: rgba(124, 58, 237, 0.14);
    color: #6d28d9;
}

/* Gris: lo normal. A propósito el más apagado de todos — si todo lleva color
   fuerte, el color deja de avisar nada. */
.consultas-tipo.is-neutro {
    background: rgba(148, 163, 184, 0.16);
    color: var(--synbex-text-muted);
}

/* --------------------------------------------------------------------------
   Portería Â· botón de la cámara plegada
   -------------------------------------------------------------------------- */

/* Pegado al borde derecho, a un cuarto de la altura. NO se mueve nunca, aunque
   el guarda arrastre el panel al otro lado: es el punto fijo desde el que se
   vuelve a abrir, y moverlo obligaría a buscarlo cada vez.

   Redondeado solo por la izquierda para que se lea como una pestaña que sale
   del borde de la pantalla. */
.visitor-live-camera-tab {
    position: fixed;
    right: 0;
    z-index: 1100;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    padding: 10px 8px;
    border: none;
    border-radius: 12px 0 0 12px;
    background: var(--synbex-primary);
    color: #fff;
    font-size: 10.5px;
    font-weight: 700;
    cursor: pointer;
    box-shadow: 0 4px 14px rgba(15, 23, 42, 0.22);
}

.visitor-live-camera-tab:hover {
    filter: brightness(1.08);
}

.visitor-live-camera-tab svg {
    width: 18px;
    height: 18px;
}

/* El panel se pliega tocándolo, así que conviene que se note que es tocable. */
.visitor-live-camera {
    cursor: pointer;
}

.visitor-live-camera.is-arrastrando {
    cursor: grabbing;
}

@media (max-width: 900px) {
    /* La cámara flotante es de computadora, y su botón también. */
    .visitor-live-camera-tab {
        display: none;
    }
}

/* --------------------------------------------------------------------------
   Portería Â· cámara en vivo: aviso de "sigue grabando" y cambiar tamaño
   -------------------------------------------------------------------------- */

/* Punto rojo parpadeando en el botón (pedido explícito: rojo, no verde — se lee
   como "grabando", que es justo lo que está pasando). Plegar la cámara NO la apaga —sigue
   encendida para que la foto automática salga en el momento exacto—, así que
   hace falta decirlo: sin esto, un guarda podría creer que al plegarla dejó de
   grabar. Solo se ve cuando de verdad está dando imagen. */
.visitor-live-camera-punto {
    display: none;
    position: absolute;
    top: 5px;
    right: 5px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: #ef4444;
    box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.35);
}


.visitor-live-camera-tab.is-en-vivo .visitor-live-camera-punto {
    display: block;
    animation: camara-en-vivo 1.4s ease-in-out infinite;
}

/* Solo opacidad: es lo más barato que puede animar un equipo viejo, y esto
   parpadea todo el turno. */
@keyframes camara-en-vivo {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.25; }
}
/* Esquinas para cambiar el tamaño, en las cuatro. Chicas a propósito: la anterior
   ocupaba media esquina del video y tapaba justo lo que hay que mirar. */
.visitor-live-camera-resize {
    position: absolute;
    width: 11px;
    height: 11px;
    z-index: 2;
    /* Dos rayitas en diagonal con un degradado: sin imagen ni ícono que cargar. */
    background: repeating-linear-gradient(
        45deg,
        transparent 0 2px,
        rgba(255, 255, 255, 0.5) 2px 3px
    );
}

.visitor-live-camera-resize.is-arriba-izq {
    top: 0;
    left: 0;
    cursor: nwse-resize;
}

.visitor-live-camera-resize.is-arriba-der {
    top: 0;
    right: 0;
    cursor: nesw-resize;
}

.visitor-live-camera-resize.is-abajo-izq {
    bottom: 0;
    left: 0;
    cursor: nesw-resize;
}

.visitor-live-camera-resize.is-abajo-der {
    bottom: 0;
    right: 0;
    cursor: nwse-resize;
}

/* Invisible hasta que JS la coloca. Con visibility y no display: así conserva su
   tamaño y JS puede medirla para calcular dónde ponerla. */
.visitor-live-camera.is-colocando {
    visibility: hidden;
}

/* Mientras se cambia el tamaño no hay transición: tiene que seguir al puntero
   sin retraso. */
.visitor-live-camera.is-redimensionando {
    transition: none;
    cursor: nesw-resize;
}

@media (prefers-reduced-motion: reduce) {
    .visitor-live-camera-tab.is-en-vivo .visitor-live-camera-punto {
        animation: none;
    }
}

/* --------------------------------------------------------------------------
   Portería Â· la cámara, limpia hasta que se le pasa el mouse
   -------------------------------------------------------------------------- */

/* Sin el mouse encima solo se ve la imagen. El nombre de la puerta, el selector
   de cámaras y las esquinas para cambiar el tamaño aparecen al pasar por encima.

   Van SUPERPUESTOS sobre el video y no debajo: si estuvieran en el flujo, el
   panel cambiaría de alto al aparecer y desaparecer, y daría un salto cada vez
   que el mouse pasa cerca. */
.visitor-live-camera-etiqueta,
.visitor-live-camera-select {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    opacity: 0;
    transition: opacity 0.14s ease;
    pointer-events: none;
}

.visitor-live-camera-resize {
    opacity: 0;
    transition: opacity 0.14s ease;
}

.visitor-live-camera:hover .visitor-live-camera-etiqueta,
.visitor-live-camera:hover .visitor-live-camera-select,
.visitor-live-camera:hover .visitor-live-camera-resize,
.visitor-live-camera.is-arrastrando .visitor-live-camera-etiqueta,
.visitor-live-camera.is-arrastrando .visitor-live-camera-resize,
.visitor-live-camera.is-redimensionando .visitor-live-camera-resize {
    opacity: 1;
}

/* El selector sí se puede tocar cuando está a la vista. */
.visitor-live-camera:hover .visitor-live-camera-select {
    pointer-events: auto;
}

/* Con la etiqueta superpuesta, el panel mide lo que mide el video. */
.visitor-live-camera-video {
    display: block;
}

/* --------------------------------------------------------------------------
   Configuración: lista de secciones a la izquierda, panel a la derecha
   -------------------------------------------------------------------------- */

.config-shell {
    display: grid;
    grid-template-columns: 240px 1fr;
    gap: 0;
    padding: 0;
    overflow: hidden;
}

.config-secciones {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 14px 10px;
    border-right: 1px solid var(--synbex-border);
    background: var(--synbex-bg-soft, rgba(148, 163, 184, 0.07));
}

.config-seccion {
    display: flex;
    align-items: center;
    gap: 11px;
    width: 100%;
    padding: 11px 12px;
    border: none;
    border-radius: 10px;
    background: none;
    color: var(--synbex-text);
    text-align: left;
    cursor: pointer;
    font-family: inherit;
}

.config-seccion:hover {
    background: rgba(148, 163, 184, 0.16);
}

.config-seccion.is-activa {
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.config-seccion-icono {
    display: flex;
    flex-shrink: 0;
    color: var(--synbex-text-muted);
}

.config-seccion.is-activa .config-seccion-icono {
    color: var(--synbex-primary);
}

.config-seccion-icono svg {
    width: 18px;
    height: 18px;
}

.config-seccion-texto {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.config-seccion-titulo {
    font-size: 14px;
    font-weight: 600;
}

.config-seccion-desc {
    font-size: 11.5px;
    color: var(--synbex-text-muted);
}

.config-seccion.is-activa .config-seccion-desc {
    color: var(--synbex-primary);
    opacity: 0.75;
}

.config-contenido {
    min-width: 0;
    padding: 20px 24px;
}

/* Panel de una sección */
.config-panel-header {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 18px;
}

.config-panel-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    flex-shrink: 0;
    border-radius: 12px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.config-panel-icon svg {
    width: 21px;
    height: 21px;
}

.config-panel-title {
    margin: 0;
    font-size: 16px;
    font-weight: 700;
}

.config-panel-sub {
    margin: 2px 0 0;
    font-size: 13px;
    color: var(--synbex-text-muted);
}

.config-fila {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding: 12px 0;
    border-bottom: 1px solid var(--synbex-border);
}

.config-fila-texto {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.config-fila-desc {
    font-size: 12.5px;
    color: var(--synbex-text-muted);
}

.config-nota {
    margin: 12px 0 0;
    font-size: 13px;
    line-height: 1.5;
    color: var(--synbex-text-muted);
}

/* Vista previa de la cámara elegida, para confirmar que es la correcta. */
.config-camara-vista {
    display: block;
    width: 100%;
    max-width: 320px;
    margin-top: 14px;
    border-radius: 12px;
    background: #0f172a;
    transform: scaleX(-1);
}

@media (max-width: 820px) {
    /* En un teléfono, una columna de títulos al lado dejaría al panel sin
       espacio: las secciones pasan a ser una fila de pestañas arriba. */
    .config-shell {
        grid-template-columns: 1fr;
    }

    .config-secciones {
        flex-direction: row;
        overflow-x: auto;
        border-right: none;
        border-bottom: 1px solid var(--synbex-border);
    }

    .config-seccion {
        width: auto;
        flex-shrink: 0;
    }

    /* La descripción se va: en horizontal alarga tanto cada pestaña que no
       entran ni dos en pantalla. */
    .config-seccion-desc {
        display: none;
    }

    .config-contenido {
        padding: 16px;
    }
}

/* --------------------------------------------------------------------------
   Configuración Â· tema en cuadros, cámara centrada, barra de guardar
   -------------------------------------------------------------------------- */

.tema-opciones {
    display: flex;
    gap: 14px;
    flex-wrap: wrap;
}

/* 100Ã100, con una muestra del color adentro: acá se elige un aspecto, y verlo
   se entiende sin leer nada. */
.tema-opcion {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100px;
    height: 100px;
    padding: 10px;
    border: 2px solid var(--synbex-border);
    border-radius: 14px;
    background: var(--synbex-surface, #fff);
    color: var(--synbex-text);
    cursor: pointer;
    font-family: inherit;
    transition: border-color 0.14s ease;
}

.tema-opcion:hover {
    border-color: var(--synbex-primary);
}

.tema-opcion.is-elegido {
    border-color: var(--synbex-primary);
    box-shadow: 0 0 0 3px var(--synbex-primary-soft);
}

/* La muestra es una MINIATURA de la pantalla, no un cuadro de un solo color: la
   franja de la izquierda es el menú y el resto el contenido, con los mismos dos
   tonos que usa el tema de verdad. Un cuadro liso del gris oscuro (#1c1c1f) se
   leía como negro y no decía nada; así se ve que es gris y cómo queda repartido. */
.tema-muestra {
    display: flex;
    width: 46px;
    height: 32px;
    border-radius: 7px;
    border: 1px solid var(--synbex-border);
    overflow: hidden;
}

.tema-muestra::before {
    content: "";
    width: 14px;
    flex-shrink: 0;
}

.tema-muestra::after {
    content: "";
    flex: 1;
}

/* Claro: menú #eef0f3 sobre contenido #e3e5eb. */
.tema-muestra.is-claro::before { background: #eef0f3; }
.tema-muestra.is-claro::after { background: #e3e5eb; }

/* Oscuro: el gris del menú sobre el gris más oscuro del contenido. */
.tema-muestra.is-oscuro::before { background: #2b2b31; }
.tema-muestra.is-oscuro::after { background: #1c1c1f; }

/* Automático: la mitad de cada uno, que es lo que hace — seguir al sistema. */
.tema-muestra.is-auto::before { background: #eef0f3; }
.tema-muestra.is-auto::after { background: #1c1c1f; }

.tema-nombre {
    font-size: 12.5px;
    font-weight: 600;
}

/* Botones de acción de un panel de configuración. */
.config-acciones {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 14px;
}

/* La vista previa de la cámara, centrada en el panel. */
.config-camara-vista {
    margin-left: auto;
    margin-right: auto;
}

/* La cámara y sus dos botones son un solo bloque centrado: los botones quedan
   debajo del recuadro, uno a cada lado y juntos del mismo ancho que la imagen
   — pedido explícito. */
.config-camara-bloque {
    width: 100%;
    max-width: 320px;
    margin: 0 auto;
}

.config-camara-botones {
    display: flex;
    gap: 10px;
    margin-top: 10px;
}

    /* flex:1 reparte el ancho de la cámara entre los botones que haya (dos
       normalmente, uno solo cuando todavía no hay permiso). */
    .config-camara-botones .btn-synbex {
        flex: 1;
        justify-content: center;
        padding: 7px 10px;
        font-size: 0.8rem;
    }

/* Barra superior del panel de cuenta: título a la izquierda, Guardar a la
   derecha. Antes el botón vivía al final y en un panel largo quedaba fuera de la
   vista — había que bajar hasta el fondo para guardar algo cambiado arriba. */
.account-barra {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    margin-bottom: 16px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--synbex-border);
}

.account-barra-titulo {
    font-size: 15px;
    font-weight: 700;
    color: var(--synbex-text);
}

/* Fila de "Guardar cambios" del panel de cuenta cuando ese panel se dibuja solo
   (página de Mi cuenta y modal del engranaje). En Configuración no se usa: ahí
   el botón vive arriba, al nivel del título, y guarda todo de una vez. */
.account-guardar-fila {
    display: flex;
    justify-content: flex-end;
    margin-top: 18px;
}

/* Guardar de Configuración: a la derecha del encabezado, a la altura del título
   y su descripción — pedido explícito. */
.catalog-header-row .config-guardar {
    align-self: center;
    white-space: nowrap;
}

/* El ojo de "Ver detalles" en Consultas: 14px en vez de los 18px que trae
   .synbex-icon-btn — pedido explícito. Va acotado a esa tabla a propósito: ese
   botón lo usa media aplicación y ahí el tamaño de siempre está bien. */
.consultas-table .synbex-icon-btn svg {
    width: 14px;
    height: 14px;
}

/* La cuenta de registros, a la derecha del buscador, en el azul de la aplicación
   —el mismo de la línea del encabezado de la tabla— para que se vea de entrada. */
.consultas-conteo {
    margin: 0;
    color: var(--synbex-primary);
    font-weight: 700;
}

/* ==========================================================================
   Contactos: las mismas tarjetas de Documentos, también en computadora
   ========================================================================== */

/* Pedido explícito: que Contactos se vea como Documentos. Misma tarjeta y mismo
   orden — arriba el nombre en lugar del título del archivo, debajo el rótulo de
   la categoría, y en el renglón chico de datos el teléfono y el correo en vez
   del tamaño y la fecha. Abajo la nota y los dos botones (Llamar y WhatsApp).

   Va acotado a .contacts-cards, igual que el bloque de .documents-cards: las
   otras pantallas con tarjetas de celular siguen con su tabla en computadora. */
@media (min-width: 601px) {
    .contacts-table-wrapper {
        display: none;
    }

    .contacts-cards {
        display: grid;
        grid-template-columns: 1fr;
        gap: 14px;
        align-items: start;
    }

    /* Franja horizontal: el bloque de datos a la izquierda y los botones a la
       derecha, en vez de los botones abajo ocupando alto de más. */
    .contacts-cards .mobile-card {
        flex-direction: row;
        align-items: center;
        gap: 18px;
        max-width: none;
        width: 100%;
    }

    .contacts-cards .mobile-card-header {
        flex: 1 1 auto;
        min-width: 0;
        align-items: flex-start;
    }

    /* Sin esto el bloque de nombre/categoría/datos se encoge a 0 y desaparece:
       sus hijos son nowrap con overflow oculto. */
    .contacts-cards .mobile-card-headertext {
        flex: 1 1 auto;
        min-width: 0;
    }

    .contacts-cards .mobile-card-buttons {
        flex: 0 0 auto;
        width: auto;
        margin-top: 0;
    }

    .contacts-cards .mobile-card-btn {
        flex: 0 0 auto;
        min-width: 130px;
    }

    /* La nota se queda debajo del nombre, no en la franja de los botones: es un
       texto libre y puede ser largo. */
    .contacts-cards .mobile-card-row {
        width: 100%;
    }
}

/* El correo es lo único que puede ser muy largo: se corta con puntos suspensivos
   en vez de empujar el teléfono fuera de la tarjeta. */
.contacts-meta-email {
    min-width: 0;
}

    .contacts-meta-email a,
    .contacts-meta-email span {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

/* En un teléfono los dos datos no entran en una línea: ahí sí se permite que el
   correo baje al renglón siguiente, con su propio ícono, que se sigue leyendo
   bien. La divisoria se esconde para que no quede colgando al final. */
@media (max-width: 600px) {
    .contacts-meta-line {
        flex-wrap: wrap;
        gap: 4px 10px;
    }

        .contacts-meta-line .documents-meta-divider {
            display: none;
        }
}

/* En celular el botón de Guardar cae en su propio renglón debajo del título (el
   encabezado envuelve): centrado con márgenes automáticos y un poco más grande,
   que es el único botón de la pantalla y se toca con el dedo. */
@media (max-width: 640px) {
    .catalog-header-row .config-guardar {
        display: flex;
        margin: 4px auto 0;
        padding: 13px 34px;
        font-size: 15px;
    }
}

/* Los dos controles de la cámara, en la esquina de arriba a la derecha: el de
   cambiar de cámara ARRIBA (en la esquina) y el flash debajo — pedido explícito.
   Entre los dos va un hueco de 14px para que no se toquen y no se toque uno
   queriendo el otro con el pulgar.

   El flash mide unos 68px de alto (14px de relleno arriba y abajo, más el ícono
   y la palabra), así que el de abajo arranca a 68 + 14. */
.id-scanner-torch {
    top: calc(max(16px, env(safe-area-inset-top)) + 82px);
}

.id-scanner-flip {
    top: max(16px, env(safe-area-inset-top));
}

/* La cámara de adelante devuelve la imagen en espejo. Para leer un documento eso
   estorba —las letras salen al revés—, así que se le da la vuelta. */
.id-scanner-video.is-espejo,
video.is-espejo {
    transform: scaleX(-1);
}

/* Renglón del botón de vaciar, arriba de la lista del registro del sistema.
   A la derecha: es una acción de limpieza, no lo primero que hay que leer. */
.monitor-list-actions {
    display: flex;
    justify-content: flex-end;
    margin-bottom: 12px;
}

/* En rojo tenue, no en el gris de los botones secundarios: borra cientos de
   registros de una y eso tiene que verse antes de tocarlo. */
.monitor-clear-btn {
    color: var(--synbex-danger);
    border-color: var(--synbex-danger);
}

    .monitor-clear-btn svg {
        width: 15px;
        height: 15px;
    }

/* El botón de liberar caché y su explicación, debajo de la barra del disco. */
.monitor-disk-cache {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--synbex-border);
}

    .monitor-disk-cache svg {
        width: 15px;
        height: 15px;
    }

/* ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
   BARRA DE ACCIONES FLOTANTE (Components/Shared/BarraDeAcciones.razor)
   ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
   Cancelar y Guardar siempre a la vista en los formularios largos. Sticky y no
   fixed: se pega al borde de abajo mientras el formulario ocupa la pantalla y al
   llegar al final se apoya en su lugar, sin quedar flotando sobre otras secciones
   ni tapar el menú lateral.

   Fondo translúcido con desenfoque, como la isla del menú inferior del celular,
   con un tinte leve del color principal (pedido explícito). color-mix sobre las
   variables del tema, así funciona igual en claro y en oscuro. */

.barra-acciones {
    position: sticky;
    bottom: 12px;
    z-index: 40;
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 20px;
    /* Del ancho de sus botones (a lo ancho de todo el formulario quedaba un panel
       grande casi vacío) y centrada respecto al formulario: queda justo en medio de
       las dos columnas de tarjetas, siempre abajo (pedidos explícitos). width
       max-content + márgenes automáticos a los dos lados centra igual dentro de un
       flex en columna o de un bloque normal, así sirve para cualquier formulario. */
    width: max-content;
    max-width: 100%;
    margin-left: auto;
    margin-right: auto;
    padding: 10px 18px;
    border-radius: 16px;
    border: 1px solid color-mix(in srgb, var(--synbex-primary) 18%, var(--synbex-border));
    background: color-mix(in srgb, color-mix(in srgb, var(--synbex-primary) 8%, var(--synbex-surface)) 68%, transparent);
    backdrop-filter: blur(14px) saturate(140%);
    -webkit-backdrop-filter: blur(14px) saturate(140%);
    box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
    box-sizing: border-box;
}

.barra-acciones-extra {
    min-width: 0;
    font-size: 13px;
    color: var(--synbex-text-muted);
}

/* Sin nada que mostrar a la izquierda, no ocupa lugar: la barra queda justo del
   tamaño de los botones. */
.barra-acciones-extra:empty {
    display: none;
}

.barra-acciones-botones {
    display: flex;
    gap: 10px;
    margin-left: auto;
}

.barra-acciones-boton {
    min-width: 132px;
    padding: 10px 18px;
}

.barra-acciones-boton svg {
    width: 17px;
    height: 17px;
    flex-shrink: 0;
}

/* En celular la isla del menú inferior ocupa el borde de abajo (14px de margen
   más unos 61px de alto): la barra se ubica justo encima para no quedar tapada. */
@media (max-width: 600px) {
    .barra-acciones {
        bottom: calc(88px + env(safe-area-inset-bottom, 0px));
        padding: 10px;
        /* En celular sí va a lo ancho: dos botones grandes y fáciles de tocar, sin
           espacio sobrante porque ocupan toda la fila. */
        width: auto;
        margin-left: 0;
        margin-right: 0;
    }

    .barra-acciones-extra:empty {
        display: none;
    }

    .barra-acciones-botones {
        width: 100%;
    }

    .barra-acciones-boton {
        flex: 1 1 0;
        min-width: 0;
    }
}

/* ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
   FORMULARIO EN TARJETAS
   ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
   El formato del editor de áreas comunes: un banner arriba con el interruptor
   principal, dos columnas de tarjetas con título e ícono, etiquetas con ícono y
   campos con el ícono adentro. Clases genéricas (form-*, campo-*) para poder
   llevar el mismo formato a otros formularios. */

.form-tarjetas {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.form-banner {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 18px 22px;
    border-radius: var(--synbex-radius-lg);
    border: 1px solid var(--synbex-border);
    background: linear-gradient(120deg, var(--synbex-primary-soft) 0%, color-mix(in srgb, var(--synbex-primary-soft) 35%, var(--synbex-surface)) 100%);
    box-shadow: var(--synbex-shadow);
}

.form-banner-icono {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 52px;
    height: 52px;
    border-radius: 14px;
    background: color-mix(in srgb, var(--synbex-primary) 14%, var(--synbex-surface));
    color: var(--synbex-primary);
}

.form-banner-texto {
    min-width: 0;
}

.form-banner-texto h2 {
    margin: 0;
    font-size: 19px;
    font-weight: 700;
    color: var(--synbex-text);
}

.form-banner-texto p {
    margin: 3px 0 0;
    font-size: 13.5px;
    color: var(--synbex-text-muted);
}

.form-banner-interruptor {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 10px 8px 16px;
    border-radius: 999px;
    border: 1px solid var(--synbex-border);
    background: var(--synbex-surface);
    box-shadow: var(--synbex-shadow);
    white-space: nowrap;
}

.form-banner-interruptor-texto {
    font-size: 13px;
    font-weight: 600;
    color: var(--synbex-text);
    cursor: pointer;
}

.form-columnas {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 16px;
    /* stretch: las dos columnas miden lo mismo, y la última tarjeta de cada una
       crece hasta el fondo (ver abajo). Así la tarjeta de la izquierda termina a
       la misma altura que la última de la derecha (pedido explícito). */
    align-items: stretch;
}

.form-columna {
    display: flex;
    flex-direction: column;
    gap: 16px;
    min-width: 0;
}

.form-columna > .form-tarjeta:last-child {
    flex: 1 1 auto;
}

.form-tarjeta {
    background: var(--synbex-surface);
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-lg);
    box-shadow: var(--synbex-shadow);
    padding: 20px 22px;
    min-width: 0;
}

.form-tarjeta > .synbex-field:last-child,
.form-tarjeta > .form-fila-2:last-child .synbex-field {
    margin-bottom: 0;
}

.form-tarjeta-encabezado {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 16px;
}

.form-tarjeta-encabezado h3 {
    margin: 0;
    font-size: 16px;
    font-weight: 700;
    color: var(--synbex-text);
}

.form-tarjeta-icono {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border-radius: 50%;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.form-tarjeta-icono svg {
    width: 19px;
    height: 19px;
}

.form-tarjeta-contador {
    margin-left: auto;
    font-size: 12.5px;
    color: var(--synbex-text-muted);
}

.form-etiqueta {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--synbex-text);
}

.form-etiqueta svg {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}

.form-opcional {
    font-weight: 400;
    color: var(--synbex-text-muted);
}

.form-fila-2 {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 16px;
}

/* Campo con el ícono adentro, separado del texto por una línea. */
.campo-icono {
    position: relative;
}

.campo-icono-simbolo {
    position: absolute;
    top: 1px;
    bottom: 1px;
    left: 1px;
    width: 42px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--synbex-text-muted);
    border-right: 1px solid var(--synbex-border);
    pointer-events: none;
}

.campo-icono-simbolo svg {
    width: 17px;
    height: 17px;
}

.campo-icono .synbex-input {
    padding-left: 54px;
}

/* Campo con una unidad pegada a la derecha ("horas"). */
.campo-sufijo {
    display: flex;
    align-items: stretch;
    max-width: 240px;
}

.campo-sufijo .synbex-input {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
    min-width: 0;
}

.campo-sufijo-unidad {
    display: flex;
    align-items: center;
    padding: 0 14px;
    border: 1px solid var(--synbex-border);
    border-left: none;
    border-radius: 0 var(--synbex-radius-md) var(--synbex-radius-md) 0;
    background: var(--synbex-bg);
    font-size: 13px;
    color: var(--synbex-text-muted);
}

.form-textarea {
    /* Alto fijo y sin la esquina para estirarlo: el recuadro no cambia de tamaño
       mientras se escribe ni se puede agrandar a mano (pedido explícito). Con
       500 caracteres de tope, lo que no entra se lee con la barra de scroll. */
    height: 125px;
    min-height: 125px;
    resize: none;
}

.form-contador {
    margin-top: 6px;
    text-align: right;
    font-size: 12px;
    color: var(--synbex-text-muted);
}

.form-nota {
    display: flex;
    gap: 10px;
    margin: 0;
    padding: 12px 14px;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-primary-soft);
    font-size: 12.8px;
    line-height: 1.5;
    color: var(--synbex-text);
}

.form-nota svg {
    flex: 0 0 auto;
    width: 17px;
    height: 17px;
    margin-top: 1px;
    color: var(--synbex-primary);
}

.form-nota-suave {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 10px 0 0;
    font-size: 12.5px;
    color: var(--synbex-text-muted);
}

.form-nota-suave svg {
    flex: 0 0 auto;
    width: 15px;
    height: 15px;
}

/* Zona para subir fotos: el input de archivo invisible cubre toda la zona, así
   funciona igual tocando que arrastrando las imágenes encima. */
.form-fotos-zona {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 22px 16px;
    border: 1.5px dashed color-mix(in srgb, var(--synbex-primary) 38%, var(--synbex-border));
    border-radius: var(--synbex-radius-md);
    background: color-mix(in srgb, var(--synbex-primary-soft) 45%, var(--synbex-surface));
    text-align: center;
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease;
}

.form-fotos-zona:hover {
    border-color: var(--synbex-primary);
    background: var(--synbex-primary-soft);
}

.form-fotos-zona input[type="file"] {
    position: absolute;
    inset: 0;
    opacity: 0;
    cursor: pointer;
}

.form-fotos-zona.is-disabled {
    opacity: 0.6;
    cursor: default;
}

.form-fotos-zona.is-disabled input[type="file"] {
    display: none;
}

.form-fotos-zona-icono {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    margin-bottom: 6px;
    border-radius: 12px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.form-fotos-zona-titulo {
    font-size: 14.5px;
    font-weight: 700;
    color: var(--synbex-text);
}

.form-fotos-zona-ayuda {
    font-size: 12.5px;
    color: var(--synbex-text-muted);
}

.form-fotos-miniaturas {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 10px;
    margin-top: 12px;
}

.form-foto {
    position: relative;
    aspect-ratio: 4 / 3;
    border-radius: 10px;
    overflow: hidden;
    cursor: pointer;
    background: var(--synbex-bg);
}

.form-foto img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.form-foto-borrar {
    position: absolute;
    top: 6px;
    right: 6px;
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: rgba(15, 23, 42, 0.55);
    color: #fff;
    transition: background 0.15s ease;
}

.form-foto:hover .form-foto-borrar {
    background: var(--synbex-danger);
}

.form-foto-borrar svg {
    width: 15px;
    height: 15px;
}

@media (max-width: 900px) {
    .form-columnas {
        grid-template-columns: minmax(0, 1fr);
    }
}

@media (max-width: 600px) {
    .form-banner {
        flex-wrap: wrap;
        padding: 16px;
    }

    .form-banner-interruptor {
        margin-left: 0;
        width: 100%;
        justify-content: space-between;
        box-sizing: border-box;
    }

    .form-tarjeta {
        padding: 16px;
    }

    .form-fila-2 {
        grid-template-columns: minmax(0, 1fr);
        gap: 0;
    }

    .campo-sufijo {
        max-width: none;
    }
}

/* ââ Ãcono de ayuda con explicación (Components/Shared/AyudaInfo.razor) ââââââââ */

.ayuda-info {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    color: var(--synbex-text-muted);
    cursor: help;
    outline: none;
    transition: color 0.15s ease;
}

.ayuda-info svg {
    width: 16px;
    height: 16px;
}

.ayuda-info:hover,
.ayuda-info:focus-visible {
    color: var(--synbex-primary);
}

.ayuda-info:focus-visible {
    box-shadow: 0 0 0 3px var(--synbex-primary-soft);
}

/* El globo: oscuro en los dos temas, para que siempre se lea y se distinga del
   formulario que tiene detrás. */
.ayuda-info-globo {
    position: absolute;
    top: calc(100% + 10px);
    left: -12px;
    z-index: 60;
    width: 320px;
    max-width: calc(100vw - 48px);
    padding: 12px 14px;
    border-radius: 10px;
    background: #1f2937;
    color: #f9fafb;
    font-size: 12.8px;
    font-weight: 400;
    line-height: 1.55;
    text-align: left;
    white-space: normal;
    box-shadow: 0 10px 28px rgba(15, 23, 42, 0.28);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px);
    transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;
    pointer-events: none;
}

/* La flechita que apunta al ícono. */
.ayuda-info-globo::before {
    content: "";
    position: absolute;
    top: -6px;
    left: 16px;
    width: 12px;
    height: 12px;
    background: #1f2937;
    transform: rotate(45deg);
    border-radius: 2px;
}

.ayuda-info-globo p {
    margin: 0;
}

.ayuda-info-globo p + p {
    margin-top: 8px;
}

.ayuda-info-globo strong {
    color: #ffffff;
    font-weight: 600;
}

.ayuda-info:hover .ayuda-info-globo,
.ayuda-info:focus .ayuda-info-globo,
.ayuda-info:focus-within .ayuda-info-globo {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

/* La etiqueta del campo es el punto de anclaje del globo en celular (ver abajo). */
.form-etiqueta {
    position: relative;
}

@media (max-width: 600px) {
    .ayuda-info {
        position: static;
    }

    /* Debajo de la etiqueta y a lo ancho del campo: anclado al ícono se cortaba
       contra los bordes de la pantalla. */
    .ayuda-info-globo {
        top: calc(100% + 8px);
        left: 0;
        right: 0;
        width: auto;
        max-width: none;
    }

    .ayuda-info-globo::before {
        display: none;
    }
}

/* ââ Campo con interruptor, con la misma forma que un campo de texto ââââââââââ */

/* Mismo borde y fondo gris que cualquier campo de texto, esté encendido o no:
   lo único de color es el interruptor (pedido explícito). */
.campo-interruptor {
    position: relative;
    display: flex;
    align-items: center;
    gap: 12px;
    min-height: 42px;
    padding: 6px 12px 6px 54px;
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-surface);
    box-sizing: border-box;
    transition: border-color 0.15s ease, background 0.15s ease;
}

.campo-interruptor-texto {
    flex: 1;
    min-width: 0;
    font-size: 14px;
    color: var(--synbex-text);
    cursor: pointer;
}

.campo-interruptor-texto small {
    display: block;
    font-size: 12px;
    color: var(--synbex-text-muted);
}

/* ââ Botones de la barra de acciones del mismo ancho ââââââââââââââââââââââââââ */

/* Dos columnas iguales: cada botón toma el ancho del más ancho de los dos, así
   Cancelar y Guardar quedan exactamente del mismo tamaño aunque sus textos no
   midan lo mismo (pedido explícito). */
.barra-acciones .barra-acciones-botones {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
}

/* ââ Marca de agua "Portada" sobre la primera foto ââââââââââââââââââââââââââââ */

/* La primera foto es la que sale en el catálogo: se marca en el centro de la
   miniatura para que no haya que leer la nota de abajo para saberlo. Sin eventos
   de puntero, así tocar la foto sigue abriendo la confirmación para quitarla. */
.form-foto-portada {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}

.form-foto-portada span {
    padding: 4px 10px;
    border-radius: 999px;
    background: rgba(15, 23, 42, 0.42);
    color: rgba(255, 255, 255, 0.95);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
    white-space: nowrap;
}

/* ââ Limpieza vencida: Â¿la realiza o no? (Cleaning/Index.razor) ââââââââââââââââ */

.cleaning-vencida-card {
    max-width: 420px;
}

.cleaning-vencida-acciones {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.cleaning-vencida-acciones .btn-synbex {
    justify-content: center;
    width: 100%;
    min-height: 44px;
}

.cleaning-vencida-ayuda {
    display: block;
    margin-top: 6px;
    font-size: 12px;
    color: var(--synbex-text-muted);
}

.cleaning-vencida-invalido,
.cleaning-vencida-invalido:focus {
    border-color: var(--synbex-danger) !important;
    box-shadow: 0 0 0 3px var(--synbex-danger-soft) !important;
}

/* Encabezado: ícono + "Limpieza asignada vencida" + hace cuánto. */
.cleaning-vencida-encabezado {
    display: flex;
    align-items: flex-start;
    gap: 12px;
}

.cleaning-vencida-icono {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: var(--synbex-danger-soft);
    color: var(--synbex-danger);
}

.cleaning-vencida-icono svg {
    width: 22px;
    height: 22px;
}

.cleaning-vencida-encabezado > div {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.cleaning-vencida-titulo {
    font-size: 16px;
    line-height: 1.3;
    color: var(--synbex-text);
}

.cleaning-vencida-fecha {
    font-size: 13px;
    font-weight: 600;
    color: var(--synbex-danger);
}

/* El lugar, en su propio recuadro con etiqueta. */
.cleaning-vencida-lugar {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 12px 14px;
    border: 1px solid var(--synbex-border);
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-bg);
}

.cleaning-vencida-etiqueta {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--synbex-text-muted);
}

.cleaning-vencida-lugar strong {
    font-size: 15px;
    line-height: 1.35;
    color: var(--synbex-text);
    overflow-wrap: anywhere;
}

.cleaning-vencida-notas {
    margin-top: 4px;
    font-size: 13px;
    color: var(--synbex-text-muted);
}

.cleaning-vencida-pregunta {
    margin: 0;
    font-size: 15px;
    font-weight: 600;
    text-align: center;
    color: var(--synbex-text);
}

/* Flash mientras la cámara confirma el cambio (lo pone idScanner.js): se ve que
   el toque se registró, y los toques de mientras se ignoran. */
.id-scanner-torch.is-busy {
    opacity: 0.75;
}

/* Celular en vertical: la guía se para (el lado largo a lo alto de la pantalla)
   para indicar que el documento va DE LADO. Así ocupa casi toda la pantalla y se
   lee sin alejarlo; a lo ancho, un celular en vertical lo obligaba a ponerlo
   lejos. idScanner.js lee el MRZ girado hacia cualquiera de los dos lados. */
.id-scanner-sub-vertical {
    display: none;
}

@media (orientation: portrait) {
    .id-scanner-guide {
        top: 46%;
        width: auto;
        max-width: none;
        height: min(78vh, calc(96vw * 1.586));
        aspect-ratio: 0.6305;
    }

    .id-scanner-sub-vertical {
        display: inline;
    }
}

/* Capa de la guía, los botones y los textos, encima del video. No recibe toques
   (el video de abajo tampoco los necesita); los botones los vuelven a pedir. */
.id-scanner-capa {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

.id-scanner-capa .id-scanner-close,
.id-scanner-capa .id-scanner-torch {
    pointer-events: auto;
}

/* Celular acostado con el giro automático activado: la pantalla se dio vuelta,
   así que la capa se gira al revés (idScanner.js pone la clase y los grados) y
   se ve igual que con el celular parado. La guía usa las medidas de "vertical"
   con vw y vh intercambiados, porque dentro de la capa girada el alto es el
   ancho de la pantalla. */
.id-scanner-fullscreen.is-girado .id-scanner-capa {
    inset: auto;
    top: 50%;
    left: 50%;
    width: 100vh;
    height: 100vw;
    transform: translate(-50%, -50%) rotate(var(--id-scanner-giro, 0deg));
}

.id-scanner-fullscreen.is-girado .id-scanner-guide {
    top: 46%;
    width: auto;
    max-width: none;
    height: min(78vw, calc(96vh * 1.586));
    aspect-ratio: 0.6305;
}

.id-scanner-fullscreen.is-girado .id-scanner-sub-vertical {
    display: inline;
}

/* Renglón único debajo de la guía (el título y los avisos amarillos se quitaron). */
.id-scanner-caption-simple .id-scanner-sub {
    margin-top: 0;
    font-size: 15px;
    font-weight: 600;
    color: #fff;
}

/* iPhone / iPad (Safari en iOS): el campo de fecha se salía del panel aunque ya
   tuviera width:100% y min-width:0. Safari lo dibuja con su control nativo y le
   impone un ancho propio mientras conserve la apariencia del sistema; en Android y
   en computadora no pasa. Se le quita la apariencia nativa (el selector de fecha
   sigue abriendo igual al tocarlo) y se le devuelve el alto de los demás campos,
   porque sin apariencia nativa y vacío Safari lo deja más bajo. Solo iOS: la
   consulta -webkit-touch-callout únicamente la entiende Safari de iOS. */
@supports (-webkit-touch-callout: none) {
    .synbex-input.synbex-date-input {
        -webkit-appearance: none;
        appearance: none;
        display: block;
        width: 100%;
        min-width: 0;
        max-width: 100%;
        min-height: calc(1.25em + 22px);
        line-height: 1.25;
    }

    .synbex-input.synbex-date-input::-webkit-date-and-time-value {
        text-align: left;
        min-height: 1.25em;
    }
}

/* Ventana de datos adicionales (solo celular, Registro de visitantes). El título y
   los botones quedan siempre a la vista; si el conjunto tiene muchos campos, lo que
   se desplaza es solo la lista. */
.settings-modal-dialog.visitor-adicionales-modal {
    display: flex;
    flex-direction: column;
    max-height: calc(100dvh - 32px);
    overflow: hidden;
}

.visitor-adicionales-cuerpo {
    flex: 1 1 auto;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

.visitor-adicionales-quien {
    margin: 0 0 14px;
    font-size: 14px;
    color: var(--synbex-text-muted);
}

.visitor-adicionales-botones {
    display: flex;
    gap: 10px;
    padding: 12px 18px max(14px, env(safe-area-inset-bottom));
    border-top: 1px solid var(--synbex-border);
    background: var(--synbex-surface);
}

.visitor-adicionales-botones .btn-synbex {
    flex: 1;
    justify-content: center;
}

/* Ventana de datos adicionales en celular: de borde a borde y de al menos la mitad
   de la pantalla de alto, con los botones siempre abajo (pedido explícito). */
@media (max-width: 600px) {
    .settings-modal-backdrop.visitor-adicionales-backdrop {
        padding: 16px 0 0;
        align-items: flex-end;
    }

    .settings-modal-dialog.visitor-adicionales-modal {
        width: 100%;
        max-width: none;
        min-height: 50vh;
        min-height: 50dvh;
        max-height: calc(100dvh - 16px);
        border-left: none;
        border-right: none;
        border-radius: 16px 16px 0 0;
    }

    .settings-modal-dialog.visitor-adicionales-modal .visitor-adicionales-botones {
        margin-top: auto;
    }
}

/* ââ Marca "S" de Synbex con el logo nuevo (pedido explícito, 2026-09-17) ââââââ
   Antes era la letra "S" blanca sobre un cuadro morado. Ahora es el logo de 4
   piezas (el mismo de favicon e íconos de la app). El texto "S" se deja en el
   HTML y solo se vuelve invisible: tenant-logo.js depende de que el nodo de
   #login-brand-mark no cambie (ver project_blazor_enhanced_nav_js_gotchas).

   Selectores con "elemento.clase.clase" a propósito: cada página trae su propia
   regla con alcance de Blazor (.clase[b-xxxx], que empata en especificidad y
   carga después). Así esta le gana en todas. #login-brand-mark.has-logo (el logo
   propio del conjunto) sigue ganando sobre esta porque lleva id. */
.login-brand-mark {
    width: 56px;
    height: 56px;
    flex-shrink: 0;
}

div.login-brand-mark.login-brand-mark,
div.landing-brand-mark.landing-brand-mark,
span.assembly-topbar-mark.assembly-topbar-mark {
    background: url("/icons/icon-192.png?v=2") center / contain no-repeat;
    color: transparent;
    border-radius: 0;
}

/* En el menú lateral la marca va sobre el fondo morado del encabezado: sin una
   caja blanca, las piezas azul y morado del logo casi no se verían. */
div.dashboard-brand-mark.dashboard-brand-mark {
    background: #fff url("/icons/icon-192.png?v=2") center / 72% no-repeat;
    color: transparent;
}

/* Checklist y Fotos del día de limpieza: cada uno en su tarjeta (pedido explícito
   con un diseño de ejemplo, 2026-09-17). En celular también se ven así, apiladas. */
.cleaning-day-card {
    border: 1px solid var(--synbex-border);
    border-radius: 14px;
    padding: 14px 16px 16px;
    background: var(--synbex-surface);
}

.cleaning-day-card > .cleaning-day-card-titulo {
    margin: 0 0 12px;
    padding-top: 0;
    border-top: 0;
}

/* El checklist es un <details>: el título es el <summary> y hace de botón. */
summary.cleaning-day-card-titulo {
    cursor: pointer;
    list-style: none;
}

summary.cleaning-day-card-titulo::-webkit-details-marker {
    display: none;
}

.cleaning-day-card-flecha {
    margin-left: auto;
    display: flex;
    align-items: center;
    color: var(--synbex-text-muted);
    transition: transform 0.15s ease;
}

.cleaning-day-card-flecha svg {
    width: 18px;
    height: 18px;
}

/* Abierto: la flecha apunta hacia arriba. */
.cleaning-day-card[open] .cleaning-day-card-flecha {
    transform: rotate(180deg);
}

/* "3 fotos" a la derecha del título. */
.cleaning-day-card-conteo {
    margin-left: auto;
    padding: 3px 10px;
    border-radius: 999px;
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
    font-size: 12px;
    font-weight: 700;
}

.cleaning-day-card .cleaning-checklist-item:last-child {
    border-bottom: 0;
}

/* Las fotos, centradas dentro de su tarjeta (pedido explícito): la cuadrícula tiene
   columnas de ancho fijo, así que se centra la cuadrícula entera, no cada foto. */
.cleaning-day-fotos .cleaning-photo-grid-preview {
    justify-content: center;
    align-content: center;
}

/* Un comentario largo no puede estirar la fila de datos ni descuadrar lo de abajo:
   se corta a tres renglones (el texto completo sale al pasar el mouse) y las palabras
   larguísimas se parten en vez de ensanchar la columna. */
.cleaning-day-detail-meta-value {
    overflow-wrap: anywhere;
}

.cleaning-day-datos .cleaning-day-detail-meta-value {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Checklist del día: ítems un poco más grandes y separados por una línea, para que se
   lea cada uno como un renglón aparte (pedido explícito, 2026-09-17). */
.cleaning-checklist-dia {
    gap: 0;
}

.cleaning-checklist-dia .cleaning-checklist-item {
    font-size: 15px;
    padding: 11px 0;
    border-bottom: 1px solid var(--synbex-border);
}

.cleaning-checklist-dia .cleaning-checklist-item:last-child {
    border-bottom: 0;
}

/* Avance en el mismo renglón del título, chiquito y pegado a la derecha. */
.cleaning-checklist-avance {
    margin-left: auto;
    padding: 3px 10px;
    border-radius: 999px;
    background: rgba(22, 163, 74, 0.14);
    color: var(--synbex-success);
    font-size: 12px;
    font-weight: 700;
    white-space: nowrap;
}


/* Fila de datos: cada dato centrado en vertical, así "Fecha de realización" y
   "Realizado por" no quedan arriba cuando el comentario ocupa más renglones. */
.cleaning-day-datos .cleaning-day-detail-meta-row {
    align-items: center;
}


/* Cuántas limpiezas hubo en el mes que se está viendo, debajo del nombre del mes. */
.cleaning-calendar-conteo {
    margin: 0 0 12px;
    text-align: center;
    font-size: 13px;
    font-weight: 600;
    color: var(--synbex-text-muted);
}

/* Leyenda del calendario de Mi limpieza: centrada y en el color de la app, sin
   cuadrito de muestra (pedido explícito, 2026-09-17). */
.cleaning-calendar-leyenda {
    margin: 0 0 12px;
    text-align: center;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--synbex-primary);
}

/* Las flechas, pegadas al nombre del mes. */
.cleaning-calendar-mes-fila {
    display: flex;
    position: relative;
    align-items: center;
    justify-content: center;
    gap: 8px;
    margin-bottom: 4px;
}

/* El nombre del mes es un botón que abre el selector propio de mes y año. Ancho FIJO
   para que las flechas de los lados no se muevan según lo largo que sea el nombre. */
.cleaning-calendar-mes-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: 190px;
    padding: 4px 8px;
    border: 0;
    border-radius: 10px;
    background: transparent;
    font: inherit;
    font-weight: 700;
    color: var(--synbex-text);
    text-align: center;
    cursor: pointer;
}

.cleaning-calendar-mes-btn:hover {
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

/* Selector propio de mes y año: meses a la izquierda, años a la derecha (pedido
   explícito, 2026-09-17). Es propio y no el del navegador porque Firefox no tiene
   selector de mes: dejaba escribir el año a mano y no aplicaba nada. */
.cleaning-mes-selector {
    position: absolute;
    top: calc(100% + 6px);
    left: 50%;
    transform: translateX(-50%);
    z-index: 30;
    display: flex;
    gap: 6px;
    padding: 8px;
    border: 1px solid var(--synbex-border);
    border-radius: 14px;
    background: var(--synbex-surface);
    box-shadow: var(--synbex-shadow);
}

.cleaning-mes-selector-columna {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-height: 240px;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

.cleaning-mes-selector-opcion {
    padding: 8px 18px;
    border: 0;
    border-radius: 9px;
    background: transparent;
    font: inherit;
    font-size: 14px;
    font-weight: 600;
    color: var(--synbex-text);
    text-align: center;
    white-space: nowrap;
    cursor: pointer;
}

.cleaning-mes-selector-opcion:hover {
    background: var(--synbex-primary-soft);
}

.cleaning-mes-selector-opcion.is-elegido {
    background: var(--synbex-primary);
    color: #fff;
}

/* Tapa toda la pantalla por detrás del selector: un clic afuera lo cierra. */
.cleaning-mes-selector-fondo {
    position: fixed;
    inset: 0;
    z-index: 20;
}

.cleaning-calendar-conteo-numero {
    color: var(--synbex-primary);
    font-weight: 700;
}

/* Flechas de mes: el ícono se centra solo dentro del círculo. Antes eran los
   caracteres "â¹" y "âº", que se apoyan en su línea base y quedaban por debajo del
   centro (pedido explícito, 2026-09-17). */
.cleaning-calendar-nav-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
}

.cleaning-calendar-nav-icono {
    display: flex;
    align-items: center;
    justify-content: center;
}

.cleaning-calendar-nav-icono svg {
    width: 16px;
    height: 16px;
}

.cleaning-calendar-nav-icono.is-anterior {
    transform: rotate(90deg);
}

.cleaning-calendar-nav-icono.is-siguiente {
    transform: rotate(-90deg);
}

/* Selector de mes: en CELULAR el del propio teléfono (ruedas de mes y año), en
   computadora el propio de la app (pedido explícito, 2026-09-17). */
.cleaning-calendar-mes-nativo {
    display: none;
}

@media (max-width: 600px) {
    .cleaning-calendar-mes-btn {
        display: none;
    }

    .cleaning-calendar-mes-nativo {
        display: block;
        width: 190px;
        padding: 4px 8px;
        border: 0;
        border-radius: 10px;
        background: transparent;
        font: inherit;
        font-size: 16px;
        font-weight: 700;
        color: var(--synbex-text);
        text-align: center;
        cursor: pointer;
    }

    .cleaning-calendar-mes-nativo::-webkit-calendar-picker-indicator {
        cursor: pointer;
        opacity: 0.6;
    }
}

/* ===== Hub con encabezado centrado (maqueta del usuario, Comunidad) =====
   El ícono grande de la sección arriba del título, todo centrado, una rayita de
   color debajo, y las tarjetas de tres en tres dentro de un ancho cómodo (no
   estiradas de lado a lado).

   Las manchas de color en las esquinas que tenía este bloque se quitaron (pedido
   explícito): en tema oscuro se veían como una franja más clara detrás de las
   tarjetas, corrida hacia el menú. */
.hub-page {
    position: relative;
}

.hub-encabezado {
    text-align: center;
    margin: 4px 0 var(--ui-hub-encabezado-mb);
}

.hub-encabezado-icono {
    display: inline-flex;
    color: var(--synbex-primary);
}

.hub-encabezado-icono svg {
    width: var(--ui-hub-icono);
    height: var(--ui-hub-icono);
    stroke-width: 1.4;
}

.hub-encabezado .page-title {
    margin: 8px 0 0;
    font-size: var(--ui-hub-titulo);
}

.hub-encabezado .page-subtitle {
    margin: 6px 0 0;
    font-size: var(--ui-hub-sub);
}

.hub-encabezado-linea {
    width: 56px;
    height: 4px;
    margin: 14px auto 0;
    border-radius: 99px;
    background: var(--synbex-primary);
}

/* Tres por fila en pantallas anchas, dos en tabletas y una en celular. El
   max-width es lo que las mantiene juntas y centradas en un monitor grande. */
.hub-grid-3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    max-width: min(100%, var(--ui-hub-3-ancho));
    margin: 0 auto;
    gap: var(--ui-hub-gap);
}

@media (max-width: 1150px) {
    .hub-grid-3 {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 720px) {
    .hub-grid-3 {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* El botón redondo con la comilla al final de cada tarjeta. */
.hub-tile-chevron {
    flex-shrink: 0;
    margin-left: auto;
    width: var(--ui-tile-chevron);
    height: var(--ui-tile-chevron);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--synbex-bg);
    color: var(--synbex-text-muted);
    transition: background 0.25s ease, color 0.25s ease, transform 0.25s ease;
}

.hub-tile-chevron svg {
    width: 15px;
    height: 15px;
}

.hub-tile:hover .hub-tile-chevron {
    background: var(--synbex-primary);
    color: #fff;
    transform: translateX(2px);
}

/* ===== Filas agendadas: alineación en columnas y menú de tres puntos =====
   (pedido explícito) Antes cada fila se acomodaba sola, así que la hora, el
   estado y los botones bailaban de una visita a otra según el largo del nombre.
   Ahora la parte de arriba es una cuadrícula con anchos fijos: nombre elástico,
   fecha, hora, estado y acciones siempre en el mismo sitio. */
.reserva-row-top {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) 250px 196px;
    align-items: center;
    gap: 16px;
}

/* La fila es un bloque y su contenido ocupa TODO el ancho: siendo flex, la parte
   de arriba solo medía lo que medía su contenido y dejaba un hueco grande a la
   derecha del panel (pedido explícito). */
.reserva-row {
    display: block;
}

.reserva-row-top {
    width: 100%;
}

/* La fila entera abre el detalle, no solo la flecha. La columna de la flecha solo
   existe cuando hay algo que desplegar; si no, dejaba un hueco a la derecha. */
.reserva-row.tiene-detalles .reserva-row-top {
    grid-template-columns: auto minmax(0, 1fr) 250px 196px 34px;
    cursor: pointer;
}

.reserva-row-lines {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 118px;
    align-items: center;
    gap: 6px 16px;
}

.reserva-row-hora {
    justify-content: flex-start;
}

/* Estado y aviso en el MISMO renglón (pedido explícito): apilados se veían uno
   encima del otro y dejaban un hueco enorme a la derecha del panel. */
.reserva-row-state {
    flex-direction: row;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px 10px;
}

.reserva-row-state .reservation-expiry-note {
    font-size: 12px;
    line-height: 1.25;
}

/* Columna de botones de ancho fijo y pegada a la derecha: así todos los botones
   empiezan y terminan en la misma línea, fila tras fila. */
.reserva-row-actions {
    justify-content: flex-end;
}

/* Todos los botones del mismo ancho: "Ver entrada" y "Renovar" tienen que verse
   iguales (pedido explícito). */
.reserva-row-actions .reserva-row-btn {
    min-width: 142px;
}

/* Un poco más claro que el fondo de la página, para que la fila se despegue de
   la tarjeta que la contiene. */
.reserva-row {
    background: color-mix(in srgb, var(--synbex-surface) 93%, var(--synbex-text-muted));
    border-left-width: 4px;
    border-left-style: solid;
    border-left-color: var(--synbex-border);
}

/* La rayita de la izquierda es lo que se lee de un vistazo al recorrer la lista,
   así que va en el tono FUERTE de cada color y no en el suave de la insignia
   (pedido explícito). #b91c1c es exactamente el rojo del botón "Cerrar sesión", y
   #15803d es su par en verde: mismo peso, misma saturación, misma familia.

   El resplandor es la misma rayita derramándose unos píxeles sobre la fila, y
   tiene que morir ANTES de llegar a los bordes de arriba y de abajo.

   Dos intentos que no sirvieron, para que nadie los repita:
   - Sombra por dentro (inset): se difumina para todos los lados por igual, así
     que teñía el borde de arriba y el de abajo.
   - Degradado lineal de izquierda a derecha: solo cambia en horizontal, o sea
     que a lo alto pinta parejo de punta a punta — el color seguía tocando los
     dos bordes, solo que con otra forma.

   La forma correcta es un degradado RADIAL anclado al medio del borde izquierdo:
   se apaga en las dos direcciones a la vez, así que dibuja un óvalo de luz
   pegado a la rayita que se desvanece antes de llegar arriba y abajo. Las
   esquinas quedan limpias. */
/* Mismos cuatro tonos para .reserva-row Y .paquete-row — sin ningún
   resplandor. Antes reservas/visitas sí lo tenían ("puede ser que tenga algún
   tipo de brillo"), pero se quitó después para que las dos listas se vean
   igual ("también quitar el brillo... igual que se hizo en mis [paquetes]").
   Con las dos pidiendo lo mismo ya no hace falta mantenerlo separado. */
.reserva-row.es-verde,
.paquete-row.es-verde {
    border-left-color: #15803d;
}

.reserva-row.es-rojo,
.paquete-row.es-rojo {
    border-left-color: #b91c1c;
}

.reserva-row.es-naranja,
.paquete-row.es-naranja {
    border-left-color: #d97706;
}

/* Lo que ya pasó de fecha. Gris y no rojo a propósito: una reserva que ya ocurrió
   no salió mal, simplemente terminó — el rojo queda reservado para lo que sí es
   una marcha atrás (cancelada o rechazada). Apagado para que no compita con las
   filas que todavía piden algo. */
.reserva-row.es-gris,
.paquete-row.es-gris {
    border-left-color: var(--synbex-text-muted);
}

/* ââ Menú de tres puntos âââââââââââââââââââââââââââââââââââââââââââââââââââââââ */
.reserva-row-menu {
    position: relative;
    flex-shrink: 0;
}

.reserva-row-dots {
    width: 34px;
    height: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--synbex-border);
    border-radius: 10px;
    background: var(--synbex-surface);
    color: var(--synbex-text-muted);
    cursor: pointer;
    transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}

.reserva-row-dots svg {
    width: 18px;
    height: 18px;
}

.reserva-row-dots:hover {
    background: var(--synbex-primary-soft);
    border-color: var(--synbex-primary);
    color: var(--synbex-primary);
}

/* Tapa transparente a pantalla completa: un clic afuera cierra el menú. */
.reserva-row-menu-fondo {
    position: fixed;
    inset: 0;
    z-index: 40;
}

.reserva-row-menu-pop {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 41;
    min-width: 186px;
    display: flex;
    flex-direction: column;
    padding: 6px;
    border: 1px solid var(--synbex-border);
    border-radius: 12px;
    background: var(--synbex-surface);
    box-shadow: 0 16px 34px -18px rgba(15, 23, 42, 0.55);
}

.reserva-row-menu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 9px 10px;
    border: none;
    border-radius: 8px;
    background: none;
    color: var(--synbex-text);
    font-size: 14px;
    font-weight: 600;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.reserva-row-menu-item svg {
    width: 17px;
    height: 17px;
    flex-shrink: 0;
    color: var(--synbex-text-muted);
}

.reserva-row-menu-item:hover {
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.reserva-row-menu-item:hover svg {
    color: var(--synbex-primary);
}

.reserva-row-menu-item.es-peligro:hover,
.reserva-row-menu-item.es-peligro:hover svg {
    background: var(--synbex-danger-soft);
    color: var(--synbex-danger);
}

/* Los datos del detalle, en una sola fila centrada mientras quepan (pedido
   explícito) — antes iban en dos columnas fijas, con huecos cuando un dato no
   aplicaba. */
.reserva-row-detalles {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 14px 28px;
}

.reserva-row-detalles .reserva-detalle {
    flex: 0 1 auto;
}

@media (max-width: 900px) {
    /* De acá para abajo ya no caben cuatro columnas: el estado y los botones se
       van a su propio renglón, debajo del nombre. */
    .reserva-row-top {
        grid-template-columns: auto minmax(0, 1fr) 34px;
    }

    .reserva-row-state,
    .reserva-row-actions {
        grid-column: 1 / -1;
    }

    .reserva-row-state {
        flex-direction: row;
        align-items: center;
        gap: 10px;
    }

    .reserva-row-lines {
        grid-template-columns: minmax(0, 1fr);
    }

    .reserva-row-detalles {
        justify-content: flex-start;
    }
}

/* ===== Catálogo de áreas comunes: ficha horizontal (pedido explícito) =====
   Antes eran tarjetas verticales de tres por fila, con la foto arriba. Ahora cada
   área es una ficha ancha: foto a la izquierda, nombre/descripción/conjunto en el
   medio y el botón de reservar a la derecha. */
.synbex-area-grid {
    flex-direction: column;
    align-items: stretch;
    gap: 16px;
    max-width: 1100px;
    margin: 0 auto;
}

.synbex-area-card {
    flex: 0 0 auto;
    max-width: none;
    min-width: 0;
    flex-direction: row;
    align-items: stretch;
}

/* La foto toma el alto que le fije el texto de al lado, no al revés: con
   height:auto una foto cuadrada estiraba la ficha a 230px de alto. */
.synbex-area-photo {
    width: 230px;
    height: 100%;
    min-height: 148px;
    max-height: 172px;
    flex-shrink: 0;
    align-self: stretch;
}

.synbex-area-body {
    justify-content: center;
    gap: 8px;
    padding: 18px 22px;
}

.synbex-area-body .page-subtitle {
    margin: 0;
}

.synbex-area-acciones {
    flex-shrink: 0;
    width: 226px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 8px;
    padding: 18px 22px;
    border-left: 1px solid var(--synbex-border);
}

.synbex-area-acciones-admin {
    display: flex;
    gap: 8px;
}

/* Celular y tabletas angostas: vuelve a apilarse (foto arriba, luego los datos y
   el botón a todo el ancho). */
@media (max-width: 820px) {
    .synbex-area-card {
        flex-direction: column;
    }

    .synbex-area-photo {
        width: 100%;
        height: 150px;
        min-height: 0;
    }

    .synbex-area-acciones {
        width: auto;
        border-left: none;
        border-top: 1px solid var(--synbex-border);
        padding-top: 14px;
    }
}

/* ââ Franja informativa del pie ââââââââââââââââââââââââââââââââââââââââââââââ
   Anclada al borde de abajo (pedido explícito), pero DENTRO del panel gris del
   contenido y con el mismo ancho que las fichas de las áreas: no cruza de lado a
   lado de la ventana ni se mete debajo del menú lateral. Al bajar se esconde y al
   subir vuelve (la clase la pone barra-info-scroll.js).

   Son dos piezas: el ANCLAJE, que es la capa fija y transparente que la sostiene y
   la centra, y la franja en sí, que es la caja blanca que se ve. Así la caja puede
   tener su ancho máximo y quedar centrada sin pelearse con la posición fija. */
.areas-info-anclaje {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 18px;
    z-index: 30;
    display: flex;
    justify-content: center;
    padding: 0 24px;
    /* La capa no debe robar clics: solo la caja de adentro los recibe. */
    pointer-events: none;
    /* Al VOLVER a aparecer (se quita .is-oculta) se usa esta, rápida — como ya
       estaba. Al escondERSE manda la de abajo, más lenta (pedido explícito: "se
       esconde muy rápido... más despacio"). El navegador toma la transición del
       estado de LLEGADA, así que cada sentido queda con su propia velocidad sin
       necesitar JS. */
    transition: transform 0.28s ease, opacity 0.28s ease;
}

.areas-info-anclaje.is-oculta {
    transform: translateY(160%);
    opacity: 0;
    transition: transform 0.7s ease, opacity 0.7s ease;
}

/* Arranca donde termina el menú lateral, para quedar centrada respecto del
   contenido y no respecto de la ventana. */
@media (min-width: 901px) and (max-width: 1800px) {
    .areas-info-anclaje {
        left: 74px;
    }
}

@media (min-width: 1801px) {
    .areas-info-anclaje {
        left: 250px;
    }
}

.areas-info {
    pointer-events: auto;
    box-sizing: border-box;
    width: 100%;
    max-width: 1100px;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 10px 0;
    padding: 10px 16px;
    border: 1px solid var(--synbex-border);
    border-radius: 16px;
    background: var(--synbex-surface);
    box-shadow: 0 10px 26px -14px rgba(15, 23, 42, 0.45);
}

/* Espacio al final de la página para que la última ficha no quede tapada. */
.catalog-page-container:has(.areas-info) {
    padding-bottom: 104px;
}

/* En computadora esta capa no hace nada (pedido explícito: "si se ve todo pues
   dejarlo así") — "display: contents" saca el div de la caja sin quitar los
   ítems: siguen siendo hijos directos de .areas-info, igual que antes de este
   cambio. Solo en celular (ver el media query de abajo) se vuelve una fila de
   verdad que se anima. La copia (para lectores de pantalla, invisible) ni
   siquiera se dibuja acá. */
.areas-info-track {
    display: contents;
}

.areas-info-track-copia {
    display: none;
}

.areas-info-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 2px 22px;
    border-left: 1px solid var(--synbex-border);
}

.areas-info-item:first-child {
    border-left: none;
}

.areas-info-icono {
    display: flex;
    flex-shrink: 0;
    color: var(--synbex-primary);
}

.areas-info-icono svg {
    width: 20px;
    height: 20px;
}

.areas-info-titulo {
    display: block;
    font-size: 13px;
    font-weight: 700;
    color: var(--synbex-text);
    line-height: 1.25;
}

.areas-info-desc {
    display: block;
    font-size: 12px;
    color: var(--synbex-text-muted);
    line-height: 1.25;
}

@media (max-width: 900px) {
    /* En celular la isla del menú inferior ya ocupa el borde de abajo: la franja
       se pone justo encima, no debajo, para que no queden una sobre otra. */
    .areas-info-anclaje {
        bottom: calc(80px + env(safe-area-inset-bottom, 0px));
        padding: 0 14px;
    }

    .catalog-page-container:has(.areas-info) {
        padding-bottom: 168px;
    }
}

@media (max-width: 760px) {
    /* Antes acá iban de a dos por renglón (uno sobre otro). Pedido explícito:
       "una sola fila... que se vaya deslizando... despacio". Ahora .areas-info
       deja de repartir en columnas y se vuelve la "ventana" que recorta lo que
       no cabe; adentro, las DOS copias de .areas-info-track (ver la marca
       "display: contents" de arriba, que acá se anula) se animan juntas y a la
       misma velocidad, así que siempre quedan pegadas y el reinicio del bucle
       no se nota (técnica de las dos copias del carrusel). */
    .areas-info {
        flex-wrap: nowrap;
        justify-content: flex-start;
        overflow: hidden;
    }

    .areas-info-track,
    .areas-info-track-copia {
        display: flex;
        align-items: center;
        flex: 0 0 auto;
        animation: areas-info-desliza 26s linear infinite;
    }

    .areas-info-item {
        flex: 0 0 auto;
        white-space: nowrap;
        padding: 2px 18px;
    }

    /* La rayita de ":first-child" (que en computadora borra la del primer ítem
       del panel) acá también apagaría la del primer ítem de la SEGUNDA copia —
       pero esa, en la fila que se desliza, va pegada justo detrás del último
       ítem de la primera. Sin esto, esa unión perdería su rayita cada vuelta. */
    .areas-info-track-copia .areas-info-item:first-child {
        border-left: 1px solid var(--synbex-border);
    }

    @media (prefers-reduced-motion: reduce) {
        .areas-info-track,
        .areas-info-track-copia {
            animation: none;
        }
    }
}

/* Cada copia se corre exactamente su propio ancho: cuando la primera termina de
   salir por la izquierda, la segunda —que iba pegada detrás— ya está justo
   donde empezó la primera, y el reinicio del bucle (infinite vuelve de golpe a
   "from") cae ahí sin que se note el salto. */
@keyframes areas-info-desliza {
    from {
        transform: translateX(0);
    }

    to {
        transform: translateX(-100%);
    }
}

/* ===== Ãrea de texto con contador (ver CampoTextoLargo.razor) ===== */
.campo-texto-largo {
    position: relative;
    display: block;
}

.campo-texto-largo textarea {
    /* Espacio abajo para que el contador nunca se monte sobre la última línea. */
    padding-bottom: 24px;
}

.campo-texto-contador {
    position: absolute;
    right: 10px;
    bottom: 7px;
    font-size: 11.5px;
    font-variant-numeric: tabular-nums;
    color: var(--synbex-text-muted);
    background: transparent;
    pointer-events: none;
}

/* Llegó al tope: el contador en rojo, para que se note sin contar. */
.campo-texto-largo.es-al-tope .campo-texto-contador {
    color: var(--synbex-danger);
    font-weight: 700;
}

.campo-texto-largo.es-al-tope textarea {
    border-color: var(--synbex-danger);
}

/* ===== Dato fijo dentro de un formulario =====
   Para lo que se muestra pero no se elige (por ejemplo la unidad de la solicitud
   de mantenimiento, que ya viene de la sesión). Se ve como un dato, no como un
   campo desactivado: un campo gris invita a intentar tocarlo. */
.dato-fijo {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0;
    padding: 11px 14px;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-primary-soft);
    color: var(--synbex-text);
    font-weight: 600;
    font-size: 15px;
}

.dato-fijo-icono {
    display: flex;
    flex-shrink: 0;
    color: var(--synbex-primary);
}

.dato-fijo-icono svg {
    width: 19px;
    height: 19px;
}


/* Los íconos de las tarjetas también entran en la escala. Va con max- y no con
   width/height a propósito: los dibujos de HubIcons no miden todos lo mismo (la
   mayoría son de 26, unos pocos de 22), y poniendo un ancho fijo los estaría
   igualando a todos. Con un tope, cada uno conserva su tamaño mientras quepa y
   solo se achica cuando el tope baja. */
.hub-tile-icon svg {
    max-width: var(--ui-tile-icono-svg);
    max-height: var(--ui-tile-icono-svg);
}

/* ===== "Reservas y visitas": más compacta y con la lista desplegable por dentro ==

   Todo lo de este bloque cuelga de .misreservas-page, así que vale SOLO en esa
   pantalla (pedido explícito: "solamente esta página, por el momento"). El resto
   de la aplicación no se entera.

   Por qué acá y no en la escala general: esta pantalla mete dos paneles con foto,
   datos, estado y botones en una sola vista. Con los tamaños de siempre, en un
   portátil hay que desplazarse para llegar a "Visitas esperadas", que es media
   pantalla que nadie ve. Los valores salen de los tokens de la escala de interfaz
   (--ui-*), así que se siguen achicando solos con el zoom en vez de quedarse
   clavados. */
.misreservas-page .synbex-card {
    padding: var(--ui-tile-pad-x);
}

.misreservas-page .catalog-section-header h3 {
    font-size: var(--ui-tile-titulo);
}

/* Los dos botones que traen a esta pantalla ("Reservar área común" y "Registrar
   visita"): siguen siendo la acción principal, pero no tienen que gritar. */
.misreservas-page .synbex-cta-btn {
    padding: calc(var(--ui-boton-pad-y) + 2px) calc(var(--ui-boton-pad-x) + 4px);
    font-size: var(--ui-nav-fz);
}

.misreservas-page .reserva-rows {
    gap: 8px;
}

.misreservas-page .reserva-row {
    padding: 9px 10px;
    gap: 12px;
}

/* La foto del área baja de 120x76 a 92x58: se sigue reconociendo el lugar, que es
   para lo que está, y deja de mandar el alto de toda la fila. */
.misreservas-page .reserva-row-photo,
.misreservas-page .reserva-row-photo-empty {
    width: 92px;
    height: 58px;
}

.misreservas-page .reserva-row-initials {
    width: 42px;
    height: 42px;
    font-size: var(--ui-tile-titulo);
}

.misreservas-page .reserva-row-title {
    font-size: var(--ui-tile-titulo);
}

.misreservas-page .reserva-row-line {
    font-size: var(--ui-tile-desc);
}

.misreservas-page .reserva-row-btn {
    padding: 5px 10px;
    font-size: var(--ui-etiqueta-fz);
}

/* El ancho fijo por columna quedó obsoleto: el diseño en bloques de abajo
   (.es-tarjeta) reparte el espacio solo, en vez de columnas de píxeles fijos.
   Se deja nada más el respiro más angosto entre bloques para esta página. */
.misreservas-page .reserva-row-top,
.misreservas-page .reserva-row.tiene-detalles .reserva-row-top {
    gap: 12px;
}

/* ---- La lista: tres filas a la vista, el resto se despliega por dentro ----

   El problema que resuelve: si alguien tiene veinte reservas, la lista completa
   empujaba "Visitas esperadas" al fondo de la página y había que desplazarse un
   buen rato para llegar. Ahora cada bloque conserva su tamaño y lo que crece es
   solo lo que cabe adentro.

   --agenda-alto-fila es el alto de UNA fila más su separación; se fija por bloque
   porque la fila de reservas trae foto (más alta) y la de visitas un redondel con
   las iniciales. El tope al desplegar son 3,5 filas a propósito: esa media fila
   asomando por abajo es lo que le dice a la persona que hay más y que puede
   deslizar ahí adentro (pedido explícito). Sin ella, un recorte limpio parece el
   final de la lista. */
.misreservas-page .agenda-lista.es-reservas {
    --agenda-alto-fila: 78px;
}

.misreservas-page .agenda-lista.es-visitas {
    --agenda-alto-fila: 63px;
}

.misreservas-page .agenda-lista.es-desplegada {
    /* 3 filas + sus 3 separaciones + media fila asomando. Los altos están medidos
       en el navegador y no dependen del ancho de la pantalla, porque lo que manda
       el alto de la fila es la foto (o el redondel de iniciales), que va en px. */
    max-height: calc(3.5 * var(--agenda-alto-fila) + 3 * 8px);
    overflow-y: auto;
    /* Aire a la derecha para que la barra de desplazamiento no se monte encima de
       los botones de la fila. */
    padding-right: 6px;
}

/* Al abrir una fila para ver su detalle, esa fila ya no mide lo mismo que las
   demás y el tope de 3,5 filas la cortaría por la mitad. Mientras haya un detalle
   abierto se deja crecer hasta media pantalla, que sigue siendo un tope. */
.misreservas-page .agenda-lista.es-desplegada:has(.reserva-row.is-abierto) {
    max-height: 50vh;
}

/* En celular la fila se parte en dos renglones y pasa de 78px a unos 170: con el
   tope de 3,5 filas no entraría ni la segunda. Acá el tope se mide contra la
   pantalla, que es lo que de verdad limita, y el corte a media fila sale solo. */
@media (max-width: 700px) {
    .misreservas-page .agenda-lista.es-desplegada,
    .misreservas-page .agenda-lista.es-desplegada:has(.reserva-row.is-abierto) {
        max-height: 60vh;
    }
}

.misreservas-page .agenda-mostrar-mas {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: 100%;
    margin-top: 10px;
    padding: var(--ui-boton-pad-y) var(--ui-boton-pad-x);
    border: 1px dashed var(--synbex-border);
    border-radius: var(--synbex-radius-md);
    background: transparent;
    color: var(--synbex-text-muted);
    font-family: inherit;
    font-size: var(--ui-boton-fz);
    font-weight: 600;
    cursor: pointer;
    transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}

.misreservas-page .agenda-mostrar-mas:hover {
    color: var(--synbex-primary);
    border-color: var(--synbex-primary);
    background: var(--synbex-primary-soft);
}

.misreservas-page .agenda-mostrar-mas svg {
    transition: transform 0.2s ease;
}

/* Un solo dibujo de flecha que gira, en vez de dos íconos distintos: así el botón
   no "salta" al cambiar de estado. */
.misreservas-page .agenda-mostrar-mas.es-abierto svg {
    transform: rotate(180deg);
}

/* ===== "Mis paquetes": tarjeta individual en vez de tabla ==============
   Pedido explícito, con una imagen de referencia: cada paquete es su propia
   fila con margen arriba y abajo, ícono, unidad, fecha y estado — nunca una
   tabla. Sirve igual para "Pendientes" y para "Historial de retiros"
   (PackageRow.razor), así que las reglas de acá valen para las dos pestañas
   sin repetir nada. Ajustado una segunda vez (pedido explícito): sin el
   resplandor de color, con líneas verticales entre cada bloque de datos, y el
   ícono del calendario al lado de la fecha en vez de arriba de la etiqueta. */
.paquetes-lista {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

/* Tres columnas EXACTAMENTE iguales (pedido explícito: "quiero que las líneas
   verticales estén exactamente igual de tamaño") — Unidad (ícono + texto,
   los dos juntos), Fecha y Estado se reparten el ancho en tercios parejos.
   Con el botón "Detalles" (solo Historial) se agrega una cuarta columna
   aparte, de su propio ancho — los tres tercios se recalculan solos sobre lo
   que queda, que es justamente "restarle el espacio del botón y repartir el
   resto en tres" (así funciona 1fr en CSS Grid, sin tener que calcularlo a
   mano). */
.paquete-row {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    align-items: center;
    gap: 20px;
    padding: 14px 18px;
    border-radius: var(--synbex-radius-md);
    /* Mismo fondo y borde de siempre para una fila agendada — .es-verde/.es-gris/
       etc. (arriba) le agregan encima solo la rayita, sin el resplandor que sí
       tienen reservas y visitas. */
    background: color-mix(in srgb, var(--synbex-surface) 93%, var(--synbex-text-muted));
    border: 1px solid var(--synbex-border);
    border-left-width: 4px;
    border-left-style: solid;
    border-left-color: var(--synbex-border);
}

.paquete-row.tiene-detalles {
    grid-template-columns: 1fr 1fr 1fr auto;
}

/* Primera columna: ícono + "Unidad" juntos, como un solo bloque — alineado a
   la izquierda (pedido explícito), a diferencia de Fecha y Estado que van
   centrados dentro de su tercio. */
.paquete-row-unidad {
    display: flex;
    align-items: center;
    gap: 14px;
    min-width: 0;
}

/* Caja cuadrada con el ícono, en el tono de la marca — a propósito NO un
   color fijo: el mismo --synbex-primary-soft/--synbex-primary de cualquier
   botón, así que si la marca cambia de color esta caja cambia sola con ella. */
.paquete-row-icono {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    flex-shrink: 0;
    border-radius: var(--synbex-radius-md);
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.paquete-row-icono svg {
    width: 24px;
    height: 24px;
}

.paquete-row-grupo {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

/* "UNIDAD" chiquito y en mayúsculas arriba, el número grande abajo — mismo
   patrón de etiqueta-sobre-valor que ya usan las fichas de Mi edificio
   (.ficha-dato-label/.ficha-dato-valor), reusado acá en vez de inventar uno
   nuevo para lo mismo. Tamaños con la escala fluida (--ui-paq-*): a 1920 sin
   zoom se ven un poco más grandes que antes (pedido explícito), y de ahí para
   abajo se van achicando solos hasta llegar al tamaño de siempre. */
.paquete-row-etiqueta {
    font-size: var(--ui-paq-etiqueta);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--synbex-text-muted);
}

.paquete-row-valor {
    font-size: var(--ui-paq-valor);
    font-weight: 700;
    color: var(--synbex-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.paquete-row-sub {
    font-size: var(--ui-paq-sub);
    color: var(--synbex-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* La fecha, la segunda columna: el ícono va A LA IZQUIERDA del texto —no
   arriba, pegado a la etiqueta— porque así se ve en la imagen de referencia.
   Centrada dentro de su tercio (pedido explícito), a diferencia de Unidad. */
.paquete-row-fecha {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    white-space: nowrap;
}

.paquete-row-fecha-icono {
    flex-shrink: 0;
    display: flex;
    color: var(--synbex-text-muted);
}

.paquete-row-fecha-icono svg {
    width: 18px;
    height: 18px;
}

.paquete-row-valor-fecha {
    font-size: var(--ui-paq-valor-fecha);
    font-weight: 600;
    color: var(--synbex-text);
}

/* Estado, la tercera columna — centrada dentro de su tercio (pedido
   explícito), a diferencia de Unidad. */
.paquete-row-estado {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
}

.paquete-row-nota {
    font-size: var(--ui-paq-nota);
    color: var(--synbex-text-muted);
}

/* Rayitas verticales entre "Unidad", "Fecha" y "Estado" (pedido explícito,
   comparando con la imagen de referencia) — un border-left en vez de un div
   aparte, para que crezca solo con el alto real de cada fila. */
.paquete-row-fecha,
.paquete-row-estado {
    padding-left: 20px;
    border-left: 1px solid var(--synbex-border);
}

/* Botón "Detalles" — solo en Historial de retiros (pedido explícito: en
   Pendientes no hay nada que contar, así que ahí no se dibuja ni esto ni la
   rayita de antes). Reemplaza al "mayor que" (›) que se veía suelto y sin
   decir nada: un texto siempre se entiende sin tener que adivinar. */
.paquete-row-detalles {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 6px 12px;
    border: 1px solid var(--synbex-border);
    border-radius: 999px;
    background: transparent;
    color: var(--synbex-primary);
    font-family: inherit;
    font-size: var(--ui-paq-detalles);
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
}

.paquete-row-detalles svg {
    width: 14px;
    height: 14px;
}

.paquete-row-detalles:hover {
    background: var(--synbex-primary-soft);
    border-color: var(--synbex-primary);
}

/* Tablet: la fecha se corre debajo de la unidad (comparte su columna) y el
   estado se queda a la derecha — todavía cabe en una fila. Las rayitas
   verticales desaparecen: ya no separan bloques en la misma línea, así que
   no tendrían nada que partir. Los tercios exactos son cosa de escritorio,
   donde sí hay ancho de sobra para repartir con cuidado. */
@media (max-width: 900px) {
    .paquete-row {
        grid-template-columns: minmax(0, 1fr) auto;
    }

    .paquete-row-fecha,
    .paquete-row-estado {
        border-left: none;
    }

    .paquete-row-fecha {
        grid-column: 1;
        grid-row: 2;
        justify-content: flex-start;
        padding-left: 0;
    }
}

/* Celular: una columna, todo apilado — mismo criterio que .reserva-row en
   @media (max-width: 700px) más abajo. El ícono ya no es un área aparte:
   ahora vive DENTRO de .paquete-row-unidad (el mismo bloque que se mueve
   entero), así que la lista de áreas queda con una menos. */
@media (max-width: 600px) {
    .paquete-row {
        grid-template-columns: 1fr auto;
        grid-template-areas:
            "unidad unidad"
            "fecha  detalles"
            "estado estado";
        row-gap: 8px;
        padding: 12px;
    }

    .paquete-row-unidad {
        grid-area: unidad;
    }

    .paquete-row-detalles {
        grid-area: detalles;
        justify-self: end;
        align-self: center;
    }

    .paquete-row-fecha {
        grid-area: fecha;
        justify-content: flex-start;
        padding-left: 0;
        border-left: none;
    }

    .paquete-row-estado {
        grid-area: estado;
        align-items: flex-start;
        padding-left: 0;
        border-left: none;
    }
}

/* ===== Caja de ícono de paquete + modal de línea de tiempo (compartidos) =====
   Nacieron en Paqueteria/Index.razor.css (panel del guarda) y se mudan acá
   porque "Mis paquetes" del residente ahora necesita el mismo modal de detalle
   —"casi la misma información que le sale al guarda" (pedido explícito)—; con
   CSS aislado de Blazor cada página tiene su propio ámbito y una no puede usar
   los estilos de la otra, así que promoverlos a global es lo que permite
   compartirlos de verdad en vez de mantener dos copias que se van a ir
   separando con el tiempo. Nada cambia para Paquetería: es el mismo CSS, en
   otro archivo. */
.package-icon-box {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--synbex-radius-md);
}

.package-icon-box.size-lg {
    width: 56px;
    height: 56px;
}

.package-icon-box.size-md {
    width: 44px;
    height: 44px;
}

.package-icon-box.size-sm {
    width: 36px;
    height: 36px;
}

.package-icon-box.is-primary {
    background: var(--synbex-primary-soft);
    color: var(--synbex-primary);
}

.package-icon-box.is-orange {
    background: rgba(217, 119, 6, 0.12);
    color: #d97706;
}

.package-icon-box.is-green {
    background: rgba(22, 163, 74, 0.12);
    color: var(--synbex-success);
}

.package-icon-box.is-slate {
    background: rgba(107, 114, 128, 0.14);
    color: var(--synbex-text-muted);
}

/* Modal de "Ver detalles" de Entrega reciente — antes era una lista plana de 4
   campos (Llegó/Recibido por/Recogido/Entregado por); ahora es una línea de
   tiempo de dos pasos (llegada → retiro), cada uno con su ícono, fecha, "hace
   X" y quién estuvo a cargo — se lee de un vistazo como una historia, no como
   un formulario.
   Más ancho y con más aire que el modal genérico (pedido explícito: "está muy
   unida") — mismo patrón de clase combinada que .entry-point-settings-modal
   en app.css, para no tocar el modal base que usan el resto de pantallas. */
/* 750px — 50% más ancho que el genérico de 500px que tenía antes (pedido
   explícito: "por lo menos un 50% más"). El límite de celular sigue en su
   propia media query más abajo, sin cambios. */
.package-detail-modal {
    max-width: 750px;
}

.package-detail-modal-body {
    padding: 26px 30px;
}

@media (max-width: 560px) {
    /* En celular el ancho de sobra no sirve de nada y el padding grande le
       come espacio útil a la pantalla — vuelve al tamaño normal ahí. */
    .package-detail-modal-body {
        padding: 18px;
    }
}

.package-detail-status-row {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 20px;
}

.package-detail-note {
    margin: 0 0 22px;
    padding: 12px 14px;
    background: var(--synbex-bg);
    border-radius: var(--synbex-radius-md);
}

.package-detail-timeline {
    display: flex;
    flex-direction: column;
}

.package-detail-step {
    display: flex;
    gap: 14px;
    position: relative;
    padding-bottom: 26px;
}

.package-detail-step:last-child {
    padding-bottom: 0;
}

/* La línea que conecta los dos puntos — un pseudo-elemento en vez de un div
   aparte, para no tener que medir alturas a mano: siempre llega exacto desde
   el centro de un ícono hasta el siguiente. */
.package-detail-step:not(:last-child)::before {
    content: '';
    position: absolute;
    left: 15px;
    top: 32px;
    bottom: 0;
    width: 2px;
    background: var(--synbex-border);
}

.package-detail-step-dot {
    flex-shrink: 0;
    z-index: 1;
}

.package-detail-step-body {
    flex: 1;
    min-width: 0;
    padding-top: 5px;
}

.package-detail-step-title {
    font-weight: 700;
    font-size: 14px;
}

.package-detail-step-time {
    font-size: 12.5px;
    color: var(--synbex-text-muted);
    margin-top: 4px;
}

.package-detail-step-relative {
    color: var(--synbex-primary);
    font-weight: 600;
    margin-left: 4px;
}

.package-detail-step-person {
    font-size: 13px;
    margin-top: 8px;
}

/* ===== "Reservas y visitas": el mismo diseño en bloques que "Mis paquetes" ====
   Pedido explícito: "quiero usar el mismo diseño que usamos en mis
   paquetes... separarlo igual, tener el texto centrado, poner un mini título
   encima de cada cosa, separado por esos paneles verticales, y quitar el
   brillo que está a la izquierda". Solo para AgendaRow.razor (marca
   ".es-tarjeta" en su raíz) — "Mis reservas" completo (MyReservations.razor)
   tiene su propia fila con markup propio y no se tocó.

   Los bloques se reparten el espacio en partes iguales con flex:1, igual que
   .paquete-row con grid; se usa flex y no grid acá porque la columna de
   "Hora" es opcional (un área reservada por el día completo no tiene una
   franja horaria) y con flex las que sí están presentes se reparten solas lo
   que sobra, sin necesidad de una clase que cambie la plantilla a mano. */
.reserva-row.es-tarjeta .reserva-row-top {
    display: flex;
    align-items: center;
    gap: 20px;
}

.reserva-row-principal {
    display: flex;
    align-items: center;
    gap: 14px;
    flex: 1 1 0px;
    min-width: 0;
}

.reserva-row-principal-texto {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.reserva-row.es-tarjeta .reserva-row-title {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* El mini título encima de cada dato (pedido explícito: "poner un mini título
   encima de cada cosa") — mismo estilo que ya usa "Mis paquetes"
   (.paquete-row-etiqueta), repetido acá porque son componentes distintos. */
.reserva-row-etiqueta {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--synbex-text-muted);
}

/* Fecha y Hora: centradas dentro de su bloque, con una rayita vertical antes
   (pedido explícito) — a diferencia de "Principal", que va a la izquierda. */
/* El ítem flexible en sí NO lleva padding (ver el comentario en AgendaRow.razor:
   con flex:1 1 0px, un padding-left puesto directo acá repartía el ancho
   desigual entre columnas "iguales" — comprobado en el navegador). Solo la
   rayita vertical vive acá; el espacio hacia el texto lo pone
   .reserva-row-dato-inner, que ya no es el ítem flexible. */
.reserva-row-dato {
    display: flex;
    align-items: center;
    flex: 1 1 0px;
    min-width: 0;
    border-left: 1px solid var(--synbex-border);
}

.reserva-row-dato-inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    width: 100%;
    box-sizing: border-box;
    text-align: center;
    padding-left: 20px;
    white-space: nowrap;
}

.reserva-row-dato-valor {
    font-size: 14px;
    font-weight: 600;
    color: var(--synbex-text);
}

/* Estado: mismo reparto y la misma rayita — sin mini título propio, porque la
   insignia de color ya dice de qué se trata (mismo criterio que "Mis
   paquetes", donde el estado tampoco lleva etiqueta encima). */
/* Mismo motivo que .reserva-row-dato arriba: el ítem flexible se queda solo
   con la rayita, sin padding — el espacio hacia el badge lo pone
   .reserva-row-state-inner. */
.reserva-row.es-tarjeta .reserva-row-state {
    display: flex;
    align-items: center;
    flex: 1 1 0px;
    min-width: 0;
    border-left: 1px solid var(--synbex-border);
}

.reserva-row-state-inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    width: 100%;
    box-sizing: border-box;
    padding-left: 20px;
    text-align: center;
}

/* Celular: se cae la repartición en bloques iguales y las rayitas verticales
   —ya no separan nada en la misma línea— y todo se apila a la izquierda,
   mismo criterio que "Mis paquetes" en su propio punto de quiebre. */
@media (max-width: 700px) {
    .reserva-row.es-tarjeta .reserva-row-top {
        flex-wrap: wrap;
        row-gap: 10px;
    }

    .reserva-row-principal {
        flex: 1 1 100%;
    }

    .reserva-row-dato,
    .reserva-row.es-tarjeta .reserva-row-state {
        flex: 1 1 auto;
        border-left: none;
    }

    .reserva-row-dato-inner,
    .reserva-row-state-inner {
        align-items: flex-start;
        text-align: left;
        padding-left: 0;
    }
}

/* Aire extra abajo del todo, después de "Visitas esperadas" (pedido explícito:
   "un espacio como un margin abajo grande para poderse deslizar hacia abajo...
   un poquito grande, no tan exagerado"). El relleno normal de la página
   (.dashboard-main) es el mismo en los cuatro lados; esto es ADEMÁS de eso,
   solo en esta pantalla — el último panel no queda pegado al borde de abajo. */
.misreservas-page {
    padding-bottom: 56px;
}

/* Variante de dos tarjetas centradas (pedido explícito: "Reservas y visitas"
   con solo dos botones, "que salga en el centro... igual que Comunidad y Mi
   hogar"). .hub-grid-3 fuerza TRES columnas siempre — con solo dos tarjetas
   dentro, la tercera columna quedaba vacía y las dos se veían pegadas a la
   izquierda en vez de centradas. Con dos columnas y 2/3 del ancho máximo de
   siempre, cada tarjeta queda del mismo tamaño que tendría en la grilla de
   tres, nada más que ahora sí centrada. */
.hub-grid-2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    max-width: min(100%, calc(var(--ui-hub-3-ancho) * 0.667));
    margin: 0 auto;
    gap: var(--ui-hub-gap);
}

@media (max-width: 720px) {
    .hub-grid-2 {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* --------------------------------------------------------------------------
   Pestañas pegadas a un panel (2026-10-02): Documento/Vehículo en Registrar e
   Ingresos/Parqueaderos en Consultas
   --------------------------------------------------------------------------
   Como las pestañas de una carpeta: la abierta es del mismo color que el
   panel, sin borde abajo, y tapa el borde de arriba del panel justo donde está
   (por eso baja 1px y queda encima), así se ve como una sola pieza con lo de
   abajo. La cerrada conserva su borde de abajo —que es la misma línea del borde
   del panel— y un fondo más apagado, así se ve cerrada.

   Uso: <div class="panel-pestanas"> con <button class="panel-pestana is-active">
   justo ANTES del panel (.synbex-card) al que se pegan, y ese panel con
   .has-pestanas. Sin transición de color a propósito: el cambio de pestaña se
   ve mejor instantáneo. */
.panel-con-pestanas {
    display: flex;
    flex-direction: column;
}

.panel-pestanas {
    position: relative;
    z-index: 1;
    display: flex;
    gap: 6px;
    margin-bottom: -1px;
}

.panel-pestana {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-width: 160px;
    padding: 11px 22px;
    border: 1px solid var(--synbex-border);
    border-radius: 12px 12px 0 0;
    background: var(--synbex-bg-soft, var(--synbex-bg));
    color: var(--synbex-text-muted);
    font-size: 15px;
    font-weight: 700;
    cursor: pointer;
}

.panel-pestana svg {
    width: 22px;
    height: 22px;
}

.panel-pestana:hover:not(.is-active) {
    color: var(--synbex-text);
}

.panel-pestana.is-active {
    background: var(--synbex-surface);
    border-bottom-color: var(--synbex-surface);
    color: var(--synbex-primary);
}

/* La esquina de arriba a la izquierda del panel queda recta: ahí arranca la
   primera pestaña. */
.synbex-card.has-pestanas {
    border-top-left-radius: 0;
}

/* Celular: las pestañas se reparten el ancho, así que las dos esquinas de
   arriba del panel quedan rectas. */
@media (max-width: 600px) {
    .panel-pestana {
        flex: 1 1 0;
        min-width: 0;
        padding: 11px 10px;
    }

    .synbex-card.has-pestanas {
        border-top-right-radius: 0;
    }
}
