@import '_content/Microsoft.AspNetCore.Components.QuickGrid/Microsoft.AspNetCore.Components.QuickGrid.boiwgh0w5b.bundle.scp.css';

/* /Components/Layout/MainLayout.razor.rz.scp.css */
/* Persistent top app bar — always visible (desktop + mobile), full width, themed like the sidebar. */
.app-header[b-krouy4ql8r] {
    position: sticky;
    top: 0;
    /* 🔴 FIX (Lupe, NO APTO mega-build): .bimp-banner (impersonación) también usa
       z-index:1050 — un EMPATE con .app-header. Al empatar, gana quien va
       DESPUÉS en el DOM (el banner), así que el banner tapaba el HIT-TEST del
       dropdown de la campanita (notif-panel, z-index:1061) aunque ese z-index
       más alto sea solo LOCAL al stacking context del header — nunca "se
       escapa" para vencer a un hermano con z-index igual/mayor. Resultado:
       bajo impersonación, "Mark all read" (y cualquier otro control del header
       que caiga bajo la franja del banner) recibía el click en el banner, NO
       en el botón — cero llamada al servidor, cero excepción, silencioso.
       Subir el header por encima del banner (1055 > 1050) deja que TODO su
       contenido (dropdowns, campanita, menú) gane el hit-test sin tocar el
       z-index interno del banner ni del panel. */
    z-index: 1055;
    /* ÍTEM 968: el alto del encabezado ya NO se escribe aquí. Es `--bsp-header-h`
       (`wwwroot/app.css`, :root) porque otras TRES reglas dependían de este mismo
       número escrito a mano — y ahora también la banda de suplantación se cuelga
       de él para pegarse POR DEBAJO. El valor no cambia: sigue siendo 3.5rem. */
    height: var(--bsp-header-h);
    display: flex;
    align-items: center;
    padding: 0 1.25rem;
    background: var(--gradient-sidebar, linear-gradient(180deg, #1E3A5F 0%, #15294A 100%));
    box-shadow: 0 1px 6px rgba(0, 0, 0, 0.25);
    transition: background 0.3s ease;
}

    .app-brand[b-krouy4ql8r] {
        color: #fff;
        font-size: 1.2rem;
        font-weight: 700;
        letter-spacing: -0.2px;
        text-decoration: none;
        /* ÍTEM 876 · LV-31: el WORDMARK va en Cinzel (romano lapidario — pinta
           "Barber Shop Pro" como mayúsculas/versalitas, el look del logo de
           Copper). El fallback es la pila de SIEMPRE: si el WOFF2 no carga,
           el brand se ve exactamente como antes. Cinzel JAMÁS en cuerpo ni
           botones — solo wordmark/títulos en mayúsculas. */
        font-family: 'Cinzel', 'Inter', 'Helvetica Neue', Helvetica, Arial, sans-serif;
    }

    .app-brand:hover[b-krouy4ql8r] { color: #fff; opacity: 0.92; }

    /* Pole fotorrealista del brand (reemplaza el emoji 💈, 2026-07-08) */
    .app-brand .app-brand-pole[b-krouy4ql8r] {
        height: 1.6rem;
        vertical-align: -0.35rem;
        margin-left: 0.15rem;
    }

.page[b-krouy4ql8r] {
    position: relative;
    display: flex;
    flex-direction: column;
}

main[b-krouy4ql8r] {
    flex: 1;
}

.sidebar[b-krouy4ql8r] {
    /* Background is controlled by the active THEME (theme-*.css → `.sidebar { background: var(--gradient-sidebar) }`),
       so the sidebar recolors with the selected theme. Do NOT hardcode a background here (the old
       Blazor-template gradient was scoped CSS and overrode every theme). Default theme = Lucid. */
    transition: background 0.3s ease;
}

/* ── Floating draggable sidebar toggle ───────────────────────────── */
.sidebar-float-wrap[b-krouy4ql8r] {
    position: fixed;
    top: 200px;
    left: 0;
    z-index: 1060;
    touch-action: none;
    transition: left 0.25s ease;
}

/* Desktop: the toggle sits at the RIGHT edge of the 250px sidebar; when the sidebar
   is collapsed it slides back to the left edge. (Mobile override below pins it top-right.) */
@media (min-width: 641px) {
    .sidebar-float-wrap[b-krouy4ql8r]            { left: 250px; }
    .sidebar-float-wrap.collapsed[b-krouy4ql8r]  { left: 0; }
}

.sft-hamburger[b-krouy4ql8r] {
    width: 2.4rem;
    height: 2.75rem;
    border: none;
    border-radius: 0 0.5rem 0.5rem 0;
    background: rgba(5, 39, 103, 0.92);
    color: #fff;
    font-size: 1.35rem;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: grab;
    box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.3);
    touch-action: none;
}

.sft-hamburger:active[b-krouy4ql8r] { cursor: grabbing; }
.sft-hamburger:hover[b-krouy4ql8r]  { background: rgb(5, 39, 103); }
.sft-hamburger:focus-visible[b-krouy4ql8r] { outline: 3px solid #5C8EF0; outline-offset: 2px; }

/* On small screens the toggle sits pinned at the top-right (like the original
   navbar toggler), not as a draggable tab on the left edge. !important beats the
   inline `top` the drag script writes, so it stays put on mobile. */
@media (max-width: 640.98px) {
    .sidebar-float-wrap[b-krouy4ql8r] {
        top: 0.5rem !important;
        left: auto;
        right: 1rem;
    }
    .sft-hamburger[b-krouy4ql8r] {
        border-radius: 0.5rem;
    }
}

/* Collapsed state: hide the whole sidebar so content gets the full width. */
.page.sidebar-hidden .sidebar[b-krouy4ql8r] {
    display: none;
}

@media (min-width: 641px) {
    .page[b-krouy4ql8r] {
        flex-direction: row;
    }

    /* El sidebar se pega DEBAJO de la pila superior entera y ocupa lo que sobra.
       ── ÍTEM 968, LA MITAD QUE NO SE VE VENIR (la destapó Kahlo de su PROPIA
       propuesta, y por eso este bloque no es opcional):
       bajar solo la banda NO hace desaparecer el solapamiento — LO MUDA AL MENÚ.
       Medido a 1280×800 con scroll, suplantando:
         · hoy .............. encabezado ∩ banda = 59.455 px² · banda ∩ sidebar = 0
         · solo bajar banda .. encabezado ∩ banda = 0 ......... banda ∩ sidebar = 11.750 px² 🔴
         · esto ............. 0 y 0 ✅
       Desde 641 px el sidebar TAMBIÉN se pegaba a 3.5rem, o sea al mismo sitio
       que la banda. `--bsp-bimp-h` (0 sin suplantación) lo empuja el alto REAL de
       la banda, que va de 47 a 185,75 px — por eso lo mide un observador y no una
       constante. Ver la nota del token en `wwwroot/app.css`. */
    .sidebar[b-krouy4ql8r] {
        width: 250px;
        height: calc(100vh - var(--bsp-header-h) - var(--bsp-bimp-h));
        position: sticky;
        top: calc(var(--bsp-header-h) + var(--bsp-bimp-h));
    }

    article[b-krouy4ql8r] {
        /* FIX QA (Tito 2026-07-22): la pestaña flotante del hamburguesa
           (.sft-hamburger, 2.4rem de ancho) sobresale ~2.4rem del borde del
           sidebar; con padding-left:2rem el contenido no la libraba y a ~980px
           la 1ra card de Quick Access ("Citas") se solapaba ~8.7px. Se sube el
           padding izquierdo a 3rem para librar la pestaña (expandido Y colapsado)
           con holgura, sin media-queries frágiles. */
        padding-left: 3rem !important;
        padding-right: 1.5rem !important;
    }
}

/* #blazor-error-ui styles moved to global wwwroot/app.css so they apply to ALL
   layouts. As a SCOPED rule here it only covered MainLayout, so other layouts
   (e.g. PublicLayout = /c/{token}) showed the banner permanently with no real
   error behind it (display:none never reached them). Keep them global. */
/* /Components/Layout/NavMenu.razor.rz.scp.css */
.navbar-toggler[b-jr0v5yjoeo] {
    /* Replaced by the floating draggable hamburger in MainLayout. */
    display: none;
    appearance: none;
    cursor: pointer;
    width: 3.5rem;
    height: 2.5rem;
    color: white;
    position: absolute;
    top: 0.5rem;
    right: 1rem;
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
}

.navbar-toggler:checked[b-jr0v5yjoeo] {
    background-color: rgba(255, 255, 255, 0.5);
}

.top-row[b-jr0v5yjoeo] {
    min-height: 3.5rem;
    background-color: rgba(0,0,0,0.4);
}

.navbar-brand[b-jr0v5yjoeo] {
    font-size: 1.1rem;
}

.bi[b-jr0v5yjoeo] {
    display: inline-block;
    position: relative;
    width: 1.25rem;
    height: 1.25rem;
    margin-right: 0.75rem;
    top: -1px;
    background-size: cover;
}

.bi-house-door-fill-nav-menu[b-jr0v5yjoeo] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
}

.bi-plus-square-fill-nav-menu[b-jr0v5yjoeo] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
}

.bi-list-nested-nav-menu[b-jr0v5yjoeo] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
}

.bi-lock-nav-menu[b-jr0v5yjoeo] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath d='M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2zm3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2zM5 8h6a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1z'/%3E%3C/svg%3E");
}

.bi-person-nav-menu[b-jr0v5yjoeo] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-person' viewBox='0 0 16 16'%3E%3Cpath d='M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6Zm2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0Zm4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4Zm-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10c-2.29 0-3.516.68-4.168 1.332-.678.678-.83 1.418-.832 1.664h10Z'/%3E%3C/svg%3E");
}

.bi-person-badge-nav-menu[b-jr0v5yjoeo] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-person-badge' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 2a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1h-3zM11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0z'/%3E%3Cpath d='M4.5 0A2.5 2.5 0 0 0 2 2.5V14a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2.5A2.5 2.5 0 0 0 11.5 0h-7zM3 2.5A1.5 1.5 0 0 1 4.5 1h7A1.5 1.5 0 0 1 13 2.5v10.795a4.2 4.2 0 0 0-.776-.492C11.392 12.387 10.063 12 8 12s-3.392.387-4.224.803a4.2 4.2 0 0 0-.776.492V2.5z'/%3E%3C/svg%3E");
}

.bi-person-fill-nav-menu[b-jr0v5yjoeo] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-person-fill' viewBox='0 0 16 16'%3E%3Cpath d='M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1H3Zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z'/%3E%3C/svg%3E");
}

.bi-arrow-bar-left-nav-menu[b-jr0v5yjoeo] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-arrow-bar-left' viewBox='0 0 16 16'%3E%3Cpath d='M12.5 15a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 1 0v13a.5.5 0 0 1-.5.5ZM10 8a.5.5 0 0 1-.5.5H3.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L3.707 7.5H9.5a.5.5 0 0 1 .5.5Z'/%3E%3C/svg%3E");
}

.nav-item[b-jr0v5yjoeo] {
    font-size: 0.9rem;
    padding-bottom: 0.5rem;
}

    .nav-item:first-of-type[b-jr0v5yjoeo] {
        padding-top: 1rem;
    }

    .nav-item:last-of-type[b-jr0v5yjoeo] {
        padding-bottom: 1rem;
    }

    .nav-item[b-jr0v5yjoeo]  .nav-link {
        color: #d7d7d7;
        background: none;
        border: none;
        border-radius: 4px;
        /* Item 29 (el dueño 2026-07-10): nombres LARGOS hacian overlap — la
           altura fija de 3rem no crecia con el wrap. min-height + line-height
           normal + wrap permitido = nombres largos en 2 lineas sin montarse.
           Tambien aguanta los fonts de accesibilidad grandes (item 30/31). */
        min-height: 3rem;
        height: auto;
        display: flex;
        align-items: center;
        line-height: 1.3;
        width: 100%;
        white-space: normal;
        overflow-wrap: anywhere;
    }

.nav-item[b-jr0v5yjoeo]  a.active {
    background-color: rgba(255,255,255,0.37);
    color: white;
}

.nav-item[b-jr0v5yjoeo]  .nav-link:hover {
    background-color: rgba(255,255,255,0.1);
    color: white;
}

/* About uses a Bootstrap icon (no Lordicon for it); size + color it to match the nav lord-icons.
   Placed after the scoped `.bi` rule so these win for the shared properties. */
.nav-bi-icon[b-jr0v5yjoeo] {
    width: 24px;
    height: 24px;
    margin-right: 0;
    top: 0;
    color: #fff;
    font-size: 1.2rem;
    line-height: 24px;
    text-align: center;
    background: none;
}

.nav-scrollable[b-jr0v5yjoeo] {
    /* Always shown; the floating hamburger collapses the entire sidebar instead. */
    display: block;
}

@media (min-width: 641px) {
    .navbar-toggler[b-jr0v5yjoeo] {
        display: none;
    }

    .nav-scrollable[b-jr0v5yjoeo] {
        /* Never collapse the sidebar for wide screens */
        display: block;

        /* Allow sidebar to scroll for tall menus */
        /* 🆕 ÍTEM 968 — LA CUARTA COPIA, que no estaba en el encargo. El barrido de
           `3.5rem` sobre TODO el proyecto sacó cuatro dependencias del alto del
           encabezado, no tres: esta es la del scroll interno del menú. Dejarla en
           `3.5rem` mientras su padre `.sidebar` baja el alto de la banda le habría
           dado al menú MÁS alto que el hueco que tiene ⇒ el final del menú se sale
           por debajo del viewport justo bajo suplantación. Mismos tokens, misma
           cuenta que `.sidebar` (`MainLayout.razor.css`). */
        height: calc(100vh - var(--bsp-header-h) - var(--bsp-bimp-h));
        overflow-y: auto;
    }
}
/* /Components/Pages/Schedule.razor.rz.scp.css */
/* ══════════════════════════════════════════════════════════════════════════════
   ÍTEM 429 — ESTADOS VISUALES DE ARRASTRAR UNA CITA EN LA AGENDA
   ══════════════════════════════════════════════════════════════════════════════
   🔒 VA EN CSS **CON ÁMBITO** (Schedule.razor.css), no en `wwwroot/app.css`:
   app.css es terreno compartido y este ítem no tiene por qué tocarlo. Aquí solo
   se pinta lo que el arrastre AÑADE; ni una regla existente de `.sch-*` se
   modifica.
   ⚠️ El fantasma que sigue al dedo y la marca de aterrizaje NO están aquí: nacen
   colgados de <body>, fuera del ámbito de este componente, y por eso se visten
   en línea desde `wwwroot/js/bsp-schedule-drag.js`.
   ══════════════════════════════════════════════════════════════════════════════ */

/* La tarjeta que se está arrastrando se queda en su sitio, apagada: el usuario
   necesita ver DE DÓNDE la sacó mientras decide dónde la suelta. */
.sch-appt-block.bsp-dragging[b-2u0w64whx2] {
    opacity: .32;
    filter: grayscale(.5);
}

/* La columna sobre la que se está soltando. Sutil a propósito: la información
   fuerte la da la marca de aterrizaje (dónde cae y cuánto ocupa); la columna
   solo confirma DE QUIÉN es. */
.sch-barber-col.bsp-drop-col[b-2u0w64whx2] {
    background: rgba(201, 162, 39, .10);
    box-shadow: inset 0 0 0 2px rgba(201, 162, 39, .55);
}

/* Señal de que la tarjeta se puede agarrar. `grab` solo aplica a ratón; en
   táctil la pista la da el texto del panel (rb_drag_hint). */
.sch-appt-block[data-bsp-drag="1"][b-2u0w64whx2] {
    cursor: grab;
}

.sch-appt-block[data-bsp-drag="1"]:active[b-2u0w64whx2] {
    cursor: grabbing;
}

/* ⚠️ El "no seleccionar texto mientras se arrastra" NO puede vivir aquí: se
   aplica a <body>, que está FUERA del ámbito de este componente y por tanto no
   lleva el atributo de ámbito. Se pone en línea desde el JS (`arm()`), y se
   quita en `reset()`. */

/* ══════════════════════════════════════════════════════════════════════════
   ÍTEM 1072 · LV-38 — LOS DOS BOTONES GRANDES DEL SELECTOR "¿cita o bloqueo?"
   R-238: min-height 64px (medido en el mockup de Kahlo, por encima del
   mínimo táctil de 44px de la casa — margen para el dedo, no solo el rect). */
.sch-choice-btn[b-2u0w64whx2] {
    display: flex;
    align-items: center;
    gap: .75rem;
    min-height: 64px;
    padding: .6rem .9rem;
}

.sch-choice-ic[b-2u0w64whx2] {
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.3rem;
    border-radius: 10px;
    background: rgba(20, 34, 78, .08);
}

.sch-choice-txt[b-2u0w64whx2] {
    text-align: left;
}
/* /Components/Shared/AccessibilityPanel.razor.rz.scp.css */
/* ── Floating trigger button ──────────────────────────────────────── */
.a11y-trigger-btn[b-2kan77xkhn] {
    position:        fixed;
    bottom:          1.5rem;
    right:           1.5rem;
    width:           3.25rem;
    height:          3.25rem;
    border-radius:   50%;
    border:          none;
    background:      var(--accent-color, #3B82F6);
    color:           var(--accent-text-color, #fff);
    font-size:       1.3rem;
    display:         flex;
    align-items:     center;
    justify-content: center;
    box-shadow:      0 4px 14px rgba(0,0,0,0.28);
    /* ÍTEM 1089 (2026-09-03): antes 1060 — quedaba POR DEBAJO de `.modal`
       (`--bs-modal-zindex: 1070`, wwwroot/app.css) y de `.sch-cap-panel`
       (1071), así que este botón se pintaba DETRÁS del modal aunque el
       ítem 737 ya lo escondiera aparte. Sube a 1075: por encima de TODA
       la familia de diálogo (backdrop 1050/1065, .modal 1070,
       .sch-cap-back/-panel 1070/1071) y sigue por DEBAJO de la pantalla de
       reconexión (1080, tiene que poder tapar cualquier cosa) y del globo
       de ayuda ⓘ (1090). Ver wwwroot/app.css junto a `.a11y-trigger-btn`
       en el bloque del ítem 737 para la mitad que dejó de esconderlo. */
    z-index:         1075;
    cursor:          grab;
    touch-action:    none; /* required for pointer-drag on touch devices */
    transition:      transform 0.15s ease, box-shadow 0.15s ease;
}

.a11y-trigger-btn:active[b-2kan77xkhn] {
    cursor: grabbing;
}

.a11y-trigger-btn:hover[b-2kan77xkhn] {
    transform:  scale(1.08);
    box-shadow: 0 6px 20px rgba(0,0,0,0.34);
}

.a11y-trigger-btn:focus-visible[b-2kan77xkhn] {
    outline:        3px solid var(--accent-color, #3B82F6);
    outline-offset: 3px;
}

/* ── Panel container ──────────────────────────────────────────────── */
.a11y-panel[b-2kan77xkhn] {
    position:        fixed;
    bottom:          5.75rem;
    right:           1.5rem;
    width:           300px;
    max-height:      82vh;
    overflow-y:      auto;
    background:      var(--surface-color, #fff);
    border:          1px solid var(--border-color, #CBD5E1);
    border-radius:   0.75rem;
    box-shadow:      0 8px 32px rgba(0,0,0,0.20);
    /* ÍTEM 1089: mismo motivo que el botón, un piso por debajo de él (era
       la misma relación 1059/1060 antes del arreglo). */
    z-index:         1074;
    font-size:       0.85rem;
    color:           var(--bs-body-color, #0F172A);
    scrollbar-width: thin;
}

/* Panel header */
.a11y-panel-header[b-2kan77xkhn] {
    display:         flex;
    align-items:     center;
    justify-content: space-between;
    padding:         0.65rem 0.9rem;
    border-bottom:   1px solid var(--border-color, #CBD5E1);
    background:      var(--secondary-color, #F8FAFC);
    border-radius:   0.75rem 0.75rem 0 0;
    font-size:       0.875rem;
    color:           var(--text-primary, #fff);
}

.a11y-panel-close[b-2kan77xkhn] {
    background: none;
    border:     none;
    padding:    0.2rem 0.4rem;
    color:      var(--text-primary, #fff);
    cursor:     pointer;
    border-radius: 0.25rem;
    line-height: 1;
    opacity:    0.8;
}

.a11y-panel-close:hover[b-2kan77xkhn]  { opacity: 1; }
.a11y-panel-close:focus-visible[b-2kan77xkhn] {
    outline:        2px solid var(--accent-color, #3B82F6);
    outline-offset: 2px;
}

/* ── Sections ─────────────────────────────────────────────────────── */
.a11y-section[b-2kan77xkhn] {
    padding:       0.65rem 0.9rem;
    border-bottom: 1px solid var(--border-color, #CBD5E1);
}

.a11y-section:last-child[b-2kan77xkhn] { border-bottom: none; }

.a11y-row-label[b-2kan77xkhn] {
    display:       flex;
    align-items:   center;
    gap:           0.4rem;
    font-weight:   600;
    font-size:     0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-bottom: 0.5rem;
    color:         var(--text-secondary, #94A3B8);
}

/* ── Theme swatches ───────────────────────────────────────────────── */
.a11y-theme-grid[b-2kan77xkhn] {
    display:               grid;
    grid-template-columns: repeat(4, 1fr);
    gap:                   0.35rem;
}

.a11y-swatch[b-2kan77xkhn] {
    display:         flex;
    flex-direction:  column;
    align-items:     center;
    gap:             0.25rem;
    padding:         0.35rem 0.2rem;
    background:      none;
    border:          2px solid transparent;
    border-radius:   0.4rem;
    cursor:          pointer;
    transition:      border-color 0.15s ease;
}

.a11y-swatch:hover[b-2kan77xkhn] {
    border-color: var(--accent-color, #3B82F6);
}

.a11y-swatch:focus-visible[b-2kan77xkhn] {
    outline:        2px solid var(--accent-color, #3B82F6);
    outline-offset: 1px;
}

.a11y-swatch--active[b-2kan77xkhn] {
    border-color: var(--accent-color, #3B82F6) !important;
    background:   rgba(59,130,246,0.08);
}

.a11y-swatch-dot[b-2kan77xkhn] {
    width:         1.6rem;
    height:        1.6rem;
    border-radius: 50%;
    background:    var(--swatch-bg, #888);
    border:        4px solid var(--swatch-border, #888);
    box-shadow:    0 0 0 2px #ffffff;
    flex-shrink:   0;
}

.a11y-swatch-name[b-2kan77xkhn] {
    font-size:   0.65rem;
    line-height: 1.1;
    text-align:  center;
    color:       var(--bs-body-color, #0F172A);
    white-space: nowrap;
    overflow:    hidden;
    text-overflow: ellipsis;
    max-width:   100%;
}

/* ── Toggle rows ──────────────────────────────────────────────────── */
.a11y-toggles[b-2kan77xkhn] { display: flex; flex-direction: column; gap: 0.45rem; }

.a11y-toggle-row[b-2kan77xkhn] {
    display:     flex;
    align-items: center;
    margin:      0;
    padding:     0;
}

.a11y-toggle-row .form-check-label[b-2kan77xkhn] {
    cursor:    pointer;
    font-size: 0.84rem;
    color:     var(--bs-body-color, #0F172A);
}

.a11y-toggle-row .form-check-input[b-2kan77xkhn] {
    cursor: pointer;
    flex-shrink: 0;
    /* Bootstrap's .form-switch adds margin-left:-2.5em to the input, which (with this flex row)
       pushed the switch off the panel's left edge and clipped it. Reset it so the switch starts
       exactly where the section content / the "100%" font-size button begins, with a small gap
       before its label. */
    margin-left: 0;
    margin-right: 0.6rem;
}

/* ── Reset row ────────────────────────────────────────────────────── */
.a11y-reset-row[b-2kan77xkhn] { padding-top: 0.6rem; padding-bottom: 0.7rem; }

/* ── Scrollbar styling (Webkit) ───────────────────────────────────── */
.a11y-panel[b-2kan77xkhn]::-webkit-scrollbar       { width: 5px; }
.a11y-panel[b-2kan77xkhn]::-webkit-scrollbar-track { background: transparent; }
.a11y-panel[b-2kan77xkhn]::-webkit-scrollbar-thumb { background: var(--border-color, #CBD5E1); border-radius: 99px; }
/* /Components/Shared/PaymentMethodsConfig.razor.rz.scp.css */
/* ÍTEM 1104 — el ícono del set 3D de Héctor en la lista de switches de
   métodos de pago (Barbero/Tienda/Compañía). Dimensiona por ALTURA, nunca
   por caja cuadrada (GooglePay/Zelle son píldoras anchas — mismo motivo que
   el Menú TV, `TvMenuPanel.razor.css`). Vive sobre fondo CLARO (el propio
   `form-check-label` de Bootstrap, blanco/gris claro) — los 11 PNG están
   renderizados para blanco. */
/* ÍTEM 1186 — el chip del piso (Efectivo «Siempre prendido (respaldo)») en su
   propia línea, envolviendo si la celda es estrecha: nunca se monta sobre la
   celda vecina («ATH Móvil»). `max-width:100%` es lo que evita el desborde. */
/* El label de cada switch es inline-block y, sin tope, se ensancha al max-content
   de su contenido (183 px con el chip en ES) por encima de su celda (146 px);
   con el tope, el chip de abajo envuelve dentro de la celda. Medido: sin esta
   regla el chip seguía en 183 px aunque ya fuera block con max-width:100%. */
.form-check-label[b-t89ljoyhsv] {
  max-width: 100%;
}

.pm-cfg-badge[b-t89ljoyhsv] {
  display: block;
  width: max-content;
  max-width: 100%;
  white-space: normal;
  text-align: left;
  margin-top: .2rem;
}

.pm-cfg-ico[b-t89ljoyhsv] {
  height: 28px;
  width: auto;
  max-width: 68px;
  object-fit: contain;
  vertical-align: middle;
  margin-right: .25rem;
}
/* /Components/Shared/PayMethodIcon.razor.rz.scp.css */
/* ÍTEM 1104 — roundel BLANCO forzado detrás del ícono, para que se lea
   igual sobre CUALQUIER fondo de botón (los PNG están renderizados para
   fondo claro; ver el comentario del .razor). Dimensiona por ALTURA, nunca
   por caja cuadrada (GooglePay/Zelle son píldoras anchas). */
.pay-ico-wrap[b-q6sbdfimsg] {
  display: inline-flex; align-items: center; justify-content: center;
  background: #fff; border-radius: .3rem; padding: 2px 4px;
  line-height: 0;
}
.pay-ico-img[b-q6sbdfimsg] {
  height: 22px; width: auto; max-width: 56px; object-fit: contain; display: block;
}
/* /Components/Shared/ServiceGalleryEditor.razor.rz.scp.css */
/* ══ ÍTEM 664 · LV-17 — GALERÍA DE FOTOS DEL SERVICIO (lado del barbero) ══════
   Paleta de la casa (R-228): porcelana #F7F5F0 lienzo · navy #14224E estructura
   · rojo barber #C8102E acento. El acento se reserva para la ⭐ principal —
   marcar CUÁL representa al servicio ES la decisión de esta pantalla.

   📱 390x844 es la superficie principal: las tarjetas son de 150px con
   flex-wrap ⇒ 2 por fila con holgura, y todo botón llega a 44px de alto. */

.svcgal-grid[b-11um054c90] {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem;
    margin-top: .35rem;
}

.svcgal-card[b-11um054c90] {
    width: 150px;
    border: 1px solid #D8D4C8;
    border-radius: 12px;
    background: #FBFAF6;
    padding: .4rem;
    display: flex;
    flex-direction: column;
    gap: .35rem;
}

/* La ⭐ principal se distingue SIN color solo: borde grueso + halo. En temas de
   alto contraste el color puede colapsar; el grosor del borde nunca. */
.svcgal-card.svcgal-primary[b-11um054c90] {
    border: 2px solid #C8102E;
    box-shadow: 0 0 0 3px rgba(200, 16, 46, .12);
}

.svcgal-thumb-wrap[b-11um054c90] { position: relative; }

.svcgal-thumb[b-11um054c90] {
    width: 100%;
    height: 110px;
    object-fit: cover;
    /* 🔄 R-472 / ÍTEM 1164 — AHORA PROTEGE EL RECORTE, NO LA CARA. Decía
       "el encuadre PROTEGE LA CARA… 50% 25%" (R-237 / ítem 206) y era cierto
       para la cara: con este hueco (110 px de alto) el 25 % empezaba en el
       19,1 % de la foto, POR DEBAJO de la coronilla de las 153 fotos de
       retrato del catálogo (Kahlo, 1164: el aire sobre la cabeza va de 2,08 %
       a 11,41 %). Héctor: «lo que siempre queremos destacar no es la camisa,
       es el recorte, la cabeza». Un corte de pelo con el TOPE cortado es
       anti-marketing pagado — que es lo que aquel comentario quería evitar.
       ⚠️ El popup del mapa que ese comentario citaba NO cambia: su foto es la
       de la TIENDA (`/stores/{id}/photo.jpg`), no la del catálogo. */
    object-position: 50% 0;
    border-radius: 8px;
    background: #F7F5F0;
    display: block;
}

/* Insignias sobre la foto: par porcelana + navy (la REGLA DEL PAR, R-236c) —
   la foto de fondo no la controlamos, así que superficie clara + tinta oscura
   SIEMPRE juntas. */
.svcgal-star[b-11um054c90],
.svcgal-pos[b-11um054c90] {
    position: absolute;
    top: .3rem;
    background: #F7F5F0;
    border: 1px solid #14224E;
    color: #14224E;
    border-radius: 999px;
    font-size: .7rem;
    font-weight: 700;
    line-height: 1;
    padding: .2rem .4rem;
}

.svcgal-star[b-11um054c90] { left: .3rem; }
.svcgal-pos[b-11um054c90] { right: .3rem; min-width: 1.35rem; text-align: center; }

/* ══ ✅ LV-26 pieza 1 · R-430 — EL SELLO "Esta es la que ve el cliente" ═══════
   Verde de CONFIRMACIÓN, no el rojo de la ⭐: el rojo de la casa es acento y
   alarma; esto no avisa de nada, confirma un hecho. Par claro+tinta (R-236c):
   superficie #E8F6EE + tinta #0F5D35 — el mismo verde del chip del Modelo
   Virtual, para que "verde = esto es lo que está saliendo" signifique lo mismo
   en las dos piezas de la misma pantalla.
   📱 A 390px la tarjeta mide 150px: .68rem con `line-height` holgado deja la
   frase en 2 líneas sin desbordar ni empujar los botones fuera de sus 44px. */
.svcgal-seal[b-11um054c90] {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .25rem;
    text-align: center;
    background: #E8F6EE;
    border: 1px solid #0F7B45;
    color: #0F5D35;
    border-radius: 8px;
    font-size: .68rem;
    font-weight: 800;
    line-height: 1.25;
    padding: .3rem .35rem;
}

.svcgal-seal i[b-11um054c90] { font-size: .8rem; flex: none; }

/* 🌑 n-3 DE LA VUELTA 11 (Lupe) — EL SELLO **NO** SE TOCA EN TEMA OSCURO.
   🚨 CORRECCIÓN MEDIDA, EN CONTRA DE MI PROPIA PRIMERA VERSIÓN.
   El hallazgo dice que en `hc-dark` el sello "se queda claro". Es cierto — pero
   la causa NO es el sello: es su CONTENEDOR. `.svcgal-card` trae la porcelana
   `#FBFAF6` quemada y **no se adapta a ningún tema oscuro**, así que el sello se
   pinta sobre una superficie CLARA aunque el resto de la pantalla sea negra.
   📏 Lo escribí primero como los demás (tinte verde translúcido + tinta clara
   `#A7F3D0`) y lo MEDÍ en `hc-dark`: **1.06 : 1 — ilegible**. El sello verde
   oscuro de LV-26 sobre esa porcelana da **7.15 : 1**. La versión "arreglada"
   era objetivamente peor.
   ⇒ El sello se queda EXACTAMENTE como lo aprobó LV-26, en todos los temas.
   📌 EL ÍTEM DE VERDAD, para su propio lote: `.svcgal-card` (y las superficies
   quemadas hermanas de este editor) no siguen al tema oscuro. Mientras eso siga
   así, teñir lo que vive DENTRO de la tarjeta con colores de tema oscuro es
   pintar de noche encima de una pared de día.
   📕 La lección: *"se ve claro en tema oscuro" no dice QUIÉN está claro.* El
   ancestro pintado se mide antes de tocar al hijo. */

.svcgal-actions[b-11um054c90] {
    display: flex;
    flex-direction: column;
    gap: .3rem;
}

/* Botones de dedo: 44px de alto (estándar táctil de la casa, R-347). */
.svcgal-actions .btn[b-11um054c90] {
    min-height: 44px;
    font-size: .74rem;
    padding: .25rem .35rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .25rem;
}

.svcgal-move[b-11um054c90] { display: flex; gap: .3rem; }
/* Las flechas de reordenar son un control de dedo IGUAL que los demás botones
   de la tarjeta: 44px de alto (R-347) y área de toque llena — antes solo tenían
   `flex:1` y quedaban cortas y poco obvias en 390x844 (no se podían usar a dedo
   en el teléfono, R-270). El ícono se agranda para que se lea claro cuál mueve
   atrás y cuál adelante. Solo afordancia; la lógica de Move() no se toca. */
.svcgal-move .btn[b-11um054c90] {
    flex: 1;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.svcgal-move .btn i[b-11um054c90] { font-size: 1.1rem; }

.svcgal-empty[b-11um054c90] {
    display: flex;
    align-items: center;
    gap: .5rem;
    border: 1px dashed #D8D4C8;
    border-radius: 12px;
    padding: .75rem .9rem;
    font-size: .82rem;
    background: #FBFAF6;
    flex: 1;
    min-width: 220px;
}

.svcgal-empty i[b-11um054c90] { font-size: 1.3rem; }

/* ══ LV-24 · 716-DISEÑO(b) — EL MARCADOR "SIN DECLARAR" ═══════════════════════
   Tres tintas, tres significados (mockup congelado): rojo SÓLIDO = ⭐ principal ·
   navy = insignias de posición · ámbar PUNTEADO = pendiente (punteado =
   incompleto). Ámbar de la casa: #B45309 tinta / #92400E tinta oscura /
   #FFF7ED superficie (par claro+tinta, R-236c).
   Va DESPUÉS de .svcgal-primary a propósito: si la foto pendiente es además la
   ⭐ (la legada adoptada como #1), gana el ámbar punteado — lo pendiente es lo
   accionable; la ⭐ sigue dicha por su insignia de arriba-izquierda. */
.svcgal-card.svcgal-undeclared[b-11um054c90] {
    border: 2px dashed #B45309;
    box-shadow: 0 0 0 3px rgba(180, 83, 9, .10);
}

/* El chip vive ABAJO de la foto — no compite con la ⭐ (arriba-izq) ni con el
   numerito (arriba-der). */
.svcgal-undeclared-chip[b-11um054c90] {
    position: absolute;
    left: .3rem;
    right: .3rem;
    bottom: .3rem;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .25rem;
    background: #FFF7ED;
    border: 1px solid #B45309;
    color: #92400E;
    border-radius: 999px;
    font-size: .62rem;
    font-weight: 800;
    line-height: 1;
    padding: .25rem .35rem;
}

/* La caja ámbar de pendientes, arriba de la galería. */
.svcgal-pendbox[b-11um054c90] {
    background: #FFF7ED;
    border: 1px solid rgba(180, 83, 9, .4);
    border-left: 5px solid #B45309;
    border-radius: 12px;
    padding: .6rem .75rem;
    display: flex;
    align-items: flex-start;
    gap: .5rem;
    margin-bottom: .5rem;
    font-size: .84rem;
    color: #92400E;
}

.svcgal-pend-txt[b-11um054c90] {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: .15rem;
    line-height: 1.35;
}

.svcgal-pend-sub[b-11um054c90] {
    font-size: .74rem;
    opacity: .85;
}

/* El ⓘ de la caja, en tinta ámbar sobre blanco (mockup: help-btn "dark"). */
.svcgal-pendbox[b-11um054c90]  .bsp-help-btn {
    border-color: #B45309;
    color: #92400E;
    background: #fff;
}

/* ══ ÍTEM 725 · LV-22 — CERTIFICACIÓN DE PERMISO DE IMAGEN ════════════════════
   Clona el mockup congelado (mockup-certificacion-fotos.html): cabecera navy con
   la RAYA DEL POSTE, cuerpo porcelana, acento rojo barber. Paleta de la casa
   (R-228). Móvil-primero: el modal cabe en 390px y todo control llega a 44px. */

/* El <input type=file> queda oculto pero accesible (lo activa el <label>). No se
   usa display:none para no romper el foco/activación del label en algún navegador. */
.svcgal-file-hidden[b-11um054c90] {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    border: 0;
}

/* ══ LV-24 (MENOR de Lupe) — LA FILA DE ACCIONES DE LA GALERÍA ═══════════════
   "Subir fotos" · "Usar Modelo Virtual" (R-423, por verbatim va AL LADO) ·
   "Vista previa". `wrap` para que a 390px caigan en dos líneas limpias en vez
   de desbordar, y `gap` en lugar de márgenes sueltos: al envolver, un `ms-1`
   deja el hueco descuadrado. `align-items: center` mantiene alineados botones
   de altos distintos. */
.svcgal-btnrow[b-11um054c90] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
}

.svcgal-upload-btn[b-11um054c90] {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* ÍTEM 740 — "Vista previa": mismo porte de dedo que el botón de subir. */
.svcgal-preview-btn[b-11um054c90] {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* ── El velo y el modal (overlay real, centrado en el viewport) ── */
.svccert-scrim[b-11um054c90] {
    position: fixed;
    inset: 0;
    background: rgba(11, 19, 48, .55);
    backdrop-filter: blur(1.5px);
    z-index: 1080;
}

.svccert-modal[b-11um054c90] {
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: min(420px, calc(100vw - 24px));
    max-height: calc(100vh - 40px);
    overflow: auto;
    z-index: 1081;
    background: #FFFFFF;
    border: 1px solid #D8D4C8;
    border-radius: 20px;
    box-shadow: 0 10px 24px rgba(20, 34, 78, .22);
}

/* Cabecera navy + raya del poste al pie (border-image no se lleva con el radio,
   así que la raya va en un ::after de 4px). */
.svccert-head[b-11um054c90] {
    position: relative;
    background: #14224E;
    color: #fff;
    padding: 14px 16px 16px;
}

    .svccert-head[b-11um054c90]::after {
        content: "";
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0;
        height: 4px;
        background: repeating-linear-gradient(135deg, #C8102E 0 12px, #FFFFFF 12px 24px, #14224E 24px 36px);
    }

.svccert-eyebrow[b-11um054c90] {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: .68rem;
    letter-spacing: .4px;
    text-transform: uppercase;
    color: #BFDBFE;
    font-weight: 700;
    margin-bottom: 5px;
}

.svccert-title-row[b-11um054c90] {
    display: flex;
    align-items: flex-start;
    gap: 8px;
}

.svccert-title[b-11um054c90] {
    flex: 1;
    font-size: 1.05rem;
    font-weight: 800;
    line-height: 1.25;
}

/* El ⓘ de HelpHint vive en la cabecera navy: se aclara para que contraste
   (::deep alcanza el markup interno del componente hijo). */
.svccert-title-row[b-11um054c90]  .bsp-help-btn {
    border-color: #BFDBFE;
    color: #fff;
    background: rgba(255, 255, 255, .16);
}

.svccert-body[b-11um054c90] {
    padding: 15px 16px 6px;
}

.svccert-statement[b-11um054c90] {
    font-size: .86rem;
    line-height: 1.5;
    color: #14224E;
    white-space: pre-wrap;
    margin: 0;
}

/* El cotejo — fila de checkbox grande, de dedo. */
.svccert-cotejo[b-11um054c90] {
    margin-top: 13px;
    display: flex;
    align-items: center;
    gap: 12px;
    background: #fff;
    border: 2px solid #D8D4C8;
    border-radius: 13px;
    padding: 13px 14px;
    min-height: 44px;
    cursor: pointer;
    user-select: none;
    transition: border-color .15s, background .15s;
}

    .svccert-cotejo:hover[b-11um054c90] {
        border-color: #2B4AA0;
    }

    .svccert-cotejo.checked[b-11um054c90] {
        border-color: #15803D;
        background: rgba(21, 128, 61, .06);
    }

    .svccert-cotejo:focus-visible[b-11um054c90] {
        outline: 2px solid #2B4AA0;
        outline-offset: 2px;
    }

.svccert-cbox[b-11um054c90] {
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    border-radius: 8px;
    border: 2px solid #B9B4A5;
    background: #fff;
    display: grid;
    place-items: center;
    color: #fff;
    transition: all .15s;
}

.svccert-cotejo.checked .svccert-cbox[b-11um054c90] {
    background: #15803D;
    border-color: #15803D;
}

.svccert-cbox i[b-11um054c90] {
    opacity: 0;
    transform: scale(.5);
    transition: all .15s;
    font-size: 1.05rem;
}

.svccert-cotejo.checked .svccert-cbox i[b-11um054c90] {
    opacity: 1;
    transform: scale(1);
}

.svccert-cotejo-lbl[b-11um054c90] {
    font-weight: 700;
    font-size: .92rem;
    color: #14224E;
}

/* Pie: acciones apiladas (móvil-primero) sobre fondo footer. */
.svccert-foot[b-11um054c90] {
    background: #EFEDE6;
    border-top: 1px solid #D8D4C8;
    padding: 13px 16px;
    display: flex;
    flex-direction: column;
    gap: 9px;
}

.svccert-certify[b-11um054c90] {
    width: 100%;
    min-height: 48px;
    font-weight: 800;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.svccert-cancel[b-11um054c90] {
    width: 100%;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.svccert-gate[b-11um054c90] {
    text-align: center;
    font-size: .72rem;
    color: #64748B;
}

/* ══ LV-24 — LO PROPIO DEL MODAL DEL BACKFILL (el resto clona svccert-*) ══════ */

/* La franja ámbar de contexto: miniaturas de LAS VIEJAS + la línea aprobada.
   ══ 🔎 CORRECCIÓN MEDIDA (Tito 2026-08-25, LV-26 pieza 3) ═══════════════════
   LV-26 dice que este modal "se construyó SIN las miniaturas". **No es exacto:
   las miniaturas están desde LV-24** (`Items.Where(IsUndeclared)` → `<img>`), y
   se verificaron RENDERIZANDO en DEV con la foto sin declarar de Classic Cut.
   🔴 PERO EL REPORTE DE HÉCTOR SIGUE SIENDO VÁLIDO, y esta es la razón medida:
   a 44×44, colocadas A LA IZQUIERDA del texto, con borde punteado ámbar sobre
   fondo ámbar, la foto NO SE LEE COMO UNA FOTO — se lee como el ícono de la
   franja. Él vio el cuadrito y siguió de largo hasta el texto. Un dato que se
   pinta con la forma de un adorno no cuenta como "enseñar la foto".
   ✅ EL ARREGLO (que es lo que LV-26 pide de verdad: no decidir a ciegas):
     ① el texto va ARRIBA y las fotos DEBAJO — se lee "estas N fotos" y el ojo
       cae en ellas, en vez de saltárselas por la izquierda;
     ② 72×72 en vez de 44×44 (2.7× de área) — a ese tamaño una cabeza se
       reconoce; a 44 no;
     ③ el punteado ámbar se queda (es la tinta de "pendiente", R-236c) pero
       sobre marco BLANCO, para que el borde separe la foto del fondo en vez de
       fundirla con él. */
.svccert-oldstrip[b-11um054c90] {
    background: #FFF7ED;
    border: 1px solid rgba(180, 83, 9, .4);
    border-radius: 12px;
    padding: .6rem .7rem;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: .5rem;
    margin-bottom: 12px;
}

.svccert-oldstrip-thumbs[b-11um054c90] {
    display: flex;
    gap: .4rem;
    flex: 0 0 auto;
    flex-wrap: wrap;
}

    .svccert-oldstrip-thumbs img[b-11um054c90] {
        width: 72px;
        height: 72px;
        object-fit: cover;
        object-position: 50% 0;     /* R-472 (1164): mismo encuadre protege-RECORTE de la galería */
        border-radius: 8px;
        border: 2px dashed #B45309; /* el mismo punteado ámbar del marcador */
        background: #fff;
        padding: 2px;
        display: block;
    }

.svccert-oldstrip-cap[b-11um054c90] {
    font-size: .82rem;
    font-weight: 600;
    color: #92400E;
    line-height: 1.4;
}

/* "Guardar sin certificar" — secundario, de dedo, con su pista debajo. */
.svccert-skip[b-11um054c90] {
    width: 100%;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.svccert-skip-hint[b-11um054c90] {
    text-align: center;
    font-size: .7rem;
    color: #64748B;
    line-height: 1.4;
}

/* "Volver a editar" — terciario, sin caja (mockup), pero con alto de dedo. */
.svccert-back[b-11um054c90] {
    width: 100%;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: #64748B;
    text-decoration: none;
    font-weight: 700;
}

/* Contenido del ⓘ: filas Regla → Problema → Solución con su chip de color. Este
   markup viaja como ChildContent de HelpHint pero lo declara ESTE componente, así
   que el CSS scoped lo alcanza sin ::deep. */
.svccert-help-row[b-11um054c90] {
    margin: 0 0 6px;
    font-size: .8rem;
    line-height: 1.4;
    color: #14224E;
}

    .svccert-help-row:last-child[b-11um054c90] {
        margin-bottom: 0;
    }

.svccert-help-k[b-11um054c90] {
    display: inline-block;
    font-weight: 800;
    font-size: .6rem;
    letter-spacing: .5px;
    text-transform: uppercase;
    padding: 1px 7px;
    border-radius: 999px;
    margin-right: 5px;
}

    .svccert-help-k.regla[b-11um054c90] {
        background: rgba(29, 78, 216, .12);
        color: #1D4ED8;
    }

    .svccert-help-k.problema[b-11um054c90] {
        background: rgba(180, 83, 9, .14);
        color: #B45309;
    }

    .svccert-help-k.solucion[b-11um054c90] {
        background: rgba(21, 128, 61, .12);
        color: #15803D;
    }
/* /Components/Shared/ServicePhotoModal.razor.rz.scp.css */
/* ══ ÍTEM 664 · LV-17 — EL MODAL DEL CLIENTE (fotos del servicio girando) ═══
   El look clona el mockup aprobado (mockup-664-fotos-rotando.html):
   · escenario TRANSPARENTE (punto 5) — el fondo del modal se ve entre fotos,
     jamás negro;
   · perspectiva LEJANA 2200px (punto 3) — el rotateY inclina sin deformar;
   · SIN scale en ninguna parte del giro — la foto no se achica (bug #2 cazado);
   · ✕ = clase compartida .sch-m-close (LV-05, app.css) — aquí no se re-declara.
   z-index: mismas capas que el resto de modales tras el ítem 410 (por encima
   de la hamburguesa 1060; debajo de la pantalla de reconexión 1080). */

.svrot-scrim[b-4n70pqgx7h] {
    position: fixed;
    inset: 0;
    background: rgba(10, 14, 25, .72);
    z-index: 1070;
}

.svrot-modal[b-4n70pqgx7h] {
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    z-index: 1071;
    width: min(420px, calc(100vw - 24px));
    max-height: calc(100vh - 24px);
    overflow: auto;
    background: var(--surface-color, #fff);
    border-radius: 18px;
    box-shadow: 0 24px 60px rgba(0, 0, 0, .4);
    outline: none;
}

/* Cabecera navy con la ✕ LV-05 (posicionada absolute por la clase compartida
   ⇒ esta cabecera es su ancestro posicionado) */
.svrot-head[b-4n70pqgx7h] {
    position: relative;
    background: var(--primary-900, #14224E);
    color: #fff;
    padding: .7rem 3.2rem .7rem 1rem;   /* aire a la derecha para la ✕ */
    min-height: 52px;
    display: flex;
    align-items: center;
}
.svrot-title[b-4n70pqgx7h] { font-weight: 700; font-size: .98rem; line-height: 1.25; }
/* tamaño/fuente propios de la ✕ en ESTE modal (el diseño viene de app.css) */
.svrot-head[b-4n70pqgx7h]  .sch-m-close,
.svrot-head .sch-m-close[b-4n70pqgx7h] { width: 34px; height: 34px; font-size: 1.15rem; line-height: 1; }

/* ── El escenario ─────────────────────────────────────────────────────────── */
.svrot-stage[b-4n70pqgx7h] {
    position: relative;
    background: transparent;            /* punto 5: TRANSPARENTE, jamás negro */
    aspect-ratio: 1 / 1;
    overflow: hidden;
    perspective: 2200px;                /* punto 3: perspectiva LEJANA */
}
.svrot-frame[b-4n70pqgx7h] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
    opacity: 0;
    transition: opacity .35s ease;
    backface-visibility: hidden;
    user-select: none;
    -webkit-user-drag: none;
}
.svrot-frame.on[b-4n70pqgx7h] { opacity: 1; }
/* modo 360: sin crossfade — el giro lo hace el transform del motor */
.svrot-stage.spin .svrot-frame[b-4n70pqgx7h] { transition: none; }

.svrot-drag[b-4n70pqgx7h] {
    position: absolute;
    inset: 0;
    z-index: 900;                       /* SIEMPRE sobre los frames (z 0-2): el dedo cae aquí */
    cursor: grab;
    touch-action: pan-y;                /* el arrastre horizontal gira; el vertical sigue scrolleando */
}
.svrot-drag:active[b-4n70pqgx7h] { cursor: grabbing; }

.svrot-lbl[b-4n70pqgx7h] {
    position: absolute;
    z-index: 901;                       /* la etiqueta del ángulo SIEMPRE visible sobre la foto */
    left: 0;
    right: 0;
    bottom: 0;
    text-align: center;
    color: #fff;
    background: linear-gradient(transparent, rgba(0, 0, 0, .45));
    padding: 1.6rem .5rem .5rem;
    font-weight: 700;
    letter-spacing: .03em;
    pointer-events: none;               /* jamás roba el arrastre */
}

/* 🗑️ La fila de MODOS se eliminó (2026-08-25, revisión con ojos de cliente):
   con un solo modo el botón no ofrece alternativa. Se borra de verdad — markup,
   CSS y las dos claves de AppText — en vez de dejarlo comentado: CSS que nadie
   emite es la clase muerta que el próximo lector cree viva. Ver la nota larga en
   ServicePhotoModal.razor. */

/* ── Puntitos + ⓘ ─────────────────────────────────────────────────────────── */
.svrot-dots-row[b-4n70pqgx7h] { display: flex; align-items: center; justify-content: center; gap: .5rem; padding: .45rem .6rem; }
.svrot-dots[b-4n70pqgx7h] { display: flex; gap: .4rem; align-items: center; }
.svrot-dot[b-4n70pqgx7h] {
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: var(--border-color, #dde4ec);
    border: none;
    cursor: pointer;
    padding: 0;
    position: relative;
}
/* 🟡 LUPE 664 vuelta-1 (MENOR-2): el activo era --primary-900 y en los temas
   oscuros (Onyx: rgb(13,17,25)) desaparecía contra el modal (~1.2:1). El
   ACENTO rojo de la casa contrasta en TODOS los temas — el mismo criterio del
   disco rojo del cartel del TV (5,9:1) — y el borde fino lo separa de la foto
   pase lo que pase. */
.svrot-dot.on[b-4n70pqgx7h] {
    background: var(--accent-color, #C8102E);
    /* box-shadow y no border: un border sumaría 2px al cajón y movería la fila */
    box-shadow: 0 0 0 1px rgba(255, 255, 255, .55);
    transform: scale(1.25);
}
/* Área táctil de 44px DE ALTO sin agrandar el puntito (truco de la ✕ LV-05).
   El ANCHO del área es 20px = el paso entre puntitos (11px + gap): con 8
   puntitos, áreas de 44px de ancho se SOLAPARÍAN y el toque caería en el
   puntito vecino — peor que un blanco más angosto. El gesto principal para
   moverse entre ángulos es ARRASTRAR el escenario (ese sí es enorme); los
   puntitos son el atajo fino. */
.svrot-dot[b-4n70pqgx7h]::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 20px;
    height: 44px;
    transform: translate(-50%, -50%);
}

/* ── Controles ────────────────────────────────────────────────────────────── */
.svrot-ctl[b-4n70pqgx7h] { display: flex; gap: .5rem; padding: 0 1rem 1rem; }
.svrot-play[b-4n70pqgx7h],
.svrot-choose[b-4n70pqgx7h] {
    flex: 1;
    min-height: 44px;                   /* R-347 táctil */
    border-radius: 10px;
    padding: .5rem;
    font-weight: 600;
    font-size: .88rem;
    cursor: pointer;
}
.svrot-play[b-4n70pqgx7h] {
    border: 1.5px solid var(--border-color, #dde4ec);
    background: var(--surface-color, #fff);
    color: var(--bs-body-color, #1C2333);
}
.svrot-choose[b-4n70pqgx7h] {
    border: 1.5px solid #198754;
    background: #198754;
    color: #fff;
}

/* 🟡 LUPE 664 vuelta-1 (MENOR-3) — HORIZONTAL 844×390: el escenario 1:1 se
   comía los ~366px del modal y modos/puntitos/⏸ nacían bajo el pliegue. En
   landscape bajito el escenario deja el aspect-ratio y toma la ALTURA QUE
   SOBRA tras el chrome (~250px: cabecera 52 + modos ~54 + puntitos ~33 +
   controles ~60 + aire) — los controles quedan visibles SIN scroll interno.
   La foto sigue entera por object-fit: contain (se hace más chica, no se
   corta — R-280). */
@media (orientation: landscape) and (max-height: 500px) {
    .svrot-stage[b-4n70pqgx7h] {
        aspect-ratio: auto;
        height: calc(100vh - 250px);
        min-height: 110px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .svrot-frame[b-4n70pqgx7h] { transition: none; }
    .svrot-dot.on[b-4n70pqgx7h] { transform: none; }
}
/* /Components/Shared/TipSplitEditor.razor.rz.scp.css */
/* ══ ÍTEM 657 — REPARTO DE PROPINA · estilos portados del mockup CONGELADO ══════
   MBS\kahlo\mockup-657-reparto-propina.html (aprobado 100% por el dueño).
   Colores del poste: navy #14224E · rojo #C8102E · oro #B8860B (= .crud-price). */

.tip-split[b-i9zg0nau57] {
    border: 1px solid #D8D4C8;
    border-radius: 12px;
    padding: .85rem;
    background: #FBFAF6;
    margin-top: .6rem;
    max-width: 560px;
}

.ts-head[b-i9zg0nau57] { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin-bottom: .6rem; }
.ts-title[b-i9zg0nau57] { font-weight: 700; font-size: .95rem; color: #14224E; }
.ts-tipbadge[b-i9zg0nau57] {
    background: rgba(184,134,11,.14); color: #7a5a08; font-weight: 700; font-size: .78rem;
    border: 1px solid rgba(184,134,11,.35); border-radius: 999px; padding: .1rem .55rem;
}
.ts-count[b-i9zg0nau57] { margin-left: auto; font-size: .72rem; color: #5b6472; font-weight: 600; }

/* Chips de modo (≥44px táctil) */
.ts-modes[b-i9zg0nau57] { display: flex; gap: .4rem; flex-wrap: wrap; margin-bottom: .75rem; }
.ts-chip[b-i9zg0nau57] {
    appearance: none; cursor: pointer; border: 1px solid #D8D4C8; background: #fff; color: #14224E;
    border-radius: 999px; padding: .55rem .85rem; font-weight: 600; font-size: .82rem; min-height: 44px;
    display: inline-flex; align-items: center; gap: .35rem; transition: box-shadow .12s ease;
}
.ts-chip:hover[b-i9zg0nau57] { border-color: #14224E; }
.ts-chip.active[b-i9zg0nau57] {
    background: linear-gradient(135deg,#C8102E,#9E0C24); border-color: #9E0C24; color: #fff;
    box-shadow: 0 4px 12px rgba(200,16,46,.25);
}

.ts-hint[b-i9zg0nau57] { font-size: .8rem; color: #5b6472; margin-bottom: .6rem; }

/* Filas: Barbero · Servicio · Precio · Propina */
.ts-rows[b-i9zg0nau57] { }
.ts-row[b-i9zg0nau57] { display: flex; align-items: center; gap: .6rem; padding: .5rem 0; border-top: 1px dashed #E7E3D8; }
.ts-row:first-child[b-i9zg0nau57] { border-top: 0; }
.avatar[b-i9zg0nau57] {
    width: 34px; height: 34px; border-radius: 50%; flex: none; display: flex;
    align-items: center; justify-content: center; font-weight: 800; font-size: .85rem;
}
.who[b-i9zg0nau57] { flex: 1; min-width: 0; }
.who .nm[b-i9zg0nau57] { font-weight: 700; font-size: .9rem; color: #14224E; }
.who .svc[b-i9zg0nau57] { font-size: .75rem; color: #5b6472; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; }
.who .svc .price[b-i9zg0nau57] { font-weight: 700; color: #3a4256; }
.amt[b-i9zg0nau57] { font-weight: 800; color: #B8860B; font-size: 1.02rem; white-space: nowrap; text-align: right; }
.amt small[b-i9zg0nau57] { display: block; font-size: .58rem; font-weight: 700; color: #5b6472; text-transform: uppercase; letter-spacing: .4px; }

/* Modo Personalizada: textbox de $ (48px, prefijo $, sin flechitas) */
.ts-inputbox[b-i9zg0nau57] { position: relative; display: inline-flex; align-items: center; width: 118px; flex: none; }
.ts-inputbox .cur[b-i9zg0nau57] { position: absolute; left: 11px; font-weight: 800; color: #5b6472; pointer-events: none; font-size: 1.02rem; }
.ts-inputbox input[b-i9zg0nau57] {
    width: 100%; height: 48px; border: 1px solid #D8D4C8; border-radius: 10px;
    padding: 0 .65rem 0 1.55rem; font-size: 1.08rem; font-weight: 800; color: #14224E; background: #fff; text-align: right;
}
.ts-inputbox input:focus[b-i9zg0nau57] { outline: none; border-color: #14224E; box-shadow: 0 0 0 3px rgba(20,34,78,.15); }
.ts-inputbox.bad input[b-i9zg0nau57] { border-color: #C8102E; box-shadow: 0 0 0 3px rgba(200,16,46,.18); }
.ts-auto[b-i9zg0nau57] {
    display: inline-flex; flex-direction: column; align-items: flex-end; justify-content: center; flex: none;
    width: 118px; height: 48px; border: 1px dashed #B8860B; border-radius: 10px; background: #FBF6E9; padding: 0 .65rem;
}
.ts-auto .v[b-i9zg0nau57] { font-weight: 800; color: #B8860B; font-size: 1.08rem; line-height: 1.05; }
.ts-auto .v.bad[b-i9zg0nau57] { color: #C8102E; }
.ts-auto .tag[b-i9zg0nau57] { font-size: .56rem; color: #7a5a08; font-weight: 800; text-transform: uppercase; letter-spacing: .4px; }

/* Termómetro con los colores del poste */
.ts-bar[b-i9zg0nau57] {
    height: 18px; border-radius: 6px; overflow: hidden; display: flex; margin: .55rem 0 .35rem;
    border: 1.5px solid #14224E; background: #EAE6DA;
}
.ts-bar span[b-i9zg0nau57] {
    height: 100%; min-width: 7px; box-shadow: inset 0 0 0 1px rgba(11,18,32,.28);
    border-right: 2px solid #0B1220; transition: width .18s ease;
}
.ts-bar span:last-child[b-i9zg0nau57] { border-right: 0; }
.ts-legend[b-i9zg0nau57] { display: flex; flex-wrap: wrap; gap: .4rem .9rem; margin: .15rem 0 .12rem; font-size: .76rem; font-weight: 600; color: #14224E; }
.ts-legend .lg[b-i9zg0nau57] { display: inline-flex; align-items: center; gap: .35rem; }
.ts-legend .sw[b-i9zg0nau57] { width: 13px; height: 13px; border-radius: 3px; border: 1px solid rgba(11,18,32,.4); flex: none; }
.ts-barcap[b-i9zg0nau57] { font-size: .68rem; color: #5b6472; margin: 0 0 .55rem; }

.ts-totalrow[b-i9zg0nau57] { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.ts-total-chip[b-i9zg0nau57] {
    font-size: .78rem; font-weight: 800; border-radius: 999px; padding: .22rem .6rem;
    background: rgba(46,125,91,.14); color: #1f6a48; border: 1px solid rgba(46,125,91,.4);
}
.ts-total-chip.bad[b-i9zg0nau57] { background: rgba(200,16,46,.12); color: #C8102E; border-color: rgba(200,16,46,.4); }
.ts-reset[b-i9zg0nau57] {
    margin-left: auto; appearance: none; cursor: pointer; background: #fff; border: 1px solid #D8D4C8;
    color: #14224E; border-radius: 999px; padding: .4rem .7rem; font-weight: 600; font-size: .76rem; min-height: 40px;
}
.ts-reset:hover[b-i9zg0nau57] { border-color: #14224E; }

.ts-foot[b-i9zg0nau57] { margin-top: .7rem; padding-top: .6rem; border-top: 1px solid #E7E3D8; font-size: .82rem; color: #14224E; font-weight: 600; }
.ts-foot[b-i9zg0nau57]  .ok { color: #2E7D5B; font-weight: 800; }
.ts-foot[b-i9zg0nau57]  .err { color: #C8102E; font-weight: 800; }
/* /Components/Shared/TvMenuPanel.razor.rz.scp.css */
/* ══ ÍTEMS 892 / 1078 — CSS del panel del menú (Jack, 2026-09-03) ═══════════
   CSS ISOLATION (Blazor le añade un atributo b-xxxx a cada selector): por eso
   se pueden reusar nombres de clase cortos sin chocar con nada de app.css
   (medido: .action/.num/.sec/.thumb ya aparecen 59/15/52/13 veces en app.css
   con OTRO significado — de ahí el prefijo tvm- además del scope).

   Medidas en vh — ítem 610: "se fija CUÁNTOS caben, no el tamaño de letra".
   Base = lo que Kahlo midió en el mockup v3 (medir-892.py / medidas-v3.json),
   reacomodado a la REJILLA DE TARJETAS que pidió Héctor (su "4.ª versión"). */
.tvm-slide[b-j722dptvm5] {
  --navy: #14224E; --navy-2: #1E2E5E; --ink-2: #5B6B8C;
  --rojo: #C8102E;
  --porcelana: #F7F5F0; --card: #FFFFFF;
  --hair: rgba(20,34,78,.13);

  /* ══ 20:00 (Héctor/Kahlo) — LV-43 v5, `menu-tv-v5-hector.html`, el CSS de
     Héctor traducido a vh por Kahlo (1vh=10.8px). SOLO esta superficie —
     "ninguna otra pantalla toma esos colores". Paleta LITERAL de v5. ══
     `--grad-start`: el primer stop de LOS 5 degradados rojo→magenta,
     OSCURECIDO ~6% (dentro del 5-8% que Kahlo midió) — ee1438 → dc1335 —
     hasta que el blanco encima mida ≥4.5:1 en el punto más claro del
     degradado. `--band-end`: pedido explícito de Héctor (17:40/19:05) —
     "cambiar magenta↔rojo oscuro en un sitio" — el fin de las BANDAS
     (`.section-title`); los otros 4 degradados (cabecera, promo, footer,
     reservar) mantienen SU PROPIO fin literal de v5, sin atarlos a esta. */
  --grad-start: #dc1335;
  --band-end: #85006c;
  --v5-navy-950: #020c24; --v5-navy-900: #061536; --v5-navy-850: #091a42; --v5-navy-800: #102459; --v5-navy-700: #18356f;
  --v5-red: #e20b32; --v5-red-dark: #b80027; --v5-blue-neon: #1687ff; --v5-blue-light: #55b8ff;
  --v5-gold-bg: #fff1d5;
  /* Contraste (Kahlo, sin medir con `medir-v5-892.py` por mí — fórmula
     manual, ~6.3:1 calculado contra el extremo oscuro del dorado #f4c773;
     el original 8d4700 no se midió aquí, se asume por debajo de 4.5:1 tal
     como reportó el encargo): oscurecido hasta `--v5-gold-text`. */
  --v5-gold-text: #6b3600;
  --shadow-blue: 0 0 .278vh rgba(42,142,255,.8), 0 0 1.296vh rgba(26,116,255,.25);
  --shadow-red: 0 0 .370vh rgba(255,36,76,.9), 0 0 1.667vh rgba(255,0,92,.28);

  /* ══ ÍTEM LV-45 — RETOCADAS CONTRA `layout-v7-medidas.md` Y
     `menu-tv-v8-optimizado.html` (Kahlo, 2026-09-03/04 — referencia
     DEFINITIVA de esta pantalla). Antes venían del baseline de v5/LV-43
     (proporciones distintas: `.dashboard` de v5 era 350/700/290px fijos;
     LV-45 es una rejilla `21.05vw/1fr/21.17vw`, más ancha de centro) — se
     quedaban CHICAS frente a lo que Kahlo midió y optimizó para esta
     rejilla nueva. Mapeo: `--m-head`→`.services-main-header` (10.85vh),
     `--m-headfs`→su `h1` (2.3vh), `--m-shopfs`→`.shop` (1.7vh), `--m-band`/
     `--m-bandfs`→`.cat-band` (6.81vh/1.75vh — Aceptamos mide 6.38vh/1.6vh,
     ~0.4vh menos; se comparte UNA variable a propósito, LEY 14, en vez de
     una por banda), `--m-photo`→`.item-thumb` (3.2vh), `--m-num`/
     `--m-numfs`→`.item-num` (2.4vh/1.25vh), `--m-name`/`--m-price`→
     `.item-name`/`.item-price` (1.75vh/2.15vh, escalados ×1.4 aquí porque
     esta variable también alimenta el nombre/precio DOBLE de tamaño de la
     recomendación vía `calc()`, ver más abajo — el valor base de fila
     queda en 1.75vh/2.15vh reales tras los `calc()` de `.tvm-rname`/
     `.tvm-rprice`). `--m-row` no tiene equivalente directo en v8 (esa fila
     no fija alto, lo da el contenido) — se deja en el pitch medido del PNG
     (7.87vh, `layout-v7-medidas.md` §2) como mínimo, nunca menor al hueco
     que ya exige la miniatura + el padding vertical de la fila. ══ */
  --m-inset: 1vw; --m-pad: 1.1vh;
  --m-head: 10.85vh; --m-foot: 3.426vh;
  /* ══ ÍTEM 1166 v3 / 1165 / 1169 (Paul, 2026-09-05) — PORTE DEL INSTRUMENTO DE
     KAHLO `MBS\MOCKUPS\1166\menu-tv-1166-v3.html` (acta 17:47 EDT): sus medidas
     son la spec. Lo que era fijo pasa a DERIVARSE:
     · `--k` = 1,25 (LV-64): nombre, precio, banda de categoría y banda/etiquetas
       de «Aceptamos» +25 %.
     · `--m-rowmin` (LV-69): el MÍNIMO legible de fila = el contenido más alto
       (foto 4vh · número · precio×1,1) + 2×aire (`--m-air` .70vh). `--m-row`
       arranca ahí y `bspTvMenuMeasure` (bsp-tv.js) la SUBE en px a
       max(mínimo, alto útil / 8) — 8 por columna = 16 por página (LV-72,
       enmendado a 16 por LV-81: la media página sigue siendo 8) — y
       hoy la ESCRIBE EL SERVIDOR en `.tvm-flow` (`TvMenuData.BuildFlow`, LV-94);
     sin medida no se pinta cuerpo, y sin JS queda el mínimo (58 px), legible.
     · `--m-photo` = 74 % de la fila (LV-67 «casi tocando las líneas»): 71,9 px
       a 1080p. Ya no es un número suelto.
     · `--m-payico` 3,6vh es el PISO desde el que `bspTvMenuLayout` crece el
       ícono (1169 / v3 §2.2: hasta min(0,62×celda, techo `?ico=`)); `--m-paylab`
       .62×nombre (14,6 px: 5,8′ a 3 m — Kahlo v2 lo mide: la etiqueta es para
       quien está junto al TV, a distancia se lee el LOGO).
     · `--side-w` 26 % (v2 §"paga dos veces": el título «La Recomendación De
       Hoy» deja de envolver en ES y las 11 etiquetas caben); `--promo-ph` 20,5vh
       es el arranque de la foto de la Recomendación (LV-68: es la moneda —
       `bspTvMenuLayout` la mueve entre 8vh y el ancho de la columna);
       `--mos-side`/`--mos-gap` = la tesela del relleno (LV-66 v3). ══ */
  --k: 1.25;
  --m-band: 6.6vh; --m-bandfs: calc(1.75vh * var(--k));
  --m-gap: .648vh;
  --m-name: calc(1.75vh * var(--k)); --m-price: calc(2.15vh * var(--k));
  --m-num: 2.4vh; --m-numfs: 1.25vh;
  --m-air: .70vh;
  --m-rowmin: max(calc(4vh + 2 * var(--m-air)),
                  calc(var(--m-num) + 2 * var(--m-air)),
                  calc(var(--m-price) * 1.1 + 2 * var(--m-air)));
  --m-row: var(--m-rowmin);
  /* ⚠️ `--m-photo` NO se define aquí: un `calc(var(--m-row)*.74)` en el slide se
     resuelve con el `--m-row` DEL SLIDE (el mínimo, 58 px) y el px que el JS
     escribe en `.tvm-flow` no lo re-evalúa — medido: foto 43,2 en vez de 71,9.
     Vive en `.tvm-row` (abajo), donde hereda el `--m-row` derivado. */
  --m-payico: 3.6vh; --m-paylab: calc(var(--m-name) * .62);
  /* ══ ÍTEM 1237 / LV-93 ③ — LA DESCRIPCIÓN DE LA FILA. Números del
     instrumento LV-96/LV-97 de Kahlo, no inventados aquí: .82 del nombre,
     interlineado 1,22 y .30vh de aire entre los dos renglones. ══ */
  --m-descr: .82; --m-desc: calc(var(--m-name) * var(--m-descr));
  --m-desclh: 1.22; --m-descgap: .30vh;
  /* ══ 2026-09-10 (Héctor en PROD, con las tres tiendas delante) — LA
     DESCRIPCIÓN VA A DOS RENGLONES. 🔴 SUPERSEDE el «un solo renglón con
     elipsis» que el ítem 1237 dejó escrito más abajo, y el motivo NO es de
     gusto, está medido: el ítem 1237 eligió el tamaño contra un hueco de
     541 px = 61 caracteres (el mockup, UNA columna) y en la app el hueco
     real es de 264–425 px = 31–43 caracteres, porque el flujo (LV-94) pone
     DOS columnas donde el mockup tenía una. Resultado en producción: 23 %
     de las descripciones cortadas y 87,6 % de ésas A MITAD DE PALABRA — y
     en la tienda de 16 lo que se perdía era EL PRECIO («…Las cejas
     sol‸», «…El bigote so‸»). Con dos renglones el hueco pasa a
     62–86 caracteres.
     🔑 `--m-desclines` gobierna el clamp de `.tvm-rdesc`, y NADA MÁS.
     🔴 CORREGIDO EL 2026-09-11 — aquí ponía que también lo consumía la sonda
     (`.tvm-probe .tvm-rdesc`) «y es quien le dice al motor cuánto mide AHORA
     una fila con descripción». ESO ERA LA OPCIÓN A, y la opción A no se quedó:
     con la B (la que tiene luz verde) la sonda mide la constante de siempre y
     el motor NUNCA ve el alto de los dos renglones. Ver el bloque grande de
     `.tvm-probe-promo`, más abajo, con los números de las seis tiendas.
     ⇒ CONSECUENCIA PRÁCTICA, que es lo contrario de lo que decía esta nota:
     subir esto a 3 NO se paga solo. El tercer renglón se pintaría dentro del
     alto que la fila YA tiene; donde no quepa, se cortará igual. Antes de
     tocarlo hay que medir cuántas descripciones se cortan, no cuántos
     tableros pierden la foto — con B el reparto no se mueve. */
  --m-desclines: 2;
  /* ══ LV-92 / LV-99 — LA BANDA ROJA DE LA FOTO GRANDE: alto 2,6 × el cuerpo
     del precio. Y `--tvm-fg-ar` = la razón POR DEFECTO de esa foto (0,7467 =
     896×1200, el formato del catálogo de modelos virtuales); `bspTvMenuLayout`
     la reescribe con la razón REAL de la imagen en cuanto carga. ══ */
  --m-cap: var(--m-price); --m-caph: calc(var(--m-cap) * 2.6);
  --m-fgdesc: var(--m-name); --tvm-fg-ar: .7467;
  /* ══ ÍTEM 1274 (Tito, 2026-09-13) — EL TECHO DE LA DESCRIPCIÓN DE LA FOTO
     GRANDE, Y EL ALTO QUE LA SONDA TIENE QUE MEDIR. UN SOLO NÚMERO PARA LOS
     DOS: si aquí se cambia a 3, el clamp de `.tvm-fg-desc` Y el alto de
     `.tvm-probe .tvm-fg-desc` se mueven JUNTOS — que es justo lo que no pasaba
     antes, y por eso el suelo de la foto se quedaba corto.
     🔑 Cómo contar lo que esto gobierna (LEY 19: el grep, no la lista):
        grep -n "m-fgdesclines\|m-fgdesclh" TvMenuPanel.razor.css
     `--m-fgdesclh` está SEPARADO del 1.2 literal que había en `.tvm-fg-desc`
     porque el alto de la sonda se calcula con él: si uno cambia y el otro no,
     el suelo vuelve a mentir en silencio. ══ */
  --m-fgdesclines: 2; --m-fgdesclh: 1.2;
  --side-w: 26%; --promo-ph: 20.5vh;
  --mos-side: 10vh; --mos-gap: .6vh;
  /* ÍTEM 1154 (Héctor, 2026-09-04 21:30: "lo que dice menu de servicios
     tambien" [va más grande]) ⇒ LV-59 (22:37 EDT, "ok sigo la recomendacion"
     = ×2 en los TRES títulos, acta 1154 v3 de Kahlo, medida de arco a 3 m/55″
     y 4 m/65″ sobre el suelo de 16′): `--tv-menu-title-size: 4.6vh` (×2 de
     los 2.3vh de LV-45; 49.7 px a 1080p = 26,2′/23,3′; hoy 13,1′) alimenta
     `--m-headfs` (el título crece DENTRO de la banda, que sigue en
     `--m-head` 10.85vh — medido en los 165 estados de Kahlo); y `--m-shopfs`
     3.4vh (×2 de 1.7vh) para el nombre de la tienda a la DERECHA de la banda
     — los tramos ×.74/×.56 por largo (`.tvm-shop-s/-xs`) siguen igual, así
     un nombre de tienda de 26 caracteres (el más largo de las tiendas que hay
     hoy en producción — ese es el número que hay que volver a medir, no un
     nombre que memorizar) cabe con ≥86 px de aire al título. La otra
     variable del ítem es `--tv-store-name-size` (app.css, `.tv-store-name`,
     panel izquierdo). Cambiar estos números es todo el ítem. */
  --tv-menu-title-size: 4.6vh;
  --m-headfs: var(--tv-menu-title-size); --m-shopfs: 3.4vh; --m-footfs: 1.667vh;

  /* ══ ÍTEM 1101 (Héctor en PROD, 2026-09-03 ~20:10) — LA FOTO DETRÁS,
     ENCENDIDA. El `.razor` fija `--tvm-bg-photo` con DOS capas: un velo
     navy semitransparente (`rgba(7,21,51,.66)→rgba(3,13,36,.7)`, "suave
     para lo oscuro") ENCIMA de `url(BackgroundRotation.CurrentUrl())` — la
     misma rotación semanal de 4 fotos que usa `Home.razor` (LEY 14, un
     solo helper). Sin `style=` (p.ej. un caller que no lo pase), el
     fallback del `var()` de abajo sigue siendo el degradado navy de v5 —
     nunca una foto sin velo ni un hueco roto.
     🔄 LV-57 (Héctor, 2026-09-04 22:14): el velo YA NO es fijo — el
     `.razor` lo escala con `?veil=` (0-100 %, default 25 %) y la foto
     detrás es la de la TIENDA si existe (cascada tienda→compañía→rotación
     del home).
     `--frost-blur`/`--frost-alpha`: la receta del cristal. 🔄 LV-57: ya NO
     son "por si algún día" — `.tvm-frost-on` va SIEMPRE puesta y el
     `.razor` fija estos dos valores desde `?glass=` (default 45 ⇒ α .45,
     blur 14 px; `glass=100` ⇒ α 1 y blur 0 = el blanco sólido de LV-45,
     píxel a píxel). Los valores de aquí son solo el respaldo si un caller
     no pasara `style=`. */
  --frost-blur: 14px; --frost-alpha: .45;
  position: absolute; inset: 0; display: flex; gap: var(--m-pad);
  padding: var(--m-inset); box-sizing: border-box;
  /* v5 `.dashboard`: el fondo de LA PÁGINA — degradado navy oscuro, el
     fallback si `--tvm-bg-photo` no llegara fijada. */
  background-image: var(--tvm-bg-photo, linear-gradient(135deg, #071533 0%, #030d24 50%, #071533 100%));
  background-size: cover; background-position: center;
}
.tvm-slide *[b-j722dptvm5], .tvm-slide *[b-j722dptvm5]::before, .tvm-slide *[b-j722dptvm5]::after { box-sizing: border-box; }

/* ══ EL CRISTAL — LV-57 (Héctor, 2026-09-04 22:14 EDT, viendo el mockup v2 de
   Kahlo: *"me gusta este, ahora bien, veo que hay parametros arriba, quizas
   sea bueno tener este como default pero poder escoger el level y velo en
   parametros en el link del tv menu para ajustarlo"*) ═══════════════════════
   `.tvm-frost-on` DEJA DE SER una variante apagada: el `.razor` la suma
   SIEMPRE y le pasa `--frost-alpha`/`--frost-blur` desde `?glass=`. El
   default es 45 (α .45, blur 14 px) + `?veil=25`.
   🔑 POR QUÉ HAY DOS REGLAS Y NO UNA (esto es el punto (c) de LV-57, y es una
   corrección MEDIDA, no un gusto): el menú lleva DOS capas de cristal — la
   tarjeta (`.tvm-card`) sobre el panel (`.tvm-sheet`) — así que su blanco
   efectivo es `1−(1−α)²`. La columna derecha (`.tvm-blk`: Recomendación, Wifi,
   Premium, QR) va DIRECTA sobre la foto: UNA sola capa. Con el mismo α, el
   texto navy del menú daba 7,75:1 y el de la derecha 3,58:1 — **bajo AA**
   (Kahlo lo midió en el mockup sobre DOS tiendas de producción: 3,80 y 3,94
   a 45 %). Por eso
   `.tvm-blk` pinta DOS capas del mismo α (una imagen plana encima del color
   de fondo): las dos columnas quedan con el MISMO blanco efectivo a
   cualquier nivel, y el número de la derecha pasa a ser el del menú. No es
   un α distinto "solo ahí" — es la MISMA receta compuesta las mismas veces,
   que es lo único que no se descalibra cuando Héctor mueva el nivel.
   ⚠️ El blur va SOLO en las capas que tocan la foto (`.tvm-sheet` y
   `.tvm-blk`). Ponerlo también en `.tvm-card` desenfocaría el panel ya
   compuesto (doble coste de GPU en un TV barato, cero cambio visible).
   · `.tvm-marco-frosted` — variante NO usada: blanco 48%/blur 10 tapando TODO
     el `.tvm-slide`, en vez de dejar ver la foto directa en el hueco.
   · `.tvm-bg-preblurred` — respaldo de costo en TV barato: apaga el blur EN
     VIVO (para cuando exista una foto YA desenfocada en servidor — coste
     sin medir, no existe esa ruta hoy). */
.tvm-slide.tvm-frost-on .tvm-sheet[b-j722dptvm5] {
  /* porcelana, no blanco: `--porcelana` es el fondo del panel desde LV-43 y a
     `glass=100` esta regla tiene que dar EXACTAMENTE el panel de hoy. */
  background: rgba(247,245,240,var(--frost-alpha)) !important;
  -webkit-backdrop-filter: blur(var(--frost-blur)); backdrop-filter: blur(var(--frost-blur));
}
.tvm-slide.tvm-frost-on .tvm-card[b-j722dptvm5] {
  background: rgba(255,255,255,var(--frost-alpha)) !important;
}
.tvm-slide.tvm-frost-on .tvm-blk[b-j722dptvm5] {
  background:
    linear-gradient(rgba(255,255,255,var(--frost-alpha)), rgba(255,255,255,var(--frost-alpha))),
    rgba(255,255,255,var(--frost-alpha)) !important;
  -webkit-backdrop-filter: blur(var(--frost-blur)); backdrop-filter: blur(var(--frost-blur));
}
.tvm-slide.tvm-marco-frosted[b-j722dptvm5] {
  background: rgba(255,255,255,.48);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
}
.tvm-slide.tvm-frost-on.tvm-bg-preblurred .tvm-sheet[b-j722dptvm5],
.tvm-slide.tvm-frost-on.tvm-bg-preblurred .tvm-blk[b-j722dptvm5] {
  -webkit-backdrop-filter: none; backdrop-filter: none;
}

/* ══ ZONA 1 · EL MENÚ. Panel BLANCO PLANO (19:45, LV-43 — vigente hasta que
   el v5 de Kahlo diga otra cosa). `.tvm-frost-on` arriba lo sustituye por
   cristal SIN tocar esta regla. ══ */
.tvm-sheet[b-j722dptvm5] {
  flex: 1; min-width: 0; background: var(--porcelana); border-radius: 1.111vh;
  display: flex; flex-direction: column; overflow: hidden;
  /* v5 `.services-panel`: borde y resplandor azul neón sobre el panel blanco. */
  border: .093vh solid rgba(53,144,255,.6);
  box-shadow: 0 0 .741vh rgba(43,135,255,.22), 0 .370vh 2.222vh rgba(0,0,0,.18);
}
/* ── CABECERA — LV-45 (Héctor, imagen 20:45): "✂ Menú De Servicios" a la
   IZQUIERDA (título) y el NOMBRE DE LA TIENDA a la DERECHA — corrige la
   versión anterior (centrado, dos correcciones el mismo día) que la propia
   imagen del layout final descartó. Blanco, bold, y el nombre NO se trunca
   con "…" (R-466: tal como lo escribió el dueño): la letra ENCOGE. Grid de 2
   columnas: título de ancho natural | nombre flexible alineado a la derecha.
   🔄 ÍTEM 1154b / LV-62 — ESTE COMENTARIO DECÍA "nunca se trunca: la letra
   escala por tramo (.tvm-shop-s / .tvm-shop-xs)" Y PROMETÍA MÁS DE LO QUE
   CUMPLÍA. Los tramos cuentan CARACTERES, no píxeles: Eugenio (6) midió que
   tras el ×2 de LV-59 un nombre más ancho que 437 px se SALE de la banda (13
   px a 1080p, 9 a 768p; 472 con uno patológico) — no se recorta, se sale.
   Hoy son DOS piezas y hacen cosas distintas: los TRAMOS son la pasada gruesa
   del servidor (llega pintado, sin parpadeo) y `--m-shopscale`, que escribe
   `bspTvFitShopName` (bsp-tv.js) midiendo el ancho REAL contra el de la celda,
   es la GARANTÍA. Se multiplican. Sin JS, queda lo de antes: los tramos. ── */
.tvm-head[b-j722dptvm5] {
  /* 20:00 (v5, LV-43): SUPERSEDE la decisión propia de "navy, no rojo de
     punta a punta / presupuesto R-228" — v5 es la base FINAL aprobada y su
     `.services-main-header` es gradiente rojo→magenta de punta a punta.
     Primer stop oscurecido (`--grad-start`, ver arriba); el resto literal
     de v5 (fin propio #700074, NO atado a `--band-end`). */
  flex: 0 0 var(--m-head);
  background: linear-gradient(105deg, var(--grad-start) 0%, #cc072e 35%, #a6004c 70%, #700074 100%);
  box-shadow: inset 0 .093vh 0 rgba(255,255,255,.25), 0 .185vh 1.111vh rgba(232,0,66,.22);
  text-shadow: 0 .093vh .185vh rgba(0,0,0,.25);
  color: #fff;
  display: grid; grid-template-columns: auto 1fr; align-items: center;
  padding: 0 var(--m-pad); gap: var(--m-pad);
}
.tvm-head h1[b-j722dptvm5] {
  /* 🔴 QA de Eugenio (997e528, ítem 1087): `theme-theshop.css:583` trae
     `h1,h2,h3{color:var(--primary-900)}` GLOBAL, y sin `color` explícito
     aquí esa regla le gana a la herencia del blanco de `.tvm-head` — navy
     sobre navy, invisible, medido en 3 tiendas y los 2 idiomas.
     18:00 (Héctor): fuera el uppercase — el diccionario ya se dicta en
     Mayúscula Inicial ("Menu De Servicios"/"Service Menu"), un CSS que
     mayusculiza encima re-transformaría lo ya correcto. */
  margin: 0; font-size: var(--m-headfs); font-weight: 800; letter-spacing: .04em;
  color: #fff;
  line-height: 1; display: flex; align-items: center;
  gap: .74vh; white-space: nowrap; justify-self: start;
}
.tvm-shop[b-j722dptvm5] {
  /* `--m-shopscale` lo pone `bspTvFitShopName` EN EL ELEMENTO (1154b). El valor
     por defecto 1 vive aquí a propósito: si el JS no corre, la banda se comporta
     exactamente como antes del ítem, no peor. */
  grid-column: 2; min-width: 0; font-size: calc(var(--m-shopfs) * var(--m-shopscale, 1));
  font-weight: 800; color: #fff;
  text-align: right; white-space: nowrap; line-height: 1.1;
}
.tvm-shop-s[b-j722dptvm5] { font-size: calc(var(--m-shopfs) * .74 * var(--m-shopscale, 1)); }
.tvm-shop-xs[b-j722dptvm5] { font-size: calc(var(--m-shopfs) * .56 * var(--m-shopscale, 1)); }

.tvm-body[b-j722dptvm5] {
  flex: 1; min-height: 0; padding: var(--m-pad); overflow: hidden;
}

/* ══════════════════════════════════════════════════════════════════════════
   LV-94 / LV-96 / LV-99 / LV-100 — EL LAYOUT QUE FLUYE
   ══════════════════════════════════════════════════════════════════════════
   PORTE del instrumento de Kahlo (`MBS\MOCKUPS\LV96-retratopie` y
   `LV97-alineacion`) — sus medidas son la spec. Este bloque SUSTITUYE a
   `.tvm-grid` / `.tvm-grid-cats-1` / `.tvm-grid-cats-2` / `.tvm-fill*` /
   `.tvm-page-ind`, que eran el diseño anterior (rejilla `auto-fit` de tarjetas
   con paginación por categoría). No conviven: LV-94 disuelve la compuerta
   binaria de LV-90 ① — *«no hay dos diseños, hay UNO que fluye»*.

   DOS columnas. Se van poniendo categorías y servicios; cuando la primera se
   llena, se sigue en la segunda; lo que sobre de la segunda ES LA FOTO. La
   foto NO es una columna: es el RESTO de `.tvm-colw.c2`, y por eso vive
   dentro de ella, al final. El reparto (corte, alto de fila, si hay foto, si
   pagina) lo decide `TvMenuData.BuildFlow` con lo que mide `bspTvMenuFlow`;
   aquí solo está la geometría.
   El ancho lo escribe el .razor: `(1−0,467)fr 0,467fr` — LV-92 salida (d), la
   columna de la foto MÁS ESTRECHA. ══ */
.tvm-flow[b-j722dptvm5] { position: relative; height: 100%; display: grid; gap: var(--m-pad); align-items: stretch; }
.tvm-colw[b-j722dptvm5] {
  min-width: 0; min-height: 0; position: relative;
  display: flex; flex-direction: column; gap: var(--m-pad);
}
.tvm-colw > .tvm-card[b-j722dptvm5] { flex: 0 0 auto; }
/* Sin foto, la ÚLTIMA tarjeta de la columna se estira hasta el fondo. Las
   filas NO cambian de alto (siguen iguales en las dos columnas): lo único que
   cambia es que el sobrante queda DENTRO de la tarjeta en vez de suelto
   debajo. Lo levantó Kahlo MIRANDO la comparativa, no la tabla: su tabla decía
   `sobra = 0` y en la imagen había un escalón blanco. */
.tvm-colw.estirar > .tvm-card:last-child[b-j722dptvm5] { flex: 1 1 auto; }
.tvm-col[b-j722dptvm5] { flex: 1 1 0; min-width: 0; }
.tvm-col .tvm-row:last-child[b-j722dptvm5] { border-bottom: 0; }
/* «(cont.)» — LV-96: la categoría SE PARTE y el rótulo se REPITE en la segunda
   columna. Va al final de la banda, más chico que el rótulo: informa, no compite. */
.tvm-cont[b-j722dptvm5] {
  flex: 0 0 auto; margin-left: auto; font-size: calc(var(--m-bandfs) * .72);
  letter-spacing: .04em; opacity: .92; font-weight: 700;
}
/* El indicador de página, UNA vez, en el pie del tablero (LV-94 ②). */
/* ÍTEM 1265: columna 2 explícita — el indicador vive en el pie, que ahora es
   una rejilla de 3 columnas (ver `.tvm-foot`). Sin esta línea caería en la 1.ª,
   que es el hueco vacío que dejó el rótulo del idioma. */
.tvm-pag[b-j722dptvm5] { font-weight: 800; letter-spacing: .06em; font-variant-numeric: tabular-nums;
           grid-column: 2; }

/* ══ LA FOTO GRANDE ════════════════════════════════════════════════════════
   🔴 EL DEFECTO QUE ARREGLA (LV-96, Héctor: *«lo único que vi mal fue la foto
   grande»*): con `object-fit:cover` sobre un hueco APAISADO (627×352, razón
   1,78) y una foto VERTICAL (0,7467), el recorte se come arriba y abajo y solo
   sobrevive la frente. No era un problema de ALTO: era de FORMA.
   ⇒ LA FOTO CONSERVA SU PROPORCIÓN (`aspect-ratio`) Y SE CENTRA. La porcelana
   que queda a los lados no es un accidente: es lo que evita el recorte.
   `--tvm-fg-ar` la escribe `bspTvMenuLayout` con la razón REAL de la imagen;
   el default 0,7467 es 896×1200, el formato del catálogo de modelos, y es
   también el que usa el suelo (`bspTvMenuFlow`). ══ */
.tvm-fg-wrap[b-j722dptvm5] {
  flex: 1 1 auto; min-width: 0; min-height: 0;
  display: flex; flex-direction: column; align-items: center;
  gap: calc(var(--m-pad) * .55);
}
/* 🔴 CORRECCIÓN MEDIDA (Tito, 2026-09-10) — LA PRIMERA VERSIÓN SEGUÍA
   RECORTANDO, Y LO DIJO EL DETECTOR, NO EL OJO. Porté del mockup
   `aspect-ratio + height:100% + width:auto + max-width:100%`. Con eso el ALTO es
   definitivo (100 %) y el ANCHO lo clampa `max-width` cuando la columna es más
   estrecha que alto×razón: los dos quedan definitivos y **la razón se pierde**.
   Medido en los 16 tableros con foto: caja 461,3×744,8 = razón 0,6194 contra
   0,7467 de la foto ⇒ `object-fit:cover` se comía ~8,5 % por CADA orilla. Es
   exactamente lo que LV-96 vino a quitar («no se estira ni se recorta»), con la
   coronilla a salvo y las orejas no. En la captura la cara sale entera y por eso
   el ojo lo daba por bueno.
   ⇒ AHORA MANDA EL ANCHO: `width:100%` + `height:auto` + `aspect-ratio` hacen que
   el ALTO se derive de la razón (461,3 → 617,8) y la caja abrace a la foto, que
   es lo que dibujó Kahlo. `max-height:100%` es la red por si el hueco fuera más
   ancho que alto.
   Y `object-fit:contain` es la SEGUNDA compuerta, por si esa red llega a actuar:
   con `contain` no hay recorte a NINGUNA forma de hueco — sobra porcelana, que
   es justo lo que LV-96 dice que hay que dejar. Dos mecanismos distintos para el
   mismo fallo, a propósito (LEY 14: el que confía en una sola tiene un plazo). */
.tvm-foto-grande[b-j722dptvm5] {
  flex: 0 1 auto; min-width: 0; min-height: 0; position: relative; overflow: hidden;
  background: var(--porcelana); border-radius: .833vh; border: .093vh solid #e0e6f0;
  aspect-ratio: var(--tvm-fg-ar, .7467);
  width: 100%; height: auto; max-width: 100%; max-height: 100%;
}
/* `contain` y no `cover`: con la caja ya en la razón de la foto los dos pintan
   EXACTAMENTE lo mismo — pero si la razón que llegó no fuera la real (una foto
   que todavía no cargó, un formato nuevo, o el `max-height` de arriba actuando),
   `cover` recortaría y `contain` no. Centrado, porque con `contain` no hay nada
   que recortar y anclar arriba solo dejaría toda la porcelana abajo.
   📌 R-472 (`object-position: 50% 0`) sigue mandando donde SÍ hay recorte: la
   miniatura de cada fila (`.tvm-thumb img`, más arriba) y la foto de la
   Recomendación. Aquí no lo hay, y por eso no aplica — no es una excepción a
   R-472, es que R-472 habla de recortes. */
.tvm-foto-grande > img[b-j722dptvm5] {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; object-position: 50% 50%; display: block;
}
/* LV-99 (variante `pie`, confirmada 19:52) — LA BANDA ROJA, ABAJO Y EN SU
   PROPIA FRANJA, al ancho de la COLUMNA (no al de la foto: con el ancho de una
   foto en retrato el nombre se parte, «Afeitado …»). Nombre IZQUIERDA, precio
   DERECHA — LV-100 lo deja explícito: no es un párrafo, son dos datos.
   ⚠️ NO SE REABRE: con la banda ARRIBA salvan 0 de las 153 fotos del catálogo;
   abajo pasan las 153. Héctor prefería arriba y la soltó por el número. */
.tvm-fg-band[b-j722dptvm5] {
  flex: 0 0 auto; align-self: stretch; height: var(--m-caph);
  color: #fff; font-size: var(--m-cap); font-weight: 800; line-height: 1;
  display: flex; align-items: center; justify-content: space-between;
  gap: .8vh; padding: 0 calc(var(--m-pad) * .7); border-radius: .833vh;
}
.tvm-fg-band .n[b-j722dptvm5] { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tvm-fg-band .p[b-j722dptvm5] { flex: 0 0 auto; font-variant-numeric: tabular-nums; }
/* LV-100 (Héctor, 2026-09-09 19:54): *«voy con tu recomendación todas a la
   izquierda»*. El mockup la traía CENTRADA; esta descripción ocupa dos
   renglones y centrada el segundo queda corto flotando en el medio. A la
   izquierda los dos arrancan del mismo sitio — y es el mismo criterio que las
   descripciones de la lista, que ya iban a la izquierda. */
/* 🔴 ÍTEM 1274 (Tito, 2026-09-13) — ESTA CAJA NO TENÍA TECHO, Y POR ESO NO
   RECORTABA: CRECÍA HASTA EL TEXTO. `overflow:hidden` sin alto no recorta nada
   — recorta lo que se sale de una caja, y esta caja se agrandaba para que nada
   se saliera. Dos renglones como TECHO, la MISMA forma que `.tvm-rdesc` (que sí
   lo tenía desde el 1237): `-webkit-box` + `line-clamp` hacen que la caja siga
   midiendo POR CONTENIDO —una descripción de un renglón sigue ocupando uno— y a
   la vez no pueda pasar de `--m-fgdesclines`.
   ⚠️ Y el clamp corta DONDE SE ACABA EL ANCHO, o sea a mitad de palabra, que es
   el defecto que Héctor leyó como «la app está rota» («…gote solo es $5»). Quien
   mueve el corte a la frontera de palabra es `bspTvMenuDescWords` (bsp-tv.js) —
   y hasta este ítem esa función NO MIRABA esta caja: su selector era solo
   `.tvm-rdesc[data-tvm-full]`. Ahora mira las dos. Esta regla es la red de
   debajo: sin JS se ve cortado, nunca desbordado. */
.tvm-fg-desc[b-j722dptvm5] {
  flex: 0 0 auto; align-self: stretch; text-align: left;
  color: var(--navy); font-size: var(--m-fgdesc); font-weight: 600;
  line-height: var(--m-fgdesclh);
  padding: 0 calc(var(--m-pad) * .4); overflow: hidden;
  display: -webkit-box; -webkit-line-clamp: var(--m-fgdesclines); -webkit-box-orient: vertical;
}

/* ══ ÍTEM 1237 / LV-93 ③ — LA DESCRIPCIÓN BAJO CADA SERVICIO ═══════════════
   Con descripción, el texto de la fila se apila: `.tvm-linea1` (nombre +
   puntos + precio) y debajo `.tvm-rdesc`. Sin ella la fila queda EXACTAMENTE
   como estaba — la descripción no reserva renglón.
   🔴 «Un solo renglón con elipsis» ERA lo de aquí, y lo tumbó la MEDICIÓN, no
   una opinión: ver el bloque de `--m-desclines` arriba. Hoy son DOS renglones
   como TECHO — la descripción que cabe en uno sigue ocupando uno solo (el
   `-webkit-box` mide por contenido), así que el segundo renglón no es un
   renglón reservado: es el que aparece cuando hace falta. ══ */
.tvm-row.con-desc .tvm-rtext[b-j722dptvm5] {
  flex-direction: column; align-items: stretch; justify-content: center;
  gap: var(--m-descgap);
}
.tvm-row.con-desc .tvm-linea1[b-j722dptvm5] { display: flex; align-items: center; min-width: 0; }
.tvm-rdesc[b-j722dptvm5] {
  min-width: 0; text-align: left; color: var(--ink-2);
  font-size: var(--m-desc); font-weight: 400; line-height: var(--m-desclh);
  letter-spacing: .005em; overflow: hidden;
  /* Dos renglones como TECHO. `-webkit-box` + `line-clamp` es lo que hace que
     la caja mida por CONTENIDO (una descripción corta sigue ocupando un solo
     renglón) y a la vez no pueda pasar de dos.
     ⚠️ El corte del navegador cae DONDE se acabe el ancho, o sea a mitad de
     palabra. Quien lo mueve a la frontera de palabra es `bspTvMenuDescWords`
     (bsp-tv.js), que recorta el TEXTO; esta regla es la red debajo: si el JS
     no corre, se ve como antes —cortado, no desbordado—, nunca roto. */
  display: -webkit-box; -webkit-line-clamp: var(--m-desclines); -webkit-box-orient: vertical;
}

/* ══ LA SONDA DE `bspTvMenuFlow` ═══════════════════════════════════════════
   La pinta el .razor (tiene que llevar el atributo de scope de Blazor: un nodo
   creado por JS no lo lleva y NO recibe ninguna de estas reglas — medido, las
   cinco medidas salían cero); el JS solo la mide.
   Lleva `--m-row: var(--m-rowmin)` para leer el MÍNIMO legible sin pelearse con
   el px que el servidor escribe en `.tvm-flow`. Fuera del flujo del layout
   (`position:absolute`) para no mover ni un píxel de lo que se ve. ══ */
.tvm-probe[b-j722dptvm5] {
  position: absolute; left: 0; top: 0; width: 100%;
  visibility: hidden; pointer-events: none; z-index: -1; --m-row: var(--m-rowmin);
}
/* ══ ÍTEM 1274 ② (Tito, 2026-09-13) — LA SONDA MEDÍA LA DESCRIPCIÓN DE LA FOTO
   A **UN** RENGLÓN, Y CON ESE NÚMERO SE CALCULA EL SUELO DE LA FOTO.
   `bspTvMenuFlow` hace `suelo = foto/razón + banda + DESC + 2·gap` leyendo esta
   sonda; la sonda lleva una sola «X» ⇒ un renglón. Con el techo de dos, la
   descripción REAL puede ocupar dos ⇒ el suelo salía CORTO por un renglón, y un
   suelo corto es el lado peligroso: deja pasar como «aquí cabe una foto» un
   hueco donde la foto ya no cabe entera.
   ⇒ La sonda mide el PEOR CASO del techo, que es lo que el suelo significa.
   Se fija por ALTO y no metiendo texto largo: el alto de N renglones es
   N × line-height × font-size y no depende de qué letras caigan ni de lo ancha
   que sea la columna — un texto «suficientemente largo» sí depende, y a 1920
   podría caber en un renglón y romper la medida sin avisar.
   🔒 `-webkit-line-clamp` se apaga aquí a propósito: con el clamp puesto y una
   sola «X», la caja volvería a medir un renglón y esta regla no serviría. ══ */
.tvm-probe .tvm-fg-desc[b-j722dptvm5] {
  display: block; -webkit-line-clamp: none;
  height: calc(var(--m-fgdesclines) * var(--m-fgdesclh) * 1em);
}
/* ══ 2026-09-11 — OPCIÓN B, CON LUZ VERDE DE HÉCTOR: DOS RENGLONES DONDE
   CABEN, Y LA FILA-SONDA SIGUE SIENDO LA CONSTANTE DE SIEMPRE ══════════════
   MARCADOR-VARIANTE-B-1263 — esta cadena existe para que el arnés pueda decir
   QUÉ VARIANTE está viva leyendo el CSS que sirve la app, no el fichero del
   disco. Si se borra, la medición pierde su sello.

   AQUÍ VIVIÓ LA OPCIÓN A, del 2026-09-10 al 2026-09-11. La sonda pasaba a alto
   automático y el .razor le metía descripción, así que `bspTvMenuFlow` medía
   `línea1 + hueco + 2 renglones` y el mínimo que el motor reparte subía de
   58,3 px a 94,6 a 1080p. No era un fallo: hacía exactamente lo que prometía.
   NO SE QUEDA, y el motivo está MEDIDO sobre las SEIS tiendas de producción
   (tools\arnes-1263b-tito), no opinado:
     · A le ENCOGE la foto grande a la tienda de 9 servicios — la CAJA pasa de
       461×618 a 461×426 en ES a 1080p, −31,1 % de área — y a DA PR (461×618 →
       461×488). Son 8 de 24 mediciones.
       🔴 CORREGIDO EL 2026-09-11 (Vera lo levantó, Tito lo remidió): ese
       −31,1 % es de la CAJA, y la caja NO es lo que ve el cliente. Con
       `object-fit: contain` (más abajo, `.tvm-foto-grande > img`) la caja que
       no casa con la razón de la imagen NO recorta: pone BANDAS DE PORCELANA.
       Medido sobre las capturas del arnés — la foto PINTADA pasa de 460×616 a
       316×424, con 73 px de porcelana a cada lado, y su razón se conserva
       (0,7453 contra 0,7467 natural: es letterbox, no recorte).
       ⇒ LO QUE DE VERDAD PIERDE EL CLIENTE ES −52,7 % DE ÁREA DE FOTO en ES y
       −58,6 % en EN, no el −31,1 % de la caja. El número bueno es PEOR.
     · B no mueve NI UN PÍXEL del reparto: 0 tableros pierden la foto, 0 la
       encogen, 0 pasan a paginar.
     · Y no compraba nada a cambio: A y B dejan EXACTAMENTE las mismas 2
       descripciones cortadas de 50, y ninguna de las dos se come ya un `$`.
   ⇒ El segundo renglón se paga con el alto que la fila YA tiene — la fila real
   de la tienda de 9 servicios mide 75,5 px cuando el mínimo es 58,3 — en vez
   de pedirle al motor que suba el suelo de TODAS las filas. Lo que A compraba
   con la foto de un cliente, B lo saca del aire que ya sobraba.

   🔑 LO QUE ESTO NO ES: no es renunciar a las dos líneas. `--m-desclines`
   sigue en 2 (el clamp de `.tvm-rdesc`, arriba) y `bspTvMenuDescWords` sigue
   cortando en frontera de palabra. Lo ÚNICO que no se hace es subir el mínimo.
   ⚠️ AL QUE VENGA A «ARREGLAR» ESTO: si le pones alto automático a la
   fila-sonda para que mida una fila con descripción, estás reconstruyendo A —
   y con ella el encogido de la foto de un cliente real. Mide las seis tiendas
   antes, y mide el RESULTADO, no el cambio. ══ */
.tvm-probe-promo[b-j722dptvm5] { height: var(--promo-ph); }
.tvm-card[b-j722dptvm5] {
  /* 19:45 (LV-43): blanco opaco — `.tvm-frost-on` (arriba) lo cambia a
     cristal sin tocar esta regla, cuando alguien lo active. Borde/sombra
     de v5 `.service-section` (20:00). */
  background: var(--card); border: .093vh solid #e0e6f0;
  border-radius: .833vh; overflow: hidden;
  box-shadow: 0 .185vh .648vh rgba(10,34,80,.04);
  display: flex; flex-direction: column;
}
/* ══ TODOS los títulos de bloque — MISMO estilo compartido. 🔄 20:00 (v5,
   LV-43): SUPERSEDE el "borde navy .16vh" de la corrección 18:30 — v5
   `.section-title` no lleva borde: es SOLO el degradado + resalte inset.
   `--band-end` (arriba) es el pedido explícito de Héctor para mover el fin
   del degradado en un sitio — mismo valor que v5, #85006c. ══ */
.tvm-band-title[b-j722dptvm5], .tvm-card-band[b-j722dptvm5], .tvm-blk-band[b-j722dptvm5], .tvm-fg-band[b-j722dptvm5] {
  color: #fff;
  background: linear-gradient(110deg, var(--grad-start) 0%, #c90036 52%, var(--band-end) 100%);
  box-shadow: inset 0 .093vh 0 rgba(255,255,255,.25), 0 .278vh .833vh rgba(185,0,57,.16);
}
.tvm-card-band[b-j722dptvm5] {
  /* 18:00 (Héctor): fuera el uppercase — pinta TÍTULOS del diccionario (ya en
     Mayúscula Inicial, p.ej. "Aceptamos") Y nombres de categoría de DATOS
     ("como los escribió el dueño" — el `.ToUpperInvariant()` que los forzaba
     se quitó en `TvMenuData.cs`). Con uppercase aquí, el CSS deshacía esa
     corrección igual. */
  flex: 0 0 var(--m-band);
  display: flex; align-items: center; gap: .74vh; padding: 0 calc(var(--m-pad)*.8);
  font-size: var(--m-bandfs); font-weight: 800; letter-spacing: .1em;
}
.tvm-card-band i[b-j722dptvm5] { font-size: calc(var(--m-bandfs) * 1.05); }
/* ÍTEM 1150: el cuerpo toma TODO lo que la banda deja en la tarjeta (antes era
   alto de contenido y podía medir MÁS que la tarjeta: 522 dentro de 413, medido)
   — así `clientHeight` del cuerpo ES el espacio real para filas, y lo que
   `bspTvMenuCapacity` mide es exactamente lo que se ve. `overflow:hidden` aquí
   es la red, no el mecanismo: con la capacidad medida no debería recortar nada. */
/* LV-94: el cuerpo es una columna flex con `.tvm-col` dentro; las filas son
   ítems de alto fijo (`--m-row`, que escribe el servidor) y no encogen. El
   sobrante ya NO se rellena aquí: la foto grande vive al final de la
   COLUMNA del flujo, no dentro de una tarjeta. */
.tvm-card-body[b-j722dptvm5] { padding: calc(var(--m-pad)*.5) 0; flex: 1 1 auto; min-height: 0; overflow: hidden; display: flex; flex-direction: column; }



.tvm-row[b-j722dptvm5] {
  /* ÍTEM 1166 v3: `height` además de `min-height` — la fila es EXACTAMENTE la
     derivada (`--m-row`), ni más por un contenido raro ni menos: es lo que hace
     que 8 filas sean 8 y no 7,9 (instrumento v3, `.tvm-row`). */
  min-height: var(--m-row); height: var(--m-row);
  /* LV-67 «casi tocando las líneas»: la foto = 74 % de la fila DERIVADA (71,9 px a
     1080p, 51 a 768p — Kahlo v3 §3). Aquí y no en el slide: ver el aviso arriba. */
  --m-photo: calc(var(--m-row) * .74);
  display: flex; align-items: center; gap: calc(var(--m-pad)*.6);
  padding: 0 calc(var(--m-pad)*.7); border-bottom: .093vh solid var(--hair);
}
.tvm-card-body .tvm-row:last-child[b-j722dptvm5] { border-bottom: 0; }
.tvm-num[b-j722dptvm5] {
  /* v5 `.extra-number`: número en pastilla navy con degradado, no rojo plano. */
  flex: 0 0 var(--m-num); width: var(--m-num); height: var(--m-num);
  background: linear-gradient(145deg, #122d6a, #061643); color: #fff; border-radius: .37vh;
  box-shadow: inset 0 .093vh 0 rgba(255,255,255,.12);
  display: flex; align-items: center; justify-content: center;
  font-size: var(--m-numfs); font-weight: 800; line-height: 1;
}
.tvm-thumb[b-j722dptvm5] {
  flex: 0 0 var(--m-photo); width: var(--m-photo); height: var(--m-photo);
  border-radius: .55vh; overflow: hidden; background: #E7E2D8;
}
/* R-472 (ítem 1164): `object-position: 50% 0`. Sin él el navegador centra al
   50 % y, en un hueco cuadrado (f = 0,747), el recorte empieza en el 12,65 % de
   la foto — por debajo de la coronilla de LAS 153 fotos de retrato del catálogo
   (Kahlo midió el aire sobre la cabeza entre 2,08 % y 11,41 %). Lo que se
   destaca es el RECORTE, no la cara. */
.tvm-thumb img[b-j722dptvm5] { width: 100%; height: 100%; object-fit: cover; object-position: 50% 0; display: block; }
.tvm-thumb-none[b-j722dptvm5] {
  background: var(--navy); display: flex; align-items: center; justify-content: center;
  color: #fff; font-size: calc(var(--m-photo)*.42);
}
.tvm-rtext[b-j722dptvm5] { min-width: 0; flex: 1; display: flex; align-items: center; }
/* ═══ RETOQUES MENU TV (Fabio, 2026-09-03, tras QA de Eugenio) -- SIN numero de item (el 1090 ya lo usa tools/arnes-1090-backfill; ver COLA-DE-TRABAJO para el numero real cuando se le asigne) ═══
   `.tvm-rname` truncaba el BLOQUE ENTERO (nombre + pastillas Popular/Extra
   como <span> hijos) con un solo `text-overflow: ellipsis` sobre TODO el
   contenido en linea -- con un nombre largo, el navegador recorta desde el
   final y se lleva la pastilla completa por delante (medido a 1920 y 1366;
   le pasaba justo al servicio que R-460 promociona, que es el que mas
   necesita la pastilla "Popular" visible). Ahora `.tvm-rname` es el
   contenedor flex: el NOMBRE vive en `.tvm-rname-text` (el unico que
   trunca, con su propio `min-width:0`) y las pastillas (`.tvm-tag`, mas
   abajo) llevan `flex-shrink:0` -- el que se recorta es el texto, nunca
   la pastilla. */
.tvm-rname[b-j722dptvm5] {
  min-width: 0; color: var(--navy); font-size: var(--m-name); font-weight: 700;
  line-height: 1.05; display: flex; align-items: baseline; gap: .4vh;
}
.tvm-rname-text[b-j722dptvm5] {
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Hallazgo de verificación (Jack): la tarjeta mide ~476px a 1366×768, menos
   que los ~1024px de la lista de una columna que reemplazó — un nombre de
   28+ caracteres que cabía ahí ya no cabe aquí. La letra baja ANTES de que
   el navegador tenga que recortar con "…" (ver RowNameClass en el .razor). */
.tvm-rname-s[b-j722dptvm5] { font-size: calc(var(--m-name) * .82); }
.tvm-rname-xs[b-j722dptvm5] { font-size: calc(var(--m-name) * .68); }
/* línea de puntos que ata nombre→precio — convención de carta de verdad */
.tvm-lead[b-j722dptvm5] {
  flex: 1; min-width: 1vh; border-bottom: .19vh dotted rgba(20,34,78,.32);
  margin: 0 calc(var(--m-pad)*.3); transform: translateY(.4vh);
}
.tvm-rprice[b-j722dptvm5] {
  color: var(--navy); font-size: var(--m-price); font-weight: 800; line-height: 1;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

.tvm-empty[b-j722dptvm5] {
  height: 100%; display: flex; flex-direction: column; align-items: center;
  justify-content: center; text-align: center; gap: 1vh; color: var(--navy);
}
.tvm-empty-t[b-j722dptvm5] { font-size: calc(var(--m-name)*1.4); font-weight: 800; }
.tvm-empty-s[b-j722dptvm5] { font-size: var(--m-shopfs); color: var(--ink-2); font-weight: 600; }

/* ── ACEPTAMOS — LV-45 (Héctor, imagen 20:45): UNA TIRA HORIZONTAL sobre el
   panel BLANCO, ícono + etiqueta navy debajo, SIN tiles navy — ya no es una
   rejilla de 4 columnas de cajas: los 11 métodos van en UNA fila, sin caja
   ni sombra por detrás, apoyados directo en el blanco de `.tvm-card`.
   `flex-wrap:wrap` es la RED DE SEGURIDAD (cada hueco se apaga con dignidad,
   nunca rompe la rejilla), no el caso normal — 11 columnas caben holgado en
   el ancho del panel a 1920/1366 (medido, ver el informe del ítem); si algún
   día una tienda tuviera más métodos, se reparten en 2 filas antes que
   desbordar.
   🔴 ÍTEM 1104 (Kahlo, 2026-09-03, medido) — LOS 11 PNG DE KAHLO ESTÁN
   RENDERIZADOS PARA FONDO BLANCO: 9 de 11 no llegan a 3:1 de contraste sobre
   navy (medido por ella) — por eso viven SOBRE BLANCO, nunca sobre un tile
   navy como el ícono de Bootstrap de antes. ── */
/* ══ ÍTEM 1169 / LV-73 (2026-09-05) — REJILLA PAREJA, 4 POR LÍNEA. Héctor con la
   captura delante: *«se ven regados»* (7·3·1) y *«yo hubiera puesto 4 por
   línea»*. Ya no envuelve por ANCHO con etiquetas de largo distinto: cada tile
   mide EXACTAMENTE (100 % − gaps) / `--pc` (`--pc` = min(4, nº de métodos), lo
   escribe el .razor desde `TvMenuData.PayGridColumns`) y la última fila queda
   CENTRADA (`justify-content:center`) ⇒ 11 métodos = 4·4·3, 4 = una fila, 2 =
   dos celdas anchas. El ícono se DERIVA de la celda (`--m-payico`, lo sube
   `bspTvMenuLayout` hasta min(0,62×celda, techo)) en vez de ser fijo.
   Y la banda (`.tvm-blk-band`, más abajo) reserva su alto con `flex:0 0 auto`:
   los íconos ya no se montan sobre el título (defecto de la captura de LV-73).
   El 1104 sigue vigente: PNG sobre BLANCO, `.pay-ico` dimensiona por ALTURA. ══ */
.tvm-pay-grid[b-j722dptvm5] {
  padding: calc(var(--m-pad)*.55) calc(var(--m-pad)*.5);
  display: flex; flex-wrap: wrap; flex-direction: row;
  justify-content: center; align-content: center; flex: 1 1 auto; min-height: 0;
  gap: calc(var(--m-pad)*.45) calc(var(--m-pad)*.35);
}
.tvm-pay-tile[b-j722dptvm5] {
  --pc: 4;
  background: none; border: 0; box-shadow: none; border-radius: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .30vh; padding: 0; min-height: 0;
  /* 🔴 `min-width: 0` (Tito, 2026-09-10 — el arreglo de CSS del [[1251]], que
     me crucé midiendo el techo del ícono; el TEXTO de esa etiqueta sigue
     esperando palabra de Héctor y NO se toca aquí). Un ítem flex sin esto lleva
     `min-width: auto` = su tamaño mín-content: una etiqueta larga («Tarjeta de
     crédito/débito») ENSANCHA la celda por encima de su `flex-basis` y rompe la
     rejilla de min(4,n) columnas. Y de paso arregla por construcción lo que
     `bspTvMenuLayout` medía con suerte: con esto TODAS las celdas miden lo
     mismo, así que el techo del ícono ya no depende de cuál vaya primero. */
  min-width: 0;
  flex: 0 0 calc((100% - (var(--pc) - 1) * var(--m-pad) * .35) / var(--pc));
}
.tvm-pay-tile img.pay-ico[b-j722dptvm5] { height: var(--m-payico); width: auto; max-width: 100%; object-fit: contain; }
.tvm-pay-tile i[b-j722dptvm5] { font-size: var(--m-payico); line-height: 1; color: var(--navy); }
.tvm-pay-tile span[b-j722dptvm5] {
  color: var(--navy); font-weight: 700; font-size: var(--m-paylab);
  text-align: center; line-height: 1.05; max-width: 100%;
}
/* «Aceptamos» como 3.ª tarjeta de la columna derecha (LV-68) ABSORBE EL SOBRANTE
   (LV-74, Héctor 2026-09-05 18:24: *«nunca espacios vacíos en el tope… agrandaría
   el cuadro de Aceptamos»*): `flex:1 1 auto` — la única tarjeta de la columna que
   crece. ⚠️ Con eso la "holgura" de la columna es 0 SIEMPRE por construcción, así
   que el tamaño del ícono NO puede salir de medir la columna (así falló la 1.ª
   versión del 05-sep: 38,9 px fijos y con 11 métodos la tarjeta se encogía bajo
   su contenido, íconos 24,7 px sobre la banda). `bspTvMenuLayout` lo deriva de
   la CAJA de la rejilla (`.tvm-pay-grid`: alto ÷ filas − etiqueta) y mueve la
   foto de la Recomendación para que la caja dé: crece si los íconos ya tocan su
   techo (v3 §2.3), encoge si no cabe el piso. `min-height:0` es lo que permite
   encoger sin desbordar; el fondo nunca queda vacío porque la caja llega al
   borde. */
.tvm-side .tvm-pay-blk[b-j722dptvm5] { flex: 1 1 auto; min-height: 0; }

/* ÍTEM 1265 — EL PIE PASÓ DE `flex` A `grid`, Y NO ES COSMÉTICA.
   Con `display:flex; justify-content:space-between` la posición de cada hijo
   depende de CUÁNTOS HIJOS HAY. Al quitar el rótulo del idioma (1265) el pie
   se queda con dos hijos (indicador de página + marca) o con uno solo (la
   marca, que es el caso de todo menú de UNA página) — y con UN hijo,
   `space-between` lo pega al borde IZQUIERDO. Medido en la copia aislada:
   `BarberShopPro` saltaba de x=1365 a x=512 en 1920x1080.
   Con la rejilla `1fr auto 1fr` la posición la fija la COLUMNA, no el número
   de hermanos: la marca vive en la 3.ª pegada a la derecha y el indicador de
   página en la 2.ª, centrado de verdad (con `space-between` quedaba 28 px a
   la izquierda del centro). La 1.ª columna queda vacía a propósito: es el
   hueco que ocupaba el idioma, y es lo que mantiene la barra sin descuadrar.
   ⚠️ Si algún día se añade un CUARTO elemento al pie, dale su
   `grid-column` explícita: sin ella caerá en la 1.ª columna. */
.tvm-foot[b-j722dptvm5] {
  flex: 0 0 var(--m-foot); background: var(--navy); color: #fff;
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  padding: 0 var(--m-pad); font-size: var(--m-footfs);
}
.tvm-brand[b-j722dptvm5] { opacity: .7; font-weight: 600; letter-spacing: .06em;
             grid-column: 3; justify-self: end; }

/* ══ ZONA 2 · LA COLUMNA DE ACCIÓN — LV-45 (Héctor, imagen 20:45): «La
   Recomendación De Hoy» (banda + tarjeta BLANCA) y «Reserva Tu Cita» (banda +
   tarjeta blanca, QR IZQUIERDA/texto DERECHA) — ya NO son tarjetas navy con
   resplandor (v5); mismo blanco/borde gris que las categorías del menú
   (`.tvm-card`). ÍTEM 1087 sigue mandando en el DATO: hasta CUATRO bloques
   (promocionado · wifi · premium · QR), cada uno se apaga solo sin su dato
   (LV-34); sin QR (`Qr` null: NI citas NI fila) el bloque de QR desaparece y la
   recomendación crece — CORREGIDO POR EL 1163: aquí decía «sin reservas
   (QrDataUri null)», y las dos mitades eran falsas desde ese ítem — el símbolo
   `QrDataUri` ya no existe (hoy es el récord `QrPanel Qr`, con su `Kind`) y el
   bloque tampoco depende ya solo de las reservas: con las reservas apagadas y la
   FILA encendida el QR sigue ahí. Lo cazó Eugenio (8) certificando el 1163, y mi
   propio conteo de apariciones de `QrDataUri` decía 2 cuando eran 3: la tercera
   era ESTA, en el `.css`, que ningún grep de `.cs`/`.razor` toca —
   `flex-end`+`gap` no mueve nada con un único hijo, y
   sin NINGUNO la columna entera desaparece y la rejilla del menú absorbe el
   100% del ancho. Wifi/Premium (placeholder de estilo, sin luz verde de
   layout propio) heredan el mismo blanco por coherencia mientras no exista
   un mockup dedicado. ══ */
/* ══ ÍTEM 1166 / LV-68 / LV-73 (2026-09-05) — TRES TARJETAS APILADAS: la
   Recomendación ARRIBA, el QR (cita o fila, 1163) EN MEDIO y «Aceptamos» ABAJO.
   Héctor: *«a la derecha debajo del qr code»* y *«esta versión se ve muy bien»*.
   · 22 % → 26 % (`--side-w`, Kahlo v2): con 22 % el título «La Recomendación De
     Hoy» quedaba a 6 px de envolver y cualquier cosa lo volcaba — el mecanismo
     del −1,19 px que Eugenio midió solo en inglés. A 26 % ninguno de los dos
     idiomas envuelve y las 11 etiquetas de pago se leen.
   · `flex-end` → `flex-start`: los bloques se apilan desde arriba (LV-74: *«nunca
     espacios vacíos en el tope»*); el sobrante lo absorbe «Aceptamos» (abajo,
     `flex:1`) y `bspTvMenuLayout` lo reparte entre íconos y foto. Ya NO se
     centra la columna (era la v3 §5, descartada por Héctor en LV-74).
   · `.tvm-blk { flex: 0 0 auto }`: ningún bloque se comprime en silencio (el
     hallazgo v1 §5 de Kahlo: la Recomendación empujaba la columna 33 px por
     debajo del borde y el flex la aplastaba sin avisar). ══ */
.tvm-side[b-j722dptvm5] {
  flex: 0 0 var(--side-w); min-width: 0; display: flex; flex-direction: column;
  justify-content: flex-start; gap: var(--m-pad);
}
.tvm-blk[b-j722dptvm5] {
  background: var(--card); border: .093vh solid #e0e6f0; border-radius: 1.111vh;
  box-shadow: 0 .185vh .648vh rgba(10,34,80,.06);
  overflow: hidden; display: flex; flex-direction: column; flex: 0 0 auto;
}

/* ── EL PAQUETE PROMOCIONADO (R-460) — foto CUADRADA, nombre navy bold,
   precio grande navy con una RAYITA ROJA debajo (LV-45). 🔴 SIN "AHORRAS":
   no hay fuente real de descuento en el dato (`TvMenuData.PromotedPackage`
   no trae un campo de ahorro — ver el comentario largo del .razor) ⇒ el
   SELLO DE AROS (v5/1045/1082, "solo para paquetes con «Ahorras»") queda
   RETIRADO del render por defecto — inventar el ahorro sería un número
   falso (LV-34); el día que exista esa fuente, el sello vuelve condicionado
   a ella, no a gusto del CSS. ── */
.tvm-promo-body[b-j722dptvm5] { display: flex; flex-direction: column; align-items: center; gap: .74vh; padding: var(--m-pad); }
/* ══ ÍTEM 1111 (Héctor en PROD, "un cuadro dentro de un cuadro como que no
   fluye esa foto asi") — SIN MARCO INTERIOR: antes la foto llevaba su
   PROPIO borde/fondo (`#e0e6f0`) dentro de la tarjeta blanca, que ya tiene
   su propio borde — dos cuadros anidados. Ahora la foto ESCAPA el padding
   de `.tvm-promo-body` (márgenes negativos exactos a `var(--m-pad)`) y
   llega AL RAS de los cuatro lados de la tarjeta — `.tvm-blk` de arriba ya
   tiene `overflow:hidden`, así que el propio borde redondeado de la
   tarjeta recorta la foto sin que la foto necesite su radio ni su borde
   propios. Solo UN título por tarjeta (la banda `.tvm-blk-band` de arriba;
   no hay una segunda cinta encima de la foto — nunca la hubo en este
   marcado, es la corrección del v5/PROD viejo). Ancho: mismo criterio que
   `layout-v7-medidas.md` §5 (una sola magnitud, `aspect-ratio:1`, nunca
   vh+vw independientes) pero ahora AL 100% DEL CONTENEDOR en vez de un
   9.33vw fijo — "que ocupe el ancho de la tarjeta", no un cuadro angosto
   centrado. ══ */
/* 🔄 ÍTEM 1166 / LV-68 (2026-09-05) — LA FOTO DE LA RECOMENDACIÓN ES LA MONEDA:
   deja de medir el 100 % del ancho (+ el al-ras del 1111) y pasa a
   `min(100%, var(--promo-ph))`, cuadrada, CENTRADA: `--promo-ph` arranca en
   20,5vh y `bspTvMenuLayout` la mueve entre 8vh y el ancho de la columna según
   lo que sobre tras el QR (intacto) y los íconos de pago — 284 px con 4 métodos,
   193 con 11 (Kahlo v3 §4). Héctor lo aceptó al elegir el tablero «capada al
   55 %» (LV-67/LV-73). `flex:0 0 auto; min-height:0`: la foto de RETRATO ya no
   estira la caja por su alto intrínseco (v1 §5: +35 %, columna fuera del borde).
   El 1111 ("un cuadro dentro de un cuadro no fluye") se respeta en lo que
   importaba: SIN borde ni fondo propio — solo el radio, sobre el blanco de la
   tarjeta; al-ras ya no puede ser porque la foto es más chica que la tarjeta.
   R-472: sigue anclada arriba (`object-position: 50% 0`, abajo) — encogerla no
   puede cortar la coronilla; el tamaño aparente lo confirma Oller en el set real. */
.tvm-promo-photo[b-j722dptvm5] {
  width: min(100%, var(--promo-ph)); aspect-ratio: 1;
  flex: 0 0 auto; min-height: 0; margin: 0 auto;
  border-radius: .7vh; overflow: hidden;
  background: #E7E2D8;
  display: flex; align-items: center; justify-content: center; color: var(--navy); font-size: 3vh;
}
/* R-472 (ítem 1164) — ESTA es la pieza más grande del tablero y NUNCA llevó
   `object-position`: el navegador centraba al 50 % y le cortaba la coronilla a
   las 153 fotos de retrato, en los 8 ángulos. No lo destapó el ítem del foco
   del relleno: salió de medir TODAS las superficies que recortan. */
.tvm-promo-photo img[b-j722dptvm5] { width: 100%; height: 100%; object-fit: cover; object-position: 50% 0; }
/* ── ÍTEM 1100/R-439 — EL GIRO 360°. Sin sprite: los 8 `<img>` se apilan
   (`position:absolute`, mismo cuadro, cámara fija — R-423) y CADA UNO se
   "enciende" 0.8 s por ciclo de 6.4 s, con su propio `animation-delay`
   NEGATIVO (fijado por el `.razor`, espaciado exacto de 0.8 s) para que
   entren en fila sin JS. `steps(1)` en vez de una curva: el cambio de
   ángulo es un SALTO, no un fundido — igual que el `steps(8)` de un sprite
   de verdad. `prefers-reduced-motion` congela en el ángulo #1 (mismo
   mecanismo que el poste `pole-spin.webp`/`pole-still.webp`, R-441). ── */
.tvm-promo-spin[b-j722dptvm5] { position: relative; }
.tvm-promo-spin img[b-j722dptvm5] {
  /* R-472 (ítem 1164): los 8 ángulos se anclan arriba igual que la foto quieta
     — si no, el giro enseñaría 8 coronillas cortadas en vez de una. */
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 0;
  opacity: 0; animation: tvm-spin-flip-b-j722dptvm5 6.4s steps(1) infinite;
}
@keyframes tvm-spin-flip-b-j722dptvm5 {
  0%, 12.49% { opacity: 1; }
  12.5%, 100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .tvm-promo-spin img[b-j722dptvm5] { animation: none; opacity: 0; }
  .tvm-promo-spin img:first-child[b-j722dptvm5] { opacity: 1; }
}
/* Tamaño FIJO (no proporcional a --m-name de la fila): `layout-v7-medidas.md`
   §5 mide 1.85vh para el nombre de la recomendación — desacoplado a
   propósito para que un ajuste futuro del tamaño de fila no encoja esta
   tarjeta, que es la pieza más grande de la columna derecha. */
.tvm-promo-name[b-j722dptvm5] { color: var(--navy); font-weight: 800; text-align: center; font-size: 1.85vh; line-height: 1.15; }
/* ÍTEM 1111 — CORRECCIÓN (Héctor, 2026-09-04: "la línea roja debajo del
   precio no se ve bien") — SIN rayita: precio limpio, separación por AIRE
   (el `gap`/`margin` de abajo, sin el `::after` que la dibujaba). Ningún
   sello por defecto tampoco (LV-34: sin fuente real de "Ahorras" — ver el
   comentario de `.tvm-promo-body` arriba). */
.tvm-promo-price-wrap[b-j722dptvm5] {
  width: 100%; margin: .5vh auto 0; display: flex; flex-direction: column; align-items: center; gap: .46vh;
}
/* Fijo, mismo motivo que .tvm-promo-name — `layout-v7-medidas.md` §5: 3.15vh. */
.tvm-promo-price[b-j722dptvm5] { color: var(--navy); font-weight: 900; font-size: 3.15vh; line-height: 1; font-variant-numeric: tabular-nums; }

/* ── WIFI / PREMIUM — mismo molde de tarjeta que el QR, texto en vez de
   imagen. Placeholder de estilo, mismo criterio que arriba. ── */
.tvm-wifi-body[b-j722dptvm5], .tvm-premium-body[b-j722dptvm5] { padding: var(--m-pad); color: var(--navy); }
.tvm-wifi-row[b-j722dptvm5] { display: flex; justify-content: space-between; gap: .74vh; font-size: calc(var(--m-name)*.55); padding: .3vh 0; }
.tvm-wifi-row span[b-j722dptvm5] { opacity: .75; font-weight: 600; }
.tvm-wifi-row b[b-j722dptvm5] { font-weight: 800; font-variant-numeric: tabular-nums; }
.tvm-premium-body[b-j722dptvm5] { text-align: center; font-weight: 700; font-size: calc(var(--m-name)*.6); line-height: 1.25; }

/* ── POPULAR / EXTRA — pastillas chicas junto al nombre del servicio, EN LA
   REJILLA (no en la columna). Placeholder de estilo. ── */
.tvm-tag[b-j722dptvm5] {
  /* RETOQUE MENU TV (Fabio, 2026-09-03): flex-shrink:0 -- `.tvm-rname` (arriba) ahora es flex y el gap
     ya separa del nombre, `margin-left` sobra; sin flex-shrink:0 el flexbox
     encogía la pastilla igual que el texto en vez de dejarla intacta. */
  display: inline-block; flex-shrink: 0; vertical-align: middle;
  font-size: calc(var(--m-name)*.32); font-weight: 800; letter-spacing: .03em;
  padding: .18vh .6vh; border-radius: 2vh; color: #fff; white-space: nowrap;
}
.tvm-tag-pop[b-j722dptvm5] { background: var(--rojo); }
.tvm-tag-extra[b-j722dptvm5] { background: var(--navy); }
.tvm-blk-band[b-j722dptvm5] {
  /* 18:00 (Héctor): fuera el uppercase. ⚠️ Este bloque pinta `tv_welcome_cta_book`
     — clave COMPARTIDA con el slide de bienvenida de TvBoardView (grep:
     TvBoardView.razor:523) — "no toques otras pantallas" ⇒ el VALOR
     ("Reserva tu cita") no se tocó aquí. Sin uppercase queda en minúscula
     de frase, no en Mayúscula Inicial como las demás bandas — reportado,
     no resuelto: cambiar el valor movería la otra pantalla también.
     ══ LV-46 (2026-09-04) — `min-height:var(--m-band)` + flex SUMADOS aquí:
     antes esta banda no tenía una altura fija (solo padding+letra, ~3.4vh),
     mucho más baja que `.tvm-card-band` (6.6vh fijo). Con el ícono glossy
     nuevo (0.9×--m-band, MISMA fórmula que categoría/Aceptamos — LEY 14, no
     se inventa una 3.ª proporción) hacía falta la MISMA altura o el ícono
     desbordaba la banda vieja. "Reserva tu cita" (`.tvm-qr-blk .tvm-blk-band`)
     sigue SIN ícono (decisión del encargo: solo texto, como en PROD) así que
     crece igual mero por consistencia visual entre las 4 bandas de la
     columna derecha — no rompe nada, ya estaba centrada. */
  min-height: var(--m-band);
  /* ÍTEM 1169 / LV-73: la banda RESERVA su alto — sin `flex-shrink:0`, con la
     tarjeta de pago absorbiendo el sobrante, los íconos se montaban sobre el
     título «Aceptamos» (captura de Héctor 17:24). */
  flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center; gap: .6vh;
  font-size: var(--m-bandfs); font-weight: 800;
  letter-spacing: .1em; padding: .83vh var(--m-pad); text-align: center; line-height: 1.1;
}
/* ── RESERVA TU CITA — LV-45: QR a la IZQUIERDA, texto a la DERECHA (antes
   apilados). `flex-shrink:0` en la imagen para que el texto largo nunca la
   encoja; el texto trunca por LÍNEAS (`-webkit-line-clamp`), nunca desborda
   la tarjeta. ── */
/* ══ ÍTEM 1112 (Héctor en PROD: "es mejor poner la oración debajo del QR,
   así el QR tiene más espacio para que se vea mejor, se puede agrandar")
   — CORRIGE el QR-izquierda/texto-derecha de LV-45: vuelve a APILADO, QR
   centrado y grande, texto DEBAJO centrado. El QR lleva la marca en el
   centro (`RebuildStoreQrs`/`bsp-qr` — logo superpuesto) ⇒ el margen de
   silencio (padding blanco alrededor, ≥4 módulos) es lo que hace que un
   lector real distinga el borde del código pese al logo — se generosa
   a propósito, no se recorta para "ahorrar espacio". Tamaño: `min(80%,
   20vh)` — crece con la tarjeta pero nunca desborda un panel angosto a
   1366px; verificado con un decodificador QR real (`cv2.QRCodeDetector`,
   Playwright propio) contra el PNG servido a 1920 Y 1366 (ver informe del
   ítem — el logo central no rompe el scan con este margen). ══ */
.tvm-qr[b-j722dptvm5] { display: flex; flex-direction: column; align-items: center; gap: .9vh; padding: var(--m-pad); }
.tvm-qr-img[b-j722dptvm5] {
  flex: 0 0 auto; width: min(80%, 20vh); aspect-ratio: 1; background: #fff; border-radius: .74vh; padding: 1.1vh;
  border: .093vh solid #e0e6f0;
}
.tvm-qr-img img[b-j722dptvm5] { display: block; width: 100%; height: 100%; }
.tvm-qr-tx[b-j722dptvm5] {
  min-width: 0; color: var(--navy); text-align: center;
  font-size: 1.45vh; font-weight: 700; line-height: 1.25;
  /* ÍTEM 1166 (Kahlo v2, sobre la medida de Vera): la frase RESERVA DOS
     RENGLONES SIEMPRE — en inglés el texto de citas ya envuelve a 2 líneas y
     el de español no; así el salto 1↔2 cuesta 0 px en vez de tapar el precio
     de la Recomendación. El clamp de 4 sigue como red, nunca como caso normal. */
  min-height: calc(2 * 1.25 * 1.45vh);
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden;
}
/* /Components/Shared/VirtualModelPicker.razor.rz.scp.css */
/* ══ R-423 · LV-24 — EL PICKER DE MODELOS VIRTUALES ═══════════════════════════
   Traduce `MBS\kahlo\mockup-picker-modelos.html` (el mockup ES la spec) al
   modal real. Mismas convenciones de overlay que el modal de certificación del
   ítem 725 (scrim 1080 / modal 1081, radio 20px, cabecera navy con la raya del
   poste) — un solo lenguaje de modales en toda la casa.

   📱 MEDIDO A 390px: el grid es de 2 columnas y las tarjetas quedan en ~170px,
   que es el "blanco generoso" del mockup. El modal usa `min(430px, 100vw-16px)`
   para que a 390 ocupe el ancho útil sin sacar scroll horizontal. */

.vmp-scrim[b-uimjsqbn0l] {
    position: fixed;
    inset: 0;
    background: rgba(11, 19, 48, .55);
    backdrop-filter: blur(1.5px);
    z-index: 1080;
}

.vmp-modal[b-uimjsqbn0l] {
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: min(430px, calc(100vw - 16px));
    max-height: calc(100vh - 32px);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    z-index: 1081;
    background: #F7F8FB;
    border: 1px solid #D8D4C8;
    border-radius: 20px;
    box-shadow: 0 10px 24px rgba(20, 34, 78, .22);
}

/* Barra de la app: navy con la raya del poste al pie, igual que el 725. El ← /
   ✕ es un blanco de 44px (ley del dedo) aunque el glifo sea chico. */
.vmp-appbar[b-uimjsqbn0l] {
    position: relative;
    display: flex;
    align-items: center;
    gap: 8px;
    background: #14224E;
    color: #fff;
    padding: 10px 14px 12px;
    font-weight: 700;
    font-size: .95rem;
    flex: none;
}

    .vmp-appbar[b-uimjsqbn0l]::after {
        content: "";
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0;
        height: 4px;
        background: repeating-linear-gradient(135deg, #C8102E 0 12px, #FFFFFF 12px 24px, #14224E 24px 36px);
    }

.vmp-back[b-uimjsqbn0l] {
    background: transparent;
    border: 0;
    color: #fff;
    font-size: 1.25rem;
    line-height: 1;
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    border-radius: 8px;
}

    .vmp-back:hover[b-uimjsqbn0l], .vmp-back:focus-visible[b-uimjsqbn0l] { background: rgba(255,255,255,.14); }

.vmp-appbar-t[b-uimjsqbn0l] { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.vmp-body[b-uimjsqbn0l] { padding: 14px; overflow-y: auto; -webkit-overflow-scrolling: touch; }

.vmp-loading[b-uimjsqbn0l], .vmp-empty[b-uimjsqbn0l] {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 28px 12px;
    color: #5b6780;
    font-size: .9rem;
    text-align: center;
}

.vmp-head[b-uimjsqbn0l] { display: flex; align-items: flex-start; gap: 8px; margin-bottom: 10px; }
.vmp-lead[b-uimjsqbn0l] { font-weight: 800; color: #14224E; font-size: 1rem; flex: 1 1 auto; line-height: 1.25; }

/* LV-24 punto 4 — esta fila SOLO se pinta con más de una categoría; el
   componente decide, aquí solo vive su aspecto. */
.vmp-chips[b-uimjsqbn0l] { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 10px; }

.vmp-chip[b-uimjsqbn0l] {
    border: 1.5px solid #14224E;
    color: #14224E;
    background: #fff;
    border-radius: 999px;
    padding: .3rem .8rem;
    font-size: .8rem;
    font-weight: 700;
    min-height: 36px;
    cursor: pointer;
}

    .vmp-chip.on[b-uimjsqbn0l] { background: #14224E; color: #fff; }

/* Grid táctil de 2 columnas (LV-24 punto 3). */
.vmp-grid[b-uimjsqbn0l] { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }

.vmp-card[b-uimjsqbn0l] {
    background: #fff;
    border: 1.5px solid #d7dce6;
    border-radius: 12px;
    overflow: hidden;
    padding: 0;
    text-align: left;
    cursor: pointer;
    display: flex;
    flex-direction: column;
}

    .vmp-card.sel[b-uimjsqbn0l] { border-color: #C8102E; box-shadow: 0 0 0 2px rgba(200,16,46,.25); }
    .vmp-card:focus-visible[b-uimjsqbn0l] { outline: 3px solid #14224E; outline-offset: 2px; }

.vmp-card-foto[b-uimjsqbn0l] {
    position: relative;
    height: 130px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(150deg, #1b2f6b, #0d1737);
}

    /* R-472 (ítem 1164): el dueño elige el modelo por el CORTE. Sin
       `object-position` el hueco cuadrado empezaba en el 12,65 % y le cortaba
       la coronilla a las 153 fotos de retrato del catálogo. */
    .vmp-card-foto img[b-uimjsqbn0l] { width: 100%; height: 100%; object-fit: cover; object-position: 50% 0; display: block; }

.vmp-card-noimg[b-uimjsqbn0l] { color: #fff; font-size: 1.5rem; opacity: .8; }

/* El modelo YA asignado se marca con un check — el dueño reconoce el suyo sin
   leer (transparencia, LV-24 punto 2). */
.vmp-card-chk[b-uimjsqbn0l] {
    position: absolute;
    top: 6px;
    right: 6px;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: #0f7b45;
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 800;
    font-size: .85rem;
}

.vmp-card-meta[b-uimjsqbn0l] { padding: 7px 9px; display: flex; flex-direction: column; gap: 1px; }
.vmp-card-nm[b-uimjsqbn0l] { font-weight: 800; font-size: .85rem; line-height: 1.2; color: #16213d; }
.vmp-card-n2[b-uimjsqbn0l] { font-size: .72rem; color: #5b6780; }
/* El conteo es DATO (8 de cabeza, 5 de cara) — nunca una constante. */
.vmp-card-ct[b-uimjsqbn0l] { font-size: .7rem; color: #14224E; font-weight: 700; margin-top: 2px; }

/* ── Paso 3: el detalle ─────────────────────────────────────────────────── */
.vmp-hero[b-uimjsqbn0l] {
    border-radius: 12px;
    overflow: hidden;
    background: linear-gradient(150deg, #1b2f6b, #0d1737);
    margin-bottom: 8px;
}

    .vmp-hero img[b-uimjsqbn0l] { width: 100%; height: 250px; object-fit: contain; display: block; }

/* Las miniaturas de ángulo: tocar cambia el héroe. `auto-fit` = la fila se
   adapta a LO QUE TRAIGA el set (8 de cabeza, 5 de cara, 4 de uñas mañana) sin
   una sola línea distinta — el nº de fotos es dato, no layout. */
.vmp-angles[b-uimjsqbn0l] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(58px, 1fr));
    gap: 6px;
    margin-bottom: 6px;
}

.vmp-angle[b-uimjsqbn0l] {
    height: 64px;
    border-radius: 8px;
    overflow: hidden;
    border: 2px solid transparent;
    background: #0d1737;
    padding: 0;
    cursor: pointer;
}

    .vmp-angle img[b-uimjsqbn0l] { width: 100%; height: 100%; object-fit: cover; object-position: 50% 0; display: block; }   /* R-472 (1164), igual que la tarjeta */
    .vmp-angle.on[b-uimjsqbn0l] { border-color: #E0A458; }
    .vmp-angle:focus-visible[b-uimjsqbn0l] { outline: 3px solid #14224E; outline-offset: 2px; }

.vmp-angle-name[b-uimjsqbn0l] { font-size: .8rem; color: #5b6780; text-align: center; margin-bottom: 8px; }

/* UNA sola acción grande (LV-24 punto 5). Roja, de dedo, ícono + texto. */
.vmp-assign[b-uimjsqbn0l] {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    min-height: 52px;
    border: 0;
    border-radius: 10px;
    background: #C8102E;
    color: #fff;
    font-weight: 800;
    font-size: 1rem;
    cursor: pointer;
}

    .vmp-assign:hover[b-uimjsqbn0l] { background: #a80d26; }
    .vmp-assign:focus-visible[b-uimjsqbn0l] { outline: 3px solid #14224E; outline-offset: 2px; }

.vmp-secondary[b-uimjsqbn0l] {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    width: 100%;
    min-height: 44px;
    margin-top: 8px;
    border: 1.5px solid #d7dce6;
    border-radius: 10px;
    background: #fff;
    color: #5b6780;
    font-weight: 700;
    font-size: .85rem;
    cursor: pointer;
}

    .vmp-secondary:focus-visible[b-uimjsqbn0l] { outline: 3px solid #14224E; outline-offset: 2px; }
