html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

/* GUARDA CONTRA SCROLL HORIZONTAL (Hector 2026-07-09, item 13): la app NUNCA
   debe tener scroll horizontal — un elemento mas ancho que el viewport hacia
   que el hamburguer flotante (position:fixed) se viera "despegado" al arrastrar
   la pagina de lado en el telefono. `overflow-x: clip` contiene cualquier
   desborde SIN crear un contenedor de scroll (a diferencia de `hidden`, no
   rompe `position: sticky` del top bar ni del sidebar). El culpable real —
   tablas anchas — ya va contenido con .table-responsive (overflow-x:auto). */
html, body { overflow-x: clip; max-width: 100%; }
main { min-width: 0; }        /* deja que el flex-item se encoja en vez de forzar ancho */
.content { overflow-wrap: break-word; }

/* Blazor circuit error banner — GLOBAL (not scoped to a layout) so EVERY layout
   hides it by default and only shows it on a real unhandled error. Previously this
   lived in MainLayout.razor.css (scoped), so PublicLayout (/c/{token}) showed the
   banner permanently with no error behind it. */
#blazor-error-ui {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

    #blazor-error-ui .dismiss {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }

/* ═══ ÍTEM 196b (batch 30489): reconnect modal — GLOBAL, ya no scoped ═══════
   El contenido del <dialog> ahora lo inyecta ReconnectModal.razor.js (HTML
   crudo limpio para crawlers). El CSS scoped (.razor.css) marca los elementos
   del MARKUP con el atributo b-xxx, y los nodos creados por JS no lo traen —
   así que estos estilos viven aquí en global (misma lección que el banner
   #blazor-error-ui de arriba). Copiados 1:1 del ReconnectModal.razor.css. */
.components-reconnect-first-attempt-visible,
.components-reconnect-repeated-attempt-visible,
.components-reconnect-failed-visible,
.components-pause-visible,
.components-resume-failed-visible,
.components-rejoining-animation {
    display: none;
}

#components-reconnect-modal.components-reconnect-show .components-reconnect-first-attempt-visible,
#components-reconnect-modal.components-reconnect-show .components-rejoining-animation,
#components-reconnect-modal.components-reconnect-paused .components-pause-visible,
#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible,
#components-reconnect-modal.components-reconnect-retrying,
#components-reconnect-modal.components-reconnect-retrying .components-reconnect-repeated-attempt-visible,
#components-reconnect-modal.components-reconnect-retrying .components-rejoining-animation,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-failed .components-reconnect-failed-visible {
    display: block;
}

#components-reconnect-modal {
    background-color: white;
    width: 20rem;
    margin: 20vh auto;
    padding: 2rem;
    border: 0;
    border-radius: 0.5rem;
    box-shadow: 0 3px 6px 2px rgba(0, 0, 0, 0.3);
    opacity: 0;
    transition: display 0.5s allow-discrete, overlay 0.5s allow-discrete;
    animation: components-reconnect-modal-fadeOutOpacity 0.5s both;
}

    #components-reconnect-modal[open] {
        animation: components-reconnect-modal-slideUp 1.5s cubic-bezier(.05, .89, .25, 1.02) 0.3s, components-reconnect-modal-fadeInOpacity 0.5s ease-in-out 0.3s;
        animation-fill-mode: both;
    }

#components-reconnect-modal::backdrop {
    background-color: rgba(0, 0, 0, 0.4);
    animation: components-reconnect-modal-fadeInOpacity 0.5s ease-in-out;
    opacity: 1;
}

@keyframes components-reconnect-modal-slideUp {
    0% {
        transform: translateY(30px) scale(0.95);
    }

    100% {
        transform: translateY(0);
    }
}

@keyframes components-reconnect-modal-fadeInOpacity {
    0% {
        opacity: 0;
    }

    100% {
        opacity: 1;
    }
}

@keyframes components-reconnect-modal-fadeOutOpacity {
    0% {
        opacity: 1;
    }

    100% {
        opacity: 0;
    }
}

.components-reconnect-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1rem;
}

#components-reconnect-modal p {
    margin: 0;
    text-align: center;
}

#components-reconnect-modal button {
    border: 0;
    background-color: #6b9ed2;
    color: white;
    padding: 4px 24px;
    border-radius: 4px;
}

    #components-reconnect-modal button:hover {
        background-color: #3b6ea2;
    }

    #components-reconnect-modal button:active {
        background-color: #6b9ed2;
    }

.components-rejoining-animation {
    position: relative;
    width: 80px;
    height: 80px;
}

    .components-rejoining-animation div {
        position: absolute;
        border: 3px solid #0087ff;
        opacity: 1;
        border-radius: 50%;
        animation: components-rejoining-animation 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
    }

        .components-rejoining-animation div:nth-child(2) {
            animation-delay: -0.5s;
        }

@keyframes components-rejoining-animation {
    0% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 0;
    }

    4.9% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 0;
    }

    5% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 1;
    }

    100% {
        top: 0px;
        left: 0px;
        width: 80px;
        height: 80px;
        opacity: 0;
    }
}

/* ═══ ÍTEM 383 (Héctor + Daniel 2026-07-27): pantalla de reconexión PROPIA de
   las superficies NO ATENDIDAS (kiosko/TV/check-in) ═══════════════════════════
   ReconnectModal.razor.js le añade la clase .bsp-unattended al <dialog> cuando
   la ruta es /kiosk o /tv — el modal de 20rem se convierte en una pantalla
   COMPLETA porcelana con el branding de la casa (poste + raya de barbería),
   sin jerga técnica y sin botones (el kiosko se repone solo: reintentos
   infinitos en bsp-circuit.js + sonda /health en ReconnectModal.razor.js).
   En pantallas atendidas (sin la clase) nada de esto aplica. */
#components-reconnect-modal.bsp-unattended {
    width: 100vw;
    height: 100vh;
    max-width: 100vw;
    max-height: 100vh;
    margin: 0;
    border-radius: 0;
    background-color: #F7F5F0; /* porcelana theshop (la misma del wash del kiosko) */
    display: flex;
    align-items: center;
    justify-content: center;
}

#components-reconnect-modal.bsp-unattended::backdrop {
    background-color: #F7F5F0;
}

/* La rueda se muestra SIEMPRE que el diálogo esté abierto — la pantalla propia
   no distingue estados show/retrying/failed: para el cliente solo existe
   "un momento…". Los anillos van en navy (el azul default no es de la casa). */
#components-reconnect-modal.bsp-unattended .components-rejoining-animation {
    display: block;
}

#components-reconnect-modal.bsp-unattended .components-rejoining-animation div {
    border-color: #14224E;
}

.bsp-reconnect-brand {
    max-width: 560px;
    padding: 1rem;
    text-align: center;
}

.bsp-reconnect-pole {
    width: 72px;
    height: 72px;
}

.bsp-reconnect-title {
    color: #14224E; /* navy de la casa */
    font-weight: 800;
    margin: 0;
}

.bsp-reconnect-body {
    color: #14224E;
    opacity: .8;
    font-size: 1.15rem;
    margin: 0;
}

/* Raya de barbería — reusa el gradiente del tema (var), con fallback propio */
.bsp-reconnect-stripe {
    width: min(340px, 70vw);
    height: 8px;
    border-radius: 4px;
    background: var(--barber-stripe, repeating-linear-gradient(135deg,
        #C8102E 0px, #C8102E 12px,
        #FFFFFF 12px, #FFFFFF 24px,
        #14224E 24px, #14224E 36px));
}

a, .btn-link {
    color: #006bb7;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

.content {
    padding-top: 1.1rem;
}

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;
}

/* ===== BarberShopPro — mobile-first ===== */

:root {
    --bcp-radius: 14px;
    --bcp-shadow: 0 1px 3px rgba(0,0,0,.12), 0 1px 2px rgba(0,0,0,.08);
    --bcp-gap: 1rem;
}

lord-icon { display: inline-block; vertical-align: middle; }
/* Sidebar nav icons (Lordicons) ship with a dark/colored palette that's invisible on the
   dark sidebar of every theme. `brightness(0) invert(1)` forces them to solid WHITE while
   keeping the hover animation. Safe because every theme's sidebar is dark. */
.nav-lordicon { width: 24px; height: 24px; filter: brightness(0) invert(1); }
.nav-divider { border-color: rgba(255,255,255,.2); margin: .5rem 1rem; }

/* Layout content gets comfortable touch padding on phones */
.content { padding-inline: 1rem; max-width: 960px; margin-inline: auto; }

/* ANCHO OPT-IN (Hector 2026-07-09; AMPLIADO 2026-07-10, item 63 OPCION B): una
   pagina que renderiza <div class="page-wide"></div> ensancha su .content
   contenedor a ~1400px. Empezo con las pantallas de LISTA/tabla (/admin/users
   y hermanas); por DECISION DE HECTOR (2026-07-10) los FORMULARIOS grandes
   tambien van wide (Profile, CompanyEdit, StoreEdit, EmployeeEdit,
   CustomerEdit, Support) — "aprovechar el ancho del dispositivo". En tablet/
   movil no cambia nada (la pantalla es mas angosta que el max-width). Usa
   :has() (navegadores modernos). */
.content:has(.page-wide) { max-width: 1400px; }

.page-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: .75rem; flex-wrap: wrap; margin-bottom: 1rem;
}
.page-head h1 { margin: 0; font-size: 1.5rem; }

.section-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: .5rem; }

/* Home */
/* FULL-PAGE background: the artwork fills the whole main area behind the Home content (quick
   actions + recent float over it). Scoped to Home via `main:has(.home-page)` (no JS). A dimming
   overlay keeps text/cards readable; dark themes override it with --hero-overlay. */
main:has(.home-page) {
    background: var(--hero-overlay, linear-gradient(rgba(15,23,42,0.30) 0%, rgba(15,23,42,0.50) 100%)),
                url('/images/businesscardspro-background.png') center center / cover no-repeat;
}
/* Phone portrait: swap to the portrait artwork. */
@media (max-width: 640.98px) and (orientation: portrait) {
    main:has(.home-page) {
        background: var(--hero-overlay, linear-gradient(rgba(15,23,42,0.30) 0%, rgba(15,23,42,0.50) 100%)),
                    url('/images/businesscardspro-background-mobile.png') center center / cover no-repeat;
    }
}
/* Give the artwork some breathing room at the top before the cards, and make Home text readable
   over the (dark-dimmed) image. The white action/recent cards already contrast on their own. */
.home-page { padding-top: 1.5rem; }

/* == ÍTEM 80 (Héctor 2026-07-10): cards/banners ARRIBA de la foto en TODOS
   los tamaños == (antes solo en teléfono; Héctor: "abajo uno tiene que mirar
   toda la foto primero y eso distrae"). Orden: avisos → cards → foto.
   Tablet se comporta como laptop; móvil conserva su hero bajito. */
.home-flow { display: flex; flex-direction: column; }
.home-flow > .home-find { order: 1; margin-top: .25rem !important; }
.home-flow > .home-cap  { order: 2; margin-top: .5rem !important; }
.home-flow > h5         { order: 3; margin-top: .75rem !important; }
.home-flow > .qc-grid   { order: 4; }
.home-flow > .home-hero { order: 9; margin-top: 1rem; }
@media (max-width: 640.98px) {
    .home-flow > .home-hero { min-height: 300px !important; }
    /* Hero mas bajito en el cel: el saludo no necesita 250px de aire */
    .home-hero .home-hero-pad { padding-top: 130px !important; }
}
.home-page .section-head h2,
.home-page .section-head a { color: #fff; }
.home-page .text-muted,
.home-page .empty-state p { color: rgba(255,255,255,.92) !important; }

.quick-actions {
    display: grid; grid-template-columns: repeat(3, 1fr);
    gap: var(--bcp-gap); margin-bottom: 1.5rem;
}
.quick-action {
    display: flex; flex-direction: column; align-items: center; gap: .5rem;
    padding: 1rem .5rem; background: #fff; border-radius: var(--bcp-radius);
    box-shadow: var(--bcp-shadow); text-decoration: none;
    /* Card is always white, so force dark text (matches Lucid's #0F172A) instead of
       `inherit` — dark themes set a light body color that was unreadable on white. */
    color: #0F172A;
    text-align: center; font-size: .85rem; font-weight: 600;
}
.quick-action:active { transform: scale(.97); }

/* Card grid + thumbnails */
.card-grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: var(--bcp-gap);
}
.card-thumb {
    display: block; background: #fff; border-radius: var(--bcp-radius);
    overflow: hidden; box-shadow: var(--bcp-shadow); text-decoration: none; color: inherit;
}
.card-thumb:active { transform: scale(.98); }
.card-thumb-img { aspect-ratio: 16 / 10; background: #f1f3f5; }
.card-thumb-img img { width: 100%; height: 100%; object-fit: cover; }
.card-thumb-placeholder { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; }
.card-thumb-body { padding: .6rem .75rem; }
.card-thumb-name { font-weight: 600; }
.card-thumb-company { font-size: .85rem; color: #6c757d; margin-bottom: .35rem; }

.category-badge { background: #e7f1ff; color: #1b6ec2; font-weight: 600; }
.notif-badge { background: #dc3545; color: #fff; font-size: .7rem; margin-left: .35rem; }

/* Empty states */
.empty-state {
    display: flex; flex-direction: column; align-items: center; gap: .75rem;
    text-align: center; padding: 2.5rem 1rem; color: #6c757d;
}

/* Filters */
.filters { display: flex; flex-direction: column; gap: .5rem; margin-bottom: 1rem; }

/* Capture */
.capture-photos { display: flex; flex-direction: column; gap: 1rem; margin-bottom: 1rem; }

/* Camera-style upload button (replaces the native "Choose File") */
.camera-btn {
    position: relative;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: .35rem; padding: 1.1rem 1rem; cursor: pointer;
    border: 2px dashed #cbd5e1; border-radius: var(--bcp-radius);
    color: #334155; background: rgba(0, 0, 0, .02);
    transition: border-color .15s ease, background .15s ease;
}
.camera-btn:hover { border-color: #3b82f6; background: rgba(59, 130, 246, .05); }
.camera-btn i { font-size: 1.9rem; line-height: 1; }
.camera-btn span { font-size: .9rem; font-weight: 500; }
.camera-btn input[type="file"] { position: absolute; width: 1px; height: 1px; opacity: 0; }
.capture-preview, .card-image {
    width: 100%; max-width: 420px; border-radius: var(--bcp-radius);
    box-shadow: var(--bcp-shadow); margin-bottom: .5rem;
}
.card-images { display: flex; flex-wrap: wrap; gap: 1rem; margin-bottom: 1rem; }

/* Detail */
.card-image-placeholder {
    width: 100%; max-width: 420px; aspect-ratio: 7 / 4;
    display: flex; align-items: center; justify-content: center;
    background: #f1f3f5; border-radius: var(--bcp-radius);
    box-shadow: var(--bcp-shadow); margin-bottom: .5rem;
}
.detail-share { margin-bottom: .75rem; max-width: 420px; }
.detail-name { margin: 0 0 1rem; font-size: 1.5rem; }
.card-details { display: grid; grid-template-columns: auto 1fr; gap: .35rem 1rem; margin: 1rem 0; }
.card-details dt { font-weight: 600; color: #6c757d; }
.card-details dd { margin: 0; }
.category-row { margin: 1rem 0; }
.card-qr { text-align: center; margin: 1.5rem 0; }
.qr-image { width: 180px; height: 180px; }
.qr-image-lg { width: 260px; height: 260px; max-width: 80vw; }
.detail-actions { display: flex; gap: .75rem; flex-wrap: wrap; margin-top: 1rem; }

/* Categories */
.new-category { display: flex; gap: .5rem; margin-bottom: 1rem; }
.category-list { list-style: none; padding: 0; margin: 0; }
.category-list li {
    display: flex; align-items: center; justify-content: space-between; gap: .5rem;
    padding: .75rem .25rem; border-bottom: 1px solid #eee;
}
.category-list li.inactive { opacity: .55; }
.category-actions { display: flex; gap: .35rem; }

/* Notifications */
.notification-list { list-style: none; padding: 0; margin: 0; }
.notification-list li { border-bottom: 1px solid #eee; }
.notification-list li a {
    display: block; padding: .85rem .25rem; text-decoration: none; color: inherit;
}
.notification-list li.unseen { background: #f0f7ff; }

/* Profile QR + scan landing */
.profile-qr, .scan-landing {
    display: flex; flex-direction: column; align-items: center; gap: 1rem;
    text-align: center; padding: 1.5rem 0;
}
.profile-qr-url { font-size: .8rem; color: #6c757d; word-break: break-all; max-width: 90vw; }

/* Share modal */
.modal-backdrop-bcp { position: fixed; inset: 0; background: rgba(0,0,0,.5); z-index: 1040; }
.share-modal {
    position: fixed; left: 50%; bottom: 0; transform: translateX(-50%);
    width: 100%; max-width: 480px; background: #fff; z-index: 1050;
    border-radius: var(--bcp-radius) var(--bcp-radius) 0 0;
    padding: 1rem; max-height: 85vh; overflow-y: auto;
}
.share-modal-header { display: flex; align-items: center; justify-content: space-between; }
.share-modal-header h3 { margin: 0; font-size: 1.2rem; }
.share-target { font-weight: 600; margin: .5rem 0; }
.share-actions { display: grid; grid-template-columns: repeat(2, 1fr); gap: .5rem; }
.share-action { display: flex; align-items: center; justify-content: center; gap: .5rem; }
.share-results { list-style: none; padding: 0; margin: .5rem 0 0; }
.share-results li {
    display: flex; align-items: center; justify-content: space-between; gap: .5rem;
    padding: .6rem .25rem; border-bottom: 1px solid #eee;
}
.share-link { margin: .25rem 0 .5rem; }

/* Public share landing (/c/{token}) */
.public-content { max-width: 520px; margin: 0 auto; padding: 1.25rem 1rem 2.5rem; }
.public-card { display: flex; flex-direction: column; }
.public-card-images { display: flex; flex-direction: column; gap: .75rem; margin-bottom: 1rem; }
.public-card-image {
    width: 100%; border-radius: var(--bcp-radius); box-shadow: var(--bcp-shadow);
}
.public-card-name { font-size: 1.5rem; margin: 0 0 .5rem; }
.public-invite {
    margin-top: 1.75rem; padding: 1.25rem; text-align: center;
    background: #f1f3f5; border-radius: var(--bcp-radius);
}
.public-invite h2 { font-size: 1.2rem; margin: 0 0 .5rem; }
.public-invite p { color: #6c757d; margin: 0 0 1rem; }

/* Tablet / desktop */
@media (min-width: 768px) {
    .page-head h1 { font-size: 1.85rem; }
    .filters { flex-direction: row; }
    .filters .form-control, .filters .form-select { flex: 1; }
    .capture-photos { flex-direction: row; }
    .capture-slot { flex: 1; }
    .share-modal {
        bottom: auto; top: 50%; transform: translate(-50%, -50%);
        border-radius: var(--bcp-radius);
    }
}

/* ================================================================
   Accessibility utility classes — applied to <html> by
   accessibility-manager.js. !important so they win over theme
   and Bootstrap specificity.
   ================================================================ */

/* ── Font scale ────────────────────────────────────────────────── */
html.a11y-font-115 { font-size: 115% !important; }
html.a11y-font-130 { font-size: 130% !important; }
html.a11y-font-150 { font-size: 150% !important; }
/* Item 31 (Hector 2026-07-10): un nivel MAS grande para leer sin espejuelos */
html.a11y-font-175 { font-size: 175% !important; }

/* ── Items 30/54 (Hector 2026-07-10): forms RESILIENTES a fonts grandes y
   pantallas angostas — labels/textos largos hacen wrap en vez de montarse ── */
label.form-label, .form-check-label { overflow-wrap: anywhere; }
.form-floating > label { white-space: normal; overflow-wrap: anywhere; }
.form-control, .form-select { min-width: 0; }
/* La rejilla de Bootstrap no deja que una columna se desborde sobre la vecina */
.row > [class*="col-"] { min-width: 0; }

/* ── FIX QA (Tito 2026-07-22): la flecha del <select> y su reservado ──────────
   Bootstrap fija la flecha en 16px y reserva 2.25rem a la derecha; con fuentes de
   accesibilidad (175%) ese reservado en px/rem NO acompaña al valor y "Todas las
   tiendas/compañías" se truncaba a ~390px. Se expresa TODO en `em` (escala con la
   fuente del propio control): la flecha crece proporcional y el reservado se ciñe
   a su tamaño real, devolviéndole ancho al valor. En 100% queda idéntico a
   Bootstrap (1em≈16px, .75em≈12px). */
.form-select {
    background-size: 1em .75em;
    background-position: right .55em center;
    padding-right: 1.9em;
}
/* Con fuentes de accesibilidad (115–175%) el max-width FIJO en px de los <select>
   de filtro (ej. inline style="max-width:185px" en Localidades/Reportes) NO escala
   y "Todas las tiendas/compañías" se truncaba. Bajo escala a11y se libera el tope
   (los filtros viven en filas .d-flex flex-wrap → el select crece y hace wrap si
   hace falta, mostrando el valor completo). Sólo afecta a usuarios con fuentes
   grandes; el layout al 100% queda intacto. */
html[class*="a11y-font-1"] select.form-select { max-width: none !important; }

/* ── Batch F A1 (fix definitivo QA Lupe): el <select> de State se AUTO-AJUSTA a
   su opción MÁS ANCHA ("DC — District of Columbia") y NUNCA trunca, en cualquier
   viewport (móvil/tablet/desktop) y a CUALQUIER fuente de accesibilidad
   (100/115/130/150/175%). Va dentro de una columna col-auto (col-12 col-md-auto):
   en móvil ocupa todo el ancho (apilado como los demás campos); de md (768px) en
   adelante el select pasa a inline-block width:auto y la columna col-auto crece a
   su contenido, así el nombre completo del estado siempre cabe. El width:auto
   escala con el font-size, por eso resiste el panel de accesibilidad. */
.form-select.bsp-state-select { width: 100%; }
@media (min-width: 768px) {
    /* .form-select.bsp-state-select (0,2,0) gana sobre Bootstrap y cualquier
       tema que ponga width en .form-select (0,1,0), sin depender del orden de
       carga */
    .form-select.bsp-state-select { display: inline-block; width: auto; max-width: 100%; }
}

/* ── Highlight links ───────────────────────────────────────────── */
html.a11y-highlight-links a:not(.nav-link):not(.btn):not(.page-link):not(.breadcrumb-item),
html.a11y-highlight-links .btn-link {
    text-decoration:  underline !important;
    text-decoration-thickness: 2px !important;
    text-underline-offset: 2px !important;
    font-weight:      700 !important;
    outline:          1px dashed currentColor !important;
    outline-offset:   1px !important;
}

/* ── Highlight headings ────────────────────────────────────────── */
html.a11y-highlight-headings h1,
html.a11y-highlight-headings h2,
html.a11y-highlight-headings h3,
html.a11y-highlight-headings h4,
html.a11y-highlight-headings h5,
html.a11y-highlight-headings h6 {
    border-left:  4px solid var(--accent-color, #3B82F6) !important;
    padding-left: 0.6rem !important;
    margin-left:  -0.6rem !important;
}

/* ── Enhanced focus ────────────────────────────────────────────── */
html.a11y-focus-enhanced *:focus,
html.a11y-focus-enhanced *:focus-visible {
    outline:        3px solid var(--accent-color, #3B82F6) !important;
    outline-offset: 3px !important;
    box-shadow:     0 0 0 5px rgba(59, 130, 246, 0.25) !important;
}

/* ── Text spacing (WCAG 1.4.12) ────────────────────────────────── */
html.a11y-text-spacing p,
html.a11y-text-spacing li,
html.a11y-text-spacing td,
html.a11y-text-spacing th,
html.a11y-text-spacing label,
html.a11y-text-spacing span,
html.a11y-text-spacing h1,
html.a11y-text-spacing h2,
html.a11y-text-spacing h3,
html.a11y-text-spacing h4,
html.a11y-text-spacing h5,
html.a11y-text-spacing h6 {
    line-height:     1.75 !important;
    letter-spacing:  0.05em !important;
    word-spacing:    0.1em !important;
}

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

/* ── HC themes: sidebar overrides via class ────────────────────── */
.sidebar.theme-hc-light,
.top-row.theme-hc-light {
    background:   #000000 !important;
    border-bottom: 3px solid #0040CC !important;
    color:         #FFFFFF !important;
}

.sidebar.theme-hc-dark,
.top-row.theme-hc-dark {
    background:   #000000 !important;
    border-bottom: 3px solid #FFFFFF !important;
    color:         #FFFFFF !important;
}

/* ── Card cropper overlay ──────────────────────────────────────── */
#bcp-cropper-overlay {
    position: fixed; inset: 0; z-index: 1080;
    background: rgba(0, 0, 0, 0.85);
    display: none; align-items: center; justify-content: center;
}
.bcp-cropper-box {
    display: flex; flex-direction: column;
    width: 100%; max-width: 640px; height: 100%; max-height: 100vh; padding: 1rem;
}
.bcp-cropper-hint { color: #fff; text-align: center; margin-bottom: .5rem; font-weight: 600; }
.bcp-cropper-stage { flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; overflow: hidden; }
.bcp-cropper-stage img { max-width: 100%; max-height: 100%; display: block; }
.bcp-cropper-actions { display: flex; gap: .5rem; justify-content: center; padding-top: 1rem; }
.bcp-cropper-actions .btn { flex: 1; max-width: 160px; }
.bcp-cropper-actions .bcp-cropper-rotate { flex: 0 0 auto; max-width: 52px; font-size: 1.25rem; }
/* == Estilos de los CRUDs (BarberShopPro) == */
.crud-header-icon { width: 38px; height: 38px; }
.crud-avatar {
    display: inline-flex; width: 40px; height: 40px; border-radius: 50%;
    object-fit: cover; align-items: center; justify-content: center;
}
.crud-avatar-initials {
    background: linear-gradient(135deg, #14224E, #14224E);
    color: #FFFFFF; font-weight: 700; font-size: 14px;
}
.crud-store-thumb {
    width: 72px; height: 48px; object-fit: cover; border-radius: 8px;
    border: 2px solid var(--accent-color, #14224E);
}
.crud-price { color: #B8860B; font-weight: 700; }

/* == Tarjetas de acceso rapido del Home (patron PSP) == */
.qc-grid { display: flex; flex-wrap: wrap; gap: 14px; }
/* ÍTEM 270 cosmético (Lupe): a ~≤1024px el botón flotante de a11y (fijo abajo-
   derecha, 52px + 24px de margen) podía ROZAR la última fila de "Quick access"
   al hacer scroll. Un respiro inferior en la grid reserva ese alto (76px) para
   que el FAB descanse sobre espacio vacío — sin mover el FAB ni tocar el layout
   en pantallas anchas. */
@media (max-width: 1024px) {
    .qc-grid { padding-bottom: 4.75rem; }
}
.qc-card {
    flex: 1 1 200px; max-width: 260px; padding: 0 0 14px 0;
    overflow: hidden; /* la cinta respeta el radio del card */
    background: var(--surface-color, #FFFFFF);
    border: 1px solid var(--border-color, #D8D4C8);
    border-radius: 14px; text-decoration: none;
    color: var(--bs-body-color, #14224E);
    transition: transform .12s ease, box-shadow .12s ease, border-color .12s ease;
}
.qc-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 6px 18px rgba(0,0,0,.14);
    border-color: var(--accent-color, #C8102E);
    color: var(--bs-body-color, #14224E);
}
/* La CINTA (Hector 2026-07-04): banda clara de lado a lado en el tope,
   ~1/3 del card, con el icono centrado adentro — visible en TODO tema */
.qc-band {
    /* Ajuste de Hector 2026-07-04 6pm: icono ARRIBA A LA IZQUIERDA y mas
       pequeno que la cinta, para que quepa comodo */
    display: flex; align-items: center; justify-content: flex-start;
    width: 100%; height: 62px; padding-left: 16px;
    background: var(--icon-chip-bg, #F1F5F9);
    border-bottom: 1px solid var(--border-color, rgba(0,0,0,.08));
    margin-bottom: 10px;
}
.qc-icon { width: 34px; height: 34px; display: block; }
.qc-icon-bi { font-size: 1.5rem; color: var(--accent-color, #C8102E); line-height: 1; }
.qc-title { font-weight: 700; padding: 0 16px; }
.qc-sub { font-size: .85rem; opacity: .7; padding: 0 16px; }

/* == Submenus del NavMenu (reorganizacion 2026-07-04) == */
.nav-group-header {
    display: flex; align-items: center; width: 100%;
    background: none; border: none; cursor: pointer;
    padding: 0.55rem 1.5rem 0.35rem 1rem; margin-top: 0.35rem;
    color: var(--text-secondary, #BFDBFE);
    font-size: 0.72rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.06em;
    text-align: left;
}
.nav-group-header:hover { color: #FFFFFF; }
.nav-group-chevron { margin-left: auto; font-size: 0.7rem; }
.nav-sub .nav-link { padding-left: 1.6rem; font-size: 0.92rem; }

/* == Schedule grid (Hector 2026-07-04) == */
.sch-table thead th { background: var(--sidebar-bg, #14224E); color: #fff; }
.sch-appt-start { background: rgba(37, 99, 235, .18); border-left: 3px solid var(--accent-color, #2563EB); }
.sch-appt-cont { background: rgba(37, 99, 235, .08); }

/* == Schedule Pickup/Finish (Hector 2026-07-04) == */
.sch-btn { font-size: .68rem; padding: .1rem .45rem; }
.sch-appt-active { background: rgba(22, 163, 74, .22); border-left: 3px solid #16A34A; }
.sch-appt-done { background: rgba(100, 116, 139, .15); border-left: 3px solid #64748B; }
/* Pole FOTORREALISTA en linea (Hector 2026-07-08): reemplaza el emoji 💈 en
   toda la UI; escala con el font-size del texto que acompana */
.pole-icon { height: 1.05em; width: auto; vertical-align: -0.14em; }

/* PASO FALTANTE en el wizard de reserva (Hector 2026-07-08): al rebotar la
   validacion, el paso incompleto se marca en ambar/rojo — se limpia solo al
   completarlo (la clase es condicional al estado real) */
.book-missing { outline: 2px solid #DC2626; background: #FEF3C7; border-radius: 8px;
    padding: 2px 8px; animation: bookMissingPulse 1.2s ease-in-out 2; }
@keyframes bookMissingPulse { 50% { outline-color: #F59E0B; } }

/* == ESCALA DE FOTOS DE LA CASA (Batch F item 62, Hector 2026-07-10: "estan
   muy pequenas y no se distinguen bien") ==
   3 tamaños estándar reutilizables:
     .bsp-photo-sm  56px — thumbnail de LISTA/tabla
     .bsp-photo-md  96px — card de índice / detalle medio
     .bsp-photo-lg 150px — perfil / landing
   Las clases viejas (svc-photo, loc-photo, edit-title-photo, bsp-dd-photo) se
   AGRANDARON a esta escala en vez de reemplazarlas (menos churn de markup). */
.bsp-photo-sm { width: 56px; height: 56px; object-fit: cover; border-radius: 10px; flex-shrink: 0; }
.bsp-photo-md { width: 96px; height: 96px; object-fit: cover; border-radius: 14px; flex-shrink: 0; }
.bsp-photo-lg { width: 150px; height: 150px; object-fit: cover; border-radius: 18px; flex-shrink: 0; }

/* Foto del SERVICIO en chips/listas (Hector 2026-07-08): "los servicios se
   ven bien plain" — thumb cuadradito al lado del nombre; sin foto = tijeras.
   Item 62: 30px -> 48px (que se distinga el estilo sin entrecerrar los ojos) */
.svc-photo { width: 48px; height: 48px; object-fit: cover; border-radius: 8px;
    flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; }
.svc-photo-empty { background: var(--icon-chip-bg, #F1F5F9); color: #94A3B8; }
.svc-photo-sm { width: 34px; height: 34px; border-radius: 6px; }

/* == ÍTEM 93 (mockup de Héctor 2026-07-11): CARD DE SERVICIO DE 2 FILAS ==
   En teléfono real la fila única (foto | nombre | precio) rompía: nombres
   partidos palabra a palabra, precio apretado, texto fuera del rectángulo.
   DISEÑO ELEGIDO: foto GRANDE RECTANGULAR a la izquierda ABARCANDO LAS DOS
   FILAS (esquinas apenas suavizadas — NO circular: el círculo se reserva
   para fotos de PERSONAS/barberos) | columna derecha apilada: fila 1 =
   NOMBRE (todo el ancho restante, wrap natural) + (duración); fila 2 =
   PRECIO. Nada se sale del card. Mismo diseño en TODOS los tamaños.
   La foto creció con el layout (regla del ítem 62): 48px -> 104x78. */
.svc-card { display: flex; align-items: center; gap: .65rem;
    border: 1px solid var(--border-color, rgba(0,0,0,.175));
    border-radius: .5rem; padding: .5rem; margin-bottom: .4rem; min-width: 0; }
.svc-card-photo { width: 104px; height: 78px; object-fit: cover;
    border-radius: 8px; flex-shrink: 0; display: inline-flex;
    align-items: center; justify-content: center; font-size: 1.6rem; }
.svc-card-body { flex: 1 1 0; min-width: 0; display: flex;
    flex-direction: column; gap: .2rem; }
.svc-card-name { overflow-wrap: break-word; line-height: 1.3; }
.svc-card-price { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem; }
/* BUG-1 QA Batch I: con fonts de accesibilidad 150/175% la foto FIJA de 104px
   + paddings en rem que escalan colapsaban la columna de texto (~64px a
   360px@150% — nombre partido palabra a palabra y el btn-group ×N fuera del
   card; a 175% peor). FIX: a 150/175% el card APILA — la foto encoge (72x54,
   sigue rectangular) y queda arriba junto al checkbox; el TEXTO baja a su
   propia fila A TODO EL ANCHO del card (flex-wrap + basis 100%): a 360px@175%
   nada desborda. Técnica de la casa: clases del accessibility-manager en html. */
html.a11y-font-150 .svc-card,
html.a11y-font-175 .svc-card { flex-wrap: wrap; }
html.a11y-font-150 .svc-card-photo,
html.a11y-font-175 .svc-card-photo { width: 72px; height: 54px; }
html.a11y-font-150 .svc-card-body,
html.a11y-font-175 .svc-card-body { flex: 1 1 100%; min-width: 100%; }

/* Encabezado de categoria en el wizard de reserva (Hector 2026-07-09, item 5) */
.svc-cat-header {
    font-weight: 600;
    font-size: .82rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--bs-secondary-color, #64748B);
    padding: .5rem .25rem .15rem;
    border-bottom: 2px solid var(--bs-border-color, #E2E8F0);
    margin-top: .35rem;
}
.svc-cat-header:first-child { margin-top: 0; }

/* Time-off en la Agenda (Hector): celda gris rayada = barbero no disponible */
.sch-cell-off { background: repeating-linear-gradient(45deg,
    rgba(100, 116, 139, .20) 0 6px, rgba(100, 116, 139, .08) 6px 12px); }

/* == AGENDA POSICIONADA POR HORA EXACTA (Hector 2026-07-09, item 3) ==
   Cada cita es un bloque absoluto (top/height = minutos * px), no snapped a la
   celda -> dos citas de 15 min consecutivas no se encabalgan. */
.sch-agenda { display: block; }
.sch-agenda-head { display: flex; position: sticky; top: 0; z-index: 3;
    background: var(--sidebar-bg, #14224E); }
.sch-agenda-head-cell { color: #fff; text-align: center; padding: .25rem .2rem;
    line-height: 1.15; font-size: .8rem; }
.sch-agenda-body { display: flex; }
.sch-time-gutter { width: 72px; min-width: 72px; flex: 0 0 72px; }
.sch-barber-col { flex: 1 1 120px; min-width: 120px; max-width: 220px;
    border-left: 1px solid var(--bs-border-color, #E2E8F0); }
.sch-agenda-head .sch-barber-col { border-left: 1px solid rgba(255,255,255,.15); }
.sch-gutter-label { position: absolute; right: 6px; transform: translateY(-50%);
    font-size: .72rem; color: var(--bs-secondary-color, #64748B); white-space: nowrap; }
.sch-grid-line { position: absolute; left: 0; right: 0; height: 0;
    border-top: 1px dashed rgba(100,116,139,.18); }
.sch-grid-line-hour { border-top: 1px solid rgba(100,116,139,.34); }
.sch-off-block { position: absolute; left: 2px; right: 2px; border-radius: 4px;
    background: repeating-linear-gradient(45deg,
        rgba(100, 116, 139, .22) 0 6px, rgba(100, 116, 139, .08) 6px 12px);
    z-index: 1; cursor: not-allowed; }
/* ÍTEM 174: celda invisible de 15 min sobre el espacio VACÍO de la agenda —
   click/tap ofrece crear una cita ahí. Debajo del time-off (z1) y de las citas
   (z2): lo ocupado intercepta el click. El hover pinta un "+" sutil (desktop);
   en táctil no hay hover y el tap va directo a la confirmación. */
.sch-slot-hit { position: absolute; left: 0; right: 0; z-index: 0; cursor: pointer; }
.sch-slot-hit:hover { background: rgba(20, 34, 78, .07); }
.sch-slot-hit:hover::after { content: '+'; position: absolute; right: 6px; top: 50%;
    transform: translateY(-50%); font-weight: 700; font-size: .8rem;
    color: var(--primary-color, #14224E); opacity: .65; }
.sch-appt-block { position: absolute; left: 3px; right: 3px; border-radius: 6px;
    padding: 2px 5px; overflow: hidden; font-size: .74rem; z-index: 2;
    box-shadow: 0 1px 2px rgba(0,0,0,.08); }
/* ÍTEM 204 (Héctor+Daniel 2026-07-18, kiosco BAR-01): fila [BOTÓN | nombre] —
   el botón de atender/terminar va PRIMERO (izquierda) y jamás se encoge ni se
   empuja fuera del bloque; el nombre absorbe el resto y trunca con "…". Antes
   el botón iba después del nombre y en multi-persona quedaba inalcanzable. */
.sch-appt-row { display: flex; align-items: center; gap: 4px; min-width: 0; }
.sch-appt-row .sch-btn { flex-shrink: 0; white-space: nowrap;
    /* compacto vertical: el botón (~16px) cabe ENTERO hasta en el bloque de un
       corte de 15 min (21px) — sin esto el borde inferior quedaba recortado */
    padding-top: 0; padding-bottom: 0; line-height: 1.25; }
.sch-appt-name { font-weight: 600; white-space: nowrap; overflow: hidden;
    text-overflow: ellipsis; flex: 1 1 auto; min-width: 0; }
/* ÍTEM 164: nota del cliente en el bloque de la agenda — truncada (el tooltip
   lleva el texto completo); discreta pero visible */
.sch-appt-notes { white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    font-size: .7rem; opacity: .85; }

/* == ÍTEM 211 (Vera, Tour de Yamil 2026-07-19): LISTA DE ESPERA RESPONSIVA ==
   La tabla de 9 columnas desbordaba a 1024x600 (BAR-01) y móvil → los botones
   Atender / No se presentó quedaban recortados al borde derecho, alcanzables
   solo con scroll horizontal de la tabla. Réplica del patrón 204: cada turno es
   una fila flex con las ACCIONES a la IZQUIERDA — flex-shrink:0, nunca se
   encogen ni se empujan fuera del viewport; el resto de los datos van en chips
   que hacen wrap y truncan con "…". Cero overflow-x en los 3 viewports. */
.wl-queue { display: flex; flex-direction: column; gap: .45rem; }
.wl-row { display: flex; align-items: center; gap: .6rem; min-width: 0;
    border: 1px solid var(--bs-border-color, #E2E8F0); border-radius: .6rem;
    padding: .5rem .6rem; }
.wl-actions { flex-shrink: 0; display: flex; flex-direction: column; gap: .3rem; }
.wl-actions .wl-btn { white-space: nowrap; font-size: .8rem; }
.wl-turn { flex-shrink: 0; font-weight: 700; min-width: 1.4rem; text-align: center;
    font-size: 1.1rem; color: var(--bs-secondary-color, #64748B); }
.wl-body { flex: 1 1 auto; min-width: 0; }
.wl-name { font-weight: 600; white-space: nowrap; overflow: hidden;
    text-overflow: ellipsis; }
.wl-meta { display: flex; flex-wrap: wrap; align-items: center; gap: .25rem .5rem;
    font-size: .78rem; color: var(--bs-secondary-color, #64748B); margin-top: .2rem; }
.wl-chip { white-space: nowrap; max-width: 100%; overflow: hidden;
    text-overflow: ellipsis; }
.wl-chip-svc { max-width: 15rem; }
/* ÍTEM 342 (Hector 2026-07-25): el turno YA COBRADO ensena su ESTADO con el
   importe, no un boton de cobrar. Verde de exito sobre fondo tenue —
   contraste MEDIDO con getComputedStyle (#0F5132 sobre #E8F5EE = 8.35:1,
   AAA — trampa 4: una captura no prueba un color). flex-shrink:0 +
   nowrap para que jamas se recorte en la columna de acciones (patron 204). */
.wl-paid-tag { display: inline-block; white-space: nowrap; flex-shrink: 0;
    font-size: .8rem; font-weight: 700; line-height: 1.2;
    padding: .3rem .5rem; border-radius: .4rem; text-align: center;
    color: #0F5132; background: #E8F5EE; border: 1px solid #A9D5BD; }

/* == MAPA DE CALOR DE CAPACIDAD (Hector 2026-07-09, item 10) == */
.sch-cap-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; }
.sch-cap-dow { text-align: center; font-size: .7rem; font-weight: 600;
    color: var(--bs-secondary-color); padding: 2px 0; }
.sch-cap-cell { position: relative; border: 1px solid rgba(0,0,0,.08);
    border-radius: 6px; min-height: 42px; padding: 3px 4px; background: transparent;
    display: flex; flex-direction: column; align-items: flex-start; justify-content: space-between;
    cursor: pointer; font-size: .74rem; line-height: 1.1; color: inherit; }
.sch-cap-cell:hover { outline: 2px solid rgba(13,110,253,.35); }
.sch-cap-empty { border: none; background: transparent; cursor: default; }
.sch-cap-empty:hover { outline: none; }
.sch-cap-daynum { font-weight: 600; }
.sch-cap-pct { font-size: .66rem; align-self: flex-end; opacity: .85; }
.sch-cap-selected { box-shadow: 0 0 0 2px #0d6efd inset; }
.sch-cap-none { background: rgba(148,163,184,.12); color: var(--bs-secondary-color); }
.sch-cap-green { background: rgba(22,163,74,.20); border-color: rgba(22,163,74,.4); }
.sch-cap-yellow { background: rgba(234,179,8,.28); border-color: rgba(234,179,8,.5); }
.sch-cap-red { background: rgba(220,38,38,.24); border-color: rgba(220,38,38,.5); }
.sch-cap-swatch { display: inline-block; width: 12px; height: 12px; border-radius: 3px;
    vertical-align: -1px; margin-right: 3px; }

/* == Schedule compacto + header 2 lineas (Hector 2026-07-04) == */
.sch-table th, .sch-table td { padding: .15rem .3rem; font-size: .8rem; }
.sch-barber-th { white-space: normal; word-break: break-word; line-height: 1.15;
    min-width: 76px; max-width: 100px; vertical-align: top; padding: .25rem .2rem; }

/* == Reportes: altura limitada al viewport (regla de Hector 2026-07-04) == */
.rep-chart { height: min(52vh, 340px); }
@media (max-width: 767px) { .rep-chart { height: 42vh; } }

/* == Iconos visibles en temas oscuros (Hector 2026-07-04) ==
   Los lord-icons traen trazos oscuros fijos; en temas oscuros se pierden.
   Cada tema oscuro define --icon-chip-bg (chip claro); los claros no. */
.crud-header-icon {
    background: var(--icon-chip-bg, transparent);
    border-radius: 14px;
    padding: 5px;
}

/* == La franja del poste de barbero en el tope (recuperada 2026-07-04) ==
   La app vieja la ponia en .navbar; el layout nuevo usa .app-header. *@ */
.app-header {
    border-bottom: 4px solid transparent;
    border-image: var(--barber-stripe) 1;
}

/* qc-band aplicado */

/* == Impresion de reportes paginados (patron PSP, 2026-07-04) ==
   Print/Save-as-PDF: se oculta todo menos .report-printable */
@media print {
    .sidebar, .app-header, .nav-scrollable, .a11y-trigger-btn, .a11y-panel,
    #sidebar-float-wrap, #blazor-error-ui, .report-no-print {
        display: none !important;
    }
    .page, main, .content, article {
        display: block !important; margin: 0 !important; padding: 0 !important; width: 100% !important;
    }
    .report-printable { position: absolute; left: 0; top: 0; width: 100%; }
    a[href]::after { content: none !important; }
}

/* Iconos fotorrealistas dentro de la cinta (lote 1, 2026-07-04) */
.qc-photo { height: 50px; width: auto; display: block; }

/* ══ ÍTEM 240 (spec Kahlo §1.3): encabezado personalizado de reportes ═══════
   Avatar circular 48px de la entidad protagonista + eyebrow + nombre 20px +
   título + período; borde inferior rojo sutil (la cinta del layout global es
   la firma — no se duplica). Claro/oscuro por tokens del tema. */
.report-header { padding-bottom: .6rem; border-bottom: 2px solid var(--accent-color, #C8102E); margin-bottom: .9rem; }
/* ÍTEM 280: protagonista del reporte (barbero/tienda/compañía) — sesgo arriba */
.report-avatar { width: 48px; height: 48px; border-radius: 50%; object-fit: cover;
    object-position: 50% 15%; flex: none;
    border: 1px solid var(--bs-border-color, #E2E8F0); background: #fff; }
.report-avatar-mono { display: inline-flex; align-items: center; justify-content: center;
    background: #14224E; color: #fff; font-weight: 700; font-size: 1rem; border: none; }
.report-eyebrow { font-size: 11px; text-transform: uppercase; letter-spacing: .12em;
    color: var(--bs-secondary-color, #64748B); }
.report-entity { font-size: 20px; font-weight: 700; color: var(--bs-body-color, #14224E); line-height: 1.15; }
.report-title { font-size: 14px; color: var(--bs-body-color, #14224E); }
.report-period { font-size: 12.5px; color: var(--bs-secondary-color, #64748B); }
.report-logo { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; flex: none;
    border: 1px solid var(--bs-border-color, #E2E8F0); }
.min-width-0 { min-width: 0; }

/* ═══════════════════════════════════════════════════════════════════════════
   SERVICE TRACKER — VESTIDO DEL MOCKUP APROBADO (Kahlo 2026-07-20, ítems
   242/243): SEMÁFORO DEL SOPORTE (riel 5px + puntito con símbolo ▲/◐/✔ —
   regla daltónicos), pills de prioridad/status/canal, chips porcelana (activo
   navy; HOY activo ROJO), banda de filtros glass, grupos con borde de color y
   contador navy clickeable, densidad compacta, heatmap semáforo (hue = peor
   estado, intensidad = volumen). Colores de la casa: rojo #C8102E, ámbar
   #D97706 (texto AA #B45309), verde #16A34A.
   ═══════════════════════════════════════════════════════════════════════════ */
/* Banda de filtros glass sobria */
.st-filters { background: rgba(255, 255, 255, .72); border: 1px solid var(--bs-border-color, #D8D4C8);
    border-radius: 12px; padding: .55rem .65rem; }
.st-search { flex: 1 1 210px; max-width: 300px; display: flex; align-items: center; gap: .35rem;
    background: #fff; border: 1px solid var(--bs-border-color, #D8D4C8); border-radius: .45rem; padding: .3rem .55rem; }
.st-search input { border: none; outline: none; width: 100%; font: inherit; font-size: .82rem;
    color: #14224E; background: transparent; }
.st-search input::placeholder { color: rgba(20, 34, 78, .45); }
/* Chips: porcelana redonda; activo navy; HOY activo ROJO de la casa */
.st-chip { display: inline-flex; align-items: center; gap: .3rem; border-radius: 999px;
    border: 1px solid var(--bs-border-color, #D8D4C8); background: #fff; color: #14224E;
    padding: .26rem .65rem; font-size: .76rem; }
.st-chip:hover { border-color: #14224E; }
.st-chip.on { background: #14224E; border-color: #14224E; color: #fff; }
.st-chip.st-chip-today.on { background: #C8102E; border-color: #9E0C24; }
/* Sort chips: activo navy-900 + flecha */
.st-schip { display: inline-flex; align-items: center; gap: .25rem; border-radius: 999px;
    border: 1px solid var(--bs-border-color, #D8D4C8); background: #fff; color: #14224E;
    padding: .24rem .6rem; font-size: .74rem; }
.st-schip.on { background: #14224E; border-color: #14224E; color: #fff; }
/* Encabezado de grupo: porcelana + borde izq. de color + contador clickeable */
.st-ghead { width: 100%; display: flex; align-items: center; gap: .5rem; text-align: left;
    background: rgba(20, 34, 78, .06); border: 1px solid var(--bs-border-color, #D8D4C8);
    border-radius: 10px; padding: .4rem .6rem; margin: .55rem 0 .45rem;
    color: #14224E; font-weight: 700; }
.st-ghead-red { border-left: 4px solid #C8102E; }
.st-ghead-amber { border-left: 4px solid #D97706; }
.st-ghead-green { border-left: 4px solid #16A34A; }
.st-gcount { background: #14224E; color: #fff; border-radius: 999px;
    padding: .05rem .5rem; font-size: .74rem; font-weight: 700; cursor: pointer; }
.st-gcount:hover { filter: brightness(1.2); }
/* EL TICKET: card con RIEL del semáforo. QA-Vera (2): la card es BLANCA del
   mockup en TODOS los temas → texto navy EXPLÍCITO (en temas oscuros el color
   heredado era claro ≈1.2:1 — ilegible) */
.st-tcard { position: relative; background: #fff; color: #14224E;
    border: 1px solid var(--bs-border-color, #D8D4C8);
    border-left-width: 5px; border-radius: 10px; padding: .55rem .7rem .6rem; margin-bottom: .55rem;
    box-shadow: 0 2px 10px rgba(20, 34, 78, .05); }
/* Las utilidades BS dentro de la card también en la paleta clara del mockup
   (en temas oscuros sus vars dan colores claros → ilegibles sobre blanco) */
.st-tcard .text-muted { color: rgba(20, 34, 78, .62) !important; }
.st-tcard .text-success { color: #16A34A !important; }
.st-sem-red { border-left-color: #C8102E; }
.st-sem-amber { border-left-color: #D97706; }
.st-sem-green { border-left-color: #16A34A; }
.st-semdot { display: inline-flex; align-items: center; justify-content: center;
    width: 1.05rem; height: 1.05rem; border-radius: 50%; font-size: .6rem; font-weight: 800;
    color: #fff; vertical-align: -2px; margin-right: .3rem; }
.st-semdot-red { background: #C8102E; }
.st-semdot-amber { background: #D97706; }
.st-semdot-green { background: #16A34A; }
.st-tmain { flex: 1 1 380px; min-width: 0; }
.st-tnum { font-family: ui-monospace, Consolas, monospace; font-size: .72rem;
    color: rgba(20, 34, 78, .62); margin-right: .3rem; }
.st-tsubj { font-weight: 700; font-size: .92rem; line-height: 1.25; }
.st-tmeta { font-size: .74rem; color: rgba(20, 34, 78, .62); margin-top: .15rem;
    display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
.st-tmeta b { color: #14224E; }
/* QA-Vera (1): la edad vieja SÍ pinta roja — antes .st-tmeta b (0,1,1) le
   ganaba a .st-age-old (0,1,0); este selector compuesto gana siempre */
.st-tmeta b.st-age-old, .st-age-old { color: #C8102E !important; font-weight: 700; }
/* Pills */
.st-pill { display: inline-flex; align-items: center; gap: .25rem; border-radius: 999px;
    padding: .1rem .5rem; font-size: .7rem; font-weight: 700; }
.st-pill-ch { background: rgba(20, 34, 78, .10); color: #14224E; border: 1px solid rgba(20, 34, 78, .22); }
.st-pill-st-red { background: rgba(200, 16, 46, .10); color: #C8102E; border: 1px solid rgba(200, 16, 46, .25); }
.st-pill-st-amber { background: rgba(201, 162, 39, .15); color: #B45309; border: 1px solid rgba(201, 162, 39, .4); }
.st-pill-st-green { background: rgba(22, 163, 74, .12); color: #16A34A; border: 1px solid rgba(22, 163, 74, .3); }
.st-pill-pri-4 { background: #C8102E; color: #fff; }                                                       /* Urgente */
.st-pill-pri-3 { background: rgba(227, 100, 20, .14); color: #E36414; border: 1px solid rgba(227, 100, 20, .35); } /* Alta */
.st-pill-pri-2 { background: rgba(201, 162, 39, .14); color: #B45309; border: 1px solid rgba(201, 162, 39, .35); } /* Media */
.st-pill-pri-1 { background: rgba(154, 163, 178, .18); color: #5A6472; border: 1px solid rgba(154, 163, 178, .4); } /* Baja */
/* Asignación 242g — 3 estados visuales */
.st-asg { display: inline-flex; align-items: center; gap: .3rem; border-radius: 999px;
    padding: .12rem .55rem; font-size: .72rem; font-weight: 600; }
.st-asg-mine { background: #14224E; color: #fff; }
.st-asg-other { background: rgba(20, 34, 78, .10); color: #14224E; border: 1px solid rgba(20, 34, 78, .18); }
.st-asg-none { background: #fff; color: var(--bs-secondary-color, #64748B); border: 1px dashed var(--bs-border-color, #D8D4C8); }
.st-age-old { color: #C8102E; font-weight: 700; }
/* Chip del acuse de Gabriel + notas navy */
.st-tack { display: inline-flex; align-items: center; gap: .3rem; margin-top: .3rem;
    font-size: .72rem; color: #16A34A; font-weight: 600; background: rgba(22, 163, 74, .08);
    border: 1px solid rgba(22, 163, 74, .25); border-radius: 999px; padding: .12rem .55rem; }
.st-tnote { border-left: 3px solid #14224E; padding-left: .5rem; margin-bottom: .3rem; }
.st-amber-txt { color: #B45309; font-weight: 600; }
/* Densidad B — lista compacta: se esconden cuerpo/notas/acuse/cierre/solución */
.st-compact-card { padding: .32rem .55rem; margin-bottom: .3rem; border-radius: 8px; }
.st-compact-card .st-body, .st-compact-card .st-notes, .st-compact-card .st-tack,
.st-compact-card .st-closed, .st-compact-card .st-sol { display: none; }
.st-compact-card .st-tsubj { font-size: .82rem; }
.st-compact-card .st-tmeta { margin-top: .05rem; }
/* HEATMAP SEMÁFORO 243 — hue por peor estado, alphas de la receta de Kahlo */
.st-cal-r1 { background: rgba(200, 16, 46, .14); border-color: rgba(200, 16, 46, .35); }
.st-cal-r2 { background: rgba(200, 16, 46, .26); border-color: rgba(200, 16, 46, .5); }
.st-cal-r3 { background: rgba(200, 16, 46, .40); border-color: rgba(200, 16, 46, .65); }
.st-cal-y1 { background: rgba(217, 119, 6, .16); border-color: rgba(217, 119, 6, .35); }
.st-cal-y2 { background: rgba(217, 119, 6, .28); border-color: rgba(217, 119, 6, .5); }
.st-cal-y3 { background: rgba(217, 119, 6, .42); border-color: rgba(217, 119, 6, .65); }
.st-cal-g1 { background: rgba(22, 163, 74, .14); border-color: rgba(22, 163, 74, .35); }
.st-cal-g2 { background: rgba(22, 163, 74, .26); border-color: rgba(22, 163, 74, .5); }
.st-cal-g3 { background: rgba(22, 163, 74, .40); border-color: rgba(22, 163, 74, .65); }
.st-cal-worst { position: absolute; right: 3px; bottom: 2px; font-size: .62rem; font-weight: 800; }
.st-w-r { color: #9E0C24; }
.st-w-y { color: #B45309; }
.st-w-g { color: #15803D; }
.st-cal-todaycell .sch-cap-daynum { color: #C8102E; font-weight: 800; }

/* == Kiosco tactil (Fase 2, 2026-07-04) == */
.kiosk-wrap { max-width: 640px; margin: 0 auto; }

/* ══ ÍTEM 238-v2 — RECETA APROBADA E-40 (Héctor 2026-07-20 noche, verbatim
   "usar este al 40%, QR con color" = variante E "HighSetup" del mockup de
   Kahlo): foto NÍTIDA de la tienda + VELO GLOBAL PORCELANA AL 40% + WASH
   POR ELEMENTO — cada pieza (saludo, botones, QR, textboxes, keypad) vive
   en su superficie porcelana .90 (AA verificado POR ELEMENTO por Kahlo:
   navy 12:1 AAA, rojo 5.8:1, muted 5.7:1 — ningún texto directo sobre la
   foto). CAPA fixed inset:0 (lección del hotfix: el fondo JAMÁS participa
   del layout). 🚦 TODO detrás del flag PlatformSettings["KioskPhotoBackground"]
   (DEFAULT ON — aprobado; Value='false' = revert): las clases kiosk-wash/*
   solo se emiten con el flag ON, y las reglas de hermanos (.kiosk-photo-bg ~)
   solo aplican cuando la capa existe → flag OFF = kiosko actual EXACTO (el
   "original de revert" que pidió Héctor — se apaga sin republicar).
   ⚙️ SIN backdrop-filter A PROPÓSITO (decisión anotada): Kahlo lo marcó
   "adorno — si el RPi sufre, quitarlo; la legibilidad la carga el rgba .90
   solo" y el RPi del kiosko no tiene GPU (regla del 223). ══ */
.kiosk-photo-bg { position: fixed; inset: 0; z-index: -1; overflow: hidden; }
.kiosk-photo-bg img { width: 100%; height: 100%; object-fit: cover;
    object-position: 50% 50%; display: block; }
/* Punto focal LANDSCAPE (el RPi real es 1024x600 horizontal): sesgo hacia
   ARRIBA — la cabeza del barbero visible ("un barbero sin cabeza, como un
   zapatero sin pies"). Vertical/tablet: default 50% 50% intacto. */
@media (orientation: landscape) {
    .kiosk-photo-bg img { object-position: 50% 8%; }
}
/* VELO GLOBAL 40% — número calibrado por Héctor EN VIVO con el control del
   mockup (diseño original 12% mínimo de seguridad; 40 es LA cifra) */
.kiosk-photo-bg::after { content: ""; position: absolute; inset: 0;
    background: rgba(247, 245, 240, .40); }
/* ══ E-40 v2 (decisión de Héctor tras el hallazgo de Lupe +31px/+52px): el
   wash JAMÁS desplaza el layout — cada elemento interactivo queda EXACTO
   donde está con el flag OFF (el teclado en pantalla del RPi tapa lo que
   baje). Técnica: wrappers SIN padding/borde (los márgenes colapsan a
   través = altura idéntica) y el "colchón" visual es un HALO de BOX-SHADOW
   con spread (pinta fuera del elemento sin ocupar espacio): anillo 1 =
   porcelana .90 (el colchón), anillo 2 = borde de luz 1px, anillo 3 = la
   sombra de elevación. ══ */
.kiosk-wash { background: rgba(247, 245, 240, .90); border-radius: 10px;
    box-shadow: 0 0 0 12px rgba(247, 245, 240, .90),
                0 0 0 13px rgba(255, 255, 255, .65),
                0 12px 32px 8px rgba(20, 34, 78, .16); }
.kiosk-greet-card, .kiosk-qr-block, .kiosk-notify-block, .kiosk-confirm-card { border-radius: 10px; }
/* ÍTEM 249c (Héctor 2026-07-21): la caja de avisos/consent se montaba
   sobre el textbox del email (halo de 12/13px vs mb-2 de 8px). HACIA
   ADENTRO ~20% de caja visual: halo 12/13→4/5px; el div, sus checkboxes
   y los botones de abajo quedan EXACTOS (0px de delta). */
.kiosk-wash.kiosk-notify-block {
    box-shadow: 0 0 0 4px rgba(247, 245, 240, .90),
                0 0 0 5px rgba(255, 255, 255, .65),
                0 8px 24px 6px rgba(20, 34, 78, .14); }
/* Chip de título/display (phone y new): span INTERIOR sin padding vertical —
   el halo da el cuerpo; el h4/div exterior es EXACTO al del flag OFF */
.kiosk-chip-wash { background: rgba(247, 245, 240, .90); border-radius: 8px; padding: 0 .6rem;
    box-shadow: 0 0 0 8px rgba(247, 245, 240, .90),
                0 0 0 9px rgba(255, 255, 255, .65),
                0 6px 18px 4px rgba(20, 34, 78, .14); }
/* ÍTEM 249b (Héctor 2026-07-21): el display del teléfono se montaba sobre
   el título (halo de 9px vs mb-2 de 8px del h4). HACIA ADENTRO ~20%:
   fuente 1.6rem (80% del fs-2 — el strut del div fs-2 + min-height 2.6rem
   mandan la altura de línea → el keypad NO se mueve, 0px de delta) + halo
   reducido 8/9→5/6px. Solo existe con flag ON (OFF ni emite el span). */
.kiosk-display-wash { font-size: 1.6rem; vertical-align: -2px;
    box-shadow: 0 0 0 4px rgba(247, 245, 240, .90),
                0 0 0 5px rgba(255, 255, 255, .65),
                0 4px 14px 3px rgba(20, 34, 78, .14); }
/* Tarjeta del QR estilo WELCOME PACKAGE — ÍTEM 249a (Héctor 2026-07-21, en
   vivo en prod): los anillos eran box-shadow y pintaban 11px FUERA de la
   <img> → la tarjeta se montaba sobre "¿Prefieres usar tu teléfono?" y
   "Escanea para registrarte". HACIA ADENTRO: la caja de la <img> queda
   EXACTA (110x110 inline, 0px de delta) y el anillo vive DENTRO — padding
   8px (ring blanco) + border 3px navy (border-box de Bootstrap) → el QR
   pinta a 88x88 (~20% más chico, nivel H lo aguanta) y la tarjeta ya no
   toca los textos. */
.kiosk-qr-ring { background: #fff; border-radius: 8px;
    padding: 8px; border: 3px solid #14224E; }

/* ══════════════════════════════════════════════════════════════════════════
   ÍTEM 294d (Héctor 2026-07-23) — HOME DEL KIOSKO: 2 QR SEPARADOS, cada uno
   en SU PROPIA tarjeta (verbatim: "los puede separar en 2 cards separados, no
   tienen que estar los 2 en 1 solo card!"). Calcado del mockup APROBADO
   MBS\kahlo\mockup-kiosko-2qr.html (v2 corregido + ajuste de gap 44px +
   ajuste #2 de 2 tarjetas separadas) — 1 SOLA columna general (el saludo y
   los 2 botones táctiles arriba NO se tocan), el bloque del QR gana ANCHO,
   no ALTO (regla 238: 1024×600 fijo, cero scroll). Tarjeta A (Reservar cita,
   QR sin cambios) = acento navy/rojo de siempre; tarjeta B (Unirme a la fila,
   NUEVO — ítem 294a) = acento caramelo/dorado para diferenciarlas a simple
   vista. Colores fijos (no theme-driven) — el kiosko es un dispositivo
   público de marca fija, igual que el resto de .kiosk-*. ══════════════════ */
/* ÍTEM 313d (bug REAL de Daniel probando con su teléfono): con 44px de gap la
   cámara encuadraba los DOS QR y agarraba el PRIMERO cuando él apuntaba al
   SEGUNDO — el cliente terminaba en el flujo equivocado. NO es cosmético.
   190px ≈ 2 pulgadas a 96dpi (lo que pidió Héctor). Total 172+190+172 = 534px
   de 1024 — el ancho sobra; la restricción del 238 es el ALTO. */
.qr-2cols { display: flex; gap: 190px; align-items: flex-start; justify-content: center; }
.qr-col {
    width: 172px; padding: 14px 10px 12px; display: flex; flex-direction: column;
    align-items: center; position: relative;
    background: rgba(247, 245, 240, .92); border-radius: 14px;
    box-shadow: 0 10px 26px 2px rgba(20, 34, 78, .16), 0 0 0 1px rgba(255, 255, 255, .65);
}
/* ÍTEM 305a (Héctor 2026-07-23, verbatim): "el card de unirme a la fila
   ahora ponerlo con los mismos colores que tenemos en el card de reservar
   una cita" — la tarjeta B (Unirme a la fila) tenía un acento
   caramelo/dorado propio (294a); AHORA usa EXACTAMENTE la misma paleta
   navy/rojo que la tarjeta A (Reservar cita) — .qr-col-b/.qr-col-label-b/
   .qr-holder-b quedan como ALIAS 1:1 de -a, ya no acentos distintos.
   Tamaño/altura: ya eran idénticos entre A y B (mismo .qr-col de 172px,
   mismo .qr-holder de 112x112 + padding 6px + borde 3px) — se deja igual,
   solo se confirma que ningún override de -b cambie dimensiones. */
.qr-col-a, .qr-col-b { border: 2px solid #C8102E; }   /* rojo/navy — paleta única de las 2 tarjetas */
/* ÍTEM 313c (Héctor 2026-07-25, verbatim: "visualmente ahora no se ven
   parejos"): el título de un card caía en 2 líneas y el del otro en 3, así que
   los QR quedaban a distinta altura y los cards de distinto tamaño — en inglés
   Y en español. El copy se acortó y emparejó, pero el BUG DE FONDO es que la
   altura dependía del largo del texto: por eso el bloque del título ahora tiene
   ALTURA FIJA de 2 líneas (2 × line-height 1.2). Defensa permanente: ninguna
   traducción futura ni cambio de copy puede volver a descuadrar los QR. */
.qr-col-label { font-size: .8rem; font-weight: 700; line-height: 1.2; margin-bottom: .32rem;
    height: 2.4em; overflow: hidden;
    display: flex; align-items: center; justify-content: center; gap: .3rem; }
.qr-label-icon { display: inline-block; width: 1.25rem; min-width: 1.25rem; text-align: center; }
.qr-col-label-a, .qr-col-label-b { color: #14224E; }
/* ÍTEM 313b (Héctor 2026-07-25, verbatim: "toda la aplicación es nueva no
   necesitamos eso en ese card"): el listón dorado "NUEVO"/"NEW" del QR-B se
   ELIMINA — markup, wrapper y CSS. Se borra de verdad, no se deja comentado.
   ÍTEM 313a: los captions de los 2 cards (.qr-col-caption) también fuera —
   sobraban y sumaban altura, que es justo lo que escasea en 1024×600. */
.qr-holder { position: relative; display: inline-block; background: #fff; padding: 6px; border-radius: 11px; }
.qr-holder-a, .qr-holder-b { border: 3px solid #14224E; }

/* Los interactivos SIEMPRE por encima de los halos vecinos (el halo puede
   solapar el fondo, jamás tapar un control) */
.kiosk-photo-bg ~ .kiosk-wrap .form-control,
.kiosk-photo-bg ~ .kiosk-wrap .btn,
.kiosk-photo-bg ~ .kiosk-wrap .kiosk-key { position: relative; z-index: 1; }
/* Botones outline, teclas del keypad y botón de idioma → porcelana .90
   SOLO con la capa presente (flag ON); hover/active vuelven al tema */
.kiosk-photo-bg ~ .kiosk-wrap .kiosk-btn.btn-outline-primary:not(:hover):not(:active),
.kiosk-photo-bg ~ .kiosk-wrap .btn-outline-secondary:not(:hover):not(:active),
.kiosk-photo-bg ~ .kiosk-wrap .kiosk-key:not(:hover):not(:active),
.kiosk-photo-bg ~ .kiosk-lang:not(:hover):not(:active) {
    background: rgba(247, 245, 240, .90);
}
/* ÍTEM 315 · fila 10 (Kahlo — Héctor NO lo había reportado, es el mismo error en
   la misma pantalla): el botón de idioma llevaba el borde rojo de
   --accent-color, elegido por estética y no por contraste → límite 2.23:1. Se
   cambia SOLO el color del borde al navy del par → 3.54:1. Cero layout (el
   .btn de Bootstrap ya tenía 1px de borde). */
.kiosk-photo-bg ~ .kiosk-lang:not(:hover):not(:active) {
    border-color: var(--ovc-ink);
}
/* El primario mantiene su gradiente rojo (opaco = legible sobre foto);
   sombra de asiento extra para separarlo del fondo vivo.
   ÍTEM 315 · fila 12: el rojo sólido con solo un drop-shadow difuso medía
   1.40:1 de límite (rojo+blanco deja descubierto L 0.30–0.478). Gana el par —
   borde navy + halo porcelana de 2px — CONSERVANDO su asiento rojo → 3.54:1. */
.kiosk-photo-bg ~ .kiosk-wrap .kiosk-btn.btn-primary {
    border-color: var(--ovc-ink);
    box-shadow: 0 0 0 2px var(--ovc-halo),
                0 6px 18px rgba(20, 34, 78, .25), 0 0 12px rgba(200, 16, 46, .20);
}
/* TEXTBOX HighSetup ("la esencia", specs de Kahlo): porcelana .90 + borde
   de luz + sombra suave; focus = blanco .96 + borde navy; placeholder navy
   al 50% (no el gris Bootstrap).
   ÍTEM 315 · fila 11 (Kahlo — tampoco reportado por Héctor): el "borde de luz"
   blanco .75 sobre un relleno porcelana medía 1.04:1 — un textbox SIN límite
   visible. Es el mismo error de las flechas. Pasa al navy del par → 3.54:1;
   el relleno .90, la sombra y el focus quedan EXACTOS (el focus ya era #14224E,
   así que ahora el foco se lee por el fondo blanco .96, no por un salto de
   color de borde). Cero layout: seguía siendo 1px. */
.kiosk-photo-bg ~ .kiosk-wrap .form-control {
    background: rgba(247, 245, 240, .90);
    border: 1px solid var(--ovc-ink);
    box-shadow: 0 4px 16px rgba(20, 34, 78, .14);
}
.kiosk-photo-bg ~ .kiosk-wrap .form-control:focus {
    background: rgba(255, 255, 255, .96);
    border-color: #14224E;
}
.kiosk-photo-bg ~ .kiosk-wrap .form-control::placeholder { color: rgba(20, 34, 78, .50); }

/* ÍTEM 238 bonus: icono realista del kiosco en el picker de tiendas */
.kiosk-picker-icon { height: 2.4em; width: auto; vertical-align: middle; }
/* touch-action (hotfix 7/20): un toque = un click — que el gesto no se
   convierta en scroll/zoom y se robe el tap en la pantalla táctil del kiosko */
.kiosk-btn { min-width: 320px; min-height: 64px; font-size: 1.3rem; touch-action: manipulation; }
/* ══ ÍTEM 313 e/e-bis (foto del BAR-01 real de Daniel, 2026-07-25) — el
   formulario de cliente nuevo quedaba CORTADO por el teclado en pantalla y el
   botón de Check In era INALCANZABLE: el cliente no podía completar el
   registro. Bloqueo funcional, no cosmético.
   NÚMEROS REALES (MBS\kiosk\BAR-01-RPI-KIOSK-RECETA.md): pantalla 1024×600,
   teclado en 31,384 de 962×204 (y=384 → y=588), Onboard SIN docking = ventana
   X11 flotante que el navegador NO VE (por eso `visualViewport` no aplica).
   --kiosk-kb-top ES LA ÚNICA PALANCA: si mañana cambia la geometría del
   teclado, se toca aquí y nada más. ══ */
:root { --kiosk-kb-top: 384px; }

/* (1) APRETAR: el form entero tiene que caber en la franja de arriba. Se baja
   de form-control-lg y se comprimen márgenes SOLO en este paso (el resto del
   kiosko no se toca). */
/* ÍTEM 313(g): el relleno INTERNO baja (.3→.25rem, 36→34px) y ese aire se le
   pasa al hueco ENTRE campos, más abajo. 34px sigue siendo blanco táctil cómodo
   para el kiosko ([[feedback_ui_simplicity_non_tech_customers]]). */
.kiosk-new-step .form-control-lg { font-size: .95rem; padding: .25rem .6rem; min-height: 34px; }
.kiosk-new-step .btn-lg { font-size: .95rem; padding: .35rem 1rem; }
.kiosk-new-step .mb-2 { margin-bottom: .28rem !important; }
.kiosk-new-step .mb-3 { margin-bottom: .3rem !important; }
.kiosk-new-step .mb-1 { margin-bottom: .15rem !important; }
.kiosk-new-step .mt-2 { margin-top: .3rem !important; }
.kiosk-new-step h4 { font-size: 1.1rem; }
/* el consentimiento SMS (ítem 154) es texto legal largo — se queda ENTERO
   (Twilio 30513 lo exige) pero comprimido; en español ocupa más, por eso se
   aprieta el interlineado y no solo el tamaño. */
.kiosk-new-step .text-muted.small { font-size: .68rem; line-height: 1.22; }
.kiosk-new-step .form-check-label { font-size: .85rem; }
/* (e) el segundo nombre es una COLUMNA FIJA de la fila del Nombre — alineada
   con los demás campos (antes: max-width suelto, se veía corrido y roto). */
.kiosk-name-row .kiosk-mi { flex: 0 0 104px; width: 104px; text-align: center; }

/* ══ ÍTEM 313(g) (Héctor 2026-07-25, sobre una foto del kiosko YA con el 313
   aplicado): "de nuevo veo los campos muy pegados, overlaping" / "si, muy
   apretados". NO se están montando — están APRETADOS: las cajas quedaron ALTAS
   (36px) con solo 4.48px de aire entre ellas (ratio 8:1). Es el precio que se
   pagó en el 313(e-bis) por meter todo el formulario sobre y=384.
   DE DÓNDE SALE EL AIRE — Héctor lo vio en su propia captura: "hay un espacio
   arriba que quizás se pueda usar la mitad de ese espacio". Medido: el
   formulario arrancaba en y=56.8 con la banda 0–52px VACÍA en esta pantalla (el
   padding-top:52px de .kiosk-root es del fix Vera/238, pero ese margen lo
   necesitan las pantallas kbs-*, NO esta; y el botón de idioma vive en x=891-1010,
   o sea ni se cruza con el formulario, que va de x=192 a 832).
   ⚠️ POR ESO EL RECLAMO VA CON `:first-child` Y NO TOCANDO .kiosk-root: cuando
   hay BANNER DE CIERRE DE TIENDA, el <div class="alert"> es el primer hijo de
   .kiosk-wrap, .kiosk-new-step deja de ser :first-child y el margen negativo NO
   aplica — el banner conserva su aire y nada se monta encima. Los dos estados
   verificados a 1024×600 en EN y ES.
   Se recupera la MITAD (26 de 52px) y se reparte: el aire va ENTRE los campos,
   no DENTRO. Los campos bajan 36→34px (siguen siendo blanco táctil cómodo) y
   los huecos suben 4.48→9.92px → ratio 3.4:1 en vez de 8:1. ══ */
.kiosk-new-step:first-child { margin-top: -26px; }
.kiosk-new-step > .kiosk-new-scroll > .mx-auto > .mb-2 { margin-bottom: .62rem !important; }
/* 🚫 CERO OVERLAPPING (la regla permanente de Héctor, Frente 14) — MEDIDO con
   los rects INFLADOS por el spread del halo, porque `box-shadow` NO entra en
   getBoundingClientRect y a ojo (o con el rect crudo) esto NO se ve:
   · el título lleva .kiosk-chip-wash, cuyo anillo es de 8px+9px, y su <span>
     sobresale ~2px del <h4>: con el `mb-2` comprimido del 313 se montaba 5.42px
     sobre la primera fila de campos. Se le da margen REAL (.78rem = 12.5px) en
     vez de recortarle el halo — aquí sí sobra presupuesto (a diferencia de las
     pantallas kbs-*, donde hubo que achicar el anillo).
   · el bloque porcelana de avisos tiene anillo de 4px+5px y quedaba a 4.8px de
     los botones = 0.20px de solape. El `mb-3` del consentimiento (que colapsa
     hacia fuera del bloque) sube a .45rem = 7.2px.
   Coste total: ~8px de los 27px de holgura que dejó el reclamo de arriba.
   OJO con la especificidad: el `.kiosk-new-step .mb-2` de más arriba es (0,2,0)
   CON !important, así que un `> h4` a secas (0,1,1) PIERDE — hay que calificarlo
   con la clase para ganarle. */
.kiosk-new-step > .kiosk-new-scroll > h4.mb-2 { margin-bottom: .78rem !important; }
.kiosk-new-step .mb-3 { margin-bottom: .45rem !important; }

/* (2) RED DE SEGURIDAD: como el navegador no puede detectar el teclado, se le
   DICE cuál es la altura útil. El bloque desplaza POR DENTRO y bsp-kiosk.js
   hace scrollIntoView del campo enfocado. OJO ítem 238 ("cero scroll"): esa
   regla es para la PÁGINA — esto es un área contenida que solo se mueve
   cuando el teclado está encima, que es exactamente lo que hace falta.
   🐛 ÍTEM 313(g) — ESTE COMENTARIO ESTABA ROTO Y SE COMÍA LA REGLA. El bloque
   se CERRABA justo aquí arriba y seguía escribiendo prosa hasta un SEGUNDO
   cierre: para el parser CSS esa prosa pasaba a formar parte del selector
   siguiente, así que la regla `.kiosk-new-step { max-height; overflow-y }`
   NUNCA SE APLICÓ (verificado en vivo: no aparecía en `document.styleSheets` y
   el elemento computaba `overflow-y: visible`). O sea la RED DE SEGURIDAD del
   ítem 313 estaba DESCONECTADA desde que se escribió, y solo funcionaba el
   `max-height` inline que pone `bspKiosk.kbFit()`. Se cierra el comentario UNA
   sola vez y la regla vuelve a existir.
   El `max-height` de aquí es solo el PISO de seguridad por si el JS no corre:
   la altura EXACTA la calcula `bspKiosk.kbFit()`, que resta el offset real del
   bloque (el saludo/banner de cierre de arriba cambia de alto), cosa que el
   CSS no puede hacer solo — `calc(384px - ...)` mide desde el propio elemento,
   no desde el borde de la pantalla, y ahí fue donde falló el primer intento
   (el bloque terminaba en y=430, con el botón debajo del teclado). */
.kiosk-new-step {
    max-height: calc(var(--kiosk-kb-top) - 60px);
    overscroll-behavior: contain;
    display: flex; flex-direction: column; overflow: hidden;
}

/* ══ FIX QA LUPE (bloqueante 2, 2026-07-25) — LA BOTONERA NO SE MUEVE ═══════
   Antes desplazaba el bloque ENTERO, así que en cuanto el contenido crecía
   (banner de cierre de tienda y/o alerta de validación) el botón de Check In
   se iba con el scroll: medido a 1024×600 con `scrollTop=0` → y=474 con banner,
   y=431 con error de validación y y=574 en el peor caso (ES + banner + error),
   o sea POR DEBAJO del borde de la pantalla de 600px. Y NADA lo traía de vuelta:
   el `scrollIntoView` del focusin no dispara porque cada campo, por separado,
   ya cabe en la franja visible. La única pista era una barra de scroll `thin`
   de 10px… en una pantalla TÁCTIL.
   AHORA: `.kiosk-new-step` es un flex vertical con `overflow:hidden`; solo el
   CONTENIDO desplaza (`.kiosk-new-scroll`, que sigue heredando el max-height
   que le calcula `bspKiosk.kbFit()` a su padre) y la botonera va `flex:0 0 auto`
   pegada al fondo del bloque — que es exactamente la línea del teclado.
   ⚠️ EL ESTADO LIMPIO NO SE NEGOCIA: sin banner ni error el contenido cabe, el
   bloque encoge a su contenido y la botonera queda donde siempre (y=367, sobre
   y=384) en EN y ES. Pinchar no la sube ni la baja cuando no hace falta.
   OJO: al meter este envoltorio, los selectores `>` de arriba (`h4.mb-2` y
   `.mx-auto > .mb-2`) tuvieron que pasar por `.kiosk-new-scroll` — si no, el
   aire del 313(g) se pierde EN SILENCIO. ══ */
.kiosk-new-scroll {
    flex: 1 1 auto; min-height: 0;
    overflow-y: auto; overscroll-behavior: contain;
    scrollbar-width: thin;
}
/* PADDING 0 A PROPÓSITO — PARIDAD EXACTA CON EL ESTADO LIMPIO DEL 313(g).
   El aire anti-solape contra el halo del bloque porcelana ya vive DENTRO de él
   (el `.mb-3` de .45rem del consentimiento, que NO colapsa hacia fuera porque
   `.kiosk-wash` tiene relleno propio) — o sea la botonera nunca tuvo separación
   propia. Medido: con `padding-top:.45rem` el Check In del estado limpio caía a
   y=374.2 en vez de y=367, gastando 7px de la holgura contra el teclado por
   nada. Con 0 la geometría del estado limpio queda IDÉNTICA a la que verificó
   Lupe (y=367). */
.kiosk-new-actions { flex: 0 0 auto; padding-top: 0; }

.kiosk-pad { display: grid; grid-template-columns: repeat(3, 88px); gap: 10px; justify-content: center; }
.kiosk-key { height: 64px; font-size: 1.4rem; font-weight: 600; touch-action: manipulation; }

/* ══════════════════════════════════════════════════════════════════════════
   ÍTEM 282 (Héctor 2026-07-22) — KIOSKO: pantallas "¿Con quién?" / "¿Qué
   servicios?", calcadas del mockup APROBADO
   MBS\kahlo\mockup-kiosko-barbero-servicios.html. Carrusel horizontal
   (flechas + swipe nativo overflow-x/scroll-snap + drag mouse vía
   bspKiosk.initCarouselDrag) — CUALQUIER cantidad de barberos/servicios, UNA
   sola fila, CERO scroll vertical del kiosko (regla 238-v2). Cards = FOTO
   COMPLETA con banda-overlay de nombre (nunca texto directo sobre foto);
   recorte de cara sesgado hacia arriba (regla 280, jamás decapitar).
   ══════════════════════════════════════════════════════════════════════════ */
.kbs-crumb { display: flex; align-items: center; gap: .5rem; flex-shrink: 0; justify-content: center; margin-top: .25rem; }
/* ══ ÍTEM 315 · NIVEL 1 (Kahlo, "bomba de tiempo") — ESTOS CONTROLES VIVEN
   TAMBIÉN FUERA DEL KIOSKO (MobileCheckin 29 usos, WaitingList, y el ítem 314
   los va a clonar a Pos/BookAppointment/GuestBooking). Antes pintaban
   `color: var(--primary-900)` — un token de rampa "900" que es OSCURO en LOS 8
   TEMAS — sobre `--surface-color`, que es OSCURO en 5 → el texto ERA el fondo
   (deep 1.00:1, hc-dark 1.06:1, onyx 1.18:1, jade 1.02:1, rose 1.93:1).
   AHORA: tinta = --bs-body-color (claro en oscuro, oscuro en claro) y relleno =
   --surface-color (nunca un tinte hard-coded). El borde sube de #D8D4C8
   (1.36:1) al navy de marca suavizado (3.51:1 sobre #F7F5F0). Los que NO tenían
   borde (step-pill, who-badge) lo ganan con el padding COMPENSADO en 1px, para
   que la caja mida EXACTAMENTE lo mismo que antes (regla E-40 v2: cero
   desplazamiento). El Nivel 2 (porcelana + halo, sobre foto) va más abajo. ══ */
.kbs-crumb-btn { background: var(--surface-color, #fff); border: 1px solid var(--control-edge, rgba(20,34,78,.55)); border-radius: .4rem; color: var(--bs-body-color); }
.kbs-step-pill { background: var(--surface-color, #fff); border: 1px solid var(--control-edge, rgba(20,34,78,.55)); border-radius: 999px; padding: calc(.25rem - 1px) calc(.8rem - 1px); font-size: .78rem; font-weight: 600; color: var(--bs-body-color); }
.kbs-title-block { text-align: center; margin-top: .35rem; flex-shrink: 0; }
.kbs-carousel-wrap { display: flex; align-items: center; gap: 8px; margin-top: .5rem; flex-shrink: 0; }
.kbs-carousel-grow { flex: 1 1 auto; min-height: 0; }
.kbs-arrow {
    flex-shrink: 0; width: 40px; height: 40px; border-radius: 50%; padding: 0;
    background: var(--surface-color, #fff); border: 1px solid var(--control-edge, rgba(20,34,78,.55));
    color: var(--bs-body-color); font-size: 1.2rem; font-weight: 800;
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 2px 6px rgba(20,34,78,.12); touch-action: manipulation;
}
/* ÍTEM 315 · hallazgo 2️⃣ de Kahlo: `.kbs-arrow:disabled { opacity:.25 }` era CSS
   MUERTO — ni Kiosk.razor ni bsp-kiosk.js ponen jamás `disabled` en las flechas
   del carrusel. Se BORRA (no se deja comentado). Que las flechas nunca avisen
   "llegaste al final" es un hueco de UX real, pero es otro ítem. */
.kbs-scroller {
    flex: 1 1 auto; min-width: 0; height: 100%; display: flex; gap: 12px; align-items: stretch;
    overflow-x: auto; scroll-snap-type: x mandatory; padding: 4px 2px 8px;
    scrollbar-width: none; -ms-overflow-style: none; cursor: grab; touch-action: pan-x;
}
.kbs-scroller::-webkit-scrollbar { display: none; }
.kbs-scroller.kbs-dragging { cursor: grabbing; scroll-snap-type: none; }

/* Card = foto completa 148x168 (mismo tamaño para barbero Y servicio — un
   solo sistema visual entre las 2 pantallas del kiosko, ajuste de Héctor) */
.kbs-card {
    position: relative; scroll-snap-align: start; flex-shrink: 0; cursor: pointer;
    width: 148px; height: 168px; border-radius: 14px; overflow: hidden; padding: 0;
    border: 2px solid transparent; box-shadow: 0 2px 8px rgba(20,34,78,.20);
    background: var(--primary-900, #14224E); transition: transform .12s ease, box-shadow .12s ease;
    touch-action: manipulation;
}
.kbs-card:hover { transform: translateY(-2px); box-shadow: 0 8px 18px rgba(0,0,0,.32); }
.kbs-card.selected { border-color: var(--accent-color, #C8102E); box-shadow: 0 0 0 3px rgba(200,16,46,.35), 0 8px 18px rgba(0,0,0,.32); }
/* Hallazgo Lupe (2026-07-23): "Cualquier barbero" FIJO fuera del carrusel —
   separador sutil para que se lea como "anclado" y no como un card más que
   quedó suelto del scroller.
   ══ ÍTEM 339 (Héctor 2026-07-25 — SEGUNDA VEZ QUE LO REPORTA) ══════════════
   Verbatim: "el primer card, cualquier barbero, esta fijo, los otros cards se
   deslizan por detras de el". La estructura NO está mal (la tarjeta vive FUERA
   del scroller a propósito, para que nunca se pueda ir de vista); lo que
   faltaba era FRONTERA VISUAL: el scroller arrancaba pegado, así que una
   tarjeta a medio desplazar se cortaba EXACTAMENTE en el borde de la fija y el
   ojo lo lee como "pasa por debajo".
   ⚠️ LA LECCIÓN DE LA PRIMERA VEZ: el arreglo anterior fueron 2px de margen +
   una sombra de 3px y NO BASTÓ — Héctor lo volvió a reportar 2 días después.
   Cuando el problema es de PERCEPCIÓN, un arreglo sutil no lo resuelve. Aquí la
   frontera es EXPLÍCITA: separación real + una REGLA VERTICAL de 2px con el
   mismo `--control-edge` que ya usan flechas y migas (cero paleta nueva, cero
   riesgo en los 8 temas). Con la regla puesta, la tarjeta que se desplaza se
   corta EN LA LÍNEA — o sea, en el borde de una pista que se ve — en vez de
   contra el canto de la tarjeta anclada.
   Por qué el borde va en el SCROLLER y no en la tarjeta: `.kbs-card` tiene
   `overflow:hidden` + `border-radius:14px` + su propio `border` de selección,
   así que cualquier línea puesta ahí se leería como parte de la tarjeta.
   El selector es de HERMANO ADYACENTE a propósito: el carrusel de chips de la
   Lista de Espera usa `.kbs-carousel-wrap` SIN tarjeta anclada y no debe
   heredar ninguna línea.
   📍 Las reglas del SCROLLER de este ítem NO viven aquí sino más abajo, DESPUÉS
   de `.kbs-shared > .kbs-scroller` y `.kbs-compact > .kbs-scroller`: esos dos
   usan el atajo `padding` y tienen la misma especificidad (0,2,0) que
   `.kbs-card-pinned + .kbs-scroller`, así que puestas aquí el `padding-left`
   se perdería en silencio en las pantallas compartidas (trampa ④ del estándar
   de QA: "a igual especificidad manda el orden"). Buscar «ÍTEM 339 · frontera».
*/
.kbs-card-pinned { margin-right: 6px; box-shadow: 0 2px 8px rgba(20,34,78,.20), 3px 0 6px -3px rgba(20,34,78,.35); }
.kbs-card-photo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.kbs-card-mono {
    position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    font-size: 2.4rem; font-weight: 800; color: #fff;
    background: linear-gradient(160deg, #1E3A8A, var(--primary-900, #14224E));
}
/* Card "Cualquier barbero" — SIEMPRE el primer card, patrón navy + tijeras */
.kbs-card-any { position: absolute; inset: 0; background: linear-gradient(160deg, #1c2c5e, #0c1636); }
.kbs-card-any-stripe { position: absolute; inset: 0; background: var(--barber-stripe); opacity: .16; }
.kbs-card-any-icon { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 2.4rem; }
.kbs-svc-fallback {
    position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    font-size: 2.2rem; color: #fff; background: linear-gradient(160deg, #1E3A8A, var(--primary-900, #14224E));
}
.kbs-name-band {
    position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
    padding: 7px 8px 8px; text-align: center; color: #fff;
    background: linear-gradient(to top, rgba(6,10,28,.92) 0%, rgba(6,10,28,.55) 55%, rgba(6,10,28,0) 100%);
}
.kbs-name-band .nm { font-size: .8rem; font-weight: 700; line-height: 1.15; }
.kbs-name-band .tg { font-size: .68rem; opacity: .9; margin-top: 1px; }
.kbs-check {
    position: absolute; top: 6px; right: 6px; z-index: 3; width: 24px; height: 24px; border-radius: 50%;
    background: var(--accent-color, #C8102E); color: #fff; display: none; align-items: center; justify-content: center;
    font-size: .8rem; font-weight: 800; box-shadow: 0 2px 6px rgba(0,0,0,.4);
}
.kbs-card.selected .kbs-check { display: flex; }
.kbs-qty-badge {
    position: absolute; top: 6px; right: 6px; z-index: 3; min-width: 24px; height: 24px; border-radius: 12px;
    background: var(--accent-color, #C8102E); color: #fff; display: none; align-items: center; justify-content: center;
    font-size: .72rem; font-weight: 800; box-shadow: 0 2px 6px rgba(0,0,0,.4); padding: 0 .4rem;
}
.kbs-card.selected .kbs-qty-badge { display: flex; }

/* Stepper −/+ DENTRO de la banda-overlay (solo con qty≥1) — control preciso
   sin saturar la vista con 14 servicios en pantalla (pregunta de Héctor) */
.kbs-stepper { display: flex; align-items: center; justify-content: center; gap: .5rem; margin: .15rem 0 .05rem; }
.kbs-stepper button {
    width: 26px; height: 26px; border-radius: 50%; flex-shrink: 0; padding: 0;
    border: 1px solid rgba(255,255,255,.55); background: rgba(255,255,255,.16); color: #fff;
    font-weight: 800; font-size: .9rem; display: flex; align-items: center; justify-content: center;
    touch-action: manipulation;
}
.kbs-stepper button:disabled { opacity: .3; }
.kbs-stepper button:hover:not(:disabled) { background: rgba(255,255,255,.28); }
.kbs-stepper .n { min-width: 24px; text-align: center; font-weight: 800; font-size: .82rem; color: #fff; }

/* Chip "con quién" (pantalla 2) + chips de categoría */
.kbs-who-badge {
    flex-shrink: 0; display: flex; align-items: center; justify-content: center; gap: .4rem;
    background: var(--surface-color, #fff); border: 1px solid var(--control-edge, rgba(20,34,78,.55));
    border-radius: 999px; padding: calc(.3rem - 1px) calc(.9rem - 1px); margin: .35rem auto 0;
    font-size: .8rem; font-weight: 600; width: fit-content; color: var(--bs-body-color);
}
.kbs-who-badge img, .kbs-who-badge .mini-mono { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; }
.kbs-who-badge .mini-mono { display: flex; align-items: center; justify-content: center; font-size: .62rem; font-weight: 800; color: #fff; background: var(--primary-900, #14224E); }
.kbs-cat-chips { flex-shrink: 0; display: flex; gap: .4rem; justify-content: center; flex-wrap: wrap; margin-top: .5rem; }
.kbs-chip {
    background: var(--surface-color, #fff); border: 1px solid var(--control-edge, rgba(20,34,78,.55)); border-radius: 999px;
    padding: .3rem .85rem; font-size: .78rem; font-weight: 600; color: var(--bs-body-color);
    touch-action: manipulation;
}
.kbs-chip.active { background: var(--gradient-btn, linear-gradient(135deg,#C8102E,#9E0C24)); color: #fff; border-color: #9E0C24; }

/* Pie fijo: total estimado + "Ponerme en la fila" (CREA el turno — único punto) */
.kbs-footer { flex-shrink: 0; margin-top: .6rem; padding-top: .5rem; border-top: 1px solid rgba(20,34,78,.12); display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.kbs-total { font-size: .88rem; }
.kbs-total b { font-size: 1.1rem; color: var(--accent-color, #C8102E); }
.kbs-cta {
    min-height: 56px; font-size: 1.05rem; font-weight: 600; color: #fff;
    background: var(--gradient-btn, linear-gradient(135deg,#C8102E,#9E0C24)); border: 1px solid #9E0C24; border-radius: .6rem;
    padding: .5rem 1.4rem; box-shadow: 0 0 12px rgba(200,16,46,.25); touch-action: manipulation;
}
.kbs-cta:disabled { opacity: .4; box-shadow: none; }
.kbs-cta:not(:disabled):hover { background: linear-gradient(135deg,#9E0C24 0%,#7C081B 100%); }

/* ══════════════════════════════════════════════════════════════════════════
   CALENDARIO VERDE/ROJO — reservar cita Y reserva de invitado
   ITEM 314 (hallazgo de Tito 2026-07-25): estas reglas vivian en un <style>
   DENTRO de BookAppointment.razor, pero GuestBooking.razor usa exactamente las
   mismas clases. Resultado: el calendario de la pagina PUBLICA (la del QR del
   barbero) salia sin grilla — los 7 dias de la semana uno debajo del otro y los
   dias sin color verde/rojo. Aqui son GLOBALES: una sola definicion para las
   dos pantallas (misma leccion del 277).
   ────────────────────────────────────────────────────────────────────────── */
.bsp-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.bsp-cal-day { border: none; border-radius: 8px; padding: 6px 0; font-size: .9rem; font-weight: 600; }
.cal-ok { background: #d1e7dd; color: #0f5132; cursor: pointer; }
.cal-ok:hover { filter: brightness(.92); }
.cal-no { background: #f8d7da; color: #842029; opacity: .75; }
.cal-past { background: transparent; color: #adb5bd; }
.cal-sel { outline: 3px solid var(--primary-color, #14224E); outline-offset: 1px; }
.bsp-cal-dot { display: inline-block; width: 12px; height: 12px; border-radius: 4px; margin-right: 5px; vertical-align: -1px; }

/* ══════════════════════════════════════════════════════════════════════════
   ITEM 314 — LOS MISMOS CARDS, FUERA DEL KIOSKO
   Componentes compartidos Components/Shared/BarberCards.razor y
   ServiceCards.razor. El kiosko corre a 1024x600 con el carrusel dentro de una
   columna flex de altura fija (.kbs-carousel-grow); en una pagina normal el
   carrusel vive en el flujo del documento, asi que el scroller no puede pedir
   height:100% de un padre de altura indefinida (se resuelve a auto, pero el
   padding vertical del kiosko sobra). Aqui se le da altura por CONTENIDO.
   ────────────────────────────────────────────────────────────────────────── */
/* padding lateral 3px (el kiosko trae 2px): el anillo de seleccion mide 3px de
   spread y `overflow-x:auto` CLIPEA tambien en vertical/horizontal — con 2px el
   anillo del card del borde salia cortado 1px. */
.kbs-shared > .kbs-scroller { height: auto; align-items: flex-start; padding: 4px 3px 8px; }

/* VARIANTE COMPACTA — pantallas de STAFF (POS). La regla de simplicidad
   [[feedback_ui_simplicity_non_tech_customers]] es para el CLIENTE; al cajero
   lo que le importa es cobrar rapido y no hacer scroll de mas. Mismo sistema
   visual (foto entera + banda), 116x132 en vez de 148x168. */
.kbs-compact > .kbs-scroller { gap: 8px; padding: 4px 3px 7px; }
.kbs-compact .kbs-card { width: 116px; height: 132px; border-radius: 12px; }
.kbs-compact .kbs-arrow { width: 32px; height: 32px; font-size: 1rem; }
.kbs-compact .kbs-name-band { padding: 5px 6px 6px; }
.kbs-compact .kbs-name-band .nm { font-size: .72rem; line-height: 1.1; }
.kbs-compact .kbs-name-band .tg { font-size: .62rem; }
.kbs-compact .kbs-check,
.kbs-compact .kbs-qty-badge { width: 20px; height: 20px; min-width: 20px; top: 4px; right: 4px; font-size: .66rem; }
.kbs-compact .kbs-stepper { gap: .35rem; margin: .1rem 0 .05rem; }
.kbs-compact .kbs-stepper button { width: 21px; height: 21px; font-size: .78rem; }
.kbs-compact .kbs-stepper .n { min-width: 18px; font-size: .74rem; }

/* ══ ÍTEM 339 · frontera — la PISTA del carrusel empieza en una línea visible ══
   Va AQUÍ, al final de las variantes, porque `.kbs-shared > .kbs-scroller` y
   `.kbs-compact > .kbs-scroller` (arriba) escriben `padding` con el atajo y
   comparten especificidad (0,2,0) con `.kbs-card-pinned + .kbs-scroller`: más
   arriba, el `padding-left` se perdería sin avisar (trampa ④). El razonamiento
   del ítem está junto a `.kbs-card-pinned`.
   Solo aplica cuando HAY tarjeta anclada (hermano adyacente): la fila de chips
   de la Lista de Espera usa el mismo wrap SIN tarjeta fija y queda intacta. */
.kbs-card-pinned + .kbs-scroller {
    margin-left: 6px;
    border-left: 2px solid var(--control-edge, rgba(20, 34, 78, .55));
    padding-left: 10px;
}
/* En compacto (POS) todo mide menos: la línea se mantiene, el aire se ajusta. */
.kbs-compact .kbs-card-pinned { margin-right: 4px; }
.kbs-compact .kbs-card-pinned + .kbs-scroller { margin-left: 4px; padding-left: 7px; }
/* 📏 EN TELÉFONO EL AIRE SE PAGA CARO — medido, no supuesto. A 390px de ancho la
   PISTA que se desplaza mide solo 42px de `clientWidth` (dos flechas de 40px +
   la tarjeta anclada de 148px se comen la fila), o sea que de una tarjeta de
   148px se ve una rebanada. Los 20px que cuesta la frontera completa se notan
   justo donde menos sobra, así que por debajo de 480px se conserva **la línea**
   (que es lo que faltaba) y se recorta el colchón: 18px de separación con
   divisor visible, contra los 12px sin divisor de antes. Coste neto: 6px.
   ⚠️ Los 42px de pista NO los introduce este ítem — son de fábrica y merecen su
   propio arreglo (ver el hallazgo anotado en el ítem 339 del plan). */
@media (max-width: 480px) {
    .kbs-card-pinned { margin-right: 2px; }
    .kbs-card-pinned + .kbs-scroller { margin-left: 2px; padding-left: 6px; }
}

/* ══ ÍTEM 339 · HERMANO — la fila de chips de la Lista de Espera ═════════════
   Héctor (2026-07-25): "vi que pusieron un scroll bar que antes no estaba".
   `WaitingList.razor:90` traía TODO su layout en `style=` inline y, por eso,
   sin las dos reglas que ocultan la barra de scroll que el carrusel del kiosko
   sí tiene (`.kbs-scroller`, arriba). Dos carruseles de la misma app
   comportándose distinto — el meta-patrón del ítem 323.
   Se sube el inline a esta clase, con las MISMAS declaraciones (cero cambio de
   layout) más el par que oculta la barra. Un estilo inline gana a cualquier
   clase, así que el `style=` del razor se quitó: si vuelve, esta regla no
   sirve de nada. */
.wl-chips-row {
    display: flex; gap: .5rem; flex: 1 1 auto; min-width: 0;
    overflow-x: auto; scroll-snap-type: x mandatory; padding: 3px 2px 7px;
    scrollbar-width: none; -ms-overflow-style: none;
}
.wl-chips-row::-webkit-scrollbar { display: none; }

/* Nombre largo en un card estrecho: 2 lineas y elipsis — nunca desborda la
   banda ni empuja el precio fuera del card (regla de cero solapes del 249).
   SCOPEADO a .kbs-shared (lo emiten solo los componentes compartidos): las 2
   pantallas del kiosko acaban de medirse al pixel en los items 313/315 y no se
   tocan en esta corrida. */
.kbs-shared .kbs-name-band .nm {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden; overflow-wrap: anywhere;
}

/* ══════════════════════════════════════════════════════════════════════════
   ÍTEM 315 · NIVEL 2 — CONTROL SOBRE FONDO NO CONTROLADO ("la regla del PAR")
   Estándar de Kahlo: MBS\kahlo\KAHLO-ESTANDAR-CONTRASTE-CONTROLES.md (2026-07-25)

   EL DIAGNÓSTICO NO ERA LA OPACIDAD. `.kbs-arrow` y `.kbs-crumb-btn` ya
   renderizaban blanco 100% SÓLIDO (texto 15.34:1, el mejor de la pantalla) y aun
   así "no se ven": lo que fallaba era su LÍMITE contra la foto (1.22:1) con un
   borde #D8D4C8 que mide 1.00:1. Subirles el alpha no habría hecho NADA.

   LA REGLA: un control sobre fondo no controlado presenta SIEMPRE dos
   superficies adyacentes en extremos opuestos de la luminancia — una CLARA
   (porcelana L≈0.88) y una OSCURA (navy #14224E L≈0.027). El navy cubre todo
   fondo con L≥0.182 y la porcelana todo con L≤0.257: los rangos SE SOLAPAN, no
   queda hueco. Fotometría de Kahlo sobre 3,072,000 px de 5 fotos reales de
   tienda (pipeline real: cover 1024×600, object-position 50% 8%, velo .40):
       hoy (relleno 6% + #D8D4C8) ...  3.30% de píxeles pasan 3:1
       solo porcelana .90 ...........  17.54%
       solo borde blanco ............  42.21%
       solo borde navy ..............  99.52%
       PORCELANA + NAVY + HALO ...... 100.00%  ✅

   LA APP SE AUTO-VALIDA: los 2 elementos del kiosko de los que Héctor NUNCA se
   quejó son exactamente los 2 que ya llevaban borde navy — .kiosk-key (3.54:1)
   y .qr-holder (3.92:1). Por eso ninguno de los dos se toca aquí.

   CAUSA RAÍZ CRONOLÓGICA: el ítem 238-v2 (receta E-40, más arriba) ya resolvió
   esto el 7/20, pero lista los selectores POR NOMBRE; el ítem 282 creó la
   familia .kbs-* dos días después y no coincidía con ninguno — nació fuera de
   la receta. Esto la completa: mismo prefijo de hermano `.kiosk-photo-bg ~`,
   así que FLAG OFF = kiosko sin porcelana (y MobileCheckin/POS/booking jamás
   reciben porcelana sobre blanco).

   ⚠️ EL HALO ES `box-shadow` CON SPREAD, JAMÁS padding ni borde grueso (técnica
   E-40 v2): pinta FUERA del elemento sin ocupar espacio, así los controles
   quedan en el MISMO píxel y el teclado del RPi no vuelve a tapar el formulario
   (ítem 313 e-bis, botón Check In en y=369 — tiene que quedarse sobre y=384).
   ⚠️ HALO DE 2px, NO 3px: .kbs-cat-chips tiene gap:.4rem = 6.4px; con 3px por
   lado los halos se tocarían (6 de 6.4). Con 2px quedan 2.4px de aire.
   ⚠️ SIN backdrop-filter A PROPÓSITO: el ítem 223 lo apagó en el kiosko (RPi 4
   sin GPU) y, aunque fuera gratis, el blur PROMEDIA el fondo, no lo aleja — una
   pared blanca borrosa sigue siendo blanca.
   ══════════════════════════════════════════════════════════════════════════ */
:root {
    --ovc-fill: rgba(247, 245, 240, .94);   /* relleno del control — texto navy 13.02:1 */
    --ovc-ink:  #14224E;                    /* tinta Y borde — la capa OSCURA del par   */
    --ovc-halo: rgba(247, 245, 240, .90);   /* halo exterior — MISMO valor de .kiosk-wash */
    --ovc-lift: 0 2px 8px rgba(20, 34, 78, .22);
}
/* over-photo control: el par completo, listo para reusar (ítem 240) */
.ovc { border: 1px solid var(--ovc-ink);
       box-shadow: 0 0 0 2px var(--ovc-halo), var(--ovc-lift); }
.ovc-light { background: var(--ovc-fill); color: var(--ovc-ink); }

/* Filas 1,2,3,4,6 de la tabla de Kahlo — porcelana .94 + borde navy + halo.
   SIN `:not(:hover)` a propósito (a diferencia de la receta E-40): aquellos eran
   `btn-outline-*` de Bootstrap, que SÍ tienen estilo de hover propio al que
   volver; estos NO tienen ninguno, así que el guard solo lograría que el control
   PERDIERA su borde y su halo justo mientras el dedo lo toca (en Chromium táctil
   el :hover se queda pegado tras el tap). */
.kiosk-photo-bg ~ .kiosk-wrap .kbs-crumb-btn,
.kiosk-photo-bg ~ .kiosk-wrap .kbs-step-pill,
.kiosk-photo-bg ~ .kiosk-wrap .kbs-who-badge,
.kiosk-photo-bg ~ .kiosk-wrap .kbs-chip:not(.active),
.kiosk-photo-bg ~ .kiosk-wrap .kbs-arrow {
    background: var(--ovc-fill);
    color: var(--ovc-ink);
    border: 1px solid var(--ovc-ink);
    box-shadow: 0 0 0 2px var(--ovc-halo), var(--ovc-lift);
}
/* Filas 5 y 7 — los ROJOS conservan su gradiente (es la marca) y ganan el par:
   rojo+blanco es un par MAL elegido (deja descubierto L 0.30–0.478, por eso la
   chip activa medía 1.40:1); con el borde navy + halo porcelana cierra a 3.54:1 */
.kiosk-photo-bg ~ .kiosk-wrap .kbs-chip.active,
.kiosk-photo-bg ~ .kiosk-wrap .kbs-cta {
    border-color: var(--ovc-ink);
    box-shadow: 0 0 0 2px var(--ovc-halo), var(--ovc-lift);
}
/* Fila 8 — "Total estimado" era texto (rojo) DIRECTO sobre la foto. La pastilla
   la emite Kiosk.razor solo con KioskWashOn (misma puerta que .kiosk-photo-bg);
   el <b> conserva su rojo de marca por especificidad (.kbs-total b) = 5.88:1
   sobre porcelana. Padding mínimo: .kbs-footer lo manda el CTA (min-height 56px),
   así que la pastilla NO cambia la altura del pie. */
.kbs-total-pill { display: inline-flex; align-items: center; gap: .25rem; flex-wrap: wrap;
    border-radius: 999px; padding: .1rem .55rem; }
/* Fila 9 · MEDIDO EN VIVO, mismo patrón de los ítems 249b/249c ("hacia adentro"):
   .kiosk-chip-wash trae anillos de 8px+9px, pensados para el paso del teléfono,
   donde SOBRA aire. En las pantallas kbs-* NO cabe: entre el borde inferior del
   halo de ← Volver (2px) y el tope del título quedan **1.59px**, así que un
   anillo de 9px se montaba 7.41px sobre la fila de migas (medido con los rects
   INFLADOS por el spread — box-shadow no entra en getBoundingClientRect).
   Se quita el COLCHÓN, no la superficie: el <span> ya mide 31px de alto, o sea
   su porcelana .90 cubre los glifos por completo y el contraste del texto no
   cambia ni un punto (Kahlo, Paso 3: "el ancho del halo no cambia el contraste,
   solo su color y alpha"). Queda la sombra de elevación para que despegue de la
   foto. Resultado: 1.59px de aire arriba y 7.16px abajo — CERO solape. */
.kbs-title-wash { box-shadow: 0 4px 14px rgba(20, 34, 78, .18); }
/* Los interactivos y sus halos, SIEMPRE por encima del fondo (un halo puede
   solapar la foto, jamás tapar un control vecino). Las chips no son `.btn`. */
.kiosk-photo-bg ~ .kiosk-wrap .kbs-chip { position: relative; z-index: 1; }

/* ══ ÍTEM 281 (Héctor 2026-07-22) — LISTA DE ESPERA v2: chips de barbero en
   carrusel de una sola fila (14 barberos JAMÁS envuelven — el protagonismo es
   el grid de turnos), multi-select acumulable. Mismo lenguaje de carrusel del
   kiosko (arrows + swipe + drag). ══ */
.wl-chip-b {
    flex-shrink: 0; scroll-snap-align: start; display: flex; align-items: center; gap: .5rem;
    background: var(--surface-color, #fff); border: 2px solid var(--border-color, #D8D4C8); border-radius: 999px;
    padding: .35rem .9rem .35rem .35rem; font-size: .85rem; font-weight: 600; color: var(--primary-900, #14224E);
    white-space: nowrap; touch-action: manipulation;
}
.wl-chip-b.active { background: var(--primary-900, #14224E); border-color: var(--primary-900, #14224E); color: #fff; }
.wl-chip-b.all-chip { padding: .35rem 1rem; }
.wl-chip-b .avatar { width: 26px; height: 26px; border-radius: 50%; object-fit: cover; flex-shrink: 0; object-position: 50% 15%; }
.wl-chip-b .avatar-mono { width: 26px; height: 26px; border-radius: 50%; flex-shrink: 0; display: flex; align-items: center; justify-content: center; font-size: .6rem; font-weight: 800; color: #fff; background: var(--primary-900, #14224E); }
.wl-chip-b .avatar-any { width: 26px; height: 26px; border-radius: 50%; flex-shrink: 0; display: flex; align-items: center; justify-content: center; font-size: .85rem; background: rgba(20,34,78,.10); }
.wl-chip-b.active .avatar-any { background: rgba(255,255,255,.18); }
.wl-chip-b .eta { font-weight: 800; }
.wl-chip-b .eta.g { color: #178a3e; } .wl-chip-b .eta.y { color: #a9720a; } .wl-chip-b .eta.r { color: #c8102e; }
.wl-chip-b.active .eta.g, .wl-chip-b.active .eta.y, .wl-chip-b.active .eta.r { color: #fff; }
.wl-kpi-narrow { flex: 0 0 auto !important; min-width: 104px; }

/* Modal Atender/Notificar (ítem 283b) */
.wl-notify-actions button { flex: 1 1 160px; }

/* Item 43 (Hector 2026-07-10): display de foto en los edit +25%+ (56 -> 72px) */
/* ÍTEM 280: preview de foto en Employee/Store/Company edit — sesgo hacia arriba
   (Employee es SIEMPRE persona; Store/Company no sufren con el mismo sesgo leve) */
.edit-title-photo { width: 72px; height: 72px; object-fit: cover; object-position: 50% 18%; border-radius: 12px; border: 2px solid var(--border-color, rgba(0,0,0,.1)); }

/* == Cropper de fotos con rotacion (portado de BCP, 2026-07-04) == */
#bsp-cropper-overlay {
    position: fixed; inset: 0; z-index: 1080;
    background: rgba(0, 0, 0, 0.85);
    display: none; align-items: center; justify-content: center;
}
.bsp-cropper-box { display: flex; flex-direction: column; width: 100%; max-width: 640px; height: 100%; max-height: 100vh; padding: 1rem; }
.bsp-cropper-hint { color: #fff; text-align: center; margin-bottom: .5rem; font-weight: 600; }
.bsp-cropper-stage { flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; overflow: hidden; }
.bsp-cropper-stage img { max-width: 100%; max-height: 100%; display: block; }
.bsp-cropper-actions { display: flex; gap: .5rem; justify-content: center; padding-top: 1rem; }
.bsp-cropper-actions .btn { flex: 1; max-width: 160px; }
.bsp-cropper-actions .bsp-cropper-rotate { flex: 0 0 auto; max-width: 52px; font-size: 1.25rem; }

/* == Lapiz de editar foto (Hector 2026-07-04) == */
.photo-edit-wrap { position: relative; display: inline-block; }
.photo-edit-pencil {
    position: absolute; right: -7px; bottom: -7px;
    width: 26px; height: 26px; border-radius: 50%; padding: 0;
    background: var(--accent-color, #C8102E); color: #fff;
    border: 2px solid #fff; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    font-size: .72rem; box-shadow: 0 1px 4px rgba(0,0,0,.35);
}
.photo-edit-pencil:hover { filter: brightness(1.15); }
.photo-edit-thumb {
    /* Item 43/62: 56 -> 72px */
    width: 72px; height: 72px; object-fit: cover; border-radius: 12px;
    border: 2px solid var(--border-color, rgba(0,0,0,.1));
    display: flex; align-items: center; justify-content: center;
}
.photo-edit-empty { background: var(--icon-chip-bg, #F1F5F9); color: #94A3B8; font-size: 1.3rem; }

/* == Localidades (Hector 2026-07-04) == */
/* Item 62: 72 -> 96px (escala md) */
/* ÍTEM 206 (Héctor 2026-07-18): la foto de Yamil salía con la CABEZA CORTADA —
   cover centrado en un cuadro recorta arriba y abajo por igual, y en un
   retrato vertical la cabeza (tercio superior) se va. El ancla 50% 25% sesga
   el encuadre hacia arriba (donde vive la cara) sin perder el cover: vertical
   conserva la cabeza, horizontal/cuadrada casi no cambian. Mismo tratamiento
   de cover de los selectores de barbero/servicio del wizard. */
.loc-photo { width: 96px; height: 96px; object-fit: cover; object-position: 50% 25%; border-radius: 14px; flex-shrink: 0; }
.loc-photo-empty { display: flex; align-items: center; justify-content: center; background: var(--icon-chip-bg, #F1F5F9); color: #94A3B8; font-size: 1.6rem; }
.loc-card { border: 1px solid var(--border-color, rgba(0,0,0,.08)); }

/* == Paginacion estandar (Hector 2026-07-04): Rows per page abajo == */
.bsp-pager { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-top: .5rem; }
.bsp-pager .rows-per-page { display: flex; align-items: center; gap: .5rem; font-size: .85rem; color: var(--bs-secondary-color, #6c757d); }
.bsp-pager .rows-per-page select { width: auto; }

/* Encabezado de columna ORDENABLE para tablas manuales (Hector 2026-07-09, item 9) */
.bsp-sort-header { background: none; border: 0; padding: 0; font: inherit; font-weight: 600;
    color: inherit; cursor: pointer; display: inline-flex; align-items: center; gap: .35rem; }
.bsp-sort-header:hover { color: var(--accent-color, #2563EB); }
.bsp-sort-header .bsp-sort-idle { opacity: .35; font-size: .8em; }

/* Modal centrado simple para el borrado de usuario (Hector 2026-07-09, item 2) */
.modal-backdrop-bsp { position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: 1050; }
.bsp-del-modal { position: fixed; z-index: 1055; top: 50%; left: 50%;
    transform: translate(-50%, -50%); width: min(560px, 94vw);
    max-height: 90vh; overflow: auto; }

/* == Agenda: foto del barbero en el header (Hector 2026-07-04) == */
/* ÍTEM 280: círculo de barbero — sesgo hacia arriba (misma regla, cara nunca cortada) */
.sch-barber-photo { width: 34px; height: 34px; object-fit: cover; object-position: 50% 15%; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; }
.sch-barber-photo-empty { background: var(--icon-chip-bg, #F1F5F9); color: #94A3B8; font-size: 1rem; }

/* == Dropdown con foto (Reservar cita, Hector 2026-07-04) == */
.bsp-photo-dd { position: relative; }
.bsp-photo-dd-menu {
    /* Item 57: items mas altos -> menu un poco mas alto, scroll interno igual */
    position: absolute; z-index: 30; width: 100%; max-height: 420px; overflow: auto;
    background: var(--bs-body-bg, #fff); border: 1px solid rgba(0,0,0,.15); border-radius: 8px;
}
.bsp-photo-dd-item {
    /* Item 57 (Hector 2026-07-10): items ~50% MAS ALTOS para que la foto se
       aprecie — foto 32->48px + padding vertical .45->.65rem */
    display: flex; align-items: center; gap: .6rem; width: 100%;
    padding: .65rem .75rem; border: 0; background: transparent; text-align: left;
}
.bsp-photo-dd-item:hover { background: rgba(13,110,253,.08); }
.bsp-dd-photo { width: 48px; height: 48px; object-fit: cover; border-radius: 10px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; }
/* ÍTEM 280 (BARRIDO): -round SIEMPRE es foto de PERSONA (barbero) — sesgo
   hacia arriba, mismo tratamiento del kiosko/TV. La variante cuadrada (tienda/
   servicio) no se toca. */
.bsp-dd-photo-round { border-radius: 50%; object-position: 50% 15%; }
.bsp-dd-photo-empty { background: var(--icon-chip-bg, #F1F5F9); color: #94A3B8; }

/* == Cuadre del Dia (Hector 2026-07-05): al IMPRIMIR salen ambos tabs == */
@media print {
    .dc-section { display: block !important; }
}

/* == Filtros elasticos (Hector 2026-07-05): se encogen para caber en UNA linea ==
   ÍTEM 270 cosmético 14b (Lupe): el <select> recortaba "Todas las tiendas"/
   "Todas las compañías" en ES a 390px (el ancho no alcanzaba para el texto + la
   flecha nativa). min-width 0 permite encoger sin overflow horizontal; el
   max-width sube a 200px para que la etiqueta ES más larga quepa completa. */
.bsp-filter { flex: 1 1 140px; min-width: 0; max-width: 200px; width: auto; text-overflow: ellipsis; }

/* == Encuesta publica (Hector 2026-07-05): estrellas 1-5 == */
.sv-stars { display: flex; gap: .25rem; }
.sv-star {
    background: none; border: none; font-size: 1.9rem; line-height: 1;
    color: #CBD5E1; cursor: pointer; padding: 0 .15rem; transition: color .1s, transform .1s;
}
.sv-star.on { color: #F59E0B; }
.sv-star:hover { transform: scale(1.15); }

/* == ITEM 353: /encuesta - las 5 estrellas VACIAS, decorativas ==
   Van vacias (no rellenas) por la razon de Hector: "para no predisponer al
   cliente". No son pulsables: la escala se ensena aqui, se valora en la
   pantalla siguiente. */
.enc-stars {
    font-size: 1.7rem; line-height: 1; letter-spacing: .35rem;
    color: #B8860B; margin: .35rem 0 .5rem; text-align: center;
}

/* == TV BOARD (Hector 2026-07-05): 1/4 cola + 3/4 anuncios == */
.tv-root { position: fixed; inset: 0; background: #0B1220; color: #fff; z-index: 1000; overflow: hidden; }
.tv-pick { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; }
.tv-board { display: flex; height: 100%; }
.tv-queue {
    width: 25%; min-width: 260px; display: flex; flex-direction: column;
    background: linear-gradient(180deg, #14224E 0%, #0B1220 100%);
    border-right: 4px solid #C8102E; padding: 1.2rem;
}
.tv-store-name { font-size: 1.4rem; font-weight: 700; margin-bottom: .4rem; }
/* ÍTEM 239 (Héctor 2026-07-20, estándar "pantallas personalizadas"): FOTO de la
   barbería junto al ícono y el título del TV board — compacta, nunca roba la cola */
/* ÍTEM 280 (Héctor 2026-07-22, "la foto de Yamil está sin cabeza!"): mismo
   tratamiento focal del kiosko (238) — sesgo hacia ARRIBA, la cara vive en el
   tercio superior de la foto. Regla de la casa: LAS CARAS JAMÁS SE CORTAN. */
.tv-store-photo { width: 100%; max-height: 130px; object-fit: cover;
    object-position: 50% 18%;
    border-radius: 12px; margin-bottom: .55rem; border: 2px solid rgba(255, 255, 255, .18); }
.tv-queue-title { text-transform: uppercase; letter-spacing: .15em; font-size: .85rem; color: #93C5FD; margin-bottom: 1rem; }
.tv-queue-list { flex: 1; overflow: hidden; display: flex; flex-direction: column; gap: .55rem; }
.tv-queue-row {
    display: flex; align-items: center; gap: .8rem;
    background: rgba(255,255,255,.06); border-radius: 12px; padding: .55rem .8rem;
}
.tv-queue-row.next { background: #C8102E; box-shadow: 0 0 18px rgba(200,16,46,.55); }
.tv-turn {
    font-size: 1.6rem; font-weight: 800; min-width: 2.4rem; text-align: center;
    background: rgba(255,255,255,.12); border-radius: 10px; padding: .1rem .3rem;
}
.tv-queue-row.next .tv-turn { background: #fff; color: #C8102E; }
.tv-name { font-size: 1.25rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tv-empty { flex: 1; display: flex; align-items: center; justify-content: center; font-size: 1.3rem; color: #93C5FD; }
.tv-footer { text-align: center; font-size: .8rem; color: #64748B; margin-top: .8rem; }
.tv-ads { width: 75%; flex: 1; display: flex; align-items: center; justify-content: center; background: #000; }
.tv-media { width: 100%; height: 100%; object-fit: contain; }
.tv-house-ad {
    width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
    background: linear-gradient(135deg, #14224E 0%, #C8102E 100%);
}
.tv-house-inner { text-align: center; max-width: 720px; padding: 2rem; }
.tv-house-inner h1 { font-size: 3rem; font-weight: 800; color: #fff; }
.tv-house-inner p { font-size: 1.4rem; color: #E2E8F0; }
.tv-house-contact { font-weight: 700; font-size: 1.6rem !important; color: #fff !important; }
.tv-house-brand { font-size: 1rem !important; color: #93C5FD !important; margin-top: 1.5rem; }

/* House ad: QR para pedir anunciarse (Hector 2026-07-05) */
.tv-house-qr { width: 160px; height: 160px; border-radius: 12px; background: #fff; padding: 8px; margin-top: .5rem; }
.tv-house-scan { font-size: 1.1rem !important; font-weight: 600; color: #fff !important; margin-top: .4rem; }

/* ITEM 148 (Batch K): anuncio TARJETA — foto opcional (barbero/barberia) en
   circulo elegante sobre el mismo lienzo del house ad.
   ÍTEM 280: círculo centrado decapitaba retratos verticales (caso real: Yamil)
   — sesgo hacia ARRIBA, mismo tratamiento del kiosko. */
.tv-card-photo { width: 150px; height: 150px; object-fit: cover; border-radius: 50%;
                 object-position: 50% 15%;
                 border: 4px solid #fff; box-shadow: 0 6px 24px rgba(0,0,0,.35); margin-bottom: .75rem; }

/* KIOSCO fullscreen (Hector 2026-07-05): tablet sellada, salir con codigo */
/* FIX Vera/QA (2026-07-23, regla 238): padding:1rem (~16px) dejaba solo 20px
   de holgura real sobre el primer elemento (.kbs-crumb-btn) en 1024x600 —
   MISMA lección que rompió el kiosko de Daniel, la regla dura exige ≥49px.
   El mockup aprobado (mockup-kiosko-barbero-servicios.html .kiosk-wrap) usa
   padding-top:52px — se sube SOLO el top a esa medida (izq/der/abajo intactos,
   0 impacto en el resto del layout); verificado con getBoundingClientRect en
   vivo: 52px + 4px de margin-top propio de .kbs-crumb = 56px de holgura real. */
.kiosk-root { position: fixed; inset: 0; background: var(--page-bg, #F8FAFC); color: var(--text-color, #0F172A); z-index: 1000; overflow: auto; padding: 52px 1rem 1rem; }
.kiosk-exit { position: fixed; right: 14px; bottom: 14px; z-index: 1001; opacity: .45; }
.kiosk-exit:hover { opacity: 1; }
/* ÍTEM 170: botón directo de idioma del kiosco — ARRIBA a la derecha (el
   candado de salida vive abajo a la derecha); grande, dedo-amigable */
.kiosk-lang { position: fixed; top: 14px; right: 14px; z-index: 1001;
    font-size: 1.05rem; padding: .55rem 1rem; touch-action: manipulation; }

/* ÍTEM 77 (Héctor 2026-07-10, mockup + Opción 1): CARD DE CITA de DOS FILAS —
   fila 1 columnas de datos, fila 2 barra de acciones a lo ancho. Sin scroll
   horizontal; en móvil todo apila con wrap. */
.appt-card {
    border: 1px solid var(--bs-border-color, #DEE2E6);
    border-radius: 10px;
    margin-bottom: .6rem;
    overflow: hidden;
    background: var(--card-bg, #fff);
}
.appt-card-cancelled { opacity: .72; }
.appt-card-data {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem 1.25rem;
    padding: .55rem .8rem;
    align-items: flex-start;
}
.appt-col { display: flex; flex-direction: column; min-width: 84px; }
.appt-lbl {
    font-size: .68rem;
    text-transform: uppercase;
    letter-spacing: .03em;
    /* ÍTEM 270 cosmético (Lupe): las etiquetas DATE/TIME quedaban en 4.34:1 sobre
       el fondo glass-subtle de las cards (bajo AA). #5E6A7E sube a 4.99:1 (glass)
       / 5.47:1 (blanco) — AA cómodo en TODOS los temas (las cards son claras en
       todos: --card-bg no está definido), sin dejar de leerse muted. */
    color: var(--muted-color, #5E6A7E);
    font-weight: 600;
}
.appt-card-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    padding: .5rem .8rem;
    border-top: 1px dashed var(--bs-border-color, #E2E8F0);
    background: var(--bs-tertiary-bg, #F8FAFC);
}
.appt-sortbar { row-gap: .25rem; }

/* ── ÍTEM 228 (mockup Kahlo): decisión de reembolso — las DOS caras EN VIVO ──
   "Reembolsas al cliente $X" (verde, dinero que sale) · "El negocio retiene $Y"
   (neutro). Ningún número de dinero sin decir DE QUÉ es (regla de Héctor). */
.rf-faces { display: flex; gap: .5rem; margin-top: .6rem; }
.rf-face {
    flex: 1; border: 1.5px solid; border-radius: 11px;
    padding: .5rem .65rem; display: flex; flex-direction: column; gap: .1rem;
}
.rf-face-l { font-size: .72rem; font-weight: 700; display: flex; align-items: center; gap: .3rem; }
.rf-face-v { font-size: 1.25rem; font-weight: 800; line-height: 1.1; }
.rf-face-give { background: rgba(22, 163, 74, .12); border-color: #16A34A; }
.rf-face-give .rf-face-l, .rf-face-give .rf-face-v { color: #15803D; }
.rf-face-keep { background: var(--bs-tertiary-bg, #F1F5F9); border-color: var(--bs-border-color, #CBD5E1); }
.rf-face-keep .rf-face-l { color: var(--bs-secondary-color, #64748B); }
.rf-face-keep .rf-face-v { color: var(--bs-secondary-color, #475569); }
.rf-quick { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .55rem; }

/* ÍTEM 86a (Héctor 2026-07-10): keypad numérico del POS estilo Kiosk —
   touch-friendly para tablet/celular; algo más compacto que el del kiosco */
.pos-pad { display: grid; grid-template-columns: repeat(3, 76px); gap: 8px; }
/* ÍTEM 237e (Héctor 2026-07-20): los textos de tecla ("Borrar") NO se parten
   en 2 líneas — nowrap + versión reducida para teclas con palabra */
.pos-key { height: 56px; font-size: 1.25rem; font-weight: 600; white-space: nowrap; padding-left: 2px; padding-right: 2px; }
.pos-key-txt { font-size: .9rem; }

/* ── BATCH J (ítem 131a): fila Save/Cancel PEGAJOSA en los forms largos ──────
   EmployeeEdit / StoreEdit / CompanyEdit / CustomerEdit / Ads: la fila de
   botones (con su copia del StatusAlert del ítem 124) queda fija al FONDO del
   viewport mientras se scrollea el form — position:sticky (no fixed): al
   llegar al final vuelve a su lugar natural y NUNCA tapa el cierre del form.
   Fondo = superficie del TEMA (todas las 8 hojas definen --surface-color) y
   z-index para que el contenido no se transparente al pasar por debajo. */
.bsp-sticky-actions {
    position: sticky;
    bottom: 0;
    z-index: 5;
    background: var(--surface-color, #fff);
    padding: .6rem 0 .75rem;
    margin-top: .75rem;
    border-top: 1px solid var(--border-color, #DEE2E6);
    /* que el fondo cubra de borde a borde del card-body (sangra el padding) */
    box-shadow: 0 -6px 8px -8px rgba(0, 0, 0, .25);
}
/* el alert dual del ítem 124 vive DENTRO de la barra pegajosa: sin margen
   extra abajo para que alert + botones queden compactos */
.bsp-sticky-actions .alert { margin-bottom: .5rem; }

/* ── ÍTEM 189 v1: FONDO personalizado de la página del barbero (/b/{slug}) ──
   Capas (de atrás hacia delante): FOTO (fallback, object-fit cover) → IFRAME
   de video (embed construido server-side por BackgroundMedia — si no carga,
   se transparenta y queda la foto) → OVERLAY oscuro para legibilidad (mismo
   tratamiento del hero del Home). Todo fixed detrás del contenido
   (z-index:-1) y sin eventos (decoración pura).
   TEMAS OSCUROS: los 5 dark (rose/onyx/jade/hc-dark/deep) definen
   --public-bg-media:none y APAGAN el bloque completo — réplica del patrón
   --auth-bg-image:none; los temas claros no definen la variable y el
   fallback la deja en block. */
.barber-bg {
    position: fixed;
    inset: 0;
    z-index: -1;
    overflow: hidden;
    pointer-events: none;
    display: var(--public-bg-media, block);
}
.barber-bg-photo {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}
/* El iframe 16:9 se sobredimensiona para CUBRIR el viewport en cualquier
   proporción (patrón cover para video embebido): al menos 100vw de ancho y
   100vh de alto, manteniendo 16:9 (177.78vh / 56.25vw), centrado. */
.barber-bg-frame {
    position: absolute;
    top: 50%;
    left: 50%;
    width: max(100vw, 177.78vh);
    height: max(100vh, 56.25vw);
    transform: translate(-50%, -50%);
    border: 0;
    pointer-events: none;
}
.barber-bg-overlay {
    position: absolute;
    inset: 0;
    background: linear-gradient(to bottom, rgba(20, 34, 78, 0.35) 0%, rgba(20, 34, 78, 0.60) 100%);
}

/* El BODY pinta el color+gradiente del tema y tapaba la capa z-index:-1 —
   con fondo de barbero presente se destapa. En temas OSCUROS .barber-bg está
   apagado por --public-bg-media y el body conserva su fondo vía
   --public-bg-body (definido solo en las 5 hojas dark = var(--background-color)).
   Sin soporte de :has() el fondo queda tapado — degradación elegante. */
body:has(.barber-bg) {
    background-color: var(--public-bg-body, transparent);
    background-image: none;
}

/* ── 🎭 BATCH 195: IMPERSONACIÓN DE BARBEROS ──────────────────────────────
   Banner permanente e INCONFUNDIBLE (rayas de precaución, sticky, siempre
   arriba de todo) mientras el SuperAdmin actúa como un barbero — regla de
   Héctor: que jamás opere creyendo ser él mismo. */
.bimp-banner {
    position: sticky;
    top: 0;
    z-index: 1050; /* por encima del header/sidebar/toasts */
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: .75rem;
    padding: .5rem .75rem;
    background: repeating-linear-gradient(45deg, #b45309, #b45309 16px, #92400e 16px, #92400e 32px);
    color: #fff;
    font-weight: 600;
    text-shadow: 0 1px 2px rgba(0, 0, 0, .45);
    box-shadow: 0 2px 8px rgba(0, 0, 0, .3);
}

.bimp-banner-btn {
    font-weight: 600;
    white-space: nowrap;
}

/* Panel del selector de barberos en el NavMenu (lista agrupada + búsqueda) */
.bimp-picker {
    background: rgba(255, 255, 255, .08);
    border-radius: .375rem;
    padding: .4rem;
}

.bimp-list {
    max-height: 260px;
    overflow-y: auto;
}

.bimp-group {
    padding: .25rem .25rem .1rem;
    opacity: .85;
}

.bimp-item {
    color: inherit;
    border: 0;
    padding: .2rem .5rem;
}

.bimp-item:hover {
    background: rgba(255, 255, 255, .18);
    color: inherit;
}

/* ═══════════════════════════════════════════════════════════════════════
   REDISEÑO DE LA AGENDA (Tito 2026-07-19, ítems 215-224, mockup de Kahlo)
   Glass tokens (223), header sticky (218), celda 2-líneas + iconos (215),
   scroll horizontal + auto-ancho + gutter sticky (216), calendario de
   capacidad dropdown/bottom-sheet con #+% (221), modal de la cita (224).
   ═══════════════════════════════════════════════════════════════════════ */

/* ── ÍTEM 223: GLASS tokenizado. color-mix sobre la superficie del tema →
   se adapta solo a los 8 temas (claro y oscuro). Fallback sólido primero
   (si color-mix o backdrop-filter no están, queda el fondo sólido). ── */
:root { --bsp-glass-blur: 10px; }
.bsp-glass-strong {
    background: var(--surface-color, #fff);
    background: color-mix(in srgb, var(--surface-color, #fff) 84%, transparent);
    backdrop-filter: blur(var(--bsp-glass-blur)) saturate(1.15);
    -webkit-backdrop-filter: blur(var(--bsp-glass-blur)) saturate(1.15);
}
.bsp-glass-subtle {
    background: var(--bs-body-bg, #f7f5f0);
    background: color-mix(in srgb, var(--bs-body-bg, #f7f5f0) 74%, transparent);
    backdrop-filter: blur(calc(var(--bsp-glass-blur) * .7)) saturate(1.1);
    -webkit-backdrop-filter: blur(calc(var(--bsp-glass-blur) * .7)) saturate(1.1);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .bsp-glass-strong, .bsp-glass-subtle { background: var(--surface-color, #fff); }
}
/* PERFORMANCE KIOSCO (RPi4): backdrop-filter:blur es caro en GPU → sin blur */
html[data-kiosk="1"] .bsp-glass-strong,
html[data-kiosk="1"] .bsp-glass-subtle { backdrop-filter: none; -webkit-backdrop-filter: none; }

/* ── ÍTEM 218: HEADER STICKY arriba, compacto y táctil ── */
.sch-toolbar {
    display: flex; align-items: center; gap: .45rem; flex-wrap: wrap;
    padding: .45rem .7rem; position: sticky; top: 0; z-index: 20;
    border-bottom: 1px solid var(--bs-border-color, #E2E8F0);
    box-shadow: 0 2px 8px -4px rgba(11, 18, 32, .2);
    border-top-left-radius: var(--bs-card-inner-border-radius, .5rem);
    border-top-right-radius: var(--bs-card-inner-border-radius, .5rem);
}
.sch-head-ico { width: 30px; height: 30px; border-radius: 8px; object-fit: cover; flex: none; }
.sch-head-title { font-size: 1rem; }
.sch-head-spacer { flex: 1 1 auto; }
.sch-datenav { display: flex; align-items: center; gap: .28rem; }
.sch-navbtn, .sch-todaybtn, .sch-datepick, .sch-capbtn {
    height: 34px; border-radius: 8px; border: 1px solid var(--bs-border-color, #E2E8F0);
    background: var(--surface-color, #fff); color: var(--bs-body-color, #14224E);
    display: inline-flex; align-items: center; justify-content: center; line-height: 1;
}
.sch-navbtn { min-width: 34px; font-size: .9rem; }
.sch-navbtn:hover { background: var(--primary-color, #14224E); color: #fff; border-color: var(--primary-color, #14224E); }
.sch-todaybtn { padding: 0 .7rem; font-size: .8rem; font-weight: 600;
    background: var(--primary-color, #14224E); color: #fff; border-color: var(--primary-color, #14224E); }
.sch-datepick { width: auto; max-width: 148px; padding: 0 .5rem; font-size: .8rem; font-weight: 600; }
.sch-storepick { max-width: 175px; height: 34px; }
.sch-capbtn { gap: .35rem; padding: 0 .7rem; font-size: .8rem; font-weight: 600; }
.sch-capbtn.on, .sch-capbtn:hover { border-color: var(--accent-color, #C8102E); color: var(--accent-color, #C8102E); }
/* selector Día | Semana | Mes */
.sch-viewseg { display: inline-flex; background: var(--bs-body-bg, #EEF1F7);
    border: 1px solid var(--bs-border-color, #E2E8F0); border-radius: 9px; padding: 2px; gap: 2px; }
.sch-seg { height: 30px; padding: 0 .7rem; border: 0; background: transparent;
    color: var(--bs-secondary-color, #64748B); font-size: .78rem; font-weight: 600; border-radius: 7px; }
.sch-seg.on { background: var(--accent-color, #C8102E); color: #fff; }

/* ── Semana/Mes = placeholder (scope aparte) ── */
.sch-soon { padding: 2.4rem 1.2rem; text-align: center; max-width: 560px; margin: 1rem auto; }
.sch-soon-badge { display: inline-block; background: var(--bs-body-bg, #EEF1F7);
    color: var(--bs-secondary-color, #64748B); font-weight: 700; font-size: .74rem;
    letter-spacing: .06em; text-transform: uppercase; padding: .25rem .8rem;
    border-radius: 999px; margin-bottom: .7rem; }

/* ── ÍTEM 216: agenda scroll horizontal + gutter STICKY + columnas AUTO-ANCHO ── */
.sch-agenda-wrap { overflow: auto; position: relative; max-height: calc(100vh - 250px);
    border: 1px solid var(--bs-border-color, #E2E8F0); border-radius: 10px; }
.sch-agenda { width: max-content; min-width: 100%; }
.sch-time-gutter { position: sticky; left: 0; z-index: 4; background: var(--surface-color, #fff);
    box-shadow: 2px 0 6px rgba(11, 18, 32, .06); }
.sch-agenda-head { z-index: 5; }
.sch-agenda-head .sch-time-gutter { z-index: 6; background: var(--sidebar-bg, #14224E); }
.sch-barber-col { flex: 0 0 auto; min-width: 0; max-width: none; }

/* ── ÍTEM 349 (Tito 2026-07-26) — AQUÍ VIVÍA EL CASO ESPECIAL DEL ÍTEM 285 ──
   Era: `.sch-time-gutter .sch-gutter-label:first-child { top:2px !important;
   transform:none !important; }`. Resolvía que la 1ra hora quedara tapada por el
   header sticky, pero al sacarla de su centrado la bajaba ~8-10px y la dejaba
   PEGADA a la siguiente — que es justo lo que Héctor vio: "8am y 8:15am se ven
   bien apretados".
   SUSTITUIDO por un respiro superior en el CUERPO de la agenda
   (`GutterPadTopPx`, Schedule.razor): ahora cabe centrada igual que todas y no
   hay nada que tapar. El relleno va en el padre flex del gutter Y de las
   columnas, así que etiquetas, líneas y citas se mueven JUNTAS y siguen
   cuadrando. Regla borrada a propósito: el arreglo es no tener excepción. */

/* ── ÍTEM 215: celda = nombre arriba + apellido(s) 2da línea + iconos ── */
.sch-appt-block { cursor: pointer; }
.sch-appt-row { align-items: flex-start; gap: 4px; }
.sch-appt-name { display: flex; flex-direction: column; line-height: 1.1; overflow: hidden;
    flex: 1 1 auto; min-width: 0; white-space: normal; }
.sch-appt-nf { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: .76rem; }
.sch-appt-nl { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    font-size: .7rem; opacity: .68; }
/* iconitos de celda ✂️/✅/👁️ — táctiles, meaningful, sin depender de hover */
.sch-cell-ic { width: 26px; height: 26px; border-radius: 7px; flex: none; padding: 0;
    border: 1px solid var(--bs-border-color, #E2E8F0); background: var(--surface-color, #fff);
    color: var(--bs-body-color, #14224E); display: inline-flex; align-items: center; justify-content: center; }
.sch-cell-ic svg { width: 14px; height: 14px; }
.sch-cell-ic-serve { color: #16A34A; border-color: rgba(22, 163, 74, .45); }
.sch-cell-ic-serve:hover { background: #16A34A; color: #fff; }
.sch-cell-ic-finish { color: #fff; background: #16A34A; border-color: #16A34A; }
.sch-cell-ic-eye { color: var(--primary-color, #14224E); }
.sch-cell-ic-eye:hover { background: var(--primary-color, #14224E); color: #fff; border-color: var(--primary-color, #14224E); }
/* FIX 1 (Vera QA 2026-07-19): en TEMA OSCURO el ojo 👁️ usaba --primary-color
   (navy) sobre superficie navy → contraste ~1:1 (casi invisible). En los 5
   temas oscuros (onyx/deep/jade/rose/hc-dark) usa el color de texto del body
   para que se vea. Mismo mecanismo de clase-ancestro en .page que ya usa
   .sidebar.theme-hc-dark. Solo el ojo — ✂️/✅/⚠️ ya contrastan bien. */
.page.theme-onyx  .sch-cell-ic-eye,
.page.theme-deep  .sch-cell-ic-eye,
.page.theme-jade  .sch-cell-ic-eye,
.page.theme-rose  .sch-cell-ic-eye,
.page.theme-hc-dark .sch-cell-ic-eye { color: var(--bs-body-color, #E2E8F0); }
/* ÍTEM 219: indicador de nota = ⚠️ AMARILLO (señala; SIN texto) */
.sch-note-warn { flex: none; display: inline-flex; align-items: center; line-height: 0; }
.sch-note-warn svg { width: 16px; height: 16px; filter: drop-shadow(0 1px 1px rgba(0, 0, 0, .18)); }
/* FIX 2 (Vera QA 2026-07-19): en cita de 15 min el bloque mide ~22px; la 2da
   línea (apellidos, .7rem) y los iconos (26px) NO caben → el apellido se veía
   como una tira de ~7px y el ícono recortado. En bloque compacto (duración =
   mínimo de 15 min, altura < 24px) se OCULTA el apellido (el nombre de pila
   queda legible; el modal trae el nombre completo) y los iconos se fijan a un
   tamaño tappable mínimo (22px, no ~4px), fila centrada y padding reducido para
   que quepan enteros. La clase la aplica Schedule.razor según la duración. */
.sch-appt-block--compact { padding-top: 1px; padding-bottom: 1px; }
.sch-appt-block--compact .sch-appt-row { align-items: center; }
.sch-appt-block--compact .sch-appt-nl { display: none; }
.sch-appt-block--compact .sch-cell-ic { width: 22px; height: 22px; border-radius: 6px; }
.sch-appt-block--compact .sch-cell-ic svg { width: 13px; height: 13px; }

/* ── ÍTEM 221: # de clientes en el calendario de capacidad ── */
.sch-cap-cnt { font-size: .9rem; font-weight: 700; line-height: 1; align-self: center; }
.sch-cap-cnt small { font-size: .5rem; font-weight: 600; opacity: .75; margin-left: 1px; }
.sch-cap-cell { justify-content: flex-start; gap: 1px; }
.sch-cap-cell .sch-cap-daynum { position: absolute; top: 2px; left: 4px; font-size: .56rem; opacity: .6; font-weight: 600; }
.sch-cap-cell .sch-cap-pct { align-self: center; font-size: .58rem; opacity: .85; }

/* ── ÍTEM 221: dropdown (desktop) ↔ bottom-sheet (móvil) ── */
.sch-cap-back { position: fixed; inset: 0; z-index: 1040; background: transparent; }
.sch-cap-panel { position: absolute; top: 4px; right: 8px; z-index: 1050; width: 330px; max-width: calc(100vw - 24px);
    border: 1px solid rgba(255, 255, 255, .5); border-radius: 12px; box-shadow: 0 18px 50px rgba(11, 18, 32, .34); padding: 11px; }
.sch-cap-grab { display: none; }
.sch-cap-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.sch-cap-title { font-weight: 600; flex: 1; text-transform: capitalize; }
.sch-cap-nav { display: flex; gap: 5px; align-items: center; }
.sch-cap-nav button { width: 28px; height: 28px; border-radius: 7px; border: 1px solid var(--bs-border-color, #E2E8F0);
    background: var(--bs-body-bg, #EEF1F7); color: var(--bs-body-color, #14224E); display: inline-flex; align-items: center; justify-content: center; }
.sch-cap-nav .sch-cap-todaybtn { width: auto; padding: 0 .6rem; font-size: .72rem; font-weight: 600;
    border-color: var(--primary-color, #14224E); background: var(--primary-color, #14224E); color: #fff; }
/* ÍTEM 367-bis: esta X era CUADRADA y con colores de tema — quedó fuera del
   barrido del 367 porque no usa .btn-close. Ahora toma el ADN del bloque
   compartido (ver .btn-close/.sch-m-close al final del archivo). Aquí solo
   quedan el tamaño y la fuente propios. */
.sch-cap-x { width: 28px; height: 28px; font-size: 1rem; line-height: 1; }
.sch-cap-dowrow { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; margin-bottom: 3px; }
.sch-cap-legend { display: flex; gap: .7rem; flex-wrap: wrap; font-size: .64rem;
    color: var(--bs-secondary-color, #64748B); margin-top: 8px; align-items: center; }
.sch-cap-legend span { display: inline-flex; align-items: center; gap: .25rem; }

@media (max-width: 640px) {
    /* móvil: MISMA experiencia como bottom-sheet táctil (≥44px) */
    .sch-cap-back { background: rgba(11, 18, 32, .5); z-index: 1050; }
    .sch-cap-panel { position: fixed; left: 0; right: 0; bottom: 0; top: auto; width: 100%; max-width: 100%;
        border-radius: 20px 20px 0 0; padding: 8px 14px 18px; max-height: 86vh; overflow: auto; }
    .sch-cap-grab { display: block; width: 44px; height: 5px; border-radius: 3px;
        background: var(--bs-border-color, #E2E8F0); margin: 4px auto 10px; }
    .sch-cap-nav button, .sch-cap-x { width: 44px; height: 44px; font-size: 1.1rem; }
    .sch-cap-nav .sch-cap-todaybtn { height: 44px; padding: 0 .9rem; font-size: .84rem; }
    .sch-cap-cell { min-height: 52px; }
    .sch-cap-cnt { font-size: 1.1rem; }
}

/* ── ÍTEM 224: MODAL DE LA CITA ── */
.sch-modal-back { background: rgba(11, 18, 32, .55); }
.sch-modal-dialog { max-width: 420px; }
.sch-modal { border-radius: 16px; border: 1px solid rgba(255, 255, 255, .4);
    box-shadow: 0 18px 50px rgba(11, 18, 32, .34); overflow: hidden; }
.sch-m-top { position: relative; padding: 14px 16px 12px; border-bottom: 1px solid var(--bs-border-color, #E2E8F0); }
/* ÍTEM 367-bis: LA X CUADRADA QUE VIO HÉCTOR — el botón de cerrar del modal de
   la cita no usa .btn-close (es un <button> propio con &times;) y quedó fuera
   del círculo del 367. Se une al bloque compartido (final del archivo); aquí
   solo quedan posición y tamaño propios. */
/* ÍTEM 367-ter: la POSICIÓN (absolute/top/right) se declara UNA sola vez, en el
   bloque compartido del final del archivo — si se repitiera aquí volvería a
   depender del orden del archivo quién gana. Aquí solo tamaño y fuente. */
.sch-m-close { width: 34px; height: 34px; font-size: 1.1rem; line-height: 1; }
.sch-m-eyebrow { font-size: .64rem; text-transform: uppercase; letter-spacing: .16em; color: var(--accent-color, #C8102E); font-weight: 600; }
.sch-m-name { font-size: 1.25rem; font-weight: 600; margin: 2px 0 0; }
.sch-m-sub { font-size: .82rem; color: var(--bs-secondary-color, #64748B); margin-top: 1px; }
.sch-m-meta { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 9px; }
.sch-m-total { display: flex; align-items: baseline; gap: 5px; }
.sch-m-total b { color: var(--gold-color, #B45309); font-weight: 700; font-size: 1.4rem; line-height: 1; }
.sch-m-total span { font-size: .6rem; text-transform: uppercase; letter-spacing: .1em; color: var(--bs-secondary-color); font-weight: 600; }
.sch-state-tag { display: inline-flex; align-items: center; gap: 5px; border-radius: 999px;
    padding: 2px 9px; font-size: .7rem; font-weight: 600; }
.sch-state-active { background: rgba(22, 163, 74, .18); color: #16A34A; }
.sch-state-booked { background: rgba(37, 99, 235, .14); color: #2563EB; }
.sch-state-done { background: rgba(100, 116, 139, .16); color: #64748B; }
/* NOTA AL TOPE (219): acento amarillo para que salte a la vista */
.sch-note-card { margin: 11px 16px 2px; border-left: 4px solid #F5B301; border-radius: 8px;
    padding: 8px 10px; font-size: .82rem; background: var(--bs-body-bg, #EEF1F7); }
.sch-note-lbl { display: flex; justify-content: space-between; gap: 8px; align-items: center;
    font-size: .62rem; text-transform: uppercase; letter-spacing: .08em; color: var(--bs-secondary-color); margin-bottom: 3px; }
.sch-note-warnico { width: 14px; height: 14px; vertical-align: -3px; margin-right: 2px; }
.sch-note-lock { color: var(--accent-color, #C8102E); text-transform: none; letter-spacing: 0; }
.sch-note-txt { line-height: 1.35; }
/* zona de pago */
.sch-pay { padding: 10px 16px 4px; }
.sch-pay-lbl { font-size: .66rem; text-transform: uppercase; letter-spacing: .06em;
    color: var(--bs-secondary-color); margin-bottom: 7px; }
.sch-pay-hint { text-transform: none; letter-spacing: 0; }
/* 🐛 FIX QA VERA 237j (2026-07-20): en 375px el grid desbordaba — el min-content
   de los labels nowrap inflaba las columnas (repeat(3,1fr) SIN minmax(0,..) =
   min-width:auto → 645px en un viewport de 375; 6 de 11 métodos inalcanzables).
   RAÍZ arreglada aquí porque estas clases son GLOBALES (modal agenda 224 + POS):
   minmax(0,1fr) + min-width:0 SIEMPRE, y en móvil 2 columnas con labels en wrap. */
.sch-pay-methods { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 7px; }
.sch-pm { min-height: 46px; border-radius: 11px; border: 1px solid var(--bs-border-color, #E2E8F0);
    background: var(--surface-color, #fff); color: var(--bs-body-color, #14224E); display: flex;
    align-items: center; justify-content: center; gap: 6px; font-weight: 600; font-size: .82rem;
    padding: 4px 8px; position: relative; white-space: normal; text-align: center;
    line-height: 1.15; min-width: 0; }
@media (max-width: 480px) {
    .sch-pay-methods { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .sch-pm { font-size: .78rem; padding: 4px 6px; }
    .sch-pm-sub { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* FIX menor QA Vera: Google Pay tenía la clase de Venmo — color alusivo propio */
.sch-pm-gpay { background: #3C4043; border-color: #3C4043; color: #fff; }
.sch-pm:hover { box-shadow: 0 6px 22px rgba(20, 34, 78, .16); transform: translateY(-1px); }
.sch-pm-ic { width: 22px; height: 22px; display: inline-flex; align-items: center; justify-content: center; flex: none; font-weight: 800; }
.sch-pm-ic svg { width: 18px; height: 18px; }
.sch-cardart { width: 26px !important; height: 18px !important; }
.sch-pm-cash { background: #2E7D5B; border-color: #2E7D5B; color: #fff; }
.sch-pm-card { background: #14224E; border-color: #14224E; color: #fff; }
.sch-pm-mobile { background: #4F46E5; border-color: #4F46E5; color: #fff; }
.sch-pm-ath { background: #B0562B; border-color: #B0562B; color: #fff; }
.sch-pm-zelle { background: #7C4DD4; border-color: #7C4DD4; color: #fff; }
.sch-pm-venmo { background: #2478B0; border-color: #2478B0; color: #fff; }
.sch-pm-cashapp { background: #0B8A4B; border-color: #0B8A4B; color: #fff; }
.sch-pm-paypal { background: #24408E; border-color: #24408E; color: #fff; }
.sch-pm-generic { background: var(--surface-color, #fff); color: var(--bs-body-color); border-style: dashed; }
.sch-pm-badge { position: absolute; top: -7px; right: 6px; font-size: .54rem; background: #16A34A;
    color: #fff; border-radius: 999px; padding: 1px 6px; font-weight: 700; }
.sch-pm-star { margin-left: auto; font-size: .58rem; font-weight: 800; background: rgba(255, 255, 255, .28);
    border-radius: 999px; padding: 1px 7px; }
.sch-pm-chev { transition: transform .15s ease; font-size: .8rem; }
.sch-pm-mobile.open .sch-pm-chev { transform: rotate(180deg); }
.sch-pm-sub { grid-column: 1 / -1; display: grid; grid-template-columns: 1fr 1fr; gap: 6px;
    padding: 7px; border: 1px dashed var(--bs-border-color, #E2E8F0); border-radius: 11px; background: var(--bs-body-bg, #EEF1F7); }
.sch-pm-sub .sch-pm { min-height: 42px; font-size: .8rem; }
.sch-pm-athfirst { grid-column: 1 / -1; box-shadow: 0 0 0 2px rgba(176, 86, 43, .35); }
.sch-pm-split { grid-column: 1 / -1; background: transparent; border-style: dashed; color: var(--accent-color, #C8102E); }
/* ══ ÍTEMS 332 + 331-A — BLOQUE DE ACCIONES POSITIVAS DEL MODAL DE LA CITA ══
   Héctor pidió "un boton de Atender a la izquierda de No-show". Se construye
   APARTE y no como tercer hermano de esa fila, porque `.sch-m-actions` contiene
   SOLO acciones destructivas (No-show ámbar + Cancelar rojo): un botón positivo
   pegado a ellas invita al toque equivocado en una pantalla táctil.
   · Una sola columna a ancho completo (en ES "Añádela a tu calendario" no cabe
     a media anchura del modal de 420px sin partirse).
   · Línea inferior = frontera visual con lo destructivo.
   · Verde LLENO #2E7D5B: es el verde que la casa ya usa en `.sch-pm-cash`, y
     emparenta el botón con la TIJERITA verde de la celda (misma acción, mismo
     color). NO se usa `--primary-700` a propósito: ese token es amarillo en
     hc-dark (#CCCC00) y casi idéntico a la superficie en onyx (#161C2A sobre
     #1C2133 = 1.05:1) — sería invisible en 2 de los 8 temas (la "bomba de
     tiempo" que documentó Kahlo). El verde fijo mide 5.02:1 de texto blanco y
     no depende del tema, igual que los botones de método de pago de al lado.
   ⚠️ EL ORDEN IMPORTA: `.sch-mbtn-serve` tiene la MISMA especificidad (0,1,0)
   que la base `.sch-mbtn`, así que las variantes van DESPUÉS de la base o gana
   la base. Aquí solo vive el contenedor; los modificadores están abajo, junto a
   `-warn` y `-danger`. (Se cazó midiendo: con las reglas puestas ANTES, el botón
   se pintaba blanco — el CSS "estaba" y no hacía nada.) */
.sch-m-go { padding: 10px 16px 12px; display: grid; grid-template-columns: 1fr; gap: 9px;
    border-bottom: 1px solid var(--bs-border-color, #E2E8F0); }
/* acciones delicadas */
.sch-m-actions { padding: 10px 16px; display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
.sch-mbtn { min-height: 46px; border-radius: 12px; border: 1px solid var(--bs-border-color, #E2E8F0);
    background: var(--surface-color, #fff); color: var(--bs-body-color, #14224E); display: flex;
    align-items: center; justify-content: center; gap: 8px; font-size: .9rem; font-weight: 600; padding: 0 10px; }
.sch-mbtn svg { width: 20px; height: 20px; flex: none; }
.sch-mbtn-warn { color: #B45309; border-color: rgba(180, 83, 9, .4); }
.sch-mbtn-danger { color: var(--accent-color, #C8102E); border-color: rgba(200, 16, 46, .4); }
/* ÍTEM 332 — Atender: LLENO en verde (ver el bloque .sch-m-go de arriba). Va
   aquí, DESPUÉS de la base `.sch-mbtn`, porque comparten especificidad. */
.sch-mbtn-serve { background: #2E7D5B; border-color: #2E7D5B; color: #fff; }
.sch-mbtn-serve:hover, .sch-mbtn-serve:active { background: #246147; border-color: #246147; color: #fff; }
/* ÍTEM 350 — Terminar: MISMO verde fijo #2E7D5B que Atender, a propósito.
   (1) Nunca son visibles a la vez (Booked/Confirmed vs InProgress), así que no
   compiten; (2) reusar el color YA MEDIDO evita estrenar un tono sin verificar
   en los 8 temas — el #16A34A de la palomita de la celda da 3.30:1 con blanco
   (vale para un ÍCONO por 1.4.11, pero se queda corto para TEXTO, y este botón
   lleva etiqueta), mientras #2E7D5B da 5.00:1; (3) empareja el par
   ✂️/✅ del modal igual que ya empareja el par de la celda.
   Va DESPUÉS de la base `.sch-mbtn` por la misma razón de especificidad de
   arriba (trampa ④ del estándar de QA). */
.sch-mbtn-finish { background: #2E7D5B; border-color: #2E7D5B; color: #fff; }
.sch-mbtn-finish:hover, .sch-mbtn-finish:active { background: #246147; border-color: #246147; color: #fff; }
/* ÍTEM 331-A — Añadir a mi calendario: neutro a propósito (ni positivo fuerte
   ni destructivo); hereda superficie+tinta del tema, legible en los 8. */
.sch-mbtn-cal { color: var(--bs-body-color); }
/* ÍTEM 386-bis: "💳 Pagar por adelantado" es SECUNDARIA — mismo contorno que
   "Añádela a tu calendario", debajo de ella. El verde lleno se reserva para
   Atender/Terminar (las principales); pintar esta igual las empataría. */
.sch-mbtn-prepay { color: var(--bs-body-color); }
/* detalles plegables */
.sch-det-toggle { width: calc(100% - 32px); margin: 4px 16px 14px; min-height: 42px;
    border: 1px solid var(--bs-border-color, #E2E8F0); background: var(--bs-body-bg, #EEF1F7);
    border-radius: 11px; color: var(--bs-body-color, #14224E); font-weight: 600; font-size: .82rem;
    display: flex; align-items: center; justify-content: center; gap: 6px; }
.sch-det-chev { transition: transform .15s ease; }
.sch-det-toggle.open .sch-det-chev { transform: rotate(180deg); }
.sch-det-body { padding: 0 16px 14px; display: flex; flex-direction: column; gap: 7px; }
.sch-drow { display: flex; gap: 10px; font-size: .82rem; align-items: baseline; }
.sch-drow-k { color: var(--bs-secondary-color, #64748B); flex: none; min-width: 84px; }
.sch-drow-v { font-weight: 500; }
.sch-svc-list { border: 1px solid var(--bs-border-color, #E2E8F0); border-radius: 10px; overflow: hidden; }
.sch-svc { display: flex; justify-content: space-between; gap: 10px; padding: 5px 10px; font-size: .82rem; border-top: 1px solid var(--bs-border-color, #E2E8F0); }
.sch-svc:first-child { border-top: 0; }
.sch-svc-price { color: var(--gold-color, #B45309); font-weight: 600; flex: none; }
.sch-svc-total { background: var(--bs-body-bg, #EEF1F7); font-weight: 600; }
/* confirmación */
.sch-confirm { padding: 20px 18px; text-align: center; }
.sch-conf-ico { width: 52px; height: 52px; margin: 2px auto 10px; display: block; }
.sch-conf-warn { color: #B45309; }
.sch-conf-danger { color: var(--accent-color, #C8102E); }
.sch-confirm h4 { font-size: 1.2rem; margin-bottom: 5px; }
.sch-confirm p { font-size: .86rem; color: var(--bs-secondary-color, #64748B); margin-bottom: 14px; }
.sch-confirm-actions { display: flex; gap: 10px; }
.sch-confirm-actions .sch-mbtn { flex: 1; }
@media (max-width: 480px) { .sch-modal-dialog { max-width: 100%; margin: .4rem; } }

/* ═══ ÍTEM 250 — ADMIN SUBSCRIPTION PLANS: tabla COMPACTA sin scroll horizontal
   (Héctor 2026-07-21, verbatim "columnas un poco menos anchas para que quepan
   todas"): fuente 1 punto menor, paddings recortados, inputs angostos y sin
   min-widths gordos — los 5 tiers caben completos en 1280/1366; en móvil el
   scroll queda CONTENIDO en .table-responsive. */
.plans-compact { font-size: .85rem; }
.plans-compact th, .plans-compact td { padding: .22rem .3rem; }
.plans-compact th { font-size: .85rem; white-space: nowrap; }
.plans-feat-col { min-width: 150px; }
/* Pulido de Vera (2026-07-21): anchos en EM, no px — con las fuentes de
   accesibilidad al 130-175% los px fijos RECORTABAN el valor ("199."); en em
   el encasillado crece con la letra y el número entero siempre se ve. */
.plans-price { width: 7.5em; }
.plans-price .input-group-text { padding: .18rem .3rem; font-size: .8rem; }
.plans-price .form-control { padding: .18rem .3rem; font-size: .8rem; }
.plans-limit { width: 5em; padding: .18rem .25rem; font-size: .8rem; }

/* ═══ ÍTEM 263 — CAMPANITA DE NOTIFICACIONES (header) ═══════════════════════
   Bell blanco sobre el header navy; badge ROJO de la casa; panel porcelana
   estilo glass (patrón sch-cap/bsp-glass). Portrait-first: en móvil el panel
   se pega al borde derecho con ancho fluido — jamás scroll-x. */
.notif-wrap { margin-left: auto; position: relative; display: flex; align-items: center; }
.notif-bell-btn {
    position: relative;
    background: transparent; border: 0; padding: .25rem .35rem;
    border-radius: .5rem; cursor: pointer; line-height: 1;
}
.notif-bell-btn:hover { background: rgba(255, 255, 255, .12); }
.notif-bell-icon { width: 28px; height: 28px; display: block; }
.notif-badge {
    position: absolute; top: -2px; right: -4px;
    background: #C8102E; color: #fff;
    font-size: .66rem; font-weight: 700; line-height: 1;
    border-radius: 999px; padding: .18rem .32rem;
    border: 2px solid #fff; min-width: 1.15rem; text-align: center;
}
.notif-back { position: fixed; inset: 0; z-index: 1060; background: transparent; }
.notif-panel {
    position: absolute; top: calc(100% + .5rem); right: 0; z-index: 1061;
    width: min(360px, calc(100vw - 1rem));
    background: #fff; color: #14224E;
    border: 1px solid #E2E8F0; border-radius: .75rem;
    box-shadow: 0 10px 30px rgba(20, 34, 78, .25);
    overflow: hidden;
}
.notif-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: .6rem .8rem; border-bottom: 1px solid #E2E8F0;
    background: #F7F5F0;
}
/* 263-V1 (Vera): color EXPLÍCITO — en hc-dark el btn-link heredaba el amarillo
   #FFFF00 del tema sobre el head porcelana (1.01:1, invisible). El panel es
   porcelana en TODOS los temas (patrón card del tracker) → navy de la casa. */
.notif-markall { font-size: .8rem; text-decoration: none; color: #14224E !important; }
.notif-empty { padding: 1.1rem .9rem; }
.notif-list { max-height: 380px; overflow-y: auto; }
.notif-item {
    display: block; width: 100%; text-align: left;
    background: #fff; border: 0; border-bottom: 1px solid #F1F5F9;
    padding: .55rem .8rem; cursor: pointer; color: inherit;
}
.notif-item:hover { background: #F8FAFC; }
.notif-item.notif-unread { background: #F0F6FF; }
.notif-item.notif-unread:hover { background: #E6F0FD; }
.notif-item-title { display: block; font-weight: 600; font-size: .86rem; color: #14224E; }
.notif-item-body { display: block; font-size: .8rem; color: #475569; margin-top: .1rem; }
.notif-item-when { display: block; font-size: .72rem; color: #94A3B8; margin-top: .15rem; }
.notif-dot {
    display: inline-block; width: .5rem; height: .5rem; border-radius: 50%;
    background: #C8102E; margin-right: .35rem; vertical-align: middle;
}

/* Cosmético QA (backlog hito 2026-07-21): en móvil (≤576px) el botón flotante
   de accesibilidad tapaba las últimas líneas del footer — aire extra abajo
   para que el texto NUNCA quede debajo del botón (el botón no se mueve:
   regla dura de posiciones estables). */
@media (max-width: 576px) {
    .bsp-footer { padding-bottom: 5.5rem !important; }
}

/* Móvil (≤640px): el hamburger flotante se ancla arriba-derecha SOBRE el
   header — la campanita se corre a su izquierda (barrido anti-overlap del
   propio build: el badge quedaba debajo del hamburger en 390). */
@media (max-width: 640px) {
    .notif-wrap { margin-right: 3.2rem; }
}

/* Móvil: el panel de la campanita se ancla al VIEWPORT (fixed bajo el header),
   no al wrap corrido — a 390 el ancla relativa lo sacaba por la izquierda. */
@media (max-width: 640px) {
    .notif-panel {
        position: fixed;
        top: 3.9rem;
        left: .5rem;
        right: .5rem;
        width: auto;
    }
}

/* Campana bi-bell-fill blanca + SWING animado en hover (mientras llega el
   Lordicon bell.json del próximo lote de íconos) */
.notif-bell-icon { font-size: 1.35rem; color: #fff; display: block; line-height: 1; }
@keyframes notif-swing {
    0% { transform: rotate(0); } 20% { transform: rotate(14deg); }
    45% { transform: rotate(-11deg); } 70% { transform: rotate(7deg); }
    85% { transform: rotate(-4deg); } 100% { transform: rotate(0); }
}
.notif-bell-btn:hover .notif-bell-icon {
    animation: notif-swing .9s ease-in-out;
    transform-origin: top center;
}

/* ══ ÍTEM 318 — MARKETING: COMPARTIR EL LINK/QR DEL BARBERO (Tito 2026-07-25) ══
   PRIMERO EL MÓVIL: todo se dimensionó a 390x844 (el barbero lo usa de pie) y
   el escritorio solo lo centra. Contraste segun el estandar de Kahlo (315):
   el bloque del QR replica la receta .qr-holder (blanco solido + borde navy,
   3.92:1 medido) y NO sigue el tema — un QR sobre fondo oscuro no escanea. */
.mkt-wrap { max-width: 560px; margin: 0 auto; padding: .25rem 0 1rem; }

.mkt-head { display: flex; align-items: center; gap: .75rem; min-width: 0; }
.mkt-photo {
    width: 56px; height: 56px; flex: 0 0 auto; border-radius: 50%;
    object-fit: cover; border: 2px solid var(--primary-900, #14224E);
    background: var(--surface-color, #fff);
}
.mkt-photo-empty {
    display: flex; align-items: center; justify-content: center;
    font-size: 1.5rem; color: var(--bs-body-color);
}
.mkt-headtext { min-width: 0; }
.mkt-name { font-weight: 600; font-size: 1.1rem; line-height: 1.25; overflow-wrap: anywhere; }
.mkt-store { font-size: .85rem; line-height: 1.25; overflow-wrap: anywhere; }

.mkt-lead { margin: .9rem 0 .25rem; font-size: .95rem; line-height: 1.4; }

/* Separador de seccion: "Cara a cara" / "A la distancia" */
.mkt-sect { display: flex; align-items: center; margin: 1.1rem 0 .55rem; }
.mkt-sect::after {
    content: ""; flex: 1 1 auto; height: 1px; margin-left: .6rem;
    background: var(--control-edge, rgba(20,34,78,.35));
}
.mkt-sect-label {
    font-size: .8rem; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase; white-space: nowrap;
}

/* El QR ES el boton (tocarlo abre la pantalla completa) */
.mkt-qrbtn {
    display: block; width: 100%; padding: .8rem .5rem; cursor: pointer;
    background: #FFFFFF; border: 3px solid #14224E; border-radius: 14px;
}
.mkt-qrbtn img { display: block; width: min(62vw, 230px); height: auto; margin: 0 auto; }
.mkt-qrcap {
    display: block; margin-top: .5rem; text-align: center;
    color: #14224E; font-size: .85rem; font-weight: 600;
}

/* Boton principal: alto de DEDO, icono + texto (estandar de la casa) */
.mkt-cta {
    display: flex; align-items: center; justify-content: center; gap: .5rem;
    width: 100%; min-height: 56px; margin-top: .7rem;
    font-size: 1.05rem; font-weight: 600;
}

.mkt-linkbox {
    border: 1px solid var(--control-edge, rgba(20,34,78,.55));
    border-radius: 10px; padding: .55rem .7rem;
    background: var(--surface-color, #fff);
}
.mkt-link-label { font-size: .78rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; opacity: .8; }
.mkt-link { display: block; overflow-wrap: anywhere; word-break: break-word; font-size: .95rem; }

/* Confirmacion del copiado: SIEMPRE ocupa su linea (solo cambia la opacidad)
   para que aparecer no empuje los botones de abajo (regla anti-overlap). */
.mkt-flash {
    min-height: 2.4em; margin: .45rem 0 .2rem; font-size: .88rem; font-weight: 600;
    color: var(--bs-success, #198754); opacity: 0; transition: opacity .15s ease;
}
.mkt-flash-on { opacity: 1; }
.mkt-flash-err { color: var(--bs-danger, #DC3545); }

.mkt-actions { display: grid; grid-template-columns: 1fr 1fr; gap: .5rem; }
.mkt-act {
    display: flex; align-items: center; justify-content: center; gap: .4rem;
    min-height: 50px; padding: .4rem .5rem; text-align: center; line-height: 1.15;
}
.mkt-act span { overflow-wrap: anywhere; }
.mkt-act-wide { grid-column: 1 / -1; }
.mkt-hint { margin: .55rem 0 0; font-size: .82rem; opacity: .85; line-height: 1.35; }
.mkt-pick { margin-top: 1.2rem; }

/* Una sola columna en pantallas MUY angostas: en espanol "Enviar por correo"
   no cabe a dos columnas por debajo de ~360px sin apretar el icono. */
@media (max-width: 359.98px) {
    .mkt-actions { grid-template-columns: 1fr; }
}

/* ── CAPA DE PANTALLA COMPLETA (el caso de uso #1) ────────────────────────
   Blanco solido + tinta navy FIJOS a proposito: los escaneres sufren con
   fondo oscuro o con foto detras. z-index por encima de todo (header 1055,
   paneles 1061). */
.mkt-full { display: none; }
.mkt-full.mkt-full-on {
    display: flex; position: fixed; inset: 0; z-index: 2000;
    flex-direction: column; align-items: center; justify-content: space-between;
    padding: .9rem .75rem 1rem; gap: .5rem;
    background: #FFFFFF; color: #14224E; color-scheme: light;
}
.mkt-full-name {
    flex: 0 0 auto; font-size: 1.15rem; font-weight: 700; text-align: center;
    color: #14224E; overflow-wrap: anywhere;
}
.mkt-full-qr { flex: 1 1 auto; display: flex; align-items: center; justify-content: center; width: 100%; min-height: 0; }
.mkt-full-qr img { display: block; width: min(92vw, 100%); max-height: 100%; height: auto; object-fit: contain; }
.mkt-full-foot { flex: 0 0 auto; width: 100%; max-width: 520px; }
.mkt-full-cap { text-align: center; font-weight: 600; color: #14224E; font-size: 1rem; }
.mkt-full-link { text-align: center; color: #14224E; font-size: .9rem; overflow-wrap: anywhere; margin-top: .15rem; }
.mkt-full-close {
    display: flex; align-items: center; justify-content: center; gap: .5rem;
    width: 100%; min-height: 60px; margin-top: .6rem;
    background: #14224E; color: #FFFFFF; border: none; border-radius: 12px;
    font-size: 1.05rem; font-weight: 600;
}
.mkt-full-close:active { background: #0E1837; }

/* ══ ÍTEM 320 (Héctor 2026-07-25, probando en vivo) — LA FILA "label + toggle"
   DEL PASO DE IDENTIFICACIÓN DEL KIOSKO ════════════════════════════════════
   Verbatim: "mover el boton Por email para la derecha para que haya espacio
   entre el boton y el label de Escribe tu numero de telefono. Yo creo que ya
   habia reportado esto." — y sí: el solapamiento se había arreglado, pero
   quedó PEGADO, que era justo lo que pedía evitar.
   CAUSA: la fila usaba `gap-2` (8px) y el chip porcelana `.kiosk-chip-wash`
   pinta un halo de box-shadow de 8px+9px que se sale de su caja de layout →
   del gap de 8px solo sobraba ~-1px VISIBLE (el halo llegaba a tocar el botón).
   ARREGLO: 24px de gap ⇒ 24 − 9 (halo pintado) = 15px VISIBLES entre el borde
   pintado del chip y el botón. Sin wash el halo no existe y quedan los 24px
   completos — más aire, que es lo que Héctor pedía en los dos casos.
   ⚠️ SE SEPARAN LOS DOS EJES A PROPÓSITO. A 1024×600 (el BAR-01 real) la fila
   NO envuelve ni con 8px ni con 24px — medido: label 293.5/340.9px + botón
   93/98.6px sobre 640px de fila. A 390×844 SÍ envuelve, pero **ya envolvía
   antes de este cambio** (271.3+93+8 = 372.3 > 358 disponibles) — o sea el
   wrap es PREEXISTENTE, no lo trae el gap nuevo. Si el gap fuera de los dos
   ejes, envolver sumaría 16px de alto (64.9→80.9) y eso sí empujaría el
   keypad (el problema del teclado del 313 e-bis). Con `row-gap` fijo en los
   8px de siempre, el caso envuelto queda EXACTO a como estaba hoy. */
.kiosk-idrow { column-gap: 1.5rem; row-gap: .5rem; }

/* ══ ÍTEM 329 (Héctor 2026-07-25, tras publicar) — EL LABEL DE CORREO PEGADO
   AL CAMPO ══════════════════════════════════════════════════════════════════
   Verbatim: "entre el titulo de correo electronico y el textbox no hay
   espacio, podriamos bajar un poquito el textbox?"
   CAUSA (la de siempre, 5ª vez): `.kiosk-chip-wash` pinta anillos de 8px + 9px
   de box-shadow FUERA de su caja de layout — el navegador NO los cuenta para
   el acomodo. La fila usaba `mb-2` = 8px, o sea el halo (9px) se comía el
   margen entero y sobraba ~-1px VISIBLE: el chip llegaba a TOCAR el input.
   ARREGLO: margen inferior REAL de 1.5rem = 24px ⇒ 24 − 9 (halo pintado) =
   15px VISIBLES entre el borde PINTADO del chip y el campo. Es el mismo
   número que el eje horizontal de esta misma fila (ítem 320, `column-gap`),
   así que la fila queda con el mismo aire en los dos ejes.
   ⚠️ SOLO la rama de CORREO y SOLO con wash ON:
     · La rama del TELÉFONO NO lo lleva — ahí abajo va el keypad numérico y
       16px de más lo empujan hacia el teclado del RPi (ítem 313 e-bis).
       La rama de correo no tiene keypad: holgura vertical de sobra.
     · Con el flag OFF no hay chip ni halo, así que la fila conserva su `mb-2`
       de siempre y esa pantalla queda EXACTA a como está hoy.
   NOTA: esto es una 5ª compensación local. La cura de raíz es el ítem 330
   (que el chip cargue con su propio halo y se quiten las 5 compensaciones a
   la vez) — refactor visual con su propio turno, jamás en caliente. */
.kiosk-idrow-emailwash { margin-bottom: 1.5rem; }

/* ══ ÍTEM 367 (Héctor 2026-07-26) — LA X DE CERRAR, VISIBLE SOBRE CUALQUIER FONDO ══
   Verbatim: "la X para cerrar esa pantalla es Negra y casi no se ve con el borde
   azul del modal" + su propuesta: "quizas podamos poner una x mas estilizada para
   que se vea, quizas sobre un circulo blanco!".

   🔴 ERA SISTÉMICO, NO UNA PANTALLA: 38 `btn-close` en 27 archivos, y
   `btn-close-white` no se usaba ni una sola vez en código propio ⇒ TODAS eran la
   X negra por defecto de Bootstrap, invisible sobre cualquier cabecera oscura.

   ✅ POR QUÉ EL CÍRCULO Y NO `btn-close-white`: la X blanca solo sirve sobre
   fondo oscuro — el día que un modal tenga cabecera clara desaparece, y se
   cambia un problema por el contrario. El círculo blanco con la X oscura funciona
   sobre AMBOS porque EL CONTROL SE TRAE SU PROPIO FONDO en vez de depender de lo
   que haya detrás. Es la misma regla del par del chip de porcelana del kiosko
   (KAHLO-ESTANDAR-CONTRASTE-CONTROLES.md): relleno propio + borde propio.

   📌 UN SOLO SITIO, NO 38 PARCHES: se redefine `.btn-close` de Bootstrap aquí y
   con eso quedan cubiertos los 38 usos actuales Y cualquiera que se añada
   mañana, sin que nadie tenga que acordarse (ítem 323). app.css carga DESPUÉS de
   bootstrap.min.css y ningún tema toca `.btn-close`, así que este override gana
   en los 8 temas. */
.btn-close {
    position: relative;
    box-sizing: border-box;
    width: 2rem;
    height: 2rem;
    padding: 0;
    /* Bootstrap la deja en .5 y eso deslavaría el círculo entero. */
    opacity: 1;
    /* Relleno PROPIO + borde PROPIO = no depende del fondo de detrás. */
    background-color: #fff;
    /* X propia (navy de marca) en vez de la negra de Bootstrap: ~14:1 contra el
       blanco, muy por encima del 4.5:1 que pide el estándar. Va embebida para no
       depender de una variable interna de Bootstrap que puede cambiar de versión. */
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%2314224E' stroke-width='2.2' stroke-linecap='round'%3e%3cpath d='M4 4l8 8M12 4l-8 8'/%3e%3c/svg%3e");
    background-repeat: no-repeat;
    background-position: center;
    background-size: .85rem .85rem;
    /* ⚠️ MEDIDO, NO ESTIMADO: con el borde a .22 el círculo daba solo 1.63:1
       contra una cabecera BLANCA — por debajo del 3:1 que pide el estándar (la X
       seguía legible, pero el círculo se perdía). A .5 el borde compuesto sobre
       blanco da ~3.4:1 y cumple, sin ensuciar nada sobre la cabecera navy (ahí
       lo que separa es el relleno blanco, 15.3:1). */
    border: 1px solid rgba(11, 18, 32, .5);
    border-radius: 50%;
    box-shadow: 0 1px 3px rgba(11, 18, 32, .28);
    transition: transform .12s ease, box-shadow .12s ease, background-color .12s ease;
}

/* ÁREA TÁCTIL ≥44px SIN AGRANDAR EL CÍRCULO. El círculo se ve de 32px (discreto,
   como pidió Héctor) pero el dedo acierta en 44px, que es el mínimo de a11y.
   Un pseudo-elemento transparente centrado: no ocupa layout ni tapa nada. */
.btn-close::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 44px;
    height: 44px;
    transform: translate(-50%, -50%);
}

.btn-close:hover {
    background-color: #fff;
    opacity: 1;
    transform: scale(1.06);
    box-shadow: 0 2px 7px rgba(11, 18, 32, .34);
}

.btn-close:active { transform: scale(.97); }

/* Foco visible propio: el de Bootstrap era un box-shadow que sobre cabecera
   oscura casi no se distinguía. El outline se ve sobre claro y sobre oscuro. */
.btn-close:focus,
.btn-close:focus-visible {
    outline: 3px solid #C8102E;
    outline-offset: 2px;
    box-shadow: 0 1px 3px rgba(11, 18, 32, .28);
    opacity: 1;
}

/* La variante blanca de Bootstrap, si alguien la usa, no debe volver a la X
   invisible: se normaliza al mismo control. */
.btn-close-white {
    background-color: #fff;
    filter: none;
}

/* ══ ÍTEM 367-bis — LAS X QUE NO SIGUEN EL PATRÓN, TRAÍDAS AL REDIL ══════════
   Héctor: la X del modal de la cita quedó CUADRADA. Causa: `.sch-m-close` (y su
   prima `.sch-cap-x`, calendario de capacidad en Agenda y SupportTracker) son
   <button> propios con &times; — no llevan .btn-close y el barrido del 367 no
   los alcanzó. Aquí toman el MISMO ADN del círculo, en el MISMO bloque (nada de
   parche aparte): relleno blanco propio, X navy, borde medido (.5 ⇒ 3.51:1
   sobre cabecera clara), sombra y foco visible. Sus tamaños/posiciones siguen
   viviendo en sus reglas de siempre. Barrido del resto hecho: las demás ×
   sueltas del repo son "quitar elemento" (persona/parte del split), no cerrar
   ventana — semántica distinta, no entran. */
.sch-m-close,
.sch-cap-x {
    background: #fff;
    color: #14224E;
    border: 1px solid rgba(11, 18, 32, .5);
    border-radius: 50%;
    box-shadow: 0 1px 3px rgba(11, 18, 32, .28);
    transition: transform .12s ease, box-shadow .12s ease;
    /* ⛔ ÍTEM 367-ter: AQUÍ NO VA `position`. Ver el bloque de abajo. */
}

/* ══ ÍTEM 367-ter (Héctor 2026-07-27, EN PRODUCCIÓN, con captura) ═════════════
   "parece que la x del detalle de la cita está fuera de lugar."
   CAUSA REAL (no era el círculo): este bloque compartido declaraba
   `position: relative` y vive MÁS ABAJO en el archivo que
   `.sch-m-close { position: absolute; top: 10px; right: 10px; }` (~línea 2451).
   Misma especificidad (1 clase) ⇒ **gana el último** ⇒ el botón dejó de estar
   absolutamente posicionado en la esquina de `.sch-m-top` y pasó a ser un
   elemento EN FLUJO (primer hijo de la cabecera = arriba-IZQUIERDA) al que
   `top:10px; right:10px` ya solo le aplican como DESPLAZAMIENTO relativo —
   `right:10px` lo corre 10px MÁS a la izquierda: justo la X flotando
   arriba-izquierda y medio fuera que él fotografió.
   `position: relative` se había puesto para que el `::after` de 44px tuviera
   ancestro posicionado — pero `absolute` YA es ancestro posicionado, así que
   `.sch-m-close` nunca lo necesitó; la única que lo necesita es `.sch-cap-x`
   (que vive en el flex de `.sch-cap-nav` y no se posiciona sola).
   🟢 LV-05: el DISEÑO del círculo (relleno, X navy, borde, sombra, foco, área
   táctil 44×44) NO se toca — esto RESTAURA la posición aprobada, no la cambia.
   Se re-declara la posición DESPUÉS del bloque compartido para que el orden del
   archivo no vuelva a decidir quién gana. */
.sch-cap-x { position: relative; }
.sch-m-close { position: absolute; top: 10px; right: 10px; }

/* área táctil ≥44px sin agrandar el círculo — mismo truco del .btn-close */
.sch-m-close::after,
.sch-cap-x::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 44px;
    height: 44px;
    transform: translate(-50%, -50%);
}
.sch-m-close:hover,
.sch-cap-x:hover { transform: scale(1.06); box-shadow: 0 2px 7px rgba(11, 18, 32, .34); }
.sch-m-close:focus-visible,
.sch-cap-x:focus-visible { outline: 3px solid #C8102E; outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
    .sch-m-close, .sch-cap-x { transition: none; }
    .sch-m-close:hover, .sch-cap-x:hover { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .btn-close { transition: none; }
    .btn-close:hover, .btn-close:active { transform: none; }
}

/* ══ ÍTEM 378 — LA AGENDA SIEMPRE CUBRE LO QUE TIENE QUE ENSEÑAR ═════════════
   Caso real: la tienda cierra a las 5:00 pm y se atendió a un walk-in a las
   7:36 pm; la cita salía FUERA de la rejilla, sin etiqueta de hora, flotando en
   blanco — "parece hasta un error del sistema" (Héctor). No es un caso raro: un
   walk-in se sella con la hora en que se le atendió, y el último cliente del día
   casi siempre sale pasada la hora de cierre.
   La rejilla se estira para cubrirlas, y ESTA banda marca dónde estaba el
   horario REGULAR: lo de fuera se sigue viendo, pero se ve DISTINTO. La
   información de que aquello fue una excepción no se borra — se muestra. */
.sch-biz-band {
    position: absolute;
    left: 0;
    right: 0;
    z-index: 0;                 /* detrás de líneas, celdas y citas */
    background: var(--surface-color, #fff);
    pointer-events: none;       /* jamás roba un clic a las celdas de 15 min */
}

/* El fuera-de-horario queda con el fondo atenuado del contenedor. Se consigue
   tiñendo la COLUMNA entera y devolviendo el fondo normal solo a la banda. */
.sch-barber-col:has(.sch-biz-band) {
    background: repeating-linear-gradient(45deg,
        rgba(100, 116, 139, .10) 0 8px, rgba(100, 116, 139, .04) 8px 16px);
}

/* ══ ÍTEM 379 — MÁS TABS SIN DESBORDAR EL MÓVIL ══════════════════════════════
   Con el reagrupamiento, las pantallas de configuración pasan de 5-7 tabs a
   9-10. En 390px Bootstrap los ENVUELVE en dos/tres filas y la barra se come
   media pantalla. Patrón elegido: UNA fila con scroll horizontal — el mismo
   gesto que ya usa la agenda (sch-agenda-wrap) y las apps móviles que el
   barbero conoce. El degradado del borde derecho es la señal de "hay más". */
.ent-tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
    /* que el activo nunca quede tapado por el borde al enfocarlo */
    scroll-padding-inline: 2rem;
}
.ent-tabs .nav-link { white-space: nowrap; }

/* ══ ÍTEM 388 — EL NO-SHOW CON CARA PROPIA EN LA AGENDA ══════════════════════
   El bug de Héctor ("le di a No-show y la cita dice Reservada") era de PINTURA:
   el estado sí cambiaba, pero ni el bloque de la celda ni la etiqueta del modal
   tenían caso NoShow y caían al default de reservada. */
.sch-appt-noshow {
    opacity: .55;
    background: repeating-linear-gradient(45deg,
        rgba(180, 83, 9, .18) 0 6px, rgba(180, 83, 9, .06) 6px 12px);
    border-left: 3px solid #B45309;
}
/* (sin !important a propósito: las hermanas .sch-appt-active/done tampoco lo
   usan — misma especificidad, y la lección del 330 es no taparse con
   excepciones que luego producen el siguiente síntoma) */
.sch-state-noshow { background: #FEF3C7; color: #7C2D12; border: 1px solid #B45309; }

/* ══ ÍTEMS 385/389 (2026-07-27) — checkout modal con pasos + "¿con cuánto
   paga?" ══════════════════════════════════════════════════════════════════ */
/* 389: captura de efectivo (componente CashTender — citas, dividido y POS) */
.cash-quick { display: flex; flex-wrap: wrap; gap: .35rem; margin-bottom: .5rem; }
.cash-quick .btn { min-height: 38px; font-weight: 600; }
/* El CAMBIO es lo que el barbero le canta al cliente: grande y visible */
.cash-change { margin-top: .4rem; font-size: 1.15rem; font-weight: 700; color: #157347; }
.cash-hint { font-size: .72rem; margin-top: .25rem; }
/* 385: rejilla de métodos del paso 2 (ícono + texto, táctil) */
.chk-methods { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: .5rem; max-width: 520px; }
.chk-methods .btn { min-height: 44px; text-align: left; }

/* ══ ÍTEM 410 (Héctor 2026-07-28, EN PRODUCCIÓN, desde su TELÉFONO) ═══════════
   "la X del detalle de la cita queda ESCONDIDA DETRÁS DEL MENÚ en el teléfono
    — por suerte cerré el detalle y pude darle a la X."

   🔎 CAUSA REAL (medida, no supuesta) — NO es el modal ni el diseño de la X:
   · Bootstrap 5.3 pinta los modales en `--bs-modal-zindex: 1055`
     (wwwroot/lib/bootstrap/.../bootstrap.min.css) y el fondo en 1050.
   · El BOTÓN FLOTANTE DE LA HAMBURGUESA (`.sidebar-float-wrap`,
     Components/Layout/MainLayout.razor.css:62-69) vive en **z-index: 1060**, o
     sea POR ENCIMA de cualquier modal de la app. Y en móvil
     (MainLayout.razor.css:101-106) está clavado **arriba a la DERECHA**
     (`top:.5rem; right:1rem`) — exactamente donde toda X de cerrar se pinta.
   · Su propio truco lo confirma: al CERRAR el bloque "Detalles" el modal se
     encoge, deja de empezar pegado al borde superior, la X baja unos píxeles y
     se destapa. Es una colisión de capas, no un fallo de posición del botón.
   ⇒ Afecta a TODOS los modales de la app en teléfono, no solo al de la cita.

   🟢 LV-05 RESPETADO: no se toca ni un byte del diseño del círculo (relleno,
   X navy, borde, sombra, foco, área táctil 44×44). Aquí solo se decide QUIÉN VA
   ENCIMA DE QUIÉN — es la misma clase de arreglo que el 367-ter.

   ARREGLO EN DOS CAPAS (a propósito: si una falla, la otra sostiene):
   (1) el modal sube por encima de la hamburguesa (1060) y del panel de la
       campanita (1061), y se queda POR DEBAJO de la pantalla de reconexión
       (1080) — esa tiene que poder taparlo todo;
   (2) mientras haya un modal abierto, el botón flotante se esconde: en un
       teléfono con un modal delante ese botón no sirve para nada y solo estorba.
       (`:has()` — el mismo mecanismo que ya usa `body:has(.barber-bg)` más
       arriba; sin soporte, la capa (1) ya resuelve el caso.) */
.modal {
    --bs-modal-zindex: 1070;
}
.modal-backdrop {
    --bs-backdrop-zindex: 1065;
}
/* El calendario de capacidad de la Agenda es un panel propio (no `.modal`) y su
   X (`.sch-cap-x`) cae en la MISMA esquina ⇒ mismo tratamiento. */
.sch-cap-back { z-index: 1070; }
.sch-cap-panel { z-index: 1071; }

/* capa (2): fuera el botón flotante mientras hay algo delante */
body:has(.modal.show) .sidebar-float-wrap,
body:has(.sch-cap-back) .sidebar-float-wrap {
    display: none;
}
