/* ── Verin design tokens (light default) ── */
:root,
html[data-theme="light"] {
    --verin-green: #1d9e75;
    --verin-green-strong: #0f6e56;
    --verin-green-deep: #085041;

    /* Verde accesible para superficies que llevan texto encima. El verde de marca con texto
       blanco da 3,39:1, por debajo del 4,5:1 que WCAG AA pide para texto normal — afectaba a
       todos los botones primarios (incluido "Publicar mi espacio gratis" del landing), al
       enlace "Saltar al contenido" y al texto verde sobre blanco. Este tono es el mismo
       verde un paso más oscuro y llega a 4,67:1. --verin-green NO cambia: sigue siendo el
       color de identidad para logotipos y elementos decorativos. */
    --verin-green-accessible: #17845f;

    --green-dark: #085041;
    --green-mid: #0f6e56;
    --green-base: #1d9e75;
    --green-light: #5dcaa5;
    --green-pale: #e1f5ee;

    --blue-dark: #042c53;
    --blue-mid: #185fa5;
    --blue-base: #378add;
    --blue-pale: #e6f1fb;

    --amber-dark: #412402;
    --amber-mid: #ba7517;
    --amber-pale: #faeeda;

    --gray-900: #1f2937;
    --gray-700: #374151;
    --gray-500: #6b7280;
    --gray-400: #9ca3af;
    --gray-300: #d1d5db;
    --gray-200: #e5e7eb;
    --gray-100: #f3f4f6;
    --gray-50: #f9fafb;

    --bg: #f8fafc;
    --bg-gradient-end: #f1f5f9;
    --surface: #ffffff;
    --surface-muted: #f1f5f9;
    --text: #0f172a;
    --text-muted: #475569;
    --brand-foreground: var(--text);
    --border: #e2e8f0;
    --muted: #64748b;

    --primary: var(--verin-green-accessible);
    --primary-strong: var(--verin-green-strong);
    --success: #059669;
    --danger: #dc2626;
    --danger-bg: #fef2f2;
    --danger-border: #fecaca;
    --danger-text: #7f1d1d;
    --danger-strong: #b91c1c;
    --warning: #d97706;

    /* Los DOS tokens semánticos de arriba NO valen como color de texto, y esto es medición y no
     * criterio: `--success` da 3,77:1 y `--warning` 3,18:1 sobre superficie clara, por debajo del
     * 4,5 que pide AA. Sí valen para lo que están pensados —fondos, bordes e iconos—, porque el
     * umbral de un gráfico es 3:1 y los dos lo pasan.
     *
     * Para TEXTO se usan estos dos, que son los de las parejas pastel del sistema y se invierten
     * solos en tema oscuro. Existen con nombre semántico para que la página no tenga que saber que
     * "el verde de éxito, en texto, es el verde oscuro de la pareja": eso es justo el conocimiento
     * que se pierde y produce la siguiente regresión.
     *
     * Es la regla 26 —que ya prohíbe `--primary` como texto— extendida a los tokens semánticos. */
    --success-text: var(--green-dark);
    --warning-text: var(--amber-dark);

    /* Estrellas de calificación. Están tokenizadas para que el componente compartido
       StarRating salga con la paleta de cada producto sin tocarlo: Oficinas redefine
       --star-on a su --of-accent (que hoy es este mismo ámbar). Antes eran literales
       dentro del CSS aislado del componente, que es justo lo que impide que un
       componente compartido se marque por configuración. */
    --star-on: #f59e0b;
    --star-off: var(--gray-300);
    --ring: 0 0 0 3px rgba(29, 158, 117, 0.22);

    --radius-sm: 10px;
    --radius-md: 14px;
    --radius-lg: 18px;
    --radius-xl: 24px;

    --shadow-card: 0 10px 30px rgba(15, 23, 42, 0.06);

    color-scheme: light;
}

/* ── Escala de breakpoints ──────────────────────────────────────────────────────
   CSS nuevo usa SOLO estos cuatro cortes:

       480px   móvil
       768px   tableta
      1024px   escritorio estrecho
      1280px   escritorio ancho

   No son variables porque no pueden serlo: @media no evalúa var(), así que el
   valor va literal en cada consulta. La escala se sostiene con el test
   BreakpointScaleTests, que falla si aparece un corte nuevo fuera de la lista.

   Quedan cortes heredados fuera de la escala (600, 640, 560, 720, 900…). NO se
   redondearon a ciegas: mover un corte cambia a qué anchura colapsa el layout, y
   la mayoría de esas páginas todavía no se ha visto en un navegador. Se irán
   normalizando a medida que cada página se verifique en vivo; el test los tiene
   listados uno a uno y su lista solo puede encogerse.
   ────────────────────────────────────────────────────────────────────────────── */

html[data-theme="dark"] {
    --gray-900: #f4f4f5;
    --gray-700: #d4d4d8;
    --gray-500: #a1a1aa;
    --gray-400: #71717a;
    --gray-300: #52525b;
    --gray-200: #3f3f46;
    --gray-100: #27272a;
    --gray-50: #18181b;

    --bg: #0a0a0a;
    --bg-gradient-end: #171717;
    --surface: #141414;
    --surface-muted: #1c1c1c;
    --text: #fafafa;
    --text-muted: #a1a1aa;
    --brand-foreground: var(--text);
    --border: #2e2e2e;
    --muted: #a1a1aa;

    /* En oscuro --primary se usa sobre todo como color de TEXTO sobre superficies oscuras, donde
       el verde de marca contrasta mejor (5,48:1) que el accesible-sobre-blanco. Las superficies
       verdes con texto blanco las cubre .btn-primary más abajo, que fija su propio fondo. */
    --primary: var(--verin-green);
    --primary-strong: #5dcaa5;
    --danger-bg: #2a1212;
    --danger-border: #5f1d1d;
    --danger-text: #fecaca;
    --danger-strong: #f87171;
    --ring: 0 0 0 3px rgba(29, 158, 117, 0.35);

    --shadow-card: 0 10px 30px rgba(0, 0, 0, 0.45);

    /* Los tokens pastel se usan SIEMPRE en pareja: fondo `*-pale` + texto `*-dark`. Antes solo
       se redefinía --green-pale, así que las etiquetas quedaban en verde oscuro (#085041) sobre
       verde oscuro (#0d281f): 1,67:1 de contraste, prácticamente ilegible en los badges del
       panel de anfitrión ("Gran evento", "Ideal fiestas") y en el día seleccionado de los
       calendarios de anfitrión y proveedor. Al invertir también el color de texto la pareja
       vuelve a leerse (>7:1). Mismo razonamiento para azul y ámbar, que ya tenían el fondo
       claro incrustado y ahora sí acompañan al tema. */
    --green-pale: #0d281f;
    --green-dark: #7fdcbb;
    --green-mid: #4cc59d;

    --blue-pale: #0d1f33;
    --blue-dark: #9ac9f2;
    --blue-mid: #5ea9e8;

    --amber-pale: #2b1f0a;
    --amber-dark: #f0c887;
    --amber-mid: #d9a253;

    color-scheme: dark;
}

/* The admin panel renders on a fixed light surface (.admin-shell { background:#f8fafc })
   with a dark sidebar that uses literal colors — it is not dark-mode-aware. Under the
   global dark theme, themed tokens (--text, surfaces, borders) flip to dark values and
   leak onto that light surface, making page titles (var(--text)) and shared Verin
   components invisible/low-contrast. Re-pin the light token values as an island so admin
   content stays readable regardless of the global theme. (AUD-017) */
html[data-theme="dark"] .admin-shell {
    --bg: #f8fafc;
    --bg-gradient-end: #f1f5f9;
    --surface: #ffffff;
    --surface-muted: #f1f5f9;
    --text: #0f172a;
    --text-muted: #475569;
    --brand-foreground: #0f172a;
    --border: #e2e8f0;
    --muted: #64748b;

    --gray-900: #1f2937;
    --gray-700: #374151;
    --gray-500: #6b7280;
    --gray-400: #9ca3af;
    --gray-300: #d1d5db;
    --gray-200: #e5e7eb;
    --gray-100: #f3f4f6;
    --gray-50: #f9fafb;

    --primary-strong: var(--verin-green-strong);
    --danger-bg: #fef2f2;
    --danger-border: #fecaca;
    --danger-text: #7f1d1d;
    --danger-strong: #b91c1c;

    --shadow-card: 0 10px 30px rgba(15, 23, 42, 0.06);
    color-scheme: light;
    /* Re-establish the inherited text color too: re-pinning --text only fixes elements
       that use var(--text) explicitly. Elements relying on inherited color still get the
       dark-theme white from `body`, landing white-on-white on the light admin surface
       (e.g. card titles in /admin/seed-listings). (Bug #8) */
    color: var(--text);
}

#blazor-error-ui {
    background: var(--surface);
    color: var(--text);
    border-top: 1px solid var(--border);
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.15);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

/* El «Recargar» del mismo aviso medía 68x20. Es la única salida cuando la aplicación ha fallado —el
   otro control es cerrar el aviso— así que es el peor sitio para un objetivo pequeño. Va junto al
   botón de cerrar, que se arregló el 2026-08-08 por lo mismo (era un <span> con emoji). */
#blazor-error-ui .reload {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
}

/* Ahora es un <button>: hay que neutralizar el aspecto nativo y darle área táctil de 44 px, que el
   <span> anterior no tenía (medía lo que el glifo). */
#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    padding: 0;
    background: none;
    border: 0;
    color: inherit;
}

/* Wordmark: V = brand green, erin = foreground (black/white by theme) */
.verin-mark {
    text-decoration: none;
    font-weight: 800;
    letter-spacing: -0.02em;
}

.verin-mark-v {
    color: var(--verin-green);
}

.verin-mark-rest {
    color: var(--brand-foreground);
}

/* Brand logo theme switching (used in landing topbar/footer).
   ── El `display: block` vive AQUÍ y no en un CSS aislado, y el orden importa ──
   `Index.razor.css` lo declaraba sobre `.brand-logo`. El aislamiento de Blazor lo compila a
   `.brand-logo[b-xxxxx]`, especificidad (0,2,0), que gana a `.brand-logo-dark` (0,1,0): en tema
   claro la variante oscura NO se ocultaba y se pintaban los DOS logotipos, en la cabecera y en el
   pie. En oscuro no pasaba porque quien oculta es `html[data-theme="dark"] .brand-logo-light`
   (0,2,1), que sí gana por un punto — y como el tema por defecto es el claro, el defecto lo veía
   la mayoría de las visitas nuevas.

   Aquí las tres reglas comparten especificidad (0,1,0) y decide el orden de aparición, que es
   estable y legible. No muevas `.brand-logo` por debajo de `.brand-logo-dark`: volvería el defecto.
   Lo vigila BrandLogoThemeTests. */
.brand-logo { display: block; }
.brand-logo-dark { display: none; }
html[data-theme="dark"] .brand-logo-light { display: none; }
html[data-theme="dark"] .brand-logo-dark  { display: block; }

/* ── "Saltar al contenido" (WCAG 2.4.1, bypass blocks) ─────────────────────────
   Vivía en el CSS aislado de MainLayout, así que solo existía en las páginas de ese
   layout: la landing (EmptyLayout) y Oficinas se quedaban sin él, y quien navega con
   teclado tenía que recorrer la barra entera en cada página. Es una primitiva del
   sistema de diseño, no de un layout — aquí la heredan los dos productos.
   Invisible hasta recibir foco. */
.skip-link {
    position: absolute;
    left: 8px;
    top: -40px;
    /* NO usar --primary: en tema oscuro pasa al verde brillante y el blanco encima cae
       a 3,39:1. Este token se mantiene en el tono accesible en ambos temas, y cada
       producto lo redefine con su marca (Oficinas: su azul fuerte, 6,7:1). */
    background: var(--verin-green-accessible, #17845f);
    color: #fff;
    /* 175x39 medidos. Es el primer control de cada página y el único que existe para quien navega
       con teclado, así que no puede quedarse a 5px del mínimo (regla 27). `inline-flex` porque es
       un <a>: sin él, `min-height` añadiría el alto por debajo del texto en vez de alrededor. */
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0.5rem 0.9rem;
    border-radius: 6px;
    font-weight: 700;
    z-index: 10000;
    text-decoration: none;
    transition: top 0.12s ease;
}
.skip-link:focus { top: 8px; outline: 2px solid #fff; outline-offset: 2px; }
/* El destino recibe foco por programa (tabindex="-1"); no debe dibujar anillo. */
[id="main-content"]:focus { outline: none; }

/* ── Solo para lectores de pantalla ────────────────────────────────────────────
   Para rótulos que la vista ya comunica por diseño (una región cuyo encabezado se
   entiende por su posición) pero que un lector necesita oír. No usa `display:none`
   ni `visibility:hidden`: eso lo borraría también del árbol de accesibilidad. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ── Área táctil mínima (WCAG 2.5.5 / 2.5.8) ──────────────────────────────────
   Un botón de icono dibujado a 22 o 30 px se pulsa mal con el dedo: el objetivo
   real queda por debajo de los 44 px que pide la pauta, y en móvil se falla el
   toque o se acierta el control de al lado. Esta utilidad amplía la zona
   sensible SIN tocar el tamaño dibujado ni empujar el layout — el
   pseudo-elemento se centra sobre el control y captura el puntero por él, así
   que el diseño no cambia y el objetivo sí.

   `min-width/height: 100%` evita encoger un control que ya sea más grande en
   algún eje (una píldora ancha y baja conserva su ancho y solo gana alto).

   `position: relative` es el ancla del pseudo-elemento y no pisa a nadie: un
   control que ya se posicione (`absolute` en la esquina de una tarjeta) lo
   declara en su CSS aislado, y el atributo de aislamiento le da más
   especificidad que esta clase. */
.verin-tap-target { position: relative; }
.verin-tap-target::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 44px;
    height: 44px;
    min-width: 100%;
    min-height: 100%;
    transform: translate(-50%, -50%);
    /* Decorativo puro: no debe leerse ni pintarse, solo recibir el toque. */
    pointer-events: auto;
}

*,
*::before,
*::after {
    box-sizing: border-box;
}

body {
    font-family: Inter, "Segoe UI", Roboto, system-ui, -apple-system, sans-serif;
    margin: 0;
    background: linear-gradient(180deg, var(--bg) 0%, var(--bg-gradient-end) 100%);
    color: var(--text);
    line-height: 1.45;
}

/* --primary como color de enlace da 4,67:1 sobre blanco puro, pero 4,25:1 en cuanto
   el fondo se tiñe un poco (--surface-muted, tarjetas, avisos), que es por debajo de
   AA. --primary-strong pasa de 6:1 en los dos casos y en oscuro sigue siendo legible.
   Medido con axe sobre las páginas legales. */
a {
    color: var(--primary-strong);
}

button,
input,
select,
textarea {
    font: inherit;
}

/* El indicador de foco va en `outline`, NO solo en `box-shadow`.
 *
 * Antes era `outline: none; box-shadow: var(--ring)`, y eso deja sin anillo a todo control que
 * tenga sombra propia: la sombra del componente compite por la MISMA propiedad y gana por
 * especificidad o por orden de carga. Medido con el teclado sobre la página real: los botones
 * primarios de Verin y las tarjetas de tipo de Oficinas quedaban sin ninguna señal al tabular
 * —`:focus-visible` casaba, pero el box-shadow no cambiaba ni un píxel—. Es WCAG 2.4.7 (AA).
 *
 * `outline` no la puede pisar ninguna sombra, y con `outline-offset` se separa del borde en vez
 * de confundirse con él. El box-shadow se mantiene: donde no hay conflicto, suma.
 *
 * `--primary-strong` está declarado en los dos temas de la plataforma y también en
 * `offices-theme.css`, así que el anillo sale con la marca de cada producto. */
button:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid var(--primary-strong);
    outline-offset: 2px;
    box-shadow: var(--ring);
}

.topnav {
    display: flex;
    justify-content: space-between;
    padding: 12px 20px;
    background: var(--surface-muted);
}

.nav-links {
    list-style: none;
    display: flex;
    gap: 12px;
    margin: 0;
}

.cta {
    display: inline-block;
    padding: 10px 14px;
    background: var(--verin-green-accessible);
    color: white;
    border-radius: 6px;
}

/* Components */
.btn {
    padding: 8px 12px;
    border-radius: 6px;
    border: 0;
    cursor: pointer;
}

.btn.primary {
    background: var(--verin-green-accessible);
    color: #fff;
}

/* `.card` ya NO es flex, y `.card-image` se retira entera.
 *
 * Este bloque venía del patrón tarjeta-con-imagen: una miniatura de 140x100 a la izquierda y el
 * texto a la derecha, colocados por `display:flex` + `gap`. **`.card-image` no la usa nadie** —
 * comprobado sobre los tres proyectos web—, así que la fila ya no tenía nada que colocar y lo
 * único que hacía era imponerse a quien usara `class="card"` para otra cosa. Le rompió el diseño
 * a `/worker/spaces/{id}/rules`, que la usa como panel de contenido: sus tres hijos se pusieron en
 * fila y la página desbordó 397 px a 375 de ancho.
 *
 * Lo que SÍ sigue vivo es el resto de la caja: `ReviewList.razor` monta sus reseñas con
 * `class="review-card card"` + `.card-body` y no declara estilo propio, así que el borde, el radio,
 * el fondo y el relleno de aquí son toda su apariencia en la ficha de espacio. Por eso se quita
 * solo la parte de maquetación y no el bloque entero.
 *
 * Efecto secundario buscado en ReviewList: con `flex` en fila y UN hijo, `.card-body` se ajustaba
 * a su contenido en vez de ocupar el ancho; en bloque lo ocupa, que es lo que una tarjeta de
 * reseña debe hacer. */
.card {
    border: 1px solid var(--border);
    border-radius: 8px;
    overflow: hidden;
    background: var(--surface);
    margin: 8px 0;
}

.card-body {
    padding: 12px;
}

.card-title {
    margin: 0;
}

.features-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 12px;
}

.feature-icon {
    font-size: 28px;
}

.space-card {
    display: flex;
    gap: 12px;
    border: 1px solid var(--border);
    padding: 10px;
    margin-bottom: 10px;
    align-items: center;
}

.space-thumb {
    width: 120px;
    height: 80px;
    object-fit: cover;
}

.space-content h4 {
    margin: 0;
}

.space-meta {
    display: flex;
    gap: 12px;
    align-items: center;
    justify-content: space-between;
}

.muted {
    color: var(--muted);
}

.modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    display: flex;
    align-items: center;
    justify-content: center;
}

.modal {
    background: var(--surface);
    border-radius: 8px;
    width: 90%;
    max-width: 600px;
    border: 1px solid var(--border);
}

.modal-header {
    display: flex;
    justify-content: space-between;
    padding: 12px;
}

.modal-body {
    padding: 12px;
}

/* ===== App UI (2026) ===== */

/* Área táctil de la escala de botones — 44×44 (regla 27 de CLAUDE.md, WCAG 2.5.5).
 *
 * Medido renderizado en producción el 2026-08-08: TRES de las cuatro medidas incumplían, y solo
 * `.btn-lg` —la menos usada, 6 apariciones— llegaba.
 *
 *   .btn (base)     40,1 px   543 usos
 *   .btn-sm         26,2 px   114 usos
 *   .btn-ghost-sm   33,8 px     9 usos   (ya sí definido aquí — ver más abajo)
 *   .btn-lg         55,1 px     6 usos   ✅
 *
 * Es la causa raíz de una lista larga de hallazgos que se venían arreglando de uno en uno: el
 * «Buscar» del buscador, el «Nuevo espacio» y el «Publicar espacio» del anfitrión, el «Siguiente»
 * del asistente de alta. Todos son `.btn`. Se arregla aquí y no en cada página.
 *
 * `display: inline-flex` no es decorativo: la mitad de estos botones son `<a class="btn">`, y un
 * `<a>` no centra su texto verticalmente como sí hace `<button>`. Sin el flex, `min-height` le
 * añadiría el alto por debajo del texto en vez de alrededor. */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    border-radius: 12px;
    border: 1px solid transparent;
    font-weight: 600;
    transition: 0.2s ease;
}

/* El botón primario fija su propio verde en lugar de heredar --primary: es la superficie con
   texto blanco encima, así que necesita el tono accesible (4,67:1) en los dos temas. En oscuro
   --primary sigue siendo el verde de marca porque allí actúa como color de texto. */
.btn-primary,
.btn.btn-primary {
    background: var(--verin-green-accessible) !important;
    color: #fff !important;
}

.btn-primary:hover,
.btn.btn-primary:hover {
    background: var(--verin-green-strong) !important;
}

.btn-ghost,
.btn.btn-ghost {
    background: var(--surface) !important;
    border-color: var(--border) !important;
    color: var(--text) !important;
}

.btn-danger,
.btn.btn-danger {
    background: var(--danger) !important;
    color: #fff !important;
}

.btn-success,
.btn.btn-success {
    background: var(--success) !important;
    color: #fff !important;
}

.search-page,
.dashboard,
.profile-page,
.venue-content {
    max-width: 1120px !important;
}

.search-bar,
.tab-content,
.v-card,
.auth-card,
.rg-card,
.profile-hero,
.next-reservation,
.info-card,
.cta-card,
.trust-card,
.space-item,
.booking-item,
.reservation-card {
    background: var(--surface) !important;
    border: 1px solid var(--border) !important;
    border-radius: var(--radius-md) !important;
    box-shadow: var(--shadow-card) !important;
}

.page-header h1,
.dashboard h1,
.profile-summary h1,
.hero h1,
.rg-title,
.auth-card h1 {
    letter-spacing: -0.02em;
    color: var(--text);
}

.page-header p,
.sub,
.rg-sub,
.booking-meta,
.space-meta,
.profile-summary p,
.reservation-location {
    color: var(--text-muted) !important;
}

.inp,
.rg-inp,
.filter-input,
.form-input,
.cf-in,
.otp-input {
    border: 1px solid var(--border) !important;
    border-radius: 12px !important;
    background: var(--surface) !important;
    color: var(--text) !important;
}

.inp:focus,
.rg-inp:focus,
.filter-input:focus,
.form-input:focus,
.cf-in:focus,
.otp-input:focus {
    border-color: var(--primary) !important;
    box-shadow: var(--ring) !important;
}

/* ═══════════════════════════════════════════════════════════════
   Verin shared components (used by VerinPageHeader, VerinStatusBanner,
   VerinStatusBadge, VerinProgressBar, VerinIcon)
   ═══════════════════════════════════════════════════════════════ */

/* Page shell */
.verin-page {
    max-width: 880px;
    margin: 0 auto;
    padding: 2rem 1.25rem 4rem;
}

.verin-page-narrow { max-width: 720px; }
.verin-page-wide   { max-width: 1120px; }

/* Page header */
.verin-page-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 1rem;
    margin-bottom: 1.75rem;
}

.verin-page-header-text { display: flex; flex-direction: column; gap: .35rem; min-width: 0; }

.verin-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    color: var(--primary-strong);
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .12em;
    text-transform: uppercase;
}

.verin-page-title {
    font-family: "Plus Jakarta Sans", Inter, system-ui, sans-serif;
    font-weight: 800;
    letter-spacing: -0.03em;
    font-size: clamp(1.5rem, 2.4vw, 2rem);
    color: var(--text);
    margin: 0;
    line-height: 1.15;
}

.verin-page-sub {
    color: var(--text-muted);
    font-size: .95rem;
    margin: 0;
    max-width: 60ch;
}

/* Section title (inside cards/sections) */
.verin-section-title {
    font-family: "Plus Jakarta Sans", Inter, system-ui, sans-serif;
    font-weight: 800;
    letter-spacing: -0.02em;
    font-size: 1.15rem;
    margin: 0 0 .25rem;
    color: var(--text);
}

.verin-section-sub {
    margin: 0 0 1rem;
    color: var(--text-muted);
    font-size: .9rem;
}

/* Card */
.verin-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 16px;
    padding: 1.5rem;
    box-shadow: var(--shadow-card);
}

.verin-card + .verin-card { margin-top: 1rem; }

/* Banner (alert / status callout) */
/* `flex-wrap` y el cuerpo con `min-width: 0`.
 *
 * El banner es una fila de icono + texto + acción, y a 375 px la acción no cabía: en
 * `/worker/bookings/manual` el «Ir a verificación» se salía 27 px de la pantalla. Un hijo flex
 * tiene `min-width: auto` por defecto, así que NO se encoge por debajo de su contenido y empuja
 * a los de al lado fuera del contenedor — es la causa clásica del desborde en filas flex, y no se
 * ve leyendo la regla del hijo porque está en el valor por defecto del padre.
 * Con `wrap` la acción baja a su propia línea en móvil en vez de desbordar. */
.verin-banner {
    display: flex;
    flex-wrap: wrap;
    gap: .85rem;
    align-items: flex-start;
    padding: 1rem 1.15rem;
    border-radius: 14px;
    border: 1px solid var(--border);
    background: var(--surface);
    margin-bottom: 1.25rem;
}

.verin-banner-icon {
    flex-shrink: 0;
    width: 22px;
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.verin-banner-icon svg { width: 22px; height: 22px; }

/* Un aviso lleva texto que la aplicación no controla (mensajes de error, nombres,
   URLs). Sin permitir el corte, un solo token largo desborda la página entera: a
   390px un mensaje de error empujaba el body 73px, visto en el paseo por navegador. */
.verin-banner-body {
    flex: 1;
    min-width: 0;
    overflow-wrap: anywhere;
}
.verin-banner-body > strong {
    display: block;
    font-size: .95rem;
    margin-bottom: .15rem;
    color: var(--text);
}
.verin-banner-body > p {
    margin: 0;
    color: var(--text-muted);
    font-size: .88rem;
    line-height: 1.5;
}
.verin-banner-body > p + p { margin-top: .35rem; }

.verin-banner-action { flex-shrink: 0; align-self: center; }

.verin-banner.is-success {
    border-color: rgba(5, 150, 105, 0.25);
    background: rgba(5, 150, 105, 0.06);
}
/* El ICONO se queda con `--success`/`--warning` a propósito: el umbral de un gráfico es 3:1 (WCAG
   1.4.11) y los dos lo pasan (3,77 y 3,18). Migrarlo a los tokens de texto oscurecería el aviso
   sin ganar nada normativo. El TEXTO del banner sí usa `--text`. */
.verin-banner.is-success .verin-banner-icon { color: var(--success); }

.verin-banner.is-warning {
    border-color: rgba(217, 119, 6, 0.25);
    background: rgba(217, 119, 6, 0.06);
}
.verin-banner.is-warning .verin-banner-icon { color: var(--warning); }

.verin-banner.is-error {
    border-color: rgba(220, 38, 38, 0.25);
    background: rgba(220, 38, 38, 0.06);
}
.verin-banner.is-error .verin-banner-icon { color: var(--danger); }

.verin-banner.is-info {
    border-color: rgba(55, 138, 221, 0.25);
    background: rgba(55, 138, 221, 0.06);
}
.verin-banner.is-info .verin-banner-icon { color: var(--blue-base); }

.verin-banner.is-pending {
    border-color: var(--border);
    background: var(--surface-muted);
}
.verin-banner.is-pending .verin-banner-icon { color: var(--text-muted); }

/* Status badge */
.verin-badge {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .25rem .6rem;
    border-radius: 999px;
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .02em;
    border: 1px solid transparent;
    white-space: nowrap;
}
.verin-badge-dot {
    width: 6px;
    height: 6px;
    border-radius: 999px;
    background: currentColor;
    flex-shrink: 0;
}
.verin-badge.is-success { background: rgba(5, 150, 105, 0.1);  color: var(--success-text); border-color: rgba(5, 150, 105, 0.2); }
.verin-badge.is-warning { background: rgba(217, 119, 6, 0.1);  color: var(--warning-text); border-color: rgba(217, 119, 6, 0.2); }
.verin-badge.is-error   { background: rgba(220, 38, 38, 0.1);  color: var(--danger);  border-color: rgba(220, 38, 38, 0.2); }
.verin-badge.is-info    { background: rgba(55, 138, 221, 0.1); color: var(--blue-base); border-color: rgba(55, 138, 221, 0.2); }
.verin-badge.is-neutral { background: var(--surface-muted);    color: var(--text-muted); border-color: var(--border); }

/* Progress bar */
.verin-progress {
    display: flex;
    flex-direction: column;
    gap: .4rem;
    width: 100%;
}
.verin-progress-meta {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    font-size: .8rem;
}
.verin-progress-label { color: var(--text-muted); font-weight: 500; }
.verin-progress-value { color: var(--text); font-weight: 700; }
.verin-progress-track {
    width: 100%;
    height: 8px;
    background: var(--surface-muted);
    border: 1px solid var(--border);
    border-radius: 999px;
    overflow: hidden;
}
.verin-progress-fill {
    height: 100%;
    background: var(--primary);
    transition: width .25s ease;
    border-radius: 999px;
}
.verin-progress.is-success .verin-progress-fill { background: var(--success); }

/* Button variants used across pages (consolidate older .btn rules) */
.btn-outline,
.btn.btn-outline {
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--text);
    padding: .55rem 1rem;
    border-radius: 10px;
    font-weight: 600;
    transition: border-color .15s, color .15s, background .15s;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: .4rem;
}
.btn-outline:hover,
.btn.btn-outline:hover {
    border-color: var(--primary);
    color: var(--primary);
}

.btn-lg { padding: .85rem 1.4rem; font-size: 1rem; border-radius: 12px; }
/* `min-height` explícito aunque `.btn` ya lo declare: `.btn-sm` se usa 114 veces y en 30 de esos
   ficheros aparece SIN `.btn` al lado, así que no siempre hereda la base. Renderizaba a 26,2 px —
   la medida más pequeña de todo el sistema. El dibujo (padding y tipo) no cambia: crece el objetivo. */
.btn-sm {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    padding: .4rem .8rem;
    font-size: .8rem;
    border-radius: 8px;
}

/* `.btn-primary` global, y solo el COLOR salvo para quien la use sin `.btn`.
 *
 * Existe por la misma razón que `.btn-ghost-sm`: la declaran ~15 ficheros por su cuenta y la
 * página que se olvida no obtiene un estilo degradado sino NINGUNO — le pasó al «Buscar» de
 * `/vendor/affiliations`, que renderizaba como un botón del navegador de 301x29,2.
 *
 * NO se declara la caja para el caso normal: 55 usos escriben `class="btn btn-primary"`, donde
 * `.btn` ya pone alto, relleno y radio. Como las dos reglas tienen la misma especificidad, una
 * `.btn-primary` con `padding` colocada después le ganaría a `.btn` y cambiaría 55 botones de
 * golpe. Por eso la caja va en `:not(.btn)`, que solo alcanza a quien usa la clase suelta.
 *
 * Las ~15 copias por página siguen ganando donde existen (el CSS aislado compila a
 * `.btn-primary[b-xxx]`, con más especificidad), así que esto no cambia ninguna pantalla
 * existente: solo cubre las que hoy se quedan sin nada. */
.btn-primary {
    background: var(--verin-green-accessible);
    color: #fff;
}

.btn-primary:not(.btn) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    padding: .6rem 1.25rem;
    border: none;
    border-radius: 10px;
    font-weight: 700;
    cursor: pointer;
    text-decoration: none;
}

/* `.btn-ghost-sm` sube aquí desde las seis copias por página que tenía.
 *
 * El defecto que lo motiva: en `/client/vendors/{id}/reservar` el enlace «← Perfil» usa esta clase
 * y NADIE la definía en su alcance —las seis copias viven en CSS aislado de otras páginas, que por
 * construcción no las alcanza—, así que renderizaba como un ancla pelada: 39x43 px, con el texto
 * partido en dos líneas a 375 px. No era un objetivo táctil corto, era un botón sin estilo.
 *
 * Ponerla global es seguro para las seis copias: el CSS aislado compila a `.btn-ghost-sm[b-xxx]`,
 * que tiene MÁS especificidad que esta regla, así que siguen ganando donde existen. Esta solo
 * cubre las páginas que hoy se quedan sin nada. */
.btn-ghost-sm {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    padding: .4rem .9rem;
    background: none;
    border: 1px solid var(--border);
    border-radius: 8px;
    font-size: .82rem;
    font-weight: 600;
    color: var(--text-muted);
    text-decoration: none;
    cursor: pointer;
}

.btn[disabled],
.btn:disabled {
    opacity: .55;
    cursor: not-allowed;
}

/* Loading spinner */
.verin-loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 3rem 1rem;
    gap: .85rem;
    color: var(--text-muted);
    font-size: .9rem;
}
.verin-spinner {
    width: 32px;
    height: 32px;
    border: 3px solid var(--border);
    border-top-color: var(--primary);
    border-radius: 50%;
    animation: verin-spin .7s linear infinite;
}
@keyframes verin-spin { to { transform: rotate(360deg); } }

/* DESIGN.md pide soportar prefers-reduced-motion en TODA transición y animación. Este bloque
   solo cubría el spinner y la barra de progreso, mientras el sistema anima botones, tarjetas,
   chips, toasts y el menú — justo lo que molesta a quien pide movimiento reducido.
   La regla global es la red de seguridad: cualquier transición nueva queda cubierta sin que
   haya que acordarse. Las animaciones no se eliminan del todo (un valor 0 puede saltarse
   eventos `animationend` de los que depende algún componente), se reducen a 1 ms. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 1ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 1ms !important;
        scroll-behavior: auto !important;
    }

    /* El spinner sí sigue girando: comunica "esto está trabajando", y congelarlo lo
       convertiría en un adorno estático que parece un cuelgue. Solo se ralentiza. */
    .verin-spinner {
        animation-duration: 2s !important;
        animation-iteration-count: infinite !important;
    }
}

/* ─── End Verin shared components ─────────────────────────────── */

@media (max-width: 1024px) {
    .dashboard,
    .search-page,
    .profile-page,
    .venue-content {
        padding-left: 1rem !important;
        padding-right: 1rem !important;
    }

    .stats-row {
        grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
    }

    /* minmax(0, …) y no `1fr`: una pista `1fr` no baja del min-content de su contenido, así que
       un hijo poco flexible (una URL en monoespaciada sin cortar) empujaba la columna hasta
       404 px dentro de un viewport de 390 px y toda la página de detalle se desplazaba en
       horizontal. Este bloque lleva !important, así que gana sobre el CSS con ámbito de la
       página: la corrección tiene que estar aquí. */
    .venue-grid {
        grid-template-columns: minmax(0, 1fr) !important;
    }

    .venue-side {
        position: static !important;
    }
}

@media (max-width: 768px) {
    .stats-row {
        grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    }

    .tabs {
        overflow-x: auto;
        scrollbar-width: thin;
    }

    .space-item,
    .booking-item {
        flex-direction: column;
        align-items: flex-start !important;
        gap: 0.75rem;
    }

    .space-actions {
        width: 100%;
        flex-wrap: wrap;
    }

    .space-actions .btn {
        flex: 1 1 140px;
    }
}

/* ── Leaflet search map pins & popups ─────────────────────────────────── */
.verin-map-pin {
    /* Price pill anchored OVER its coordinate: Leaflet sets iconAnchor:[0,0] (the geo point),
       and translate(-50%,-100%) lifts the pill so its pointer tail touches that exact point —
       fixes the old bottom-right drift. White ring + text-shadow keep it legible over any tile. */
    position: relative;
    transform: translate(-50%, -100%);
    transform-origin: bottom center;
    display: inline-flex;
    align-items: center;
    min-height: 30px;
    background: var(--primary, #1d9e75);
    color: #fff;
    padding: 6px 12px;
    border-radius: 999px;
    border: 1.5px solid #fff;
    font-family: 'Plus Jakarta Sans', 'Inter', system-ui, sans-serif;
    font-size: 12.5px;
    font-weight: 700;
    line-height: 1;
    white-space: nowrap;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
    box-shadow: 0 3px 10px rgba(15, 23, 42, 0.28);
    cursor: pointer;
    transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease;
}
/* Downward pointer tail — a rotated square sitting behind the pill so only its tip shows. */
.verin-map-pin::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 100%;
    width: 10px;
    height: 10px;
    background: inherit;
    border-right: 1.5px solid #fff;
    border-bottom: 1.5px solid #fff;
    transform: translate(-50%, -60%) rotate(45deg);
    z-index: -1;
}
.verin-map-pin:hover { transform: translate(-50%, -100%) scale(1.06); box-shadow: 0 6px 16px rgba(15, 23, 42, 0.34); }
/* Pin emphasised when its result card is hovered (card → pin sync). */
.verin-map-pin--active {
    transform: translate(-50%, -100%) scale(1.12);
    background: var(--primary-strong, #0f6e56);
    box-shadow: 0 8px 22px rgba(15, 23, 42, 0.4);
    z-index: 1000;
}

.verin-map-popup { display: flex; flex-direction: column; gap: 4px; min-width: 150px; max-width: 90vw; word-break: break-word; }
.verin-map-popup strong { font-size: 13px; color: #18181b; }
.vmap-price { font-size: 12px; color: #6b7280; }
.vmap-link { font-size: 12px; font-weight: 700; color: var(--primary, #1d9e75); text-decoration: none; margin-top: 2px; }
.vmap-link:hover { text-decoration: underline; }
.vmap-cats { font-size: 11px; color: #6b7280; }
/* Vendor pins use a distinct blue so "espacios" (green) vs "proveedores" (blue) read apart
   at a glance; positioning/tail/hover are inherited from .verin-map-pin so they stay consistent. */
.verin-map-pin--vendor { background: var(--blue-base, #378add); }

/* ── Admin panel shared styles ───────────────────────────────────────────
   These live here (global) so they apply to all admin page components,
   not just the layout wrapper. Scoped CSS in _AdminLayout.razor.css does
   NOT penetrate child page components in Blazor.
────────────────────────────────────────────────────────────────────── */

/* Search bar */
.admin-search-bar {
    display: flex;
    gap: .5rem;
    margin-bottom: 1.25rem;
    flex-wrap: wrap;
    align-items: center;
}
.admin-search-bar input,
.admin-search-bar select {
    padding: .5rem .85rem;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    font-size: .875rem;
    outline: none;
    background: #fff;
    color: #0f172a;
    transition: border-color .15s, box-shadow .15s;
    font-family: inherit;
}
.admin-search-bar input { flex: 1; min-width: 220px; }
.admin-search-bar input:focus,
.admin-search-bar select:focus {
    border-color: #1d9e75;
    box-shadow: 0 0 0 3px rgba(29,158,117,.12);
}

/* Page header */
.admin-page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    margin-bottom: 1.5rem;
    flex-wrap: wrap;
    gap: .75rem;
}
.admin-page-header h1 {
    font-size: 1.25rem;
    margin: 0;
    font-weight: 700;
    color: #0f172a;
    line-height: 1.3;
}

/* Shared table */
/* La tabla scrollea dentro de su contenedor en vez de empujar la página. Hace falta
   porque sus cabeceras son nowrap: a 768px la tabla de disputas medía 1067px y se
   llevaba el body entero por delante (visto en el paseo por navegador). Vive aquí y
   no en _AdminLayout.razor.css porque el CSS aislado no cruza al componente hijo. */
.admin-table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    border-radius: 10px;
}
.admin-table {
    width: 100%;
    border-collapse: collapse;
    font-size: .8375rem;
    background: #fff;
    border-radius: 10px;
    overflow: hidden;
    box-shadow: 0 1px 2px rgba(0,0,0,.04), 0 0 0 1px #e2e8f0;
}
.admin-table th {
    text-align: left;
    padding: .65rem .9rem;
    border-bottom: 1px solid #e2e8f0;
    color: #64748b;
    font-size: .725rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    white-space: nowrap;
    background: #f8fafc;
}
.admin-table td {
    padding: .7rem .9rem;
    border-bottom: 1px solid #f1f5f9;
    vertical-align: middle;
    color: #1e293b;
}
.admin-table tbody tr:last-child td { border-bottom: none; }
.admin-table tbody tr { transition: background .1s; }
.admin-table tbody tr:hover { background: #f8fafc; }
.admin-table .clickable-row { cursor: pointer; }

/* Empty state */
.admin-empty-state {
    text-align: center;
    padding: 3.5rem 2rem;
    color: var(--text-muted);
    font-size: .9rem;
    background: #fff;
    border-radius: 10px;
    border: 1px solid #e2e8f0;
}

/* Filter tabs */
.admin-filters {
    display: flex;
    gap: .35rem;
    margin-bottom: 1.25rem;
    flex-wrap: wrap;
}
.admin-filter-btn {
    padding: .35rem .8rem;
    border-radius: 99px;
    border: 1px solid #e2e8f0;
    background: #fff;
    font-size: .8rem;
    cursor: pointer;
    color: #64748b;
    transition: all .15s;
    font-family: inherit;
}
.admin-filter-btn:hover { background: #f8fafc; border-color: #cbd5e1; }
.admin-filter-btn.active { background: var(--verin-green-accessible); color: #fff; border-color: #1d9e75; }

/* Pagination */
.admin-pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .75rem;
    margin-top: 1.25rem;
    font-size: .8125rem;
    color: #64748b;
}

/* Meta line */
.admin-meta { font-size: .78rem; color: var(--text-muted); margin-bottom: .85rem; }

/* Shared button system */
.a-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .35rem;
    padding: .45rem 1rem;
    border-radius: 7px;
    font-size: .8125rem;
    font-weight: 500;
    cursor: pointer;
    border: none;
    transition: opacity .15s, background .15s;
    white-space: nowrap;
    font-family: inherit;
    text-decoration: none;
}
.a-btn:disabled { opacity: .45; cursor: not-allowed; }
.a-btn-primary { background: var(--verin-green-accessible); color: #fff; }
.a-btn-primary:hover:not(:disabled) { background: #0f6e56; }
.a-btn-outline { background: #fff; color: #374151; border: 1px solid #e2e8f0; }
.a-btn-outline:hover:not(:disabled) { background: #f8fafc; border-color: #cbd5e1; }
.a-btn-sm { padding: .3rem .65rem; font-size: .775rem; border-radius: 6px; }
.a-btn-danger { background: #fef2f2; color: #b91c1c; border: 1px solid #fecaca; }
.a-btn-danger:hover:not(:disabled) { background: #fee2e2; }

/* Shared badge system */
.a-badge {
    display: inline-flex;
    align-items: center;
    padding: .18rem .5rem;
    border-radius: 99px;
    font-size: .72rem;
    font-weight: 600;
    white-space: nowrap;
}
.a-badge-green  { background: #dcfce7; color: #166534; }
.a-badge-red    { background: #fef2f2; color: #b91c1c; }
.a-badge-amber  { background: #fefce8; color: #854d0e; }
.a-badge-gray   { background: #f1f5f9; color: var(--gray-700); }
.a-badge-blue   { background: #eff6ff; color: #1d4ed8; }
.a-badge-purple { background: #faf5ff; color: #7e22ce; }

/* Risk chip */
.a-risk { display: inline-flex; padding: .18rem .5rem; border-radius: 5px; font-size: .75rem; font-weight: 600; }
.a-risk-low    { background: #dcfce7; color: #166534; }
.a-risk-medium { background: #fefce8; color: #854d0e; }
.a-risk-high   { background: #fef2f2; color: #b91c1c; }

/* Utility classes */
.a-mono { font-family: ui-monospace, 'SF Mono', Menlo, monospace; font-size: .8rem; }
.a-date { font-size: .78rem; color: var(--text-muted); white-space: nowrap; }
.a-text-muted { color: var(--text-muted); font-size: .825rem; }
.a-name { font-weight: 500; color: #0f172a; }

/* UXA-006 — Responsive data tables.
   These wide list/detail tables had no small-screen treatment, so on phones the row content
   overflowed the viewport and forced the whole *page* to scroll sideways. Making each table its
   own block-level scroll container keeps the overflow inside the table instead of the page at
   viewports <= 640px. Scoped to the tables that lacked any mobile handling — the vendor tables
   already use a `-wrap` overflow container, `bookings-table` uses a stacked-card layout, and
   `transfers-table` hides a column, so those are intentionally excluded. `payouts-table` is
   handled in its own scoped CSS because its base rule sets `overflow: hidden`. */
@media (max-width: 640px) {
    .rules-table,
    .admin-table,
    .detail-table,
    .metrics-table,
    .dlq-table {
        display: block;
        max-width: 100%;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    /* Cuando la tabla ya vive dentro de un contenedor con scroll propio, el que debe
       desplazarse es el contenedor: es el que lleva tabindex y por tanto el único que
       el teclado puede alcanzar. Si además la tabla scrollea, queda una región
       desplazable inalcanzable sin ratón (lo marcaba axe en /admin/metrics). Y como
       efecto secundario, la tabla recupera su maquetado real: display:block le quita
       la alineación de columnas. */
    .table-wrap > .metrics-table,
    .table-wrap > .dlq-table,
    .admin-table-wrap > .admin-table {
        display: table;
        overflow-x: visible;
    }
}
.a-sub { font-size: .75rem; color: var(--text-muted); }

/* ── Señales de confianza en la cola de solicitudes ────────────────────────────
   Estilos GLOBALES, no aislados: `BookingRequestsPanel` se monta desde tres padres
   distintos (gestión del espacio, la cola global del anfitrión y la del proveedor) y
   la aislación de CSS no atraviesa la frontera del componente. */
.req-signals {
    display: flex;
    flex-wrap: wrap;
    gap: .375rem;
    margin: .5rem 0 .75rem;
}

.req-signal {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    padding: .2rem .5rem;
    border-radius: 999px;
    border: 1px solid var(--gray-200);
    background: var(--gray-50);
    color: var(--text-muted);
    font-size: .75rem;
    line-height: 1.4;
}

/* `--primary-strong` y no `--primary` para el texto: sobre los verdes pálidos el segundo
   se queda en 3,89–4,45:1 (regla 26). */
.req-signal.is-good {
    border-color: var(--green-light);
    background: var(--green-pale);
    color: var(--primary-strong);
}
